net.femtoparsec.jnlmin.GradientEvaluator.java Source code

Java tutorial

Introduction

Here is the source code for net.femtoparsec.jnlmin.GradientEvaluator.java

Source

/*
 * @copyright Copyright (c) 2012, Bastien Aracil
 *    All rights reserved.
 *    New BSD license. See http://en.wikipedia.org/wiki/Bsd_license
 *
 *    Redistribution and use in source and binary forms, with or without
 *    modification, are permitted provided that the following conditions are met:
 *       * Redistributions of source code must retain the above copyright
 *         notice, this list of conditions and the following disclaimer.
 *       * Redistributions in binary form must reproduce the above copyright
 *         notice, this list of conditions and the following disclaimer in the
 *         documentation and/or other materials provided with the distribution.
 *       * The name of Bastien Aracil may not be used to endorse or promote products
 *         derived from this software without specific prior written permission.
 *
 *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *    DISCLAIMED. IN NO EVENT SHALL BASTIEN ARACIL BE LIABLE FOR ANY
 *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package net.femtoparsec.jnlmin;

import net.femtoparsec.jnlmin.model.Model;
import net.femtoparsec.jnlmin.model.ModelResult;
import org.apache.commons.lang3.Validate;

import java.util.Arrays;

/**
 * @author Bastien Aracil
 * Date: 29/11/12
 */
public class GradientEvaluator {

    private final Model model;

    private final int[] indexes;

    private final double epsilon;

    private final int nbFitted;

    public GradientEvaluator(Model model, int[] indexes, double epsilon) {
        this.model = Validate.notNull(model, "model");
        this.indexes = indexes;
        this.epsilon = epsilon;
        this.nbFitted = indexes.length;
    }

    /**
     * Evaluate the model and compute its gradient. If the model cannot compute its gradient, an approximation is obtained with forward differences.
     * If the model can compute its gradient, this call is equivalent to :
     *
     * <pre>
     * model.evaluate(parameter, true, resultBuffer)
     * return 1
     * </pre>
     * @param parameters the parameters at which the model must be evaluated.
     * @param resultBuffer the result buffer that will contain the value and the gradient of the model at the given parameters.
     * @param computationBuffer a buffer used for computation.
     * @return the number of times the model has been evaluated
     */
    public int evaluateWithGradient(double[] parameters, ModelResult resultBuffer, ModelResult computationBuffer) {
        int nbEvaluations;
        if (model.hasDerivative()) {
            model.evaluate(parameters, true, resultBuffer);
            nbEvaluations = 1;
        } else {
            model.evaluate(parameters, false, resultBuffer);

            final double[] gradient = resultBuffer.getGradient();

            Arrays.fill(gradient, 0);

            double increment, backup;
            double invIncrement;
            int subIndex;

            for (int i = 0; i < nbFitted; i++) {
                subIndex = indexes[i];
                backup = parameters[subIndex];

                increment = this.epsilon * Math.abs(backup);
                if (increment <= 0) {
                    increment = this.epsilon;
                }
                invIncrement = 1. / increment;

                parameters[subIndex] += increment;
                model.evaluate(parameters, false, computationBuffer);
                parameters[subIndex] = backup;
                gradient[subIndex] = (computationBuffer.getValue() - resultBuffer.getValue()) * invIncrement;
            }

            nbEvaluations = 1 + nbFitted;
        }

        return nbEvaluations;
    }

}