View Javadoc
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    *      https://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  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.analysis.polynomials;
23  
24  import org.hipparchus.CalculusFieldElement;
25  import org.hipparchus.analysis.differentiation.DSFactory;
26  import org.hipparchus.analysis.differentiation.DerivativeStructure;
27  import org.hipparchus.analysis.differentiation.Gradient;
28  import org.hipparchus.analysis.differentiation.UnivariateDerivative1;
29  import org.hipparchus.analysis.differentiation.UnivariateDerivative2;
30  import org.hipparchus.complex.Complex;
31  import org.hipparchus.exception.MathIllegalArgumentException;
32  import org.hipparchus.random.RandomDataGenerator;
33  import org.hipparchus.util.Binary64;
34  import org.hipparchus.util.FastMath;
35  import org.junit.jupiter.api.Test;
36  
37  import static org.junit.jupiter.api.Assertions.assertEquals;
38  import static org.junit.jupiter.api.Assertions.assertThrows;
39  
40  /**
41   * Tests the FieldPolynomialFunction implementation of a UnivariateFunction.
42   *
43   */
44  final class FieldPolynomialFunctionTest {
45      /** Error tolerance for tests */
46      protected double tolerance = 1e-12;
47  
48      /**
49       * tests the value of a constant polynomial.
50       *
51       * <p>value of this is 2.5 everywhere.</p>
52       */
53      @Test
54      void testConstants() {
55          double c0 = 2.5;
56          FieldPolynomialFunction<Binary64> f = buildD64(c0);
57  
58          // verify that we are equal to c[0] at several (nonsymmetric) places
59          assertEquals(c0, f.value(0).getReal(), tolerance);
60          assertEquals(c0, f.value(-1).getReal(), tolerance);
61          assertEquals(c0, f.value(-123.5).getReal(), tolerance);
62          assertEquals(c0, f.value(3).getReal(), tolerance);
63          assertEquals(c0, f.value(new Binary64(456.89)).getReal(), tolerance);
64  
65          assertEquals(0, f.degree());
66          assertEquals(0, f.polynomialDerivative().value(0).getReal(), tolerance);
67  
68          assertEquals(0, f.polynomialDerivative().polynomialDerivative().value(0).getReal(), tolerance);
69      }
70  
71      /**
72       * tests the value of a linear polynomial.
73       *
74       * <p>This will test the function f(x) = 3*x - 1.5</p>
75       * <p>This will have the values
76       *  <tt>f(0) = -1.5, f(-1) = -4.5, f(-2.5) = -9,
77       *      f(0.5) = 0, f(1.5) = 3</tt> and {@code f(3) = 7.5}
78       * </p>
79       */
80      @Test
81      void testLinear() {
82         FieldPolynomialFunction<Binary64> f = buildD64(-1.5, 3);
83  
84          // verify that we are equal to c[0] when x=0
85          assertEquals(-1.5, f.value(new Binary64(0)).getReal(), tolerance);
86  
87          // now check a few other places
88          assertEquals(-4.5, f.value(new Binary64(-1)).getReal(), tolerance);
89          assertEquals(-9, f.value(new Binary64(-2.5)).getReal(), tolerance);
90          assertEquals(0, f.value(new Binary64(0.5)).getReal(), tolerance);
91          assertEquals(3, f.value(new Binary64(1.5)).getReal(), tolerance);
92          assertEquals(7.5, f.value(new Binary64(3)).getReal(), tolerance);
93  
94          assertEquals(1, f.degree());
95  
96          assertEquals(0, f.polynomialDerivative().polynomialDerivative().value(0).getReal(), tolerance);
97      }
98  
99      /**
100      * Tests a second order polynomial.
101      * <p> This will test the function f(x) = 2x^2 - 3x -2 = (2x+1)(x-2)</p>
102      */
103     @Test
104     void testQuadratic() {
105         FieldPolynomialFunction<Binary64> f = buildD64(-2, -3, 2);
106 
107         // verify that we are equal to c[0] when x=0
108         assertEquals(-2, f.value(0).getReal(), tolerance);
109 
110         // now check a few other places
111         assertEquals(0, f.value(-0.5).getReal(), tolerance);
112         assertEquals(0, f.value(2).getReal(), tolerance);
113         assertEquals(-2, f.value(1.5).getReal(), tolerance);
114         assertEquals(7, f.value(-1.5).getReal(), tolerance);
115         assertEquals(265.5312, f.value(12.34).getReal(), tolerance);
116     }
117 
118     /**
119      * This will test the quintic function
120      *   f(x) = x^2(x-5)(x+3)(x-1) = x^5 - 3x^4 -13x^3 + 15x^2</p>
121      */
122     @Test
123     void testQuintic() {
124         FieldPolynomialFunction<Binary64> f = buildD64(0, 0, 15, -13, -3, 1);
125 
126         // verify that we are equal to c[0] when x=0
127         assertEquals(0, f.value(0).getReal(), tolerance);
128 
129         // now check a few other places
130         assertEquals(0, f.value(5).getReal(), tolerance);
131         assertEquals(0, f.value(1).getReal(), tolerance);
132         assertEquals(0, f.value(-3).getReal(), tolerance);
133         assertEquals(54.84375, f.value(-1.5).getReal(), tolerance);
134         assertEquals(-8.06637, f.value(1.3).getReal(), tolerance);
135 
136         assertEquals(5, f.degree());
137     }
138 
139     /**
140      * tests the firstDerivative function by comparison
141      *
142      * <p>This will test the functions
143      * {@code f(x) = x^3 - 2x^2 + 6x + 3, g(x) = 3x^2 - 4x + 6}
144      * and {@code h(x) = 6x - 4}
145      */
146     @Test
147     void testfirstDerivativeComparison() {
148         double[] f_coeff = { 3, 6, -2, 1 };
149         double[] g_coeff = { 6, -4, 3 };
150         double[] h_coeff = { -4, 6 };
151 
152         FieldPolynomialFunction<Binary64> f = buildD64(f_coeff);
153         FieldPolynomialFunction<Binary64> g = buildD64(g_coeff);
154         FieldPolynomialFunction<Binary64> h = buildD64(h_coeff);
155 
156         // compare f' = g
157         assertEquals(f.polynomialDerivative().value(0).getReal(), g.value(0).getReal(), tolerance);
158         assertEquals(f.polynomialDerivative().value(1).getReal(), g.value(1).getReal(), tolerance);
159         assertEquals(f.polynomialDerivative().value(100).getReal(), g.value(100).getReal(), tolerance);
160         assertEquals(f.polynomialDerivative().value(4.1).getReal(), g.value(4.1).getReal(), tolerance);
161         assertEquals(f.polynomialDerivative().value(-3.25).getReal(), g.value(-3.25).getReal(), tolerance);
162 
163         // compare g' = h
164         assertEquals(g.polynomialDerivative().value(FastMath.PI).getReal(), h.value(FastMath.PI).getReal(), tolerance);
165         assertEquals(g.polynomialDerivative().value(FastMath.E).getReal(),  h.value(FastMath.E).getReal(),  tolerance);
166     }
167 
168     @Test
169     void testAddition() {
170         FieldPolynomialFunction<Binary64> p1 = buildD64( -2, 1 );
171         FieldPolynomialFunction<Binary64> p2 = buildD64( 2, -1, 0 );
172         checkNullPolynomial(p1.add(p2));
173 
174         p2 = p1.add(p1);
175         checkCoeffs(Double.MIN_VALUE, p2, -4, 2);
176 
177         p1 = buildD64( 1, -4, 2 );
178         p2 = buildD64( -1, 3, -2 );
179         p1 = p1.add(p2);
180         assertEquals(1, p1.degree());
181         checkCoeffs(Double.MIN_VALUE, p1, 0, -1);
182     }
183 
184     @Test
185     void testSubtraction() {
186         FieldPolynomialFunction<Binary64> p1 = buildD64( -2, 1 );
187         checkNullPolynomial(p1.subtract(p1));
188 
189         FieldPolynomialFunction<Binary64> p2 = buildD64( -2, 6 );
190         p2 = p2.subtract(p1);
191         checkCoeffs(Double.MIN_VALUE, p2, 0, 5);
192 
193         p1 = buildD64( 1, -4, 2 );
194         p2 = buildD64( -1, 3, 2 );
195         p1 = p1.subtract(p2);
196         assertEquals(1, p1.degree());
197         checkCoeffs(Double.MIN_VALUE, p1, 2, -7);
198     }
199 
200     @Test
201     void testMultiplication() {
202         FieldPolynomialFunction<Binary64> p1 = buildD64( -3, 2 );
203         FieldPolynomialFunction<Binary64> p2 = buildD64( 3, 2, 1 );
204         checkCoeffs(Double.MIN_VALUE, p1.multiply(p2), -9, 0, 1, 2);
205 
206         p1 = buildD64( 0, 1 );
207         p2 = p1;
208         for (int i = 2; i < 10; ++i) {
209             p2 = p2.multiply(p1);
210             double[] c = new double[i + 1];
211             c[i] = 1;
212             checkCoeffs(Double.MIN_VALUE, p2, c);
213         }
214     }
215 
216     /**
217      * tests the firstDerivative function by comparison
218      *
219      * <p>This will test the functions
220      * {@code f(x) = x^3 - 2x^2 + 6x + 3, g(x) = 3x^2 - 4x + 6}
221      * and {@code h(x) = 6x - 4}
222      */
223     @Test
224     void testMath341() {
225         double[] f_coeff = { 3, 6, -2, 1 };
226         double[] g_coeff = { 6, -4, 3 };
227         double[] h_coeff = { -4, 6 };
228 
229         FieldPolynomialFunction<Binary64> f = buildD64(f_coeff);
230         FieldPolynomialFunction<Binary64> g = buildD64(g_coeff);
231         FieldPolynomialFunction<Binary64> h = buildD64(h_coeff);
232 
233         // compare f' = g
234         assertEquals(f.polynomialDerivative().value(0).getReal(), g.value(0).getReal(), tolerance);
235         assertEquals(f.polynomialDerivative().value(1).getReal(), g.value(1).getReal(), tolerance);
236         assertEquals(f.polynomialDerivative().value(100).getReal(), g.value(100).getReal(), tolerance);
237         assertEquals(f.polynomialDerivative().value(4.1).getReal(), g.value(4.1).getReal(), tolerance);
238         assertEquals(f.polynomialDerivative().value(-3.25).getReal(), g.value(-3.25).getReal(), tolerance);
239 
240         // compare g' = h
241         assertEquals(g.polynomialDerivative().value(FastMath.PI).getReal(), h.value(FastMath.PI).getReal(), tolerance);
242         assertEquals(g.polynomialDerivative().value(FastMath.E).getReal(),  h.value(FastMath.E).getReal(),  tolerance);
243     }
244 
245     @Test
246     void testAntiDerivative() {
247         // 1 + 2x + 3x^2
248         final double[] coeff = {1, 2, 3};
249         final FieldPolynomialFunction<Binary64> p = buildD64(coeff);
250         // x + x^2 + x^3
251         checkCoeffs(Double.MIN_VALUE, p.antiDerivative(), 0, 1, 1, 1);
252     }
253 
254     @Test
255     void testAntiDerivativeConstant() {
256         final double[] coeff = {2};
257         final FieldPolynomialFunction<Binary64> p = buildD64(coeff);
258         checkCoeffs(Double.MIN_VALUE, p.antiDerivative(), 0, 2);
259     }
260 
261     @Test
262     void testAntiDerivativeZero() {
263         final double[] coeff = {0};
264         final FieldPolynomialFunction<Binary64> p = buildD64(coeff);
265         checkCoeffs(Double.MIN_VALUE, p.antiDerivative(), 0);
266     }
267 
268     @Test
269     void testAntiDerivativeRandom() {
270         final RandomDataGenerator ran = new RandomDataGenerator(1000);
271         double[] coeff = null;
272         FieldPolynomialFunction<Binary64> p = null;
273         int d = 0;
274         for (int i = 0; i < 20; i++) {
275             d = ran.nextInt(1, 50);
276             coeff = new double[d];
277             for (int j = 0; j < d; j++) {
278                 coeff[j] = ran.nextUniform(-100, 1000);
279             }
280             p = buildD64(coeff);
281             checkInverseDifferentiation(p);
282         }
283     }
284 
285     @Test
286     void testIntegrate() {
287         // -x^2
288         final double[] coeff = {0, 0, -1};
289         final FieldPolynomialFunction<Binary64> p = buildD64(coeff);
290         assertEquals(-2d/3d, p.integrate(-1, 1).getReal(),Double.MIN_VALUE);
291 
292         // x(x-1)(x+1) - should integrate to 0 over [-1,1]
293         final FieldPolynomialFunction<Binary64> p2 = buildD64(0, 1).
294                                                       multiply(buildD64(-1, 1)).
295                                                       multiply(buildD64(1, 1));
296         assertEquals(0, p2.integrate(-1, 1).getReal(), Double.MIN_VALUE);
297     }
298 
299     @Test
300     void testIntegrateInfiniteBounds() {
301         assertThrows(MathIllegalArgumentException.class, () -> {
302             final FieldPolynomialFunction<Binary64> p = buildD64(1);
303             p.integrate(0, Double.POSITIVE_INFINITY);
304         });
305     }
306 
307     @Test
308     void testIntegrateBadInterval() {
309         assertThrows(MathIllegalArgumentException.class, () -> {
310             final FieldPolynomialFunction<Binary64> p = buildD64(1);
311             p.integrate(0, -1);
312         });
313     }
314 
315     @Test
316     void testIssue259WithComplex() {
317         final double nonZeroParameterForQuadraticCoeff = -1.;
318         final Complex[] coefficients = buildImaginaryCoefficients(1., 2., nonZeroParameterForQuadraticCoeff);
319         templateIssue259DisappearingCoefficients(coefficients);
320     }
321 
322     @Test
323     void testIssue259WithGradient() {
324         final double nonZeroParameterForQuadraticCoeff = -1.;
325         final Gradient[] coefficients = buildUnivariateGradientCoefficients(1.,2., nonZeroParameterForQuadraticCoeff);
326         templateIssue259DisappearingCoefficients(coefficients);
327     }
328 
329     @Test
330     void testIssue259WithDerivativeStructure() {
331         final double nonZeroParameterForQuadraticCoeff = -1.;
332         final DerivativeStructure[] coefficients = buildUnivariateDSCoefficients(1.,2., nonZeroParameterForQuadraticCoeff);
333         templateIssue259DisappearingCoefficients(coefficients);
334     }
335 
336     @Test
337     void testIssue259WithUnivariateDerivative1() {
338         final double nonZeroParameterForQuadraticCoeff = -1.;
339         final UnivariateDerivative1[] coefficients = buildUnivariateDerivative1Coefficients(1.,2., nonZeroParameterForQuadraticCoeff);
340         templateIssue259DisappearingCoefficients(coefficients);
341     }
342 
343     @Test
344     void testIssue259WithUnivariateDerivative2() {
345         final double nonZeroParameterForQuadraticCoeff = -1.;
346         final UnivariateDerivative2[] coefficients = buildUnivariateDerivative2Coefficients(1.,2., nonZeroParameterForQuadraticCoeff);
347         templateIssue259DisappearingCoefficients(coefficients);
348     }
349 
350     private <T extends CalculusFieldElement<T>> void templateIssue259DisappearingCoefficients(T[] coefficients) {
351         final FieldPolynomialFunction<T> polynomialFunction = new FieldPolynomialFunction<>(coefficients);
352         assertEquals(coefficients.length, polynomialFunction.getCoefficients().length);
353         for (int i = 0; i < coefficients.length; i++) {
354             assertEquals(coefficients[i], polynomialFunction.getCoefficients()[i]);
355         }
356     }
357 
358     private <T extends CalculusFieldElement<T>> void checkInverseDifferentiation(FieldPolynomialFunction<T> p) {
359         final T[] c0 = p.getCoefficients();
360         final T[] c1 = p.antiDerivative().polynomialDerivative().getCoefficients();
361         assertEquals(c0.length, c1.length);
362         for (int i = 0; i < c0.length; ++i) {
363             assertEquals(c0[i].getReal(), c1[i].getReal(), 1e-12);
364         }
365     }
366 
367     private <T extends CalculusFieldElement<T>> void checkCoeffs(final double tolerance, final FieldPolynomialFunction<T> p,
368                                                                  final double... ref) {
369         final T[] c = p.getCoefficients();
370         assertEquals(ref.length, c.length);
371         for (int i = 0; i < ref.length; ++i) {
372             assertEquals(ref[i], c[i].getReal(), tolerance);
373         }
374     }
375 
376     private <T extends CalculusFieldElement<T>> void checkNullPolynomial(FieldPolynomialFunction<T> p) {
377         for (T coefficient : p.getCoefficients()) {
378             assertEquals(0, coefficient.getReal(), 1e-15);
379         }
380     }
381 
382     private FieldPolynomialFunction<Binary64> buildD64(double...c) {
383         Binary64[] array = new Binary64[c.length];
384         for (int i = 0; i < c.length; ++i) {
385             array[i] = new Binary64(c[i]);
386         }
387         return new FieldPolynomialFunction<>(array);
388     }
389 
390     private Complex[] buildImaginaryCoefficients(double...c) {
391         Complex[] array = new Complex[c.length];
392         for (int i = 0; i < c.length; ++i) {
393             array[i] = new Complex(0., c[i]);
394         }
395         return array;
396     }
397 
398     private DerivativeStructure[] buildUnivariateDSCoefficients(double...c) {
399         DerivativeStructure[] array = new DerivativeStructure[c.length];
400         final DSFactory factory = new DSFactory(1, 1);
401         for (int i = 0; i < c.length; ++i) {
402             array[i] = factory.variable(0, 0.).multiply(c[i]);
403         }
404         return array;
405     }
406 
407     private Gradient[] buildUnivariateGradientCoefficients(double...c) {
408         Gradient[] array = new Gradient[c.length];
409         for (int i = 0; i < c.length; ++i) {
410             array[i] = Gradient.variable(1, 0, 0.).multiply(c[i]);
411         }
412         return array;
413     }
414 
415     private UnivariateDerivative1[] buildUnivariateDerivative1Coefficients(double...c) {
416         UnivariateDerivative1[] array = new UnivariateDerivative1[c.length];
417         for (int i = 0; i < c.length; ++i) {
418             array[i] = new UnivariateDerivative1(0., c[i]);
419         }
420         return array;
421     }
422 
423     private UnivariateDerivative2[] buildUnivariateDerivative2Coefficients(double...c) {
424         UnivariateDerivative2[] array = new UnivariateDerivative2[c.length];
425         for (int i = 0; i < c.length; ++i) {
426             array[i] = new UnivariateDerivative2(0., c[i], 0.);
427         }
428         return array;
429     }
430 
431 }