Java tutorial
/* * @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; } }