/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.math3.fitting.leastsquares;

import org.apache.commons.math3.exception.MathIllegalStateException;
import org.apache.commons.math3.exception.util.LocalizedFormats;
import org.apache.commons.math3.analysis.MultivariateMatrixFunction;
import org.apache.commons.math3.analysis.MultivariateVectorFunction;
import org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem.Evaluation;
import org.apache.commons.math3.linear.Array2DRowRealMatrix;
import org.apache.commons.math3.linear.ArrayRealVector;
import org.apache.commons.math3.linear.DiagonalMatrix;
import org.apache.commons.math3.linear.EigenDecomposition;
import org.apache.commons.math3.linear.RealMatrix;
import org.apache.commons.math3.linear.RealVector;
import org.apache.commons.math3.optim.AbstractOptimizationProblem;
import org.apache.commons.math3.optim.ConvergenceChecker;
import org.apache.commons.math3.optim.PointVectorValuePair;
import org.apache.commons.math3.util.FastMath;
import org.apache.commons.math3.util.Incrementor;
import org.apache.commons.math3.util.Pair;

A Factory for creating LeastSquaresProblems.
Since:3.3
/** * A Factory for creating {@link LeastSquaresProblem}s. * * @since 3.3 */
public class LeastSquaresFactory {
Prevent instantiation.
/** Prevent instantiation. */
private LeastSquaresFactory() {}
Create a LeastSquaresProblem from the given elements. There will be no weights applied (unit weights).
Params:
  • model – the model function. Produces the computed values.
  • observed – the observed (target) values
  • start – the initial guess.
  • weight – the weight matrix
  • checker – convergence checker
  • maxEvaluations – the maximum number of times to evaluate the model
  • maxIterations – the maximum number to times to iterate in the algorithm
  • lazyEvaluation – Whether the call to LeastSquaresProblem.evaluate(RealVector) will defer the evaluation until access to the value is requested.
  • paramValidator – Model parameters validator.
Returns:the specified General Least Squares problem.
Since:3.4
/** * Create a {@link org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem} * from the given elements. There will be no weights applied (unit weights). * * @param model the model function. Produces the computed values. * @param observed the observed (target) values * @param start the initial guess. * @param weight the weight matrix * @param checker convergence checker * @param maxEvaluations the maximum number of times to evaluate the model * @param maxIterations the maximum number to times to iterate in the algorithm * @param lazyEvaluation Whether the call to {@link Evaluation#evaluate(RealVector)} * will defer the evaluation until access to the value is requested. * @param paramValidator Model parameters validator. * @return the specified General Least Squares problem. * * @since 3.4 */
public static LeastSquaresProblem create(final MultivariateJacobianFunction model, final RealVector observed, final RealVector start, final RealMatrix weight, final ConvergenceChecker<Evaluation> checker, final int maxEvaluations, final int maxIterations, final boolean lazyEvaluation, final ParameterValidator paramValidator) { final LeastSquaresProblem p = new LocalLeastSquaresProblem(model, observed, start, checker, maxEvaluations, maxIterations, lazyEvaluation, paramValidator); if (weight != null) { return weightMatrix(p, weight); } else { return p; } }
Create a LeastSquaresProblem from the given elements. There will be no weights applied (unit weights).
Params:
  • model – the model function. Produces the computed values.
  • observed – the observed (target) values
  • start – the initial guess.
  • checker – convergence checker
  • maxEvaluations – the maximum number of times to evaluate the model
  • maxIterations – the maximum number to times to iterate in the algorithm
Returns:the specified General Least Squares problem.
/** * Create a {@link org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem} * from the given elements. There will be no weights applied (unit weights). * * @param model the model function. Produces the computed values. * @param observed the observed (target) values * @param start the initial guess. * @param checker convergence checker * @param maxEvaluations the maximum number of times to evaluate the model * @param maxIterations the maximum number to times to iterate in the algorithm * @return the specified General Least Squares problem. */
public static LeastSquaresProblem create(final MultivariateJacobianFunction model, final RealVector observed, final RealVector start, final ConvergenceChecker<Evaluation> checker, final int maxEvaluations, final int maxIterations) { return create(model, observed, start, null, checker, maxEvaluations, maxIterations, false, null); }
Create a LeastSquaresProblem from the given elements.
Params:
  • model – the model function. Produces the computed values.
  • observed – the observed (target) values
  • start – the initial guess.
  • weight – the weight matrix
  • checker – convergence checker
  • maxEvaluations – the maximum number of times to evaluate the model
  • maxIterations – the maximum number to times to iterate in the algorithm
Returns:the specified General Least Squares problem.
/** * Create a {@link org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem} * from the given elements. * * @param model the model function. Produces the computed values. * @param observed the observed (target) values * @param start the initial guess. * @param weight the weight matrix * @param checker convergence checker * @param maxEvaluations the maximum number of times to evaluate the model * @param maxIterations the maximum number to times to iterate in the algorithm * @return the specified General Least Squares problem. */
public static LeastSquaresProblem create(final MultivariateJacobianFunction model, final RealVector observed, final RealVector start, final RealMatrix weight, final ConvergenceChecker<Evaluation> checker, final int maxEvaluations, final int maxIterations) { return weightMatrix(create(model, observed, start, checker, maxEvaluations, maxIterations), weight); }
Params:
  • model – the model function. Produces the computed values.
  • jacobian – the jacobian of the model with respect to the parameters
  • observed – the observed (target) values
  • start – the initial guess.
  • weight – the weight matrix
  • checker – convergence checker
  • maxEvaluations – the maximum number of times to evaluate the model
  • maxIterations – the maximum number to times to iterate in the algorithm
Returns:the specified General Least Squares problem.
/** * Create a {@link org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem} * from the given elements. * <p> * This factory method is provided for continuity with previous interfaces. Newer * applications should use {@link #create(MultivariateJacobianFunction, RealVector, * RealVector, ConvergenceChecker, int, int)}, or {@link #create(MultivariateJacobianFunction, * RealVector, RealVector, RealMatrix, ConvergenceChecker, int, int)}. * * @param model the model function. Produces the computed values. * @param jacobian the jacobian of the model with respect to the parameters * @param observed the observed (target) values * @param start the initial guess. * @param weight the weight matrix * @param checker convergence checker * @param maxEvaluations the maximum number of times to evaluate the model * @param maxIterations the maximum number to times to iterate in the algorithm * @return the specified General Least Squares problem. */
public static LeastSquaresProblem create(final MultivariateVectorFunction model, final MultivariateMatrixFunction jacobian, final double[] observed, final double[] start, final RealMatrix weight, final ConvergenceChecker<Evaluation> checker, final int maxEvaluations, final int maxIterations) { return create(model(model, jacobian), new ArrayRealVector(observed, false), new ArrayRealVector(start, false), weight, checker, maxEvaluations, maxIterations); }
Apply a dense weight matrix to the LeastSquaresProblem.
Params:
  • problem – the unweighted problem
  • weights – the matrix of weights
Returns:a new LeastSquaresProblem with the weights applied. The original problem is not modified.
/** * Apply a dense weight matrix to the {@link LeastSquaresProblem}. * * @param problem the unweighted problem * @param weights the matrix of weights * @return a new {@link LeastSquaresProblem} with the weights applied. The original * {@code problem} is not modified. */
public static LeastSquaresProblem weightMatrix(final LeastSquaresProblem problem, final RealMatrix weights) { final RealMatrix weightSquareRoot = squareRoot(weights); return new LeastSquaresAdapter(problem) {
{@inheritDoc}
/** {@inheritDoc} */
@Override public Evaluation evaluate(final RealVector point) { return new DenseWeightedEvaluation(super.evaluate(point), weightSquareRoot); } }; }
Apply a diagonal weight matrix to the LeastSquaresProblem.
Params:
  • problem – the unweighted problem
  • weights – the diagonal of the weight matrix
Returns:a new LeastSquaresProblem with the weights applied. The original problem is not modified.
/** * Apply a diagonal weight matrix to the {@link LeastSquaresProblem}. * * @param problem the unweighted problem * @param weights the diagonal of the weight matrix * @return a new {@link LeastSquaresProblem} with the weights applied. The original * {@code problem} is not modified. */
public static LeastSquaresProblem weightDiagonal(final LeastSquaresProblem problem, final RealVector weights) { // TODO more efficient implementation return weightMatrix(problem, new DiagonalMatrix(weights.toArray())); }
Count the evaluations of a particular problem. The counter will be incremented every time LeastSquaresProblem.evaluate(RealVector) is called on the returned problem.
Params:
  • problem – the problem to track.
  • counter – the counter to increment.
Returns:a least squares problem that tracks evaluations
/** * Count the evaluations of a particular problem. The {@code counter} will be * incremented every time {@link LeastSquaresProblem#evaluate(RealVector)} is called on * the <em>returned</em> problem. * * @param problem the problem to track. * @param counter the counter to increment. * @return a least squares problem that tracks evaluations */
public static LeastSquaresProblem countEvaluations(final LeastSquaresProblem problem, final Incrementor counter) { return new LeastSquaresAdapter(problem) {
{@inheritDoc}
/** {@inheritDoc} */
@Override public Evaluation evaluate(final RealVector point) { counter.incrementCount(); return super.evaluate(point); } // Delegate the rest. }; }
View a convergence checker specified for a PointVectorValuePair as one specified for an Evaluation.
Params:
  • checker – the convergence checker to adapt.
Returns:a convergence checker that delegates to checker.
/** * View a convergence checker specified for a {@link PointVectorValuePair} as one * specified for an {@link Evaluation}. * * @param checker the convergence checker to adapt. * @return a convergence checker that delegates to {@code checker}. */
public static ConvergenceChecker<Evaluation> evaluationChecker(final ConvergenceChecker<PointVectorValuePair> checker) { return new ConvergenceChecker<Evaluation>() {
{@inheritDoc}
/** {@inheritDoc} */
public boolean converged(final int iteration, final Evaluation previous, final Evaluation current) { return checker.converged( iteration, new PointVectorValuePair( previous.getPoint().toArray(), previous.getResiduals().toArray(), false), new PointVectorValuePair( current.getPoint().toArray(), current.getResiduals().toArray(), false) ); } }; }
Computes the square-root of the weight matrix.
Params:
  • m – Symmetric, positive-definite (weight) matrix.
Returns:the square-root of the weight matrix.
/** * Computes the square-root of the weight matrix. * * @param m Symmetric, positive-definite (weight) matrix. * @return the square-root of the weight matrix. */
private static RealMatrix squareRoot(final RealMatrix m) { if (m instanceof DiagonalMatrix) { final int dim = m.getRowDimension(); final RealMatrix sqrtM = new DiagonalMatrix(dim); for (int i = 0; i < dim; i++) { sqrtM.setEntry(i, i, FastMath.sqrt(m.getEntry(i, i))); } return sqrtM; } else { final EigenDecomposition dec = new EigenDecomposition(m); return dec.getSquareRoot(); } }
Params:
  • value – the vector value function
  • jacobian – the Jacobian function
Returns:a function that computes both at the same time
/** * Combine a {@link MultivariateVectorFunction} with a {@link * MultivariateMatrixFunction} to produce a {@link MultivariateJacobianFunction}. * * @param value the vector value function * @param jacobian the Jacobian function * @return a function that computes both at the same time */
public static MultivariateJacobianFunction model(final MultivariateVectorFunction value, final MultivariateMatrixFunction jacobian) { return new LocalValueAndJacobianFunction(value, jacobian); }
Params:
  • value – the vector value function
  • jacobian – the Jacobian function
Returns:a function that computes both at the same time
/** * Combine a {@link MultivariateVectorFunction} with a {@link * MultivariateMatrixFunction} to produce a {@link MultivariateJacobianFunction}. * * @param value the vector value function * @param jacobian the Jacobian function * @return a function that computes both at the same time */
private static class LocalValueAndJacobianFunction implements ValueAndJacobianFunction {
Model.
/** Model. */
private final MultivariateVectorFunction value;
Model's Jacobian.
/** Model's Jacobian. */
private final MultivariateMatrixFunction jacobian;
Params:
  • value – Model function.
  • jacobian – Model's Jacobian function.
/** * @param value Model function. * @param jacobian Model's Jacobian function. */
LocalValueAndJacobianFunction(final MultivariateVectorFunction value, final MultivariateMatrixFunction jacobian) { this.value = value; this.jacobian = jacobian; }
{@inheritDoc}
/** {@inheritDoc} */
public Pair<RealVector, RealMatrix> value(final RealVector point) { //TODO get array from RealVector without copying? final double[] p = point.toArray(); // Evaluate. return new Pair<RealVector, RealMatrix>(computeValue(p), computeJacobian(p)); }
{@inheritDoc}
/** {@inheritDoc} */
public RealVector computeValue(final double[] params) { return new ArrayRealVector(value.value(params), false); }
{@inheritDoc}
/** {@inheritDoc} */
public RealMatrix computeJacobian(final double[] params) { return new Array2DRowRealMatrix(jacobian.value(params), false); } }
A private, "field" immutable (not "real" immutable) implementation of LeastSquaresProblem.
Since:3.3
/** * A private, "field" immutable (not "real" immutable) implementation of {@link * LeastSquaresProblem}. * @since 3.3 */
private static class LocalLeastSquaresProblem extends AbstractOptimizationProblem<Evaluation> implements LeastSquaresProblem {
Target values for the model function at optimum.
/** Target values for the model function at optimum. */
private final RealVector target;
Model function.
/** Model function. */
private final MultivariateJacobianFunction model;
Initial guess.
/** Initial guess. */
private final RealVector start;
Whether to use lazy evaluation.
/** Whether to use lazy evaluation. */
private final boolean lazyEvaluation;
Model parameters validator.
/** Model parameters validator. */
private final ParameterValidator paramValidator;
Create a LeastSquaresProblem from the given data.
Params:
  • model – the model function
  • target – the observed data
  • start – the initial guess
  • checker – the convergence checker
  • maxEvaluations – the allowed evaluations
  • maxIterations – the allowed iterations
  • lazyEvaluation – Whether the call to LeastSquaresProblem.evaluate(RealVector) will defer the evaluation until access to the value is requested.
  • paramValidator – Model parameters validator.
/** * Create a {@link LeastSquaresProblem} from the given data. * * @param model the model function * @param target the observed data * @param start the initial guess * @param checker the convergence checker * @param maxEvaluations the allowed evaluations * @param maxIterations the allowed iterations * @param lazyEvaluation Whether the call to {@link Evaluation#evaluate(RealVector)} * will defer the evaluation until access to the value is requested. * @param paramValidator Model parameters validator. */
LocalLeastSquaresProblem(final MultivariateJacobianFunction model, final RealVector target, final RealVector start, final ConvergenceChecker<Evaluation> checker, final int maxEvaluations, final int maxIterations, final boolean lazyEvaluation, final ParameterValidator paramValidator) { super(maxEvaluations, maxIterations, checker); this.target = target; this.model = model; this.start = start; this.lazyEvaluation = lazyEvaluation; this.paramValidator = paramValidator; if (lazyEvaluation && !(model instanceof ValueAndJacobianFunction)) { // Lazy evaluation requires that value and Jacobian // can be computed separately. throw new MathIllegalStateException(LocalizedFormats.INVALID_IMPLEMENTATION, model.getClass().getName()); } }
{@inheritDoc}
/** {@inheritDoc} */
public int getObservationSize() { return target.getDimension(); }
{@inheritDoc}
/** {@inheritDoc} */
public int getParameterSize() { return start.getDimension(); }
{@inheritDoc}
/** {@inheritDoc} */
public RealVector getStart() { return start == null ? null : start.copy(); }
{@inheritDoc}
/** {@inheritDoc} */
public Evaluation evaluate(final RealVector point) { // Copy so optimizer can change point without changing our instance. final RealVector p = paramValidator == null ? point.copy() : paramValidator.validate(point.copy()); if (lazyEvaluation) { return new LazyUnweightedEvaluation((ValueAndJacobianFunction) model, target, p); } else { // Evaluate value and jacobian in one function call. final Pair<RealVector, RealMatrix> value = model.value(p); return new UnweightedEvaluation(value.getFirst(), value.getSecond(), target, p); } }
Container with the model evaluation at a particular point.
/** * Container with the model evaluation at a particular point. */
private static class UnweightedEvaluation extends AbstractEvaluation {
Point of evaluation.
/** Point of evaluation. */
private final RealVector point;
Derivative at point.
/** Derivative at point. */
private final RealMatrix jacobian;
Computed residuals.
/** Computed residuals. */
private final RealVector residuals;
Create an Evaluation with no weights.
Params:
  • values – the computed function values
  • jacobian – the computed function Jacobian
  • target – the observed values
  • point – the abscissa
/** * Create an {@link Evaluation} with no weights. * * @param values the computed function values * @param jacobian the computed function Jacobian * @param target the observed values * @param point the abscissa */
private UnweightedEvaluation(final RealVector values, final RealMatrix jacobian, final RealVector target, final RealVector point) { super(target.getDimension()); this.jacobian = jacobian; this.point = point; this.residuals = target.subtract(values); }
{@inheritDoc}
/** {@inheritDoc} */
public RealMatrix getJacobian() { return jacobian; }
{@inheritDoc}
/** {@inheritDoc} */
public RealVector getPoint() { return point; }
{@inheritDoc}
/** {@inheritDoc} */
public RealVector getResiduals() { return residuals; } }
Container with the model lazy evaluation at a particular point.
/** * Container with the model <em>lazy</em> evaluation at a particular point. */
private static class LazyUnweightedEvaluation extends AbstractEvaluation {
Point of evaluation.
/** Point of evaluation. */
private final RealVector point;
Model and Jacobian functions.
/** Model and Jacobian functions. */
private final ValueAndJacobianFunction model;
Target values for the model function at optimum.
/** Target values for the model function at optimum. */
private final RealVector target;
Create an Evaluation with no weights.
Params:
  • model – the model function
  • target – the observed values
  • point – the abscissa
/** * Create an {@link Evaluation} with no weights. * * @param model the model function * @param target the observed values * @param point the abscissa */
private LazyUnweightedEvaluation(final ValueAndJacobianFunction model, final RealVector target, final RealVector point) { super(target.getDimension()); // Safe to cast as long as we control usage of this class. this.model = model; this.point = point; this.target = target; }
{@inheritDoc}
/** {@inheritDoc} */
public RealMatrix getJacobian() { return model.computeJacobian(point.toArray()); }
{@inheritDoc}
/** {@inheritDoc} */
public RealVector getPoint() { return point; }
{@inheritDoc}
/** {@inheritDoc} */
public RealVector getResiduals() { return target.subtract(model.computeValue(point.toArray())); } } } }