1/*2* Licensed to the Apache Software Foundation (ASF) under one or more3* contributor license agreements. See the NOTICE file distributed with4* this work for additional information regarding copyright ownership.5* The ASF licenses this file to You under the Apache License, Version 2.06* (the "License"); you may not use this file except in compliance with7* the License. You may obtain a copy of the License at8*9* http://www.apache.org/licenses/LICENSE-2.010*11* Unless required by applicable law or agreed to in writing, software12* 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 and15* limitations under the License.16*/17 18packageorg.apache.commons.math3.optim.nonlinear.scalar; 19 20importorg.apache.commons.math3.analysis.MultivariateFunction; 21importorg.apache.commons.math3.analysis.MultivariateVectorFunction; 22importorg.apache.commons.math3.exception.DimensionMismatchException; 23importorg.apache.commons.math3.linear.RealMatrix; 24 25/**26* This class converts27* {@link MultivariateVectorFunction vectorial objective functions} to28* {@link MultivariateFunction scalar objective functions}29* when the goal is to minimize them.30* <br/>31* This class is mostly used when the vectorial objective function represents32* a theoretical result computed from a point set applied to a model and33* the models point must be adjusted to fit the theoretical result to some34* reference observations. The observations may be obtained for example from35* physical measurements whether the model is built from theoretical36* considerations.37* <br/>38* This class computes a possibly weighted squared sum of the residuals, which is39* a scalar value. The residuals are the difference between the theoretical model40* (i.e. the output of the vectorial objective function) and the observations. The41* class implements the {@link MultivariateFunction} interface and can therefore be42* minimized by any optimizer supporting scalar objectives functions.This is one way43* to perform a least square estimation. There are other ways to do this without using44* this converter, as some optimization algorithms directly support vectorial objective45* functions.46* <br/>47* This class support combination of residuals with or without weights and correlations.48*49* @see MultivariateFunction50* @see MultivariateVectorFunction51* @version $Id: LeastSquaresConverter.java 1435539 2013-01-19 13:27:24Z tn $52* @since 2.053*/54 55publicclassLeastSquaresConverterimplementsMultivariateFunction { 56/** Underlying vectorial function. */57privatefinalMultivariateVectorFunction function; 58/** Observations to be compared to objective function to compute residuals. */59privatefinaldouble[] observations; 60/** Optional weights for the residuals. */61privatefinaldouble[] weights; 62/** Optional scaling matrix (weight and correlations) for the residuals. */63privatefinalRealMatrix scale; 64 65/**66* Builds a simple converter for uncorrelated residuals with identical67* weights.68*69* @param function vectorial residuals function to wrap70* @param observations observations to be compared to objective function to compute residuals71*/72publicLeastSquaresConverter(finalMultivariateVectorFunction function, 73finaldouble[] observations) { 74this.function = function; 75this.observations = observations.clone(); 76this.weights =null; 77this.scale =null; 78 } 79 80/**81* Builds a simple converter for uncorrelated residuals with the82* specified weights.83* <p>84* The scalar objective function value is computed as:85* <pre>86* objective = ∑weight<sub>i</sub>(observation<sub>i</sub>-objective<sub>i</sub>)<sup>2</sup>87* </pre>88* </p>89* <p>90* Weights can be used for example to combine residuals with different standard91* deviations. As an example, consider a residuals array in which even elements92* are angular measurements in degrees with a 0.01° standard deviation and93* odd elements are distance measurements in meters with a 15m standard deviation.94* In this case, the weights array should be initialized with value95* 1.0/(0.01<sup>2</sup>) in the even elements and 1.0/(15.0<sup>2</sup>) in the96* odd elements (i.e. reciprocals of variances).97* </p>98* <p>99* The array computed by the objective function, the observations array and the100* weights array must have consistent sizes or a {@link DimensionMismatchException}101* will be triggered while computing the scalar objective.102* </p>103*104* @param function vectorial residuals function to wrap105* @param observations observations to be compared to objective function to compute residuals106* @param weights weights to apply to the residuals107* @throws DimensionMismatchException if the observations vector and the weights108* vector dimensions do not match (objective function dimension is checked only when109* the {@link #value(double[])} method is called)110*/111publicLeastSquaresConverter(finalMultivariateVectorFunction function, 112finaldouble[] observations, 113finaldouble[] weights) { 114if(observations.length != weights.length) { 115thrownewDimensionMismatchException(observations.length, weights.length); 116 } 117this.function = function; 118this.observations = observations.clone(); 119this.weights = weights.clone(); 120this.scale =null; 121 } 122 123/**124* Builds a simple converter for correlated residuals with the125* specified weights.126* <p>127* The scalar objective function value is computed as:128* <pre>129* objective = y<sup>T</sup>y with y = scale×(observation-objective)130* </pre>131* </p>132* <p>133* The array computed by the objective function, the observations array and the134* the scaling matrix must have consistent sizes or a {@link DimensionMismatchException}135* will be triggered while computing the scalar objective.136* </p>137*138* @param function vectorial residuals function to wrap139* @param observations observations to be compared to objective function to compute residuals140* @param scale scaling matrix141* @throws DimensionMismatchException if the observations vector and the scale142* matrix dimensions do not match (objective function dimension is checked only when143* the {@link #value(double[])} method is called)144*/145publicLeastSquaresConverter(finalMultivariateVectorFunction function, 146finaldouble[] observations, 147finalRealMatrix scale) { 148if(observations.length != scale.getColumnDimension()) { 149thrownewDimensionMismatchException(observations.length, scale.getColumnDimension()); 150 } 151this.function = function; 152this.observations = observations.clone(); 153this.weights =null; 154this.scale = scale.copy(); 155 } 156 157/** {@inheritDoc} */158publicdoublevalue(finaldouble[] point) { 159// compute residuals160finaldouble[] residuals = function.value(point); 161if(residuals.length != observations.length) { 162thrownewDimensionMismatchException(residuals.length, observations.length); 163 } 164for(inti = 0; i < residuals.length; ++i) { 165 residuals[i] -= observations[i]; 166 } 167 168// compute sum of squares169doublesumSquares = 0; 170if(weights !=null) { 171for(inti = 0; i < residuals.length; ++i) { 172finaldoubleri = residuals[i]; 173 sumSquares += weights[i] * ri * ri; 174 } 175 }elseif(scale !=null) { 176for(finaldoubleyi : scale.operate(residuals)) { 177 sumSquares += yi * yi; 178 } 179 }else{ 180for(finaldoubleri : residuals) { 181 sumSquares += ri * ri; 182 } 183 } 184 185returnsumSquares; 186 } 187 }