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    *      http://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  package org.apache.commons.math4.legacy.analysis.interpolation;
18  
19  import org.apache.commons.math4.legacy.analysis.BivariateFunction;
20  import org.apache.commons.statistics.distribution.ContinuousDistribution;
21  import org.apache.commons.statistics.distribution.UniformContinuousDistribution;
22  import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
23  import org.apache.commons.math4.legacy.exception.InsufficientDataException;
24  import org.apache.commons.math4.legacy.exception.NonMonotonicSequenceException;
25  import org.apache.commons.math4.legacy.exception.NullArgumentException;
26  import org.apache.commons.rng.UniformRandomProvider;
27  import org.apache.commons.rng.simple.RandomSource;
28  import org.apache.commons.math4.core.jdkmath.JdkMath;
29  import org.apache.commons.numbers.core.Precision;
30  import org.junit.Assert;
31  import org.junit.Test;
32  
33  /**
34   * Test case for the piecewise bicubic function.
35   */
36  public final class PiecewiseBicubicSplineInterpolatingFunctionTest {
37      /**
38       * Test preconditions.
39       */
40      @Test
41      public void testPreconditions() {
42          double[] xval = new double[] { 3, 4, 5, 6.5, 7.5 };
43          double[] yval = new double[] { -4, -3, -1, 2.5, 3.5 };
44          double[][] zval = new double[xval.length][yval.length];
45  
46          @SuppressWarnings("unused")
47          PiecewiseBicubicSplineInterpolatingFunction bcf = new PiecewiseBicubicSplineInterpolatingFunction(xval, yval, zval);
48  
49          try {
50              bcf = new PiecewiseBicubicSplineInterpolatingFunction(null, yval, zval);
51              Assert.fail("Failed to detect x null pointer");
52          } catch (NullArgumentException iae) {
53              // Expected.
54          }
55  
56          try {
57              bcf = new PiecewiseBicubicSplineInterpolatingFunction(xval, null, zval);
58              Assert.fail("Failed to detect y null pointer");
59          } catch (NullArgumentException iae) {
60              // Expected.
61          }
62  
63          try {
64              bcf = new PiecewiseBicubicSplineInterpolatingFunction(xval, yval, null);
65              Assert.fail("Failed to detect z null pointer");
66          } catch (NullArgumentException iae) {
67              // Expected.
68          }
69  
70          try {
71              double xval1[] = { 0.0, 1.0, 2.0, 3.0 };
72              bcf = new PiecewiseBicubicSplineInterpolatingFunction(xval1, yval, zval);
73              Assert.fail("Failed to detect insufficient x data");
74          } catch (InsufficientDataException iae) {
75              // Expected.
76          }
77  
78          try {
79              double yval1[] = { 0.0, 1.0, 2.0, 3.0 };
80              bcf = new PiecewiseBicubicSplineInterpolatingFunction(xval, yval1, zval);
81              Assert.fail("Failed to detect insufficient y data");
82          } catch (InsufficientDataException iae) {
83              // Expected.
84          }
85  
86          try {
87              double zval1[][] = new double[4][4];
88              bcf = new PiecewiseBicubicSplineInterpolatingFunction(xval, yval, zval1);
89              Assert.fail("Failed to detect insufficient z data");
90          } catch (InsufficientDataException iae) {
91              // Expected.
92          }
93  
94          try {
95              double xval1[] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 };
96              bcf = new PiecewiseBicubicSplineInterpolatingFunction(xval1, yval, zval);
97              Assert.fail("Failed to detect data set array with different sizes.");
98          } catch (DimensionMismatchException iae) {
99              // Expected.
100         }
101 
102         try {
103             double yval1[] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 };
104             bcf = new PiecewiseBicubicSplineInterpolatingFunction(xval, yval1, zval);
105             Assert.fail("Failed to detect data set array with different sizes.");
106         } catch (DimensionMismatchException iae) {
107             // Expected.
108         }
109 
110         // X values not sorted.
111         try {
112             double xval1[] = { 0.0, 1.0, 0.5, 7.0, 3.5 };
113             bcf = new PiecewiseBicubicSplineInterpolatingFunction(xval1, yval, zval);
114             Assert.fail("Failed to detect unsorted x arguments.");
115         } catch (NonMonotonicSequenceException iae) {
116             // Expected.
117         }
118 
119         // Y values not sorted.
120         try {
121             double yval1[] = { 0.0, 1.0, 1.5, 0.0, 3.0 };
122             bcf = new PiecewiseBicubicSplineInterpolatingFunction(xval, yval1, zval);
123             Assert.fail("Failed to detect unsorted y arguments.");
124         } catch (NonMonotonicSequenceException iae) {
125             // Expected.
126         }
127     }
128 
129     /**
130      * Interpolating a plane.
131      * <p>
132      * z = 2 x - 3 y + 5
133      */
134     @Test
135     public void testPlane() {
136         final int numberOfElements = 10;
137         final double minimumX = -10;
138         final double maximumX = 10;
139         final double minimumY = -10;
140         final double maximumY = 10;
141         final int numberOfSamples = 100;
142 
143         final double interpolationTolerance = 7e-15;
144         final double maxTolerance = 6e-14;
145 
146         // Function values
147         BivariateFunction f = new BivariateFunction() {
148                 @Override
149                 public double value(double x, double y) {
150                     return 2 * x - 3 * y + 5;
151                 }
152             };
153 
154         testInterpolation(minimumX,
155                           maximumX,
156                           minimumY,
157                           maximumY,
158                           numberOfElements,
159                           numberOfSamples,
160                           f,
161                           interpolationTolerance,
162                           maxTolerance);
163     }
164 
165     /**
166      * Interpolating a paraboloid.
167      * <p>
168      * z = 2 x<sup>2</sup> - 3 y<sup>2</sup> + 4 x y - 5
169      */
170     @Test
171     public void testParabaloid() {
172         final int numberOfElements = 10;
173         final double minimumX = -10;
174         final double maximumX = 10;
175         final double minimumY = -10;
176         final double maximumY = 10;
177         final int numberOfSamples = 100;
178 
179         final double interpolationTolerance = 1e-13;
180         final double maxTolerance = 6e-14;
181 
182         // Function values
183         BivariateFunction f = new BivariateFunction() {
184                 @Override
185                 public double value(double x, double y) {
186                     return 2 * x * x - 3 * y * y + 4 * x * y - 5;
187                 }
188             };
189 
190         testInterpolation(minimumX,
191                           maximumX,
192                           minimumY,
193                           maximumY,
194                           numberOfElements,
195                           numberOfSamples,
196                           f,
197                           interpolationTolerance,
198                           maxTolerance);
199     }
200 
201     /**
202      * @param minimumX Lower bound of interpolation range along the x-coordinate.
203      * @param maximumX Higher bound of interpolation range along the x-coordinate.
204      * @param minimumY Lower bound of interpolation range along the y-coordinate.
205      * @param maximumY Higher bound of interpolation range along the y-coordinate.
206      * @param numberOfElements Number of data points (along each dimension).
207      * @param numberOfSamples Number of test points.
208      * @param f Function to test.
209      * @param meanTolerance Allowed average error (mean error on all interpolated values).
210      * @param maxTolerance Allowed error on each interpolated value.
211      */
212     private void testInterpolation(double minimumX,
213                                    double maximumX,
214                                    double minimumY,
215                                    double maximumY,
216                                    int numberOfElements,
217                                    int numberOfSamples,
218                                    BivariateFunction f,
219                                    double meanTolerance,
220                                    double maxTolerance) {
221         double expected;
222         double actual;
223         double currentX;
224         double currentY;
225         final double deltaX = (maximumX - minimumX) / ((double) numberOfElements);
226         final double deltaY = (maximumY - minimumY) / ((double) numberOfElements);
227         final double[] xValues = new double[numberOfElements];
228         final double[] yValues = new double[numberOfElements];
229         final double[][] zValues = new double[numberOfElements][numberOfElements];
230 
231         for (int i = 0; i < numberOfElements; i++) {
232             xValues[i] = minimumX + deltaX * (double) i;
233             for (int j = 0; j < numberOfElements; j++) {
234                 yValues[j] = minimumY + deltaY * (double) j;
235                 zValues[i][j] = f.value(xValues[i], yValues[j]);
236             }
237         }
238 
239         final BivariateFunction interpolation
240             = new PiecewiseBicubicSplineInterpolatingFunction(xValues,
241                                                               yValues,
242                                                               zValues);
243 
244         for (int i = 0; i < numberOfElements; i++) {
245             currentX = xValues[i];
246             for (int j = 0; j < numberOfElements; j++) {
247                 currentY = yValues[j];
248                 expected = f.value(currentX, currentY);
249                 actual = interpolation.value(currentX, currentY);
250                 Assert.assertTrue(Precision.equals(expected, actual));
251             }
252         }
253 
254         final UniformRandomProvider rng = RandomSource.WELL_19937_C.create(1234567L);
255         final ContinuousDistribution.Sampler distX = UniformContinuousDistribution.of(xValues[0], xValues[xValues.length - 1]).createSampler(rng);
256         final ContinuousDistribution.Sampler distY = UniformContinuousDistribution.of(yValues[0], yValues[yValues.length - 1]).createSampler(rng);
257 
258         double sumError = 0;
259         for (int i = 0; i < numberOfSamples; i++) {
260             currentX = distX.sample();
261             currentY = distY.sample();
262             expected = f.value(currentX, currentY);
263             actual = interpolation.value(currentX, currentY);
264             sumError += JdkMath.abs(actual - expected);
265             Assert.assertEquals(expected, actual, maxTolerance);
266         }
267 
268         final double meanError = sumError / numberOfSamples;
269         Assert.assertEquals(0, meanError, meanTolerance);
270     }
271 }