1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.math4.legacy.analysis.differentiation;
19
20 import org.apache.commons.math4.legacy.analysis.QuinticFunction;
21 import org.apache.commons.math4.legacy.analysis.UnivariateFunction;
22 import org.apache.commons.math4.legacy.analysis.UnivariateMatrixFunction;
23 import org.apache.commons.math4.legacy.analysis.UnivariateVectorFunction;
24 import org.apache.commons.math4.legacy.analysis.function.Gaussian;
25 import org.apache.commons.math4.legacy.analysis.function.Sin;
26 import org.apache.commons.math4.legacy.exception.MathInternalError;
27 import org.apache.commons.math4.legacy.exception.NotPositiveException;
28 import org.apache.commons.math4.legacy.exception.NumberIsTooLargeException;
29 import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
30 import org.apache.commons.math4.core.jdkmath.JdkMath;
31 import org.junit.Assert;
32 import org.junit.Test;
33
34
35
36
37 public class FiniteDifferencesDifferentiatorTest {
38
39 @Test(expected=NumberIsTooSmallException.class)
40 public void testWrongNumberOfPoints() {
41 new FiniteDifferencesDifferentiator(1, 1.0);
42 }
43
44 @Test(expected=NotPositiveException.class)
45 public void testWrongStepSize() {
46 new FiniteDifferencesDifferentiator(3, 0.0);
47 }
48
49 @Test
50 public void testConstant() {
51 FiniteDifferencesDifferentiator differentiator =
52 new FiniteDifferencesDifferentiator(5, 0.01);
53 UnivariateDifferentiableFunction f =
54 differentiator.differentiate(new UnivariateFunction() {
55 @Override
56 public double value(double x) {
57 return 42.0;
58 }
59 });
60 for (double x = -10; x < 10; x += 0.1) {
61 DerivativeStructure y = f.value(new DerivativeStructure(1, 2, 0, x));
62 Assert.assertEquals(42.0, y.getValue(), 1.0e-15);
63 Assert.assertEquals( 0.0, y.getPartialDerivative(1), 1.0e-15);
64 Assert.assertEquals( 0.0, y.getPartialDerivative(2), 1.0e-15);
65 }
66 }
67
68 @Test
69 public void testLinear() {
70 FiniteDifferencesDifferentiator differentiator =
71 new FiniteDifferencesDifferentiator(5, 0.01);
72 UnivariateDifferentiableFunction f =
73 differentiator.differentiate(new UnivariateFunction() {
74 @Override
75 public double value(double x) {
76 return 2 - 3 * x;
77 }
78 });
79 for (double x = -10; x < 10; x += 0.1) {
80 DerivativeStructure y = f.value(new DerivativeStructure(1, 2, 0, x));
81 Assert.assertEquals("" + (2 - 3 * x - y.getValue()), 2 - 3 * x, y.getValue(), 2.0e-15);
82 Assert.assertEquals(-3.0, y.getPartialDerivative(1), 4.0e-13);
83 Assert.assertEquals( 0.0, y.getPartialDerivative(2), 9.0e-11);
84 }
85 }
86
87 @Test
88 public void testGaussian() {
89 FiniteDifferencesDifferentiator differentiator =
90 new FiniteDifferencesDifferentiator(9, 0.02);
91 UnivariateDifferentiableFunction gaussian = new Gaussian(1.0, 2.0);
92 UnivariateDifferentiableFunction f =
93 differentiator.differentiate(gaussian);
94 double[] expectedError = new double[] {
95 6.939e-18, 1.284e-15, 2.477e-13, 1.168e-11, 2.840e-9, 7.971e-8
96 };
97 double[] maxError = new double[expectedError.length];
98 for (double x = -10; x < 10; x += 0.1) {
99 DerivativeStructure dsX = new DerivativeStructure(1, maxError.length - 1, 0, x);
100 DerivativeStructure yRef = gaussian.value(dsX);
101 DerivativeStructure y = f.value(dsX);
102 Assert.assertEquals(f.value(dsX.getValue()), f.value(dsX).getValue(), 1.0e-15);
103 for (int order = 0; order <= yRef.getOrder(); ++order) {
104 maxError[order] = JdkMath.max(maxError[order],
105 JdkMath.abs(yRef.getPartialDerivative(order) -
106 y.getPartialDerivative(order)));
107 }
108 }
109 for (int i = 0; i < maxError.length; ++i) {
110 Assert.assertEquals(expectedError[i], maxError[i], 0.01 * expectedError[i]);
111 }
112 }
113
114 @Test
115 public void testStepSizeUnstability() {
116 UnivariateDifferentiableFunction quintic = new QuinticFunction();
117 UnivariateDifferentiableFunction goodStep =
118 new FiniteDifferencesDifferentiator(7, 0.25).differentiate(quintic);
119 UnivariateDifferentiableFunction badStep =
120 new FiniteDifferencesDifferentiator(7, 1.0e-6).differentiate(quintic);
121 double[] maxErrorGood = new double[7];
122 double[] maxErrorBad = new double[7];
123 for (double x = -10; x < 10; x += 0.1) {
124 DerivativeStructure dsX = new DerivativeStructure(1, 6, 0, x);
125 DerivativeStructure yRef = quintic.value(dsX);
126 DerivativeStructure yGood = goodStep.value(dsX);
127 DerivativeStructure yBad = badStep.value(dsX);
128 for (int order = 0; order <= 6; ++order) {
129 maxErrorGood[order] = JdkMath.max(maxErrorGood[order],
130 JdkMath.abs(yRef.getPartialDerivative(order) -
131 yGood.getPartialDerivative(order)));
132 maxErrorBad[order] = JdkMath.max(maxErrorBad[order],
133 JdkMath.abs(yRef.getPartialDerivative(order) -
134 yBad.getPartialDerivative(order)));
135 }
136 }
137
138
139
140 final double[] expectedGood = new double[] {
141 7.276e-12, 7.276e-11, 9.968e-10, 3.092e-9, 5.432e-8, 8.196e-8, 1.818e-6
142 };
143
144
145
146 final double[] expectedBad = new double[] {
147 2.910e-11, 2.087e-5, 147.7, 3.820e7, 6.354e14, 6.548e19, 1.543e27
148 };
149
150 for (int i = 0; i < maxErrorGood.length; ++i) {
151 Assert.assertEquals(expectedGood[i], maxErrorGood[i], 0.01 * expectedGood[i]);
152 Assert.assertEquals(expectedBad[i], maxErrorBad[i], 0.01 * expectedBad[i]);
153 }
154 }
155
156 @Test(expected=NumberIsTooLargeException.class)
157 public void testWrongOrder() {
158 UnivariateDifferentiableFunction f =
159 new FiniteDifferencesDifferentiator(3, 0.01).differentiate(new UnivariateFunction() {
160 @Override
161 public double value(double x) {
162
163
164 throw new MathInternalError();
165 }
166 });
167 f.value(new DerivativeStructure(1, 3, 0, 1.0));
168 }
169
170 @Test(expected=NumberIsTooLargeException.class)
171 public void testWrongOrderVector() {
172 UnivariateDifferentiableVectorFunction f =
173 new FiniteDifferencesDifferentiator(3, 0.01).differentiate(new UnivariateVectorFunction() {
174 @Override
175 public double[] value(double x) {
176
177
178 throw new MathInternalError();
179 }
180 });
181 f.value(new DerivativeStructure(1, 3, 0, 1.0));
182 }
183
184 @Test(expected=NumberIsTooLargeException.class)
185 public void testWrongOrderMatrix() {
186 UnivariateDifferentiableMatrixFunction f =
187 new FiniteDifferencesDifferentiator(3, 0.01).differentiate(new UnivariateMatrixFunction() {
188 @Override
189 public double[][] value(double x) {
190
191
192 throw new MathInternalError();
193 }
194 });
195 f.value(new DerivativeStructure(1, 3, 0, 1.0));
196 }
197
198 @Test(expected=NumberIsTooLargeException.class)
199 public void testTooLargeStep() {
200 new FiniteDifferencesDifferentiator(3, 2.5, 0.0, 1.0);
201 }
202
203 @Test
204 public void testBounds() {
205
206 final double slope = 2.5;
207 UnivariateFunction f = new UnivariateFunction() {
208 @Override
209 public double value(double x) {
210 if (x < 0) {
211 throw new NumberIsTooSmallException(x, 0, true);
212 } else if (x > 1) {
213 throw new NumberIsTooLargeException(x, 1, true);
214 } else {
215 return slope * x;
216 }
217 }
218 };
219
220 UnivariateDifferentiableFunction missingBounds =
221 new FiniteDifferencesDifferentiator(3, 0.1).differentiate(f);
222 UnivariateDifferentiableFunction properlyBounded =
223 new FiniteDifferencesDifferentiator(3, 0.1, 0.0, 1.0).differentiate(f);
224 DerivativeStructure tLow = new DerivativeStructure(1, 1, 0, 0.05);
225 DerivativeStructure tHigh = new DerivativeStructure(1, 1, 0, 0.95);
226
227 try {
228
229
230 missingBounds.value(tLow);
231 Assert.fail("an exception should have been thrown");
232 } catch (NumberIsTooSmallException nse) {
233 Assert.assertEquals(-0.05, nse.getArgument().doubleValue(), 1.0e-10);
234 } catch (Exception e) {
235 Assert.fail("wrong exception caught: " + e.getClass().getName());
236 }
237
238 try {
239
240
241 missingBounds.value(tHigh);
242 Assert.fail("an exception should have been thrown");
243 } catch (NumberIsTooLargeException nle) {
244 Assert.assertEquals(1.05, nle.getArgument().doubleValue(), 1.0e-10);
245 } catch (Exception e) {
246 Assert.fail("wrong exception caught: " + e.getClass().getName());
247 }
248
249
250
251 Assert.assertEquals(slope, properlyBounded.value(tLow).getPartialDerivative(1), 1.0e-10);
252
253
254
255 Assert.assertEquals(slope, properlyBounded.value(tHigh).getPartialDerivative(1), 1.0e-10);
256 }
257
258 @Test
259 public void testBoundedSqrt() {
260
261 UnivariateFunctionDifferentiator differentiator =
262 new FiniteDifferencesDifferentiator(9, 1.0 / 32, 0.0, Double.POSITIVE_INFINITY);
263 UnivariateDifferentiableFunction sqrt = differentiator.differentiate(new UnivariateFunction() {
264 @Override
265 public double value(double x) {
266 return JdkMath.sqrt(x);
267 }
268 });
269
270
271 DerivativeStructure t001 = new DerivativeStructure(1, 1, 0, 0.01);
272 Assert.assertEquals(0.5 / JdkMath.sqrt(t001.getValue()), sqrt.value(t001).getPartialDerivative(1), 1.6);
273 DerivativeStructure t01 = new DerivativeStructure(1, 1, 0, 0.1);
274 Assert.assertEquals(0.5 / JdkMath.sqrt(t01.getValue()), sqrt.value(t01).getPartialDerivative(1), 7.0e-3);
275 DerivativeStructure t03 = new DerivativeStructure(1, 1, 0, 0.3);
276 Assert.assertEquals(0.5 / JdkMath.sqrt(t03.getValue()), sqrt.value(t03).getPartialDerivative(1), 2.1e-7);
277 }
278
279 @Test
280 public void testVectorFunction() {
281
282 FiniteDifferencesDifferentiator differentiator =
283 new FiniteDifferencesDifferentiator(7, 0.01);
284 UnivariateDifferentiableVectorFunction f =
285 differentiator.differentiate(new UnivariateVectorFunction() {
286
287 @Override
288 public double[] value(double x) {
289 return new double[] { JdkMath.cos(x), JdkMath.sin(x) };
290 }
291 });
292
293 for (double x = -10; x < 10; x += 0.1) {
294 DerivativeStructure dsX = new DerivativeStructure(1, 2, 0, x);
295 DerivativeStructure[] y = f.value(dsX);
296 double cos = JdkMath.cos(x);
297 double sin = JdkMath.sin(x);
298 double[] f1 = f.value(dsX.getValue());
299 DerivativeStructure[] f2 = f.value(dsX);
300 Assert.assertEquals(f1.length, f2.length);
301 for (int i = 0; i < f1.length; ++i) {
302 Assert.assertEquals(f1[i], f2[i].getValue(), 1.0e-15);
303 }
304 Assert.assertEquals( cos, y[0].getValue(), 7.0e-16);
305 Assert.assertEquals( sin, y[1].getValue(), 7.0e-16);
306 Assert.assertEquals(-sin, y[0].getPartialDerivative(1), 6.0e-14);
307 Assert.assertEquals( cos, y[1].getPartialDerivative(1), 6.0e-14);
308 Assert.assertEquals(-cos, y[0].getPartialDerivative(2), 2.0e-11);
309 Assert.assertEquals(-sin, y[1].getPartialDerivative(2), 2.0e-11);
310 }
311 }
312
313 @Test
314 public void testMatrixFunction() {
315
316 FiniteDifferencesDifferentiator differentiator =
317 new FiniteDifferencesDifferentiator(7, 0.01);
318 UnivariateDifferentiableMatrixFunction f =
319 differentiator.differentiate(new UnivariateMatrixFunction() {
320
321 @Override
322 public double[][] value(double x) {
323 return new double[][] {
324 { JdkMath.cos(x), JdkMath.sin(x) },
325 { JdkMath.cosh(x), JdkMath.sinh(x) }
326 };
327 }
328 });
329
330 for (double x = -1; x < 1; x += 0.02) {
331 DerivativeStructure dsX = new DerivativeStructure(1, 2, 0, x);
332 DerivativeStructure[][] y = f.value(dsX);
333 double cos = JdkMath.cos(x);
334 double sin = JdkMath.sin(x);
335 double cosh = JdkMath.cosh(x);
336 double sinh = JdkMath.sinh(x);
337 double[][] f1 = f.value(dsX.getValue());
338 DerivativeStructure[][] f2 = f.value(dsX);
339 Assert.assertEquals(f1.length, f2.length);
340 for (int i = 0; i < f1.length; ++i) {
341 Assert.assertEquals(f1[i].length, f2[i].length);
342 for (int j = 0; j < f1[i].length; ++j) {
343 Assert.assertEquals(f1[i][j], f2[i][j].getValue(), 1.0e-15);
344 }
345 }
346 Assert.assertEquals(cos, y[0][0].getValue(), 7.0e-18);
347 Assert.assertEquals(sin, y[0][1].getValue(), 6.0e-17);
348 Assert.assertEquals(cosh, y[1][0].getValue(), 3.0e-16);
349 Assert.assertEquals(sinh, y[1][1].getValue(), 3.0e-16);
350 Assert.assertEquals(-sin, y[0][0].getPartialDerivative(1), 2.0e-14);
351 Assert.assertEquals( cos, y[0][1].getPartialDerivative(1), 2.0e-14);
352 Assert.assertEquals( sinh, y[1][0].getPartialDerivative(1), 3.0e-14);
353 Assert.assertEquals( cosh, y[1][1].getPartialDerivative(1), 3.0e-14);
354 Assert.assertEquals(-cos, y[0][0].getPartialDerivative(2), 3.0e-12);
355 Assert.assertEquals(-sin, y[0][1].getPartialDerivative(2), 3.0e-12);
356 Assert.assertEquals( cosh, y[1][0].getPartialDerivative(2), 6.0e-12);
357 Assert.assertEquals( sinh, y[1][1].getPartialDerivative(2), 6.0e-12);
358 }
359 }
360
361 @Test
362 public void testSeveralFreeParameters() {
363 FiniteDifferencesDifferentiator differentiator =
364 new FiniteDifferencesDifferentiator(5, 0.001);
365 UnivariateDifferentiableFunction sine = new Sin();
366 UnivariateDifferentiableFunction f =
367 differentiator.differentiate(sine);
368 double[] expectedError = new double[] {
369 6.696e-16, 1.371e-12, 2.007e-8, 1.754e-5
370 };
371 double[] maxError = new double[expectedError.length];
372 for (double x = -2; x < 2; x += 0.1) {
373 for (double y = -2; y < 2; y += 0.1) {
374 DerivativeStructure dsX = new DerivativeStructure(2, maxError.length - 1, 0, x);
375 DerivativeStructure dsY = new DerivativeStructure(2, maxError.length - 1, 1, y);
376 DerivativeStructure dsT = dsX.multiply(3).subtract(dsY.multiply(2));
377 DerivativeStructure sRef = sine.value(dsT);
378 DerivativeStructure s = f.value(dsT);
379 for (int xOrder = 0; xOrder <= sRef.getOrder(); ++xOrder) {
380 for (int yOrder = 0; yOrder <= sRef.getOrder(); ++yOrder) {
381 if (xOrder + yOrder <= sRef.getOrder()) {
382 maxError[xOrder +yOrder] = JdkMath.max(maxError[xOrder + yOrder],
383 JdkMath.abs(sRef.getPartialDerivative(xOrder, yOrder) -
384 s.getPartialDerivative(xOrder, yOrder)));
385 }
386 }
387 }
388 }
389 }
390 for (int i = 0; i < maxError.length; ++i) {
391 Assert.assertEquals(expectedError[i], maxError[i], 0.01 * expectedError[i]);
392 }
393 }
394 }