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.interpolation;
23  
24  import static org.junit.Assert.assertEquals;
25  import static org.junit.Assert.assertTrue;
26  
27  import org.hipparchus.CalculusFieldElement;
28  import org.hipparchus.Field;
29  import org.hipparchus.analysis.CalculusFieldUnivariateFunction;
30  import org.hipparchus.analysis.UnivariateFunction;
31  import org.hipparchus.exception.MathIllegalArgumentException;
32  import org.hipparchus.exception.NullArgumentException;
33  import org.hipparchus.random.RandomDataGenerator;
34  import org.hipparchus.util.Binary64;
35  import org.hipparchus.util.FastMath;
36  import org.hipparchus.util.MathArrays;
37  import org.hipparchus.util.Precision;
38  import org.junit.Assert;
39  import org.junit.Test;
40  
41  public class AkimaSplineInterpolatorTest {
42  
43      @Test
44      public void testIssueModifiedWeights() {
45          double[] x = {1, 2, 3, 4, 5, 6, 7, 8};
46          double[] y = {-1, -1, -1, 0, 1, 1, 1, 1};
47          UnivariateFunction original = new AkimaSplineInterpolator().interpolate(x, y);
48          UnivariateFunction modified = new AkimaSplineInterpolator(true).interpolate(x, y);
49          for (double d = 1.01; d <=8; d += 0.02) {
50              if (d < 2) {
51                  // both method return constant
52                  Assert.assertEquals(-1.0, original.value(d), 1.0e-15);
53                  Assert.assertEquals(-1.0, modified.value(d), 1.0e-15);
54              } else if (d < 3) {
55                  // original Akima overshoots here
56                  Assert.assertTrue(original.value(d) < -1);
57                  Assert.assertEquals(-1.0, modified.value(d), 1.0e-15);
58              } else if (d < 4) {
59                  // intermediate values for both, original being above modified
60                  Assert.assertTrue(original.value(d) > -1 && original.value(d) < 0);
61                  Assert.assertTrue(modified.value(d) > -1 && modified.value(d) < 0);
62                  Assert.assertTrue(original.value(d) > modified.value(d));
63              } else if (d < 5) {
64                  // intermediate values for both, original being below modified
65                  Assert.assertTrue(original.value(d) < +1 && original.value(d) > 0);
66                  Assert.assertTrue(modified.value(d) < +1 && modified.value(d) > 0);
67                  Assert.assertTrue(original.value(d) < modified.value(d));
68              } else if (d < 6) {
69                  // original Akima overshoots here
70                  Assert.assertTrue(original.value(d) > +1);
71                  Assert.assertEquals(+1.0, modified.value(d), 1.0e-15);
72              } else {
73                  // both method return constant
74                  Assert.assertEquals(+1.0, original.value(d), 1.0e-15);
75                  Assert.assertEquals(+1.0, modified.value(d), 1.0e-15);
76              }
77          }
78      }
79  
80      @Test
81      public void testIllegalArguments() {
82              // Data set arrays of different size.
83              UnivariateInterpolator i = new AkimaSplineInterpolator();
84  
85              try
86              {
87                  double[] yval = { 0.0, 1.0, 2.0, 3.0, 4.0 };
88                  i.interpolate( null, yval );
89                  Assert.fail( "Failed to detect x null pointer" );
90              }
91              catch ( NullArgumentException iae )
92              {
93                  // Expected.
94              }
95  
96          try
97          {
98              double[] xval = { 0.0, 1.0, 2.0, 3.0, 4.0 };
99              i.interpolate( xval, null );
100             Assert.fail( "Failed to detect y null pointer" );
101         }
102         catch ( NullArgumentException iae )
103         {
104             // Expected.
105         }
106 
107         try
108         {
109             double[] xval = { 0.0, 1.0, 2.0, 3.0 };
110             double[] yval = { 0.0, 1.0, 2.0, 3.0 };
111             i.interpolate( xval, yval );
112             Assert.fail( "Failed to detect insufficient data" );
113         }
114         catch ( MathIllegalArgumentException iae )
115         {
116             // Expected.
117         }
118 
119         try
120         {
121             double[] xval = { 0.0, 1.0, 2.0, 3.0, 4.0 };
122             double[] yval = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 };
123             i.interpolate( xval, yval );
124             Assert.fail( "Failed to detect data set array with different sizes." );
125         }
126         catch ( MathIllegalArgumentException iae )
127         {
128             // Expected.
129         }
130 
131         // X values not sorted.
132         try
133         {
134             double[] xval = { 0.0, 1.0, 0.5, 7.0, 3.5, 2.2, 8.0 };
135             double[] yval = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 };
136             i.interpolate( xval, yval );
137             Assert.fail( "Failed to detect unsorted arguments." );
138         }
139         catch ( MathIllegalArgumentException iae )
140         {
141             // Expected.
142         }
143     }
144 
145     @Test
146     public void testIllegalArgumentsD64()
147     {
148         // Data set arrays of different size.
149         FieldUnivariateInterpolator i = new AkimaSplineInterpolator();
150 
151         try
152         {
153             Binary64[] yval = buildD64(0.0, 1.0, 2.0, 3.0, 4.0);
154             i.interpolate( null, yval );
155             Assert.fail( "Failed to detect x null pointer" );
156         }
157         catch ( NullArgumentException iae )
158         {
159             // Expected.
160         }
161 
162         try
163         {
164             Binary64[] xval = buildD64(0.0, 1.0, 2.0, 3.0, 4.0);
165             i.interpolate( xval, null );
166             Assert.fail( "Failed to detect y null pointer" );
167         }
168         catch ( NullArgumentException iae )
169         {
170             // Expected.
171         }
172 
173         try
174         {
175             Binary64[] xval = buildD64(0.0, 1.0, 2.0, 3.0);
176             Binary64[] yval = buildD64(0.0, 1.0, 2.0, 3.0);
177             i.interpolate( xval, yval );
178             Assert.fail( "Failed to detect insufficient data" );
179         }
180         catch ( MathIllegalArgumentException iae )
181         {
182             // Expected.
183         }
184 
185         try
186         {
187             Binary64[] xval = buildD64(0.0, 1.0, 2.0, 3.0, 4.0);
188             Binary64[] yval = buildD64(0.0, 1.0, 2.0, 3.0, 4.0, 5.0);
189             i.interpolate( xval, yval );
190             Assert.fail( "Failed to detect data set array with different sizes." );
191         }
192         catch ( MathIllegalArgumentException iae )
193         {
194             // Expected.
195         }
196 
197         // X values not sorted.
198         try
199         {
200             Binary64[] xval = buildD64(0.0, 1.0, 0.5, 7.0, 3.5, 2.2, 8.0);
201             Binary64[] yval = buildD64(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
202             i.interpolate( xval, yval );
203             Assert.fail( "Failed to detect unsorted arguments." );
204         }
205         catch ( MathIllegalArgumentException iae )
206         {
207             // Expected.
208         }
209     }
210 
211     /*
212      * Interpolate a straight line. <p> y = 2 x - 5 <p> Tolerances determined by performing same calculation using
213      * Math.NET over ten runs of 100 random number draws for the same function over the same span with the same number
214      * of elements
215      */
216     @Test
217     public void testInterpolateLine()
218     {
219         final int numberOfElements = 10;
220         final double minimumX = -10;
221         final double maximumX = 10;
222         final int numberOfSamples = 100;
223         final double interpolationTolerance = 1e-15;
224         final double maxTolerance = 1e-15;
225 
226         UnivariateFunction f = x -> 2 * x - 5;
227 
228         testInterpolation( minimumX, maximumX, numberOfElements, numberOfSamples, f, interpolationTolerance,
229                            maxTolerance );
230     }
231 
232     /*
233      * Interpolate a straight line. <p> y = 2 x - 5 <p> Tolerances determined by performing same calculation using
234      * Math.NET over ten runs of 100 random number draws for the same function over the same span with the same number
235      * of elements
236      */
237     @Test
238     public void testInterpolateLineD64()
239     {
240         final int numberOfElements = 10;
241         final Binary64 minimumX = new Binary64(-10);
242         final Binary64 maximumX = new Binary64(10);
243         final int numberOfSamples = 100;
244         final double interpolationTolerance = 1e-15;
245         final double maxTolerance = 1e-15;
246 
247         CalculusFieldUnivariateFunction<Binary64> f = x -> x.multiply(2).subtract(5);
248 
249         testInterpolation( minimumX, maximumX, numberOfElements, numberOfSamples, f, interpolationTolerance,
250                            maxTolerance );
251     }
252 
253     /*
254      * Interpolate a straight line. <p> y = 3 x<sup>2</sup> - 5 x + 7 <p> Tolerances determined by performing same
255      * calculation using Math.NET over ten runs of 100 random number draws for the same function over the same span with
256      * the same number of elements
257      */
258 
259     @Test
260     public void testInterpolateParabola()
261     {
262         final int numberOfElements = 10;
263         final double minimumX = -10;
264         final double maximumX = 10;
265         final int numberOfSamples = 100;
266         final double interpolationTolerance = 7e-15;
267         final double maxTolerance = 6e-14;
268 
269         UnivariateFunction f = x -> ( 3 * x * x ) - ( 5 * x ) + 7;
270 
271         testInterpolation( minimumX, maximumX, numberOfElements, numberOfSamples, f, interpolationTolerance,
272                            maxTolerance );
273     }
274 
275     /*
276      * Interpolate a straight line. <p> y = 3 x<sup>2</sup> - 5 x + 7 <p> Tolerances determined by performing same
277      * calculation using Math.NET over ten runs of 100 random number draws for the same function over the same span with
278      * the same number of elements
279      */
280 
281     @Test
282     public void testInterpolateParabolaD64()
283     {
284         final int numberOfElements = 10;
285         final Binary64 minimumX = new Binary64(-10);
286         final Binary64 maximumX = new Binary64(10);
287         final int numberOfSamples = 100;
288         final double interpolationTolerance = 7e-15;
289         final double maxTolerance = 6e-14;
290 
291         CalculusFieldUnivariateFunction<Binary64> f = x -> x.square().multiply(3).
292                                                         subtract(x.multiply(5)).
293                                                         add(7);
294 
295         testInterpolation( minimumX, maximumX, numberOfElements, numberOfSamples, f, interpolationTolerance,
296                            maxTolerance );
297     }
298 
299     /*
300      * Interpolate a straight line. <p> y = 3 x<sup>3</sup> - 0.5 x<sup>2</sup> + x - 1 <p> Tolerances determined by
301      * performing same calculation using Math.NET over ten runs of 100 random number draws for the same function over
302      * the same span with the same number of elements
303      */
304     @Test
305     public void testInterpolateCubic()
306     {
307         final int numberOfElements = 10;
308         final double minimumX = -3;
309         final double maximumX = 3;
310         final int numberOfSamples = 100;
311         final double interpolationTolerance = 0.37;
312         final double maxTolerance = 3.8;
313 
314         UnivariateFunction f = x -> ( 3 * x * x * x ) - ( 0.5 * x * x ) + ( 1 * x ) - 1;
315 
316         testInterpolation( minimumX, maximumX, numberOfElements, numberOfSamples, f, interpolationTolerance,
317                            maxTolerance );
318     }
319 
320     /*
321      * Interpolate a straight line. <p> y = 3 x<sup>3</sup> - 0.5 x<sup>2</sup> + x - 1 <p> Tolerances determined by
322      * performing same calculation using Math.NET over ten runs of 100 random number draws for the same function over
323      * the same span with the same number of elements
324      */
325     @Test
326     public void testInterpolateCubic64()
327     {
328         final int numberOfElements = 10;
329         final Binary64 minimumX = new Binary64(-3);
330         final Binary64 maximumX = new Binary64(3);
331         final int numberOfSamples = 100;
332         final double interpolationTolerance = 0.37;
333         final double maxTolerance = 3.8;
334 
335         CalculusFieldUnivariateFunction<Binary64> f = x -> x.square().multiply(x).multiply(3).
336                                                         subtract(x.square().multiply(0.5)).
337                                                         add(x).
338                                                         subtract(1);
339 
340         testInterpolation( minimumX, maximumX, numberOfElements, numberOfSamples, f, interpolationTolerance,
341                            maxTolerance );
342     }
343 
344     private void testInterpolation( double minimumX, double maximumX, int numberOfElements, int numberOfSamples,
345                                     UnivariateFunction f, double tolerance, double maxTolerance )
346     {
347         double expected;
348         double actual;
349         double currentX;
350         final double delta = ( maximumX - minimumX ) / ( (double) numberOfElements );
351         double[] xValues = new double[numberOfElements];
352         double[] yValues = new double[numberOfElements];
353 
354         for ( int i = 0; i < numberOfElements; i++ )
355         {
356             xValues[i] = minimumX + delta * (double) i;
357             yValues[i] = f.value( xValues[i] );
358         }
359 
360         UnivariateFunction interpolation = new AkimaSplineInterpolator().interpolate( xValues, yValues );
361 
362         for ( int i = 0; i < numberOfElements; i++ )
363         {
364             currentX = xValues[i];
365             expected = f.value( currentX );
366             actual = interpolation.value( currentX );
367             assertTrue( Precision.equals( expected, actual ) );
368         }
369 
370         final RandomDataGenerator randomDataGenerator = new RandomDataGenerator(1234567L);
371 
372         double sumError = 0;
373         for ( int i = 0; i < numberOfSamples; i++ )
374         {
375             currentX = randomDataGenerator.nextUniform(xValues[0], xValues[xValues.length - 1]);
376             expected = f.value( currentX );
377             actual = interpolation.value( currentX );
378             sumError += FastMath.abs( actual - expected );
379             assertEquals( expected, actual, maxTolerance );
380         }
381 
382         assertEquals( 0.0, ( sumError / (double) numberOfSamples ), tolerance );
383     }
384 
385     private <T extends CalculusFieldElement<T>> void testInterpolation(T minimumX, T maximumX,
386                                                                        int numberOfElements, int numberOfSamples,
387                                                                        CalculusFieldUnivariateFunction<T> f,
388                                                                        double tolerance, double maxTolerance)
389     {
390         final Field<T> field = minimumX.getField();
391         T expected;
392         T actual;
393         T currentX;
394         final T delta = maximumX.subtract(minimumX).divide(numberOfElements);
395         T[] xValues = MathArrays.buildArray(field, numberOfElements);
396         T[] yValues = MathArrays.buildArray(field, numberOfElements);
397 
398         for ( int i = 0; i < numberOfElements; i++ )
399         {
400             xValues[i] = minimumX.add(delta.multiply(i));
401             yValues[i] = f.value(xValues[i]);
402         }
403 
404         CalculusFieldUnivariateFunction<T> interpolation =
405                         new AkimaSplineInterpolator().interpolate( xValues, yValues );
406 
407         for ( int i = 0; i < numberOfElements; i++ )
408         {
409             currentX = xValues[i];
410             expected = f.value( currentX );
411             actual = interpolation.value( currentX );
412             assertTrue(Precision.equals(expected.getReal(), actual.getReal()));
413         }
414 
415         final RandomDataGenerator randomDataGenerator = new RandomDataGenerator(1234567L);
416 
417         double sumError = 0;
418         for ( int i = 0; i < numberOfSamples; i++ )
419         {
420             final double r = randomDataGenerator.nextUniform(xValues[0].getReal(),
421                                                              xValues[xValues.length - 1].getReal());
422             currentX = field.getZero().add(r);
423             expected = f.value( currentX );
424             actual = interpolation.value( currentX );
425             sumError += FastMath.norm(actual.subtract(expected));
426             assertEquals(expected.getReal(), actual.getReal(), maxTolerance);
427         }
428 
429         assertEquals(0.0, sumError / numberOfSamples, tolerance);
430     }
431 
432     private Binary64[] buildD64(double...c) {
433         Binary64[] array = new Binary64[c.length];
434         for (int i = 0; i < c.length; ++i) {
435             array[i] = new Binary64(c[i]);
436         }
437         return array;
438     }
439 
440 }