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