1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.math4.legacy.fitting.leastsquares;
18
19 import org.apache.commons.math4.legacy.TestUtils;
20 import org.apache.commons.math4.legacy.analysis.MultivariateMatrixFunction;
21 import org.apache.commons.math4.legacy.analysis.MultivariateVectorFunction;
22 import org.apache.commons.math4.legacy.exception.MathIllegalStateException;
23 import org.apache.commons.math4.legacy.fitting.leastsquares.LeastSquaresProblem.Evaluation;
24 import org.apache.commons.math4.legacy.linear.ArrayRealVector;
25 import org.apache.commons.math4.legacy.linear.DiagonalMatrix;
26 import org.apache.commons.math4.legacy.linear.MatrixUtils;
27 import org.apache.commons.math4.legacy.linear.RealMatrix;
28 import org.apache.commons.math4.legacy.linear.RealVector;
29 import org.apache.commons.math4.legacy.linear.SingularMatrixException;
30 import org.apache.commons.math4.core.jdkmath.JdkMath;
31 import org.apache.commons.math4.legacy.core.Pair;
32 import org.apache.commons.numbers.core.Precision;
33 import org.junit.Assert;
34 import org.junit.Test;
35
36 import java.io.IOException;
37 import java.util.Arrays;
38
39
40
41
42
43
44 public class EvaluationTest {
45
46
47
48
49
50
51
52 public LeastSquaresBuilder builder(StatisticalReferenceDataset dataset) {
53 StatisticalReferenceDataset.LeastSquaresProblem problem
54 = dataset.getLeastSquaresProblem();
55 final double[] start = dataset.getParameters();
56 final double[] observed = dataset.getData()[1];
57 final double[] weights = new double[observed.length];
58 Arrays.fill(weights, 1d);
59
60 return new LeastSquaresBuilder()
61 .model(problem.getModelFunction(), problem.getModelFunctionJacobian())
62 .target(observed)
63 .weight(new DiagonalMatrix(weights))
64 .start(start);
65 }
66
67 @Test
68 public void testComputeResiduals() {
69
70 RealVector point = new ArrayRealVector(2);
71 Evaluation evaluation = new LeastSquaresBuilder()
72 .target(new ArrayRealVector(new double[]{3,-1}))
73 .model(new MultivariateJacobianFunction() {
74 @Override
75 public Pair<RealVector, RealMatrix> value(RealVector point) {
76 return new Pair<>(
77 new ArrayRealVector(new double[]{1, 2}),
78 MatrixUtils.createRealIdentityMatrix(2)
79 );
80 }
81 })
82 .weight(MatrixUtils.createRealIdentityMatrix(2))
83 .build()
84 .evaluate(point);
85
86
87 Assert.assertArrayEquals(
88 evaluation.getResiduals().toArray(),
89 new double[]{2, -3},
90 Precision.EPSILON);
91 }
92
93 @Test
94 public void testComputeCovariance() throws IOException {
95
96 RealVector point = new ArrayRealVector(2);
97 Evaluation evaluation = new LeastSquaresBuilder()
98 .model(new MultivariateJacobianFunction() {
99 @Override
100 public Pair<RealVector, RealMatrix> value(RealVector point) {
101 return new Pair<>(
102 new ArrayRealVector(2),
103 MatrixUtils.createRealDiagonalMatrix(new double[]{1, 1e-2})
104 );
105 }
106 })
107 .weight(MatrixUtils.createRealDiagonalMatrix(new double[]{1, 1}))
108 .target(new ArrayRealVector(2))
109 .build()
110 .evaluate(point);
111
112
113 TestUtils.assertEquals(
114 "covariance",
115 evaluation.getCovariances(JdkMath.nextAfter(1e-4, 0.0)),
116 MatrixUtils.createRealMatrix(new double[][]{{1, 0}, {0, 1e4}}),
117 Precision.EPSILON
118 );
119
120
121 try {
122 evaluation.getCovariances(JdkMath.nextAfter(1e-4, 1.0));
123 Assert.fail("Expected Exception");
124 } catch (SingularMatrixException e) {
125
126 }
127 }
128
129 @Test
130 public void testComputeValueAndJacobian() {
131
132 final RealVector point = new ArrayRealVector(new double[]{1, 2});
133 Evaluation evaluation = new LeastSquaresBuilder()
134 .weight(new DiagonalMatrix(new double[]{16, 4}))
135 .model(new MultivariateJacobianFunction() {
136 @Override
137 public Pair<RealVector, RealMatrix> value(RealVector actualPoint) {
138
139 Assert.assertArrayEquals(
140 point.toArray(), actualPoint.toArray(), Precision.EPSILON);
141
142 return new Pair<>(
143 new ArrayRealVector(new double[]{3, 4}),
144 MatrixUtils.createRealMatrix(new double[][]{{5, 6}, {7, 8}})
145 );
146 }
147 })
148 .target(new double[2])
149 .build()
150 .evaluate(point);
151
152
153 RealVector residuals = evaluation.getResiduals();
154 RealMatrix jacobian = evaluation.getJacobian();
155
156
157 Assert.assertArrayEquals(evaluation.getPoint().toArray(), point.toArray(), 0);
158 Assert.assertArrayEquals(new double[]{-12, -8}, residuals.toArray(), Precision.EPSILON);
159 TestUtils.assertEquals(
160 "jacobian",
161 jacobian,
162 MatrixUtils.createRealMatrix(new double[][]{{20, 24},{14, 16}}),
163 Precision.EPSILON);
164 }
165
166 @Test
167 public void testComputeCost() throws IOException {
168 final StatisticalReferenceDataset dataset
169 = StatisticalReferenceDatasetFactory.createKirby2();
170
171 final LeastSquaresProblem lsp = builder(dataset).build();
172
173 final double expected = dataset.getResidualSumOfSquares();
174 final double cost = lsp.evaluate(lsp.getStart()).getCost();
175 final double actual = cost * cost;
176 Assert.assertEquals(dataset.getName(), expected, actual, 1e-11 * expected);
177 }
178
179 @Test
180 public void testComputeRMS() throws IOException {
181 final StatisticalReferenceDataset dataset
182 = StatisticalReferenceDatasetFactory.createKirby2();
183
184 final LeastSquaresProblem lsp = builder(dataset).build();
185
186 final double expected = JdkMath.sqrt(dataset.getResidualSumOfSquares() /
187 dataset.getNumObservations());
188 final double actual = lsp.evaluate(lsp.getStart()).getRMS();
189 Assert.assertEquals(dataset.getName(), expected, actual, 1e-11 * expected);
190 }
191
192 @Test
193 public void testComputeSigma() throws IOException {
194 final StatisticalReferenceDataset dataset
195 = StatisticalReferenceDatasetFactory.createKirby2();
196
197 final LeastSquaresProblem lsp = builder(dataset).build();
198
199 final double[] expected = dataset.getParametersStandardDeviations();
200
201 final Evaluation evaluation = lsp.evaluate(lsp.getStart());
202 final double cost = evaluation.getCost();
203 final RealVector sig = evaluation.getSigma(1e-14);
204 final int dof = lsp.getObservationSize() - lsp.getParameterSize();
205 for (int i = 0; i < sig.getDimension(); i++) {
206 final double actual = JdkMath.sqrt(cost * cost / dof) * sig.getEntry(i);
207 Assert.assertEquals(dataset.getName() + ", parameter #" + i,
208 expected[i], actual, 1e-6 * expected[i]);
209 }
210 }
211
212 @Test
213 public void testEvaluateCopiesPoint() throws IOException {
214
215 StatisticalReferenceDataset dataset
216 = StatisticalReferenceDatasetFactory.createKirby2();
217 LeastSquaresProblem lsp = builder(dataset).build();
218 RealVector point = new ArrayRealVector(lsp.getParameterSize());
219
220
221 Evaluation evaluation = lsp.evaluate(point);
222
223
224 Assert.assertNotSame(point, evaluation.getPoint());
225 point.setEntry(0, 1);
226 Assert.assertEquals(evaluation.getPoint().getEntry(0), 0, 0);
227 }
228
229 @Test
230 public void testLazyEvaluation() {
231 final RealVector dummy = new ArrayRealVector(new double[] { 0 });
232
233 final LeastSquaresProblem p
234 = LeastSquaresFactory.create(LeastSquaresFactory.model(dummyModel(), dummyJacobian()),
235 dummy, dummy, null, null, 0, 0, true, null);
236
237
238 final Evaluation eval = p.evaluate(dummy);
239
240 try {
241 eval.getResiduals();
242 Assert.fail("Exception expected");
243 } catch (RuntimeException e) {
244
245 Assert.assertEquals("dummyModel", e.getMessage());
246 }
247
248 try {
249 eval.getJacobian();
250 Assert.fail("Exception expected");
251 } catch (RuntimeException e) {
252
253 Assert.assertEquals("dummyJacobian", e.getMessage());
254 }
255 }
256
257
258 @Test
259 public void testLazyEvaluationPrecondition() {
260 final RealVector dummy = new ArrayRealVector(new double[] { 0 });
261
262
263
264 final MultivariateJacobianFunction m1 = new MultivariateJacobianFunction() {
265 @Override
266 public Pair<RealVector, RealMatrix> value(RealVector notUsed) {
267 return new Pair<>(null, null);
268 }
269 };
270
271 try {
272
273 LeastSquaresFactory.create(m1, dummy, dummy, null, null, 0, 0, true, null);
274 Assert.fail("Expecting MathIllegalStateException");
275 } catch (MathIllegalStateException e) {
276
277 }
278
279 final MultivariateJacobianFunction m2 = new ValueAndJacobianFunction() {
280 @Override
281 public Pair<RealVector, RealMatrix> value(RealVector notUsed) {
282 return new Pair<>(null, null);
283 }
284 @Override
285 public RealVector computeValue(final double[] params) {
286 return null;
287 }
288 @Override
289 public RealMatrix computeJacobian(final double[] params) {
290 return null;
291 }
292 };
293
294
295 LeastSquaresFactory.create(m2, dummy, dummy, null, null, 0, 0, true, null);
296 }
297
298 @Test
299 public void testDirectEvaluation() {
300 final RealVector dummy = new ArrayRealVector(new double[] { 0 });
301
302 final LeastSquaresProblem p
303 = LeastSquaresFactory.create(LeastSquaresFactory.model(dummyModel(), dummyJacobian()),
304 dummy, dummy, null, null, 0, 0, false, null);
305
306 try {
307
308 p.evaluate(dummy);
309 Assert.fail("Exception expected");
310 } catch (RuntimeException e) {
311
312
313 final String msg = e.getMessage();
314 Assert.assertTrue(msg.equals("dummyModel") ||
315 msg.equals("dummyJacobian"));
316 }
317 }
318
319
320 private MultivariateVectorFunction dummyModel() {
321 return new MultivariateVectorFunction() {
322 @Override
323 public double[] value(double[] p) {
324 throw new RuntimeException("dummyModel");
325 }
326 };
327 }
328
329
330 private MultivariateMatrixFunction dummyJacobian() {
331 return new MultivariateMatrixFunction() {
332 @Override
333 public double[][] value(double[] p) {
334 throw new RuntimeException("dummyJacobian");
335 }
336 };
337 }
338 }