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