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 org.hipparchus.analysis.BivariateFunction;
25  import org.hipparchus.exception.MathIllegalArgumentException;
26  import org.hipparchus.random.RandomDataGenerator;
27  import org.hipparchus.util.FastMath;
28  import org.hipparchus.util.Precision;
29  import org.junit.jupiter.api.Test;
30  
31  import static org.junit.jupiter.api.Assertions.assertEquals;
32  import static org.junit.jupiter.api.Assertions.assertFalse;
33  import static org.junit.jupiter.api.Assertions.assertTrue;
34  import static org.junit.jupiter.api.Assertions.fail;
35  
36  /**
37   * Test case for the bicubic function.
38   */
39  final class BicubicInterpolatingFunctionTest {
40      /**
41       * Test preconditions.
42       */
43      @Test
44      void testPreconditions() {
45          double[] xval = new double[] {3, 4, 5, 6.5};
46          double[] yval = new double[] {-4, -3, -1, 2.5};
47          double[][] zval = new double[xval.length][yval.length];
48  
49          @SuppressWarnings("unused")
50          BivariateFunction bcf = new BicubicInterpolatingFunction(xval, yval, zval,
51                                                                   zval, zval, zval);
52  
53          try {
54              bcf = new BicubicInterpolatingFunction(new double[0], yval,
55                                                     zval, zval, zval, zval);
56              fail("an exception should have been thrown");
57          } catch (MathIllegalArgumentException e) {
58              // Expected
59          }
60          try {
61              bcf = new BicubicInterpolatingFunction(xval, new double[0],
62                                                     zval, zval, zval, zval);
63              fail("an exception should have been thrown");
64          } catch (MathIllegalArgumentException e) {
65              // Expected
66          }
67          try {
68              bcf = new BicubicInterpolatingFunction(xval, yval,
69                                                      new double[0][], zval, zval, zval);
70              fail("an exception should have been thrown");
71          } catch (MathIllegalArgumentException e) {
72              // Expected
73          }
74          try {
75              bcf = new BicubicInterpolatingFunction(xval, yval,
76                                                      new double[1][0], zval, zval, zval);
77              fail("an exception should have been thrown");
78          } catch (MathIllegalArgumentException e) {
79              // Expected
80          }
81  
82          double[] wxval = new double[] {3, 2, 5, 6.5};
83          try {
84              bcf = new BicubicInterpolatingFunction(wxval, yval, zval, zval, zval, zval);
85              fail("an exception should have been thrown");
86          } catch (MathIllegalArgumentException e) {
87              // Expected
88          }
89          double[] wyval = new double[] {-4, -1, -1, 2.5};
90          try {
91              bcf = new BicubicInterpolatingFunction(xval, wyval, zval, zval, zval, zval);
92              fail("an exception should have been thrown");
93          } catch (MathIllegalArgumentException e) {
94              // Expected
95          }
96          double[][] wzval = new double[xval.length][yval.length - 1];
97          try {
98              bcf = new BicubicInterpolatingFunction(xval, yval, wzval, zval, zval, zval);
99              fail("an exception should have been thrown");
100         } catch (MathIllegalArgumentException e) {
101             // Expected
102         }
103         try {
104             bcf = new BicubicInterpolatingFunction(xval, yval, zval, wzval, zval, zval);
105             fail("an exception should have been thrown");
106         } catch (MathIllegalArgumentException e) {
107             // Expected
108         }
109         try {
110             bcf = new BicubicInterpolatingFunction(xval, yval, zval, zval, wzval, zval);
111             fail("an exception should have been thrown");
112         } catch (MathIllegalArgumentException e) {
113             // Expected
114         }
115         try {
116             bcf = new BicubicInterpolatingFunction(xval, yval, zval, zval, zval, wzval);
117             fail("an exception should have been thrown");
118         } catch (MathIllegalArgumentException e) {
119             // Expected
120         }
121 
122         wzval = new double[xval.length - 1][yval.length];
123         try {
124             bcf = new BicubicInterpolatingFunction(xval, yval, wzval, zval, zval, zval);
125             fail("an exception should have been thrown");
126         } catch (MathIllegalArgumentException e) {
127             // Expected
128         }
129         try {
130             bcf = new BicubicInterpolatingFunction(xval, yval, zval, wzval, zval, zval);
131             fail("an exception should have been thrown");
132         } catch (MathIllegalArgumentException e) {
133             // Expected
134         }
135         try {
136             bcf = new BicubicInterpolatingFunction(xval, yval, zval, zval, wzval, zval);
137             fail("an exception should have been thrown");
138         } catch (MathIllegalArgumentException e) {
139             // Expected
140         }
141         try {
142             bcf = new BicubicInterpolatingFunction(xval, yval, zval, zval, zval, wzval);
143             fail("an exception should have been thrown");
144         } catch (MathIllegalArgumentException e) {
145             // Expected
146         }
147     }
148 
149     @Test
150     void testIsValidPoint() {
151         final double xMin = -12;
152         final double xMax = 34;
153         final double yMin = 5;
154         final double yMax = 67;
155         final double[] xval = new double[] { xMin, xMax };
156         final double[] yval = new double[] { yMin, yMax };
157         final double[][] f = new double[][] { { 1, 2 },
158                                               { 3, 4 } };
159         final double[][] dFdX = f;
160         final double[][] dFdY = f;
161         final double[][] dFdXdY = f;
162 
163         final BicubicInterpolatingFunction bcf
164             = new BicubicInterpolatingFunction(xval, yval, f,
165                                                      dFdX, dFdY, dFdXdY);
166 
167         double x, y;
168 
169         x = xMin;
170         y = yMin;
171         assertTrue(bcf.isValidPoint(x, y));
172         // Ensure that no exception is thrown.
173         bcf.value(x, y);
174 
175         x = xMax;
176         y = yMax;
177         assertTrue(bcf.isValidPoint(x, y));
178         // Ensure that no exception is thrown.
179         bcf.value(x, y);
180 
181         final double xRange = xMax - xMin;
182         final double yRange = yMax - yMin;
183         x = xMin + xRange / 3.4;
184         y = yMin + yRange / 1.2;
185         assertTrue(bcf.isValidPoint(x, y));
186         // Ensure that no exception is thrown.
187         bcf.value(x, y);
188 
189         final double small = 1e-8;
190         x = xMin - small;
191         y = yMax;
192         assertFalse(bcf.isValidPoint(x, y));
193         // Ensure that an exception would have been thrown.
194         try {
195             bcf.value(x, y);
196             fail("MathIllegalArgumentException expected");
197         } catch (MathIllegalArgumentException expected) {}
198 
199         x = xMin;
200         y = yMax + small;
201         assertFalse(bcf.isValidPoint(x, y));
202         // Ensure that an exception would have been thrown.
203         try {
204             bcf.value(x, y);
205             fail("MathIllegalArgumentException expected");
206         } catch (MathIllegalArgumentException expected) {}
207     }
208 
209     /**
210      * Interpolating a plane.
211      * <p>
212      * z = 2 x - 3 y + 5
213      */
214     @Test
215     void testPlane() {
216         final int numberOfElements = 10;
217         final double minimumX = -10;
218         final double maximumX = 10;
219         final double minimumY = -10;
220         final double maximumY = 10;
221         final int numberOfSamples = 1000;
222 
223         final double interpolationTolerance = 1e-15;
224         final double maxTolerance = 1e-14;
225 
226         // Function values
227         BivariateFunction f = new BivariateFunction() {
228                 @Override
229                 public double value(double x, double y) {
230                     return 2 * x - 3 * y + 5;
231                 }
232             };
233         BivariateFunction dfdx = new BivariateFunction() {
234                 @Override
235                 public double value(double x, double y) {
236                     return 2;
237                 }
238             };
239         BivariateFunction dfdy = new BivariateFunction() {
240                 @Override
241                 public double value(double x, double y) {
242                     return -3;
243                 }
244             };
245         BivariateFunction d2fdxdy = new BivariateFunction() {
246                 @Override
247                 public double value(double x, double y) {
248                     return 0;
249                 }
250             };
251 
252         testInterpolation(minimumX,
253                           maximumX,
254                           minimumY,
255                           maximumY,
256                           numberOfElements,
257                           numberOfSamples,
258                           f,
259                           dfdx,
260                           dfdy,
261                           d2fdxdy,
262                           interpolationTolerance,
263                           maxTolerance,
264                           false);
265     }
266 
267     /**
268      * Interpolating a paraboloid.
269      * <p>
270      * z = 2 x<sup>2</sup> - 3 y<sup>2</sup> + 4 x y - 5
271      */
272     @Test
273     void testParaboloid() {
274         final int numberOfElements = 10;
275         final double minimumX = -10;
276         final double maximumX = 10;
277         final double minimumY = -10;
278         final double maximumY = 10;
279         final int numberOfSamples = 1000;
280 
281         final double interpolationTolerance = 2e-14;
282         final double maxTolerance = 1e-12;
283 
284         // Function values
285         BivariateFunction f = new BivariateFunction() {
286                 @Override
287                 public double value(double x, double y) {
288                     return 2 * x * x - 3 * y * y + 4 * x * y - 5;
289                 }
290             };
291         BivariateFunction dfdx = new BivariateFunction() {
292                 @Override
293                 public double value(double x, double y) {
294                     return 4 * (x + y);
295                 }
296             };
297         BivariateFunction dfdy = new BivariateFunction() {
298                 @Override
299                 public double value(double x, double y) {
300                     return 4 * x - 6 * y;
301                 }
302             };
303         BivariateFunction d2fdxdy = new BivariateFunction() {
304                 @Override
305                 public double value(double x, double y) {
306                     return 4;
307                 }
308             };
309 
310         testInterpolation(minimumX,
311                           maximumX,
312                           minimumY,
313                           maximumY,
314                           numberOfElements,
315                           numberOfSamples,
316                           f,
317                           dfdx,
318                           dfdy,
319                           d2fdxdy,
320                           interpolationTolerance,
321                           maxTolerance,
322                           false);
323     }
324 
325     /**
326      * @param minimumX Lower bound of interpolation range along the x-coordinate.
327      * @param maximumX Higher bound of interpolation range along the x-coordinate.
328      * @param minimumY Lower bound of interpolation range along the y-coordinate.
329      * @param maximumY Higher bound of interpolation range along the y-coordinate.
330      * @param numberOfElements Number of data points (along each dimension).
331      * @param numberOfSamples Number of test points.
332      * @param f Function to test.
333      * @param dfdx Partial derivative w.r.t. x of the function to test.
334      * @param dfdy Partial derivative w.r.t. y of the function to test.
335      * @param d2fdxdy Second partial cross-derivative of the function to test.
336      * @param meanTolerance Allowed average error (mean error on all interpolated values).
337      * @param maxTolerance Allowed error on each interpolated value.
338      */
339     private void testInterpolation(double minimumX,
340                                    double maximumX,
341                                    double minimumY,
342                                    double maximumY,
343                                    int numberOfElements,
344                                    int numberOfSamples,
345                                    BivariateFunction f,
346                                    BivariateFunction dfdx,
347                                    BivariateFunction dfdy,
348                                    BivariateFunction d2fdxdy,
349                                    double meanTolerance,
350                                    double maxTolerance,
351                                    boolean print) {
352         double expected;
353         double actual;
354         double currentX;
355         double currentY;
356         final double deltaX = (maximumX - minimumX) / numberOfElements;
357         final double deltaY = (maximumY - minimumY) / numberOfElements;
358         final double[] xValues = new double[numberOfElements];
359         final double[] yValues = new double[numberOfElements];
360         final double[][] zValues = new double[numberOfElements][numberOfElements];
361         final double[][] dzdx = new double[numberOfElements][numberOfElements];
362         final double[][] dzdy = new double[numberOfElements][numberOfElements];
363         final double[][] d2zdxdy = new double[numberOfElements][numberOfElements];
364 
365         for (int i = 0; i < numberOfElements; i++) {
366             xValues[i] = minimumX + deltaX * i;
367             final double x = xValues[i];
368             for (int j = 0; j < numberOfElements; j++) {
369                 yValues[j] = minimumY + deltaY * j;
370                 final double y = yValues[j];
371                 zValues[i][j] = f.value(x, y);
372                 dzdx[i][j] = dfdx.value(x, y);
373                 dzdy[i][j] = dfdy.value(x, y);
374                 d2zdxdy[i][j] = d2fdxdy.value(x, y);
375             }
376         }
377 
378         final BivariateFunction interpolation
379             = new BicubicInterpolatingFunction(xValues,
380                                                yValues,
381                                                zValues,
382                                                dzdx,
383                                                dzdy,
384                                                d2zdxdy);
385 
386         for (int i = 0; i < numberOfElements; i++) {
387             currentX = xValues[i];
388             for (int j = 0; j < numberOfElements; j++) {
389                 currentY = yValues[j];
390                 expected = f.value(currentX, currentY);
391                 actual = interpolation.value(currentX, currentY);
392                 assertTrue(Precision.equals(expected, actual),
393                                   "On data point: " + expected + " != " + actual);
394             }
395         }
396 
397         final RandomDataGenerator randomDataGenerator = new RandomDataGenerator(100);
398 
399         double sumError = 0;
400         for (int i = 0; i < numberOfSamples; i++) {
401             currentX = randomDataGenerator.nextUniform(xValues[0], xValues[xValues.length - 1]);
402             currentY = randomDataGenerator.nextUniform(yValues[0], yValues[yValues.length - 1]);
403             expected = f.value(currentX, currentY);
404 
405             if (print) {
406                 System.out.println(currentX + " " + currentY + " -> ");
407             }
408 
409             actual = interpolation.value(currentX, currentY);
410             sumError += FastMath.abs(actual - expected);
411 
412             if (print) {
413                 System.out.println(actual + " (diff=" + (expected - actual) + ")");
414             }
415 
416             assertEquals(expected, actual, maxTolerance);
417         }
418 
419         final double meanError = sumError / numberOfSamples;
420         assertEquals(0, meanError, meanTolerance);
421     }
422 }