All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jdplus.toolkit.desktop.plugin.ui.chart3d.functions.FunctionsSurfaceModel Maven / Gradle / Ivy

/*
 * Copyright 2013 National Bank of Belgium
 *
 * Licensed under the EUPL, Version 1.1 or – as soon they will be approved 
 * by the European Commission - subsequent versions of the EUPL (the "Licence");
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at:
 * 
 * http://ec.europa.eu/idabc/eupl
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the Licence is distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and 
 * limitations under the Licence.
 */
package jdplus.toolkit.desktop.plugin.ui.chart3d.functions;

import jdplus.toolkit.base.api.data.DoubleSeq;
import jdplus.toolkit.desktop.plugin.DemetraBehaviour;
import jdplus.toolkit.desktop.plugin.concurrent.UIExecutors;
import jdplus.toolkit.desktop.plugin.ui.chart3d.AbstractSurfaceModel;
import jdplus.toolkit.desktop.plugin.ui.chart3d.SurfaceVertex;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import javax.swing.SwingWorker;
import jdplus.toolkit.base.core.data.DataBlock;
import jdplus.toolkit.base.core.math.functions.IFunction;
import jdplus.toolkit.base.core.math.functions.IFunctionPoint;
import jdplus.toolkit.base.core.math.functions.IParametersDomain;

/**
 * Surface model used to generate likelihood data from a preprocessing model
 *
 * @author Mats Maggi
 */
public class FunctionsSurfaceModel extends AbstractSurfaceModel {

    private SurfaceVertex[] surfaceVertex;
    private final IFunction function;
    private final IFunctionPoint maxFunction;
    private int p1_index;
    private int p2_index;
    private int steps;
    private SurfaceVertex optimum;
    private float eps;
    private SwingWorker worker;
    public static final String PROGRESS_PROPERTY = "progress changed";

    public FunctionsSurfaceModel(IFunction f, IFunctionPoint maxF, int steps) {
        function = f;
        maxFunction = maxF;
        p1_index = 0;
        p2_index = 1;

        if (steps < ConfigurationToolBar.MIN_STEPS || steps > ConfigurationToolBar.MAX_STEPS) {
            throw new IllegalArgumentException("Number of steps must be between "
                    + ConfigurationToolBar.MIN_STEPS + " and " + ConfigurationToolBar.MAX_STEPS + " !");
        }

        this.steps = steps;
        eps = .2f;

        // Setting plot attributes
        setBoxed(true);
        setDisplayXY(true);
        setExpectDelay(false);
        setAutoScaleZ(true);
        setDisplayZ(true);
        setMesh(false);
        setBoxed(true);
        setDisplayGrids(true);
        setPlotType(PlotType.SURFACE);
        setFirstFunctionOnly(true);
    }

    /**
     * Returns the optimum point of the max likelihood function
     *
     * @return optimum point
     */
    public SurfaceVertex getOptimum() {
        return optimum;
    }

    /**
     * Calculates and initializes the array of data used to display the plot.
     * This method is multithreaded to increase performance. The number of
     * threads used is defined by NbDemetra options
     */
    public void generateData() {
        if (function == null || maxFunction == null) {
            throw new IllegalArgumentException("The given functions can't be null !");
        }

        final DoubleSeq parameters = maxFunction.getParameters();
        final DataBlock p = DataBlock.of(parameters);
        final IParametersDomain d = function.getDomain();

        if (p1_index >= parameters.length() || p2_index >= parameters.length()) {
            throw new IllegalArgumentException("One or more parameters' indexes are out of limits");
        }

        setDataAvailable(false);

        setXMin((float) p.get(p1_index) - eps);
        setXMax((float) p.get(p1_index) + eps);
        setYMin((float) p.get(p2_index) - eps);
        setYMax((float) p.get(p2_index) + eps);
        setCalcDivisions(steps - 1);

        final float stepx = (xMax - xMin) / steps;
        final float stepy = (yMax - yMin) / steps;
        final float xfactor = 20 / (xMax - xMin);
        final float yfactor = 20 / (yMax - yMin);

        final int total = steps * steps;    // Total of values to calculate
        surfaceVertex = new SurfaceVertex[total];
        final float[] fnPts = new float[total];
        for (int i=0; i() {
            @Override
            protected Void doInBackground() throws Exception {
                // Create all tasks calculating the points (x,y,z)
                List> tasks = createTasks();
                if (tasks == null) {
                    return null;
                }

                DemetraBehaviour options = DemetraBehaviour.get();

                ExecutorService executorService = UIExecutors.newFixedThreadPool(options.getBatchPoolSize(), options.getBatchPriority());
                try {
                    executorService.invokeAll(tasks);
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }
                executorService.shutdown();
                return null;
            }

            Callable createFn(final int i, final int j) {
                return () -> {
                    DataBlock p3 = p.deepClone();
                    float xv = xMin + i * stepx;
                    p3.set(p1_index, xv); // Change the value of 1st param (X)
                    float yv = yMin + j * stepy;
                    p3.set(p2_index, yv); // Change the value of 2nd param (Y)
                    float z = Float.NaN;
                    try {
                        if (d.checkBoundaries(p3)) {
                            // Evaluates the value of the z point
                            z = (float) function.evaluate(p3).getValue();
                        }
                    }catch (Exception err) {
                    }
                    if (Float.isInfinite(z)) {
                        z = Float.NaN;
                    }
                    /* Array used is a one dimension array so, the value k represents the position
                    * of the [i][j] value converted into a one dimension index.
                    * If i = 4, j = 5, and steps = 100, then k = 4*100 + 5 = 405
                    */
                    int k = i * steps + j;
                    fnPts[k] = z;
                    return null;
                };
            }

            List> createTasks() {
                List> list = new ArrayList<>();
                for (int i = 0; i < steps; i++) {
                    for (int j = 0; j < steps; j++) {
                        list.add(createFn(i, j));
                    }
                }
                return list;
            }

            @Override
            protected void done() {
                int q = 0;
                while (q < fnPts.length && Float.isNaN(fnPts[q])) {
                    ++q;
                }
                if (q == fnPts.length) {
                    return;
                }
                z1Min = fnPts[q];
                z1Max = fnPts[q];
                for (int i = q + 1; i < fnPts.length; ++i) {
                    float z = fnPts[i];

                    // Calculating ranges (min/max) of the z value
                    if (!Float.isNaN(z)) {
                        if (z > z1Max) {
                            z1Max = z;
                        } else if (z < z1Min) {
                            z1Min = z;
                        }
                    }
                }

                /* Array used is a one dimension array so, the value k represents the position
                 * of the [i][j] value converted into a one dimension index.
                 * If i = 4, j = 5, and steps = 100, then k = 4*100 + 5 = 405
                 */
                for (int i = 0; i < steps; i++) {
                    for (int j = 0; j < steps; j++) {
                        int k = i * steps + j;
                        float z = fnPts[k];
                        final float xv = xMin + i * stepx;
                        final float yv = yMin + j * stepy;
                        surfaceVertex[k] = new SurfaceVertex((xv - xMin) * xfactor - 10, (yv - yMin) * yfactor - 10, z);
                    }
                }
                
                autoScale();
                setDataAvailable(true);
                fireStateChanged();
            }
        };
        worker.execute();
    }

    /**
     * Defines the value of epsilon (range of the function). Default value is
     * 0.2
     *
     * @param eps New value for epsilon
     */
    public void setEps(float eps) {
        this.eps = eps;
        generateData();
    }

    /**
     * Defines the number of steps (points per parameter) to calculate. A steps'
     * value of 100 will generate 100 x 100 values.
     *
     * @param steps Number of steps
     */
    public void setSteps(int steps) {
        if (steps < ConfigurationToolBar.MIN_STEPS || steps > ConfigurationToolBar.MAX_STEPS) {
            throw new IllegalArgumentException("Number of steps must be between "
                    + ConfigurationToolBar.MIN_STEPS + " and " + ConfigurationToolBar.MAX_STEPS + " !");
        }
        this.steps = steps;
        generateData();
    }

    /**
     * Returns the surface vertex of the model. This array contains all the
     * calculated points generated by calling the generateData()
     * method.
     *
     * @return Array of surface vertices
     */
    @Override
    public SurfaceVertex[] getSurfaceVertex() {
        return surfaceVertex;
    }

    /**
     * Sets the index of the X axis parameter.
     *
     * @param p1_index Index of the parameter to use as X
     */
    public void setP1Index(int p1_index) {
        if (p1_index < 0) {
            throw new IllegalArgumentException("The index can't be < 0 !");
        }
        this.p1_index = p1_index;
    }

    /**
     * Sets the index of the Y axis parameter.
     *
     * @param p2_index Index of the parameter to use as Y
     */
    public void setP2Index(int p2_index) {
        if (p2_index < 0) {
            throw new IllegalArgumentException("The index can't be < 0 !");
        }
        this.p2_index = p2_index;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy