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.analysis.CalculusFieldBivariateFunction;
26  import org.hipparchus.exception.MathIllegalArgumentException;
27  import org.hipparchus.exception.NullArgumentException;
28  import org.hipparchus.random.RandomDataGenerator;
29  import org.hipparchus.util.Binary64;
30  import org.hipparchus.util.FastMath;
31  import org.hipparchus.util.Precision;
32  import org.junit.jupiter.api.Test;
33  
34  import static org.junit.jupiter.api.Assertions.assertEquals;
35  import static org.junit.jupiter.api.Assertions.assertFalse;
36  import static org.junit.jupiter.api.Assertions.assertTrue;
37  import static org.junit.jupiter.api.Assertions.fail;
38  
39  /**
40   * Test case for the piecewise bicubic function.
41   */
42  final class PiecewiseBicubicSplineInterpolatingFunctionTest {
43      /**
44       * Test preconditions.
45       */
46      @Test
47      void testPreconditions() {
48          double[] xval = new double[] { 3, 4, 5, 6.5, 7.5 };
49          double[] yval = new double[] { -4, -3, -1, 2.5, 3.5 };
50          double[][] zval = new double[xval.length][yval.length];
51  
52          @SuppressWarnings("unused")
53          PiecewiseBicubicSplineInterpolatingFunction bcf = new PiecewiseBicubicSplineInterpolatingFunction(xval, yval, zval);
54  
55          try {
56              new PiecewiseBicubicSplineInterpolatingFunction(null, yval, zval);
57              fail("Failed to detect x null pointer");
58          } catch (NullArgumentException iae) {
59              // Expected.
60          }
61  
62          try {
63              new PiecewiseBicubicSplineInterpolatingFunction(xval, null, zval);
64              fail("Failed to detect y null pointer");
65          } catch (NullArgumentException iae) {
66              // Expected.
67          }
68  
69          try {
70              new PiecewiseBicubicSplineInterpolatingFunction(xval, yval, null);
71              fail("Failed to detect z null pointer");
72          } catch (NullArgumentException iae) {
73              // Expected.
74          }
75  
76          try {
77              final double[][] fnull = new double[1][];
78              new PiecewiseBicubicSplineInterpolatingFunction(xval, yval, fnull);
79              fail("Failed to detect z[0] null pointer");
80          } catch (NullArgumentException iae) {
81              // Expected.
82          }
83  
84          try {
85              final double[][] f = new double[1][1];
86              new PiecewiseBicubicSplineInterpolatingFunction(new double[0], yval, f);
87              fail("Failed to detect empty x pointer");
88          } catch (MathIllegalArgumentException iae) {
89              // Expected.
90          }
91  
92          try {
93              final double[][] f = new double[1][1];
94              new PiecewiseBicubicSplineInterpolatingFunction(xval, new double[0], f);
95              fail("Failed to detect empty y pointer");
96          } catch (MathIllegalArgumentException iae) {
97              // Expected.
98          }
99  
100 
101         try {
102             final double[][] f = new double[1][0];
103             new PiecewiseBicubicSplineInterpolatingFunction(xval, yval, f);
104             fail("Failed to detect empty z[0] pointer");
105         } catch (MathIllegalArgumentException iae) {
106             // Expected.
107         }
108 
109         try {
110             final double[] xval1 = { 0.0, 1.0, 2.0, 3.0 };
111             new PiecewiseBicubicSplineInterpolatingFunction(xval1, yval, zval);
112             fail("Failed to detect insufficient x data");
113         } catch (MathIllegalArgumentException iae) {
114             // Expected.
115         }
116 
117         try {
118             final double[] yval1 = { 0.0, 1.0, 2.0, 3.0 };
119             new PiecewiseBicubicSplineInterpolatingFunction(xval, yval1, zval);
120             fail("Failed to detect insufficient y data");
121         } catch (MathIllegalArgumentException iae) {
122             // Expected.
123         }
124 
125         try {
126             final double[][] zval1 = new double[4][4];
127             new PiecewiseBicubicSplineInterpolatingFunction(xval, yval, zval1);
128             fail("Failed to detect insufficient z data");
129         } catch (MathIllegalArgumentException iae) {
130             // Expected.
131         }
132 
133         try {
134             final double[][] zval1 = new double[5][4];
135             new PiecewiseBicubicSplineInterpolatingFunction(xval, yval, zval1);
136             fail("Failed to detect insufficient z data");
137         } catch (MathIllegalArgumentException iae) {
138             // Expected.
139         }
140 
141         try {
142             final double[] xval1 = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 };
143             new PiecewiseBicubicSplineInterpolatingFunction(xval1, yval, zval);
144             fail("Failed to detect data set array with different sizes.");
145         } catch (MathIllegalArgumentException iae) {
146             // Expected.
147         }
148 
149         try {
150             final double[] yval1 = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 };
151             new PiecewiseBicubicSplineInterpolatingFunction(xval, yval1, zval);
152             fail("Failed to detect data set array with different sizes.");
153         } catch (MathIllegalArgumentException iae) {
154             // Expected.
155         }
156 
157         // X values not sorted.
158         try {
159             final double[] xval1 = { 0.0, 1.0, 0.5, 7.0, 3.5 };
160             new PiecewiseBicubicSplineInterpolatingFunction(xval1, yval, zval);
161             fail("Failed to detect unsorted x arguments.");
162         } catch (MathIllegalArgumentException iae) {
163             // Expected.
164         }
165 
166         // Y values not sorted.
167         try {
168             final double[] yval1 = { 0.0, 1.0, 1.5, 0.0, 3.0 };
169             new PiecewiseBicubicSplineInterpolatingFunction(xval, yval1, zval);
170             fail("Failed to detect unsorted y arguments.");
171         } catch (MathIllegalArgumentException iae) {
172             // Expected.
173         }
174     }
175 
176     /**
177      * Interpolating a plane.
178      * <p>
179      * z = 2 x - 3 y + 5
180      */
181     @Test
182     void testPlane() {
183         final int numberOfElements = 10;
184         final double minimumX = -10;
185         final double maximumX = 10;
186         final double minimumY = -10;
187         final double maximumY = 10;
188         final int numberOfSamples = 100;
189 
190         final double interpolationTolerance = 7e-15;
191         final double maxTolerance = 6e-14;
192 
193         // Function values
194         BivariateFunction f = (x, y) -> 2 * x - 3 * y + 5;
195         CalculusFieldBivariateFunction<Binary64> fT = (x, y) -> x.multiply(2).subtract(y.multiply(3)).add(5);
196 
197         testInterpolation(minimumX,
198                           maximumX,
199                           minimumY,
200                           maximumY,
201                           numberOfElements,
202                           numberOfSamples,
203                           f, fT,
204                           interpolationTolerance,
205                           maxTolerance);
206     }
207 
208     /**
209      * Interpolating a paraboloid.
210      * <p>
211      * z = 2 x<sup>2</sup> - 3 y<sup>2</sup> + 4 x y - 5
212      */
213     @Test
214     void testParabaloid() {
215         final int numberOfElements = 10;
216         final double minimumX = -10;
217         final double maximumX = 10;
218         final double minimumY = -10;
219         final double maximumY = 10;
220         final int numberOfSamples = 100;
221 
222         final double interpolationTolerance = 2e-14;
223         final double maxTolerance = 6e-14;
224 
225         // Function values
226         BivariateFunction f = (x, y) -> 2 * x * x - 3 * y * y + 4 * x * y - 5;
227         CalculusFieldBivariateFunction<Binary64> fT = (x, y) -> x.square().multiply(2).
228                                                              subtract(y.square().multiply(3)).
229                                                              add(x.multiply(y).multiply(4)).
230                                                              subtract(5);
231 
232         testInterpolation(minimumX,
233                           maximumX,
234                           minimumY,
235                           maximumY,
236                           numberOfElements,
237                           numberOfSamples,
238                           f, fT,
239                           interpolationTolerance,
240                           maxTolerance);
241     }
242 
243     /**
244      * @param minimumX Lower bound of interpolation range along the x-coordinate.
245      * @param maximumX Higher bound of interpolation range along the x-coordinate.
246      * @param minimumY Lower bound of interpolation range along the y-coordinate.
247      * @param maximumY Higher bound of interpolation range along the y-coordinate.
248      * @param numberOfElements Number of data points (along each dimension).
249      * @param numberOfSamples Number of test points.
250      * @param f Function to test.
251      * @param fT Binary64 version of the function to test
252      * @param meanTolerance Allowed average error (mean error on all interpolated values).
253      * @param maxTolerance Allowed error on each interpolated value.
254      */
255     private void testInterpolation(double minimumX,
256                                    double maximumX,
257                                    double minimumY,
258                                    double maximumY,
259                                    int numberOfElements,
260                                    int numberOfSamples,
261                                    BivariateFunction f,
262                                    final CalculusFieldBivariateFunction<Binary64> fT,
263                                    double meanTolerance,
264                                    double maxTolerance) {
265         double expected;
266         double actual;
267         Binary64 expected64;
268         Binary64 actual64;
269         double currentX;
270         double currentY;
271         Binary64 currentX64;
272         Binary64 currentY64;
273         final double deltaX = (maximumX - minimumX) / ((double) numberOfElements);
274         final double deltaY = (maximumY - minimumY) / ((double) numberOfElements);
275         final double[] xValues = new double[numberOfElements];
276         final double[] yValues = new double[numberOfElements];
277         final double[][] zValues = new double[numberOfElements][numberOfElements];
278 
279         for (int i = 0; i < numberOfElements; i++) {
280             xValues[i] = minimumX + deltaX * (double) i;
281             for (int j = 0; j < numberOfElements; j++) {
282                 yValues[j] = minimumY + deltaY * (double) j;
283                 zValues[i][j] = f.value(xValues[i], yValues[j]);
284             }
285         }
286 
287         final PiecewiseBicubicSplineInterpolatingFunction interpolation
288             = new PiecewiseBicubicSplineInterpolatingFunction(xValues,
289                                                               yValues,
290                                                               zValues);
291 
292         for (int i = 0; i < numberOfElements; i++) {
293             currentX = xValues[i];
294             currentX64 = new Binary64(currentX);
295             for (int j = 0; j < numberOfElements; j++) {
296                 currentY = yValues[j];
297                 currentY64 = new Binary64(currentY);
298                 assertTrue(Precision.equals(f.value(currentX, currentY),
299                                                    interpolation.value(currentX, currentY)));
300                 assertTrue(Precision.equals(fT.value(currentX64, currentY64).getReal(),
301                                                    interpolation.value(currentX64, currentY64).getReal()));
302             }
303         }
304 
305         final RandomDataGenerator gen = new RandomDataGenerator(1234567L);
306 
307         double sumError   = 0;
308         double sumError64 = 0;
309         for (int i = 0; i < numberOfSamples; i++) {
310             currentX = gen.nextUniform(xValues[0], xValues[xValues.length - 1]);
311             currentY = gen.nextUniform(yValues[0], yValues[yValues.length - 1]);
312             currentX64 = new Binary64(currentX);
313             currentY64 = new Binary64(currentY);
314             expected = f.value(currentX, currentY);
315             actual = interpolation.value(currentX, currentY);
316             expected64 = fT.value(currentX64, currentY64);
317             actual64 = interpolation.value(currentX64, currentY64);
318             sumError   += FastMath.abs(actual - expected);
319             sumError64 += FastMath.abs(actual64.subtract(expected64)).getReal();
320             assertEquals(expected, actual, maxTolerance);
321             assertEquals(expected64.getReal(), actual64.getReal(), maxTolerance);
322         }
323 
324         final double meanError = sumError / numberOfSamples;
325         assertEquals(0, meanError, meanTolerance);
326         final double meanError64 = sumError64 / numberOfSamples;
327         assertEquals(0, meanError64, meanTolerance);
328 
329     }
330 
331     @Test
332     void testIsValidPoint() {
333         // GIVEN
334         final double[] x = new double[] { 0., 1., 2., 3., 4. };
335         final double[] y = x.clone();
336         final PiecewiseBicubicSplineInterpolatingFunction interpolatingFunction =
337                 new PiecewiseBicubicSplineInterpolatingFunction(x, y, new double[x.length][y.length]);
338         // WHEN & THEN
339         assertFalse(interpolatingFunction.isValidPoint(x[0] - 1, y[0]));
340         assertFalse(interpolatingFunction.isValidPoint(x[x.length - 1] + 1, y[0]));
341         assertFalse(interpolatingFunction.isValidPoint(x[0], y[0] - 1));
342         assertFalse(interpolatingFunction.isValidPoint(x[0], y[y.length - 1] + 1));
343     }
344 
345 }