AbstractLeastSquaresOptimizer.java

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

  18. import org.apache.commons.math3.exception.DimensionMismatchException;
  19. import org.apache.commons.math3.exception.TooManyEvaluationsException;
  20. import org.apache.commons.math3.linear.ArrayRealVector;
  21. import org.apache.commons.math3.linear.RealMatrix;
  22. import org.apache.commons.math3.linear.DiagonalMatrix;
  23. import org.apache.commons.math3.linear.DecompositionSolver;
  24. import org.apache.commons.math3.linear.MatrixUtils;
  25. import org.apache.commons.math3.linear.QRDecomposition;
  26. import org.apache.commons.math3.linear.EigenDecomposition;
  27. import org.apache.commons.math3.optim.OptimizationData;
  28. import org.apache.commons.math3.optim.ConvergenceChecker;
  29. import org.apache.commons.math3.optim.PointVectorValuePair;
  30. import org.apache.commons.math3.optim.nonlinear.vector.Weight;
  31. import org.apache.commons.math3.optim.nonlinear.vector.JacobianMultivariateVectorOptimizer;
  32. import org.apache.commons.math3.util.FastMath;

  33. /**
  34.  * Base class for implementing least-squares optimizers.
  35.  * It provides methods for error estimation.
  36.  *
  37.  * @since 3.1
  38.  * @deprecated All classes and interfaces in this package are deprecated.
  39.  * The optimizers that were provided here were moved to the
  40.  * {@link org.apache.commons.math3.fitting.leastsquares} package
  41.  * (cf. MATH-1008).
  42.  */
  43. @Deprecated
  44. public abstract class AbstractLeastSquaresOptimizer
  45.     extends JacobianMultivariateVectorOptimizer {
  46.     /** Square-root of the weight matrix. */
  47.     private RealMatrix weightMatrixSqrt;
  48.     /** Cost value (square root of the sum of the residuals). */
  49.     private double cost;

  50.     /**
  51.      * @param checker Convergence checker.
  52.      */
  53.     protected AbstractLeastSquaresOptimizer(ConvergenceChecker<PointVectorValuePair> checker) {
  54.         super(checker);
  55.     }

  56.     /**
  57.      * Computes the weighted Jacobian matrix.
  58.      *
  59.      * @param params Model parameters at which to compute the Jacobian.
  60.      * @return the weighted Jacobian: W<sup>1/2</sup> J.
  61.      * @throws DimensionMismatchException if the Jacobian dimension does not
  62.      * match problem dimension.
  63.      */
  64.     protected RealMatrix computeWeightedJacobian(double[] params) {
  65.         return weightMatrixSqrt.multiply(MatrixUtils.createRealMatrix(computeJacobian(params)));
  66.     }

  67.     /**
  68.      * Computes the cost.
  69.      *
  70.      * @param residuals Residuals.
  71.      * @return the cost.
  72.      * @see #computeResiduals(double[])
  73.      */
  74.     protected double computeCost(double[] residuals) {
  75.         final ArrayRealVector r = new ArrayRealVector(residuals);
  76.         return FastMath.sqrt(r.dotProduct(getWeight().operate(r)));
  77.     }

  78.     /**
  79.      * Gets the root-mean-square (RMS) value.
  80.      *
  81.      * The RMS the root of the arithmetic mean of the square of all weighted
  82.      * residuals.
  83.      * This is related to the criterion that is minimized by the optimizer
  84.      * as follows: If <em>c</em> if the criterion, and <em>n</em> is the
  85.      * number of measurements, then the RMS is <em>sqrt (c/n)</em>.
  86.      *
  87.      * @return the RMS value.
  88.      */
  89.     public double getRMS() {
  90.         return FastMath.sqrt(getChiSquare() / getTargetSize());
  91.     }

  92.     /**
  93.      * Get a Chi-Square-like value assuming the N residuals follow N
  94.      * distinct normal distributions centered on 0 and whose variances are
  95.      * the reciprocal of the weights.
  96.      * @return chi-square value
  97.      */
  98.     public double getChiSquare() {
  99.         return cost * cost;
  100.     }

  101.     /**
  102.      * Gets the square-root of the weight matrix.
  103.      *
  104.      * @return the square-root of the weight matrix.
  105.      */
  106.     public RealMatrix getWeightSquareRoot() {
  107.         return weightMatrixSqrt.copy();
  108.     }

  109.     /**
  110.      * Sets the cost.
  111.      *
  112.      * @param cost Cost value.
  113.      */
  114.     protected void setCost(double cost) {
  115.         this.cost = cost;
  116.     }

  117.     /**
  118.      * Get the covariance matrix of the optimized parameters.
  119.      * <br/>
  120.      * Note that this operation involves the inversion of the
  121.      * <code>J<sup>T</sup>J</code> matrix, where {@code J} is the
  122.      * Jacobian matrix.
  123.      * The {@code threshold} parameter is a way for the caller to specify
  124.      * that the result of this computation should be considered meaningless,
  125.      * and thus trigger an exception.
  126.      *
  127.      * @param params Model parameters.
  128.      * @param threshold Singularity threshold.
  129.      * @return the covariance matrix.
  130.      * @throws org.apache.commons.math3.linear.SingularMatrixException
  131.      * if the covariance matrix cannot be computed (singular problem).
  132.      */
  133.     public double[][] computeCovariances(double[] params,
  134.                                          double threshold) {
  135.         // Set up the Jacobian.
  136.         final RealMatrix j = computeWeightedJacobian(params);

  137.         // Compute transpose(J)J.
  138.         final RealMatrix jTj = j.transpose().multiply(j);

  139.         // Compute the covariances matrix.
  140.         final DecompositionSolver solver
  141.             = new QRDecomposition(jTj, threshold).getSolver();
  142.         return solver.getInverse().getData();
  143.     }

  144.     /**
  145.      * Computes an estimate of the standard deviation of the parameters. The
  146.      * returned values are the square root of the diagonal coefficients of the
  147.      * covariance matrix, {@code sd(a[i]) ~= sqrt(C[i][i])}, where {@code a[i]}
  148.      * is the optimized value of the {@code i}-th parameter, and {@code C} is
  149.      * the covariance matrix.
  150.      *
  151.      * @param params Model parameters.
  152.      * @param covarianceSingularityThreshold Singularity threshold (see
  153.      * {@link #computeCovariances(double[],double) computeCovariances}).
  154.      * @return an estimate of the standard deviation of the optimized parameters
  155.      * @throws org.apache.commons.math3.linear.SingularMatrixException
  156.      * if the covariance matrix cannot be computed.
  157.      */
  158.     public double[] computeSigma(double[] params,
  159.                                  double covarianceSingularityThreshold) {
  160.         final int nC = params.length;
  161.         final double[] sig = new double[nC];
  162.         final double[][] cov = computeCovariances(params, covarianceSingularityThreshold);
  163.         for (int i = 0; i < nC; ++i) {
  164.             sig[i] = FastMath.sqrt(cov[i][i]);
  165.         }
  166.         return sig;
  167.     }

  168.     /**
  169.      * {@inheritDoc}
  170.      *
  171.      * @param optData Optimization data. In addition to those documented in
  172.      * {@link JacobianMultivariateVectorOptimizer#parseOptimizationData(OptimizationData[])
  173.      * JacobianMultivariateVectorOptimizer}, this method will register the following data:
  174.      * <ul>
  175.      *  <li>{@link org.apache.commons.math3.optim.nonlinear.vector.Weight}</li>
  176.      * </ul>
  177.      * @return {@inheritDoc}
  178.      * @throws TooManyEvaluationsException if the maximal number of
  179.      * evaluations is exceeded.
  180.      * @throws DimensionMismatchException if the initial guess, target, and weight
  181.      * arguments have inconsistent dimensions.
  182.      */
  183.     @Override
  184.     public PointVectorValuePair optimize(OptimizationData... optData)
  185.         throws TooManyEvaluationsException {
  186.         // Set up base class and perform computation.
  187.         return super.optimize(optData);
  188.     }

  189.     /**
  190.      * Computes the residuals.
  191.      * The residual is the difference between the observed (target)
  192.      * values and the model (objective function) value.
  193.      * There is one residual for each element of the vector-valued
  194.      * function.
  195.      *
  196.      * @param objectiveValue Value of the the objective function. This is
  197.      * the value returned from a call to
  198.      * {@link #computeObjectiveValue(double[]) computeObjectiveValue}
  199.      * (whose array argument contains the model parameters).
  200.      * @return the residuals.
  201.      * @throws DimensionMismatchException if {@code params} has a wrong
  202.      * length.
  203.      */
  204.     protected double[] computeResiduals(double[] objectiveValue) {
  205.         final double[] target = getTarget();
  206.         if (objectiveValue.length != target.length) {
  207.             throw new DimensionMismatchException(target.length,
  208.                                                  objectiveValue.length);
  209.         }

  210.         final double[] residuals = new double[target.length];
  211.         for (int i = 0; i < target.length; i++) {
  212.             residuals[i] = target[i] - objectiveValue[i];
  213.         }

  214.         return residuals;
  215.     }

  216.     /**
  217.      * Scans the list of (required and optional) optimization data that
  218.      * characterize the problem.
  219.      * If the weight matrix is specified, the {@link #weightMatrixSqrt}
  220.      * field is recomputed.
  221.      *
  222.      * @param optData Optimization data. The following data will be looked for:
  223.      * <ul>
  224.      *  <li>{@link Weight}</li>
  225.      * </ul>
  226.      */
  227.     @Override
  228.     protected void parseOptimizationData(OptimizationData... optData) {
  229.         // Allow base class to register its own data.
  230.         super.parseOptimizationData(optData);

  231.         // The existing values (as set by the previous call) are reused if
  232.         // not provided in the argument list.
  233.         for (OptimizationData data : optData) {
  234.             if (data instanceof Weight) {
  235.                 weightMatrixSqrt = squareRoot(((Weight) data).getWeight());
  236.                 // If more data must be parsed, this statement _must_ be
  237.                 // changed to "continue".
  238.                 break;
  239.             }
  240.         }
  241.     }

  242.     /**
  243.      * Computes the square-root of the weight matrix.
  244.      *
  245.      * @param m Symmetric, positive-definite (weight) matrix.
  246.      * @return the square-root of the weight matrix.
  247.      */
  248.     private RealMatrix squareRoot(RealMatrix m) {
  249.         if (m instanceof DiagonalMatrix) {
  250.             final int dim = m.getRowDimension();
  251.             final RealMatrix sqrtM = new DiagonalMatrix(dim);
  252.             for (int i = 0; i < dim; i++) {
  253.                 sqrtM.setEntry(i, i, FastMath.sqrt(m.getEntry(i, i)));
  254.             }
  255.             return sqrtM;
  256.         } else {
  257.             final EigenDecomposition dec = new EigenDecomposition(m);
  258.             return dec.getSquareRoot();
  259.         }
  260.     }
  261. }