• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 package org.apache.commons.math.estimation;
19 
20 import java.util.Arrays;
21 
22 import org.apache.commons.math.exception.util.LocalizedFormats;
23 import org.apache.commons.math.linear.InvalidMatrixException;
24 import org.apache.commons.math.linear.LUDecompositionImpl;
25 import org.apache.commons.math.linear.MatrixUtils;
26 import org.apache.commons.math.linear.RealMatrix;
27 import org.apache.commons.math.util.FastMath;
28 
29 /**
30  * Base class for implementing estimators.
31  * <p>This base class handles the boilerplates methods associated to thresholds
32  * settings, jacobian and error estimation.</p>
33  * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
34  * @since 1.2
35  * @deprecated as of 2.0, everything in package org.apache.commons.math.estimation has
36  * been deprecated and replaced by package org.apache.commons.math.optimization.general
37  *
38  */
39 @Deprecated
40 public abstract class AbstractEstimator implements Estimator {
41 
42     /** Default maximal number of cost evaluations allowed. */
43     public static final int DEFAULT_MAX_COST_EVALUATIONS = 100;
44 
45     /** Array of measurements. */
46     protected WeightedMeasurement[] measurements;
47 
48     /** Array of parameters. */
49     protected EstimatedParameter[] parameters;
50 
51     /**
52      * Jacobian matrix.
53      * <p>This matrix is in canonical form just after the calls to
54      * {@link #updateJacobian()}, but may be modified by the solver
55      * in the derived class (the {@link LevenbergMarquardtEstimator
56      * Levenberg-Marquardt estimator} does this).</p>
57      */
58     protected double[] jacobian;
59 
60     /** Number of columns of the jacobian matrix. */
61     protected int cols;
62 
63     /** Number of rows of the jacobian matrix. */
64     protected int rows;
65 
66     /** Residuals array.
67      * <p>This array is in canonical form just after the calls to
68      * {@link #updateJacobian()}, but may be modified by the solver
69      * in the derived class (the {@link LevenbergMarquardtEstimator
70      * Levenberg-Marquardt estimator} does this).</p>
71      */
72     protected double[] residuals;
73 
74     /** Cost value (square root of the sum of the residuals). */
75     protected double cost;
76 
77     /** Maximal allowed number of cost evaluations. */
78     private int maxCostEval;
79 
80     /** Number of cost evaluations. */
81     private int costEvaluations;
82 
83     /** Number of jacobian evaluations. */
84     private int jacobianEvaluations;
85 
86     /**
87      * Build an abstract estimator for least squares problems.
88      * <p>The maximal number of cost evaluations allowed is set
89      * to its default value {@link #DEFAULT_MAX_COST_EVALUATIONS}.</p>
90      */
AbstractEstimator()91     protected AbstractEstimator() {
92         setMaxCostEval(DEFAULT_MAX_COST_EVALUATIONS);
93     }
94 
95     /**
96      * Set the maximal number of cost evaluations allowed.
97      *
98      * @param maxCostEval maximal number of cost evaluations allowed
99      * @see #estimate
100      */
setMaxCostEval(int maxCostEval)101     public final void setMaxCostEval(int maxCostEval) {
102         this.maxCostEval = maxCostEval;
103     }
104 
105     /**
106      * Get the number of cost evaluations.
107      *
108      * @return number of cost evaluations
109      * */
getCostEvaluations()110     public final int getCostEvaluations() {
111         return costEvaluations;
112     }
113 
114     /**
115      * Get the number of jacobian evaluations.
116      *
117      * @return number of jacobian evaluations
118      * */
getJacobianEvaluations()119     public final int getJacobianEvaluations() {
120         return jacobianEvaluations;
121     }
122 
123     /**
124      * Update the jacobian matrix.
125      */
updateJacobian()126     protected void updateJacobian() {
127         incrementJacobianEvaluationsCounter();
128         Arrays.fill(jacobian, 0);
129         int index = 0;
130         for (int i = 0; i < rows; i++) {
131             WeightedMeasurement wm = measurements[i];
132             double factor = -FastMath.sqrt(wm.getWeight());
133             for (int j = 0; j < cols; ++j) {
134                 jacobian[index++] = factor * wm.getPartial(parameters[j]);
135             }
136         }
137     }
138 
139     /**
140      * Increment the jacobian evaluations counter.
141      */
incrementJacobianEvaluationsCounter()142     protected final void incrementJacobianEvaluationsCounter() {
143       ++jacobianEvaluations;
144     }
145 
146     /**
147      * Update the residuals array and cost function value.
148      * @exception EstimationException if the number of cost evaluations
149      * exceeds the maximum allowed
150      */
updateResidualsAndCost()151     protected void updateResidualsAndCost()
152     throws EstimationException {
153 
154         if (++costEvaluations > maxCostEval) {
155             throw new EstimationException(LocalizedFormats.MAX_EVALUATIONS_EXCEEDED,
156                                           maxCostEval);
157         }
158 
159         cost = 0;
160         int index = 0;
161         for (int i = 0; i < rows; i++, index += cols) {
162             WeightedMeasurement wm = measurements[i];
163             double residual = wm.getResidual();
164             residuals[i] = FastMath.sqrt(wm.getWeight()) * residual;
165             cost += wm.getWeight() * residual * residual;
166         }
167         cost = FastMath.sqrt(cost);
168 
169     }
170 
171     /**
172      * Get the Root Mean Square value.
173      * Get the Root Mean Square value, i.e. the root of the arithmetic
174      * mean of the square of all weighted residuals. This is related to the
175      * criterion that is minimized by the estimator as follows: if
176      * <em>c</em> if the criterion, and <em>n</em> is the number of
177      * measurements, then the RMS is <em>sqrt (c/n)</em>.
178      *
179      * @param problem estimation problem
180      * @return RMS value
181      */
getRMS(EstimationProblem problem)182     public double getRMS(EstimationProblem problem) {
183         WeightedMeasurement[] wm = problem.getMeasurements();
184         double criterion = 0;
185         for (int i = 0; i < wm.length; ++i) {
186             double residual = wm[i].getResidual();
187             criterion += wm[i].getWeight() * residual * residual;
188         }
189         return FastMath.sqrt(criterion / wm.length);
190     }
191 
192     /**
193      * Get the Chi-Square value.
194      * @param problem estimation problem
195      * @return chi-square value
196      */
getChiSquare(EstimationProblem problem)197     public double getChiSquare(EstimationProblem problem) {
198         WeightedMeasurement[] wm = problem.getMeasurements();
199         double chiSquare = 0;
200         for (int i = 0; i < wm.length; ++i) {
201             double residual = wm[i].getResidual();
202             chiSquare += residual * residual / wm[i].getWeight();
203         }
204         return chiSquare;
205     }
206 
207     /**
208      * Get the covariance matrix of unbound estimated parameters.
209      * @param problem estimation problem
210      * @return covariance matrix
211      * @exception EstimationException if the covariance matrix
212      * cannot be computed (singular problem)
213      */
getCovariances(EstimationProblem problem)214     public double[][] getCovariances(EstimationProblem problem)
215       throws EstimationException {
216 
217         // set up the jacobian
218         updateJacobian();
219 
220         // compute transpose(J).J, avoiding building big intermediate matrices
221         final int n = problem.getMeasurements().length;
222         final int m = problem.getUnboundParameters().length;
223         final int max  = m * n;
224         double[][] jTj = new double[m][m];
225         for (int i = 0; i < m; ++i) {
226             for (int j = i; j < m; ++j) {
227                 double sum = 0;
228                 for (int k = 0; k < max; k += m) {
229                     sum += jacobian[k + i] * jacobian[k + j];
230                 }
231                 jTj[i][j] = sum;
232                 jTj[j][i] = sum;
233             }
234         }
235 
236         try {
237             // compute the covariances matrix
238             RealMatrix inverse =
239                 new LUDecompositionImpl(MatrixUtils.createRealMatrix(jTj)).getSolver().getInverse();
240             return inverse.getData();
241         } catch (InvalidMatrixException ime) {
242             throw new EstimationException(LocalizedFormats.UNABLE_TO_COMPUTE_COVARIANCE_SINGULAR_PROBLEM);
243         }
244 
245     }
246 
247     /**
248      * Guess the errors in unbound estimated parameters.
249      * <p>Guessing is covariance-based, it only gives rough order of magnitude.</p>
250      * @param problem estimation problem
251      * @return errors in estimated parameters
252      * @exception EstimationException if the covariances matrix cannot be computed
253      * or the number of degrees of freedom is not positive (number of measurements
254      * lesser or equal to number of parameters)
255      */
guessParametersErrors(EstimationProblem problem)256     public double[] guessParametersErrors(EstimationProblem problem)
257       throws EstimationException {
258         int m = problem.getMeasurements().length;
259         int p = problem.getUnboundParameters().length;
260         if (m <= p) {
261             throw new EstimationException(
262                     LocalizedFormats.NO_DEGREES_OF_FREEDOM,
263                     m, p);
264         }
265         double[] errors = new double[problem.getUnboundParameters().length];
266         final double c = FastMath.sqrt(getChiSquare(problem) / (m - p));
267         double[][] covar = getCovariances(problem);
268         for (int i = 0; i < errors.length; ++i) {
269             errors[i] = FastMath.sqrt(covar[i][i]) * c;
270         }
271         return errors;
272     }
273 
274     /**
275      * Initialization of the common parts of the estimation.
276      * <p>This method <em>must</em> be called at the start
277      * of the {@link #estimate(EstimationProblem) estimate}
278      * method.</p>
279      * @param problem estimation problem to solve
280      */
initializeEstimate(EstimationProblem problem)281     protected void initializeEstimate(EstimationProblem problem) {
282 
283         // reset counters
284         costEvaluations     = 0;
285         jacobianEvaluations = 0;
286 
287         // retrieve the equations and the parameters
288         measurements = problem.getMeasurements();
289         parameters   = problem.getUnboundParameters();
290 
291         // arrays shared with the other private methods
292         rows      = measurements.length;
293         cols      = parameters.length;
294         jacobian  = new double[rows * cols];
295         residuals = new double[rows];
296 
297         cost = Double.POSITIVE_INFINITY;
298 
299     }
300 
301     /**
302      * Solve an estimation problem.
303      *
304      * <p>The method should set the parameters of the problem to several
305      * trial values until it reaches convergence. If this method returns
306      * normally (i.e. without throwing an exception), then the best
307      * estimate of the parameters can be retrieved from the problem
308      * itself, through the {@link EstimationProblem#getAllParameters
309      * EstimationProblem.getAllParameters} method.</p>
310      *
311      * @param problem estimation problem to solve
312      * @exception EstimationException if the problem cannot be solved
313      *
314      */
estimate(EstimationProblem problem)315     public abstract void estimate(EstimationProblem problem)
316     throws EstimationException;
317 
318 }
319