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.MultivariateFunction;
25  import org.hipparchus.exception.LocalizedCoreFormats;
26  import org.hipparchus.exception.MathIllegalArgumentException;
27  import org.hipparchus.exception.MathIllegalStateException;
28  import org.hipparchus.random.RandomGenerator;
29  import org.hipparchus.random.UnitSphereRandomVectorGenerator;
30  import org.hipparchus.random.Well1024a;
31  import org.hipparchus.random.Well19937a;
32  import org.hipparchus.util.FastMath;
33  import org.junit.jupiter.api.Test;
34  
35  import java.lang.reflect.InvocationTargetException;
36  import java.lang.reflect.Method;
37  
38  import static org.junit.jupiter.api.Assertions.assertEquals;
39  import static org.junit.jupiter.api.Assertions.assertFalse;
40  import static org.junit.jupiter.api.Assertions.assertTrue;
41  import static org.junit.jupiter.api.Assertions.fail;
42  
43  /**
44   * Test case for the {@link MicrosphereProjectionInterpolator
45   * "microsphere projection"} interpolator.
46   */
47  final class MicrosphereProjectionInterpolatorTest {
48      /**
49       * Test of interpolator for a plane.
50       * <p>
51       * y = 2 x<sub>1</sub> - 3 x<sub>2</sub> + 5
52       */
53      @Test
54      void testLinearFunction2D() {
55          MultivariateFunction f = new MultivariateFunction() {
56              @Override
57              public double value(double[] x) {
58                  if (x.length != 2) {
59                      throw new IllegalArgumentException();
60                  }
61                  return 2 * x[0] - 3 * x[1] + 5;
62              }
63          };
64  
65          final double darkFraction = 0.5;
66          final double darkThreshold = 1e-2;
67          final double background = Double.NaN;
68          final double exponent = 1.1;
69          final boolean shareSphere = true;
70          final double noInterpolationTolerance = Math.ulp(1d);
71          final RandomGenerator random = new Well1024a(0x1c7a150c83a6d9dal);
72  
73          // N-dimensional interpolator.
74          final MultivariateInterpolator interpolator
75              = new MicrosphereProjectionInterpolator(new InterpolatingMicrosphere(2, 500,
76                                                      darkFraction,
77                                                      darkThreshold,
78                                                      background,
79                                                      new UnitSphereRandomVectorGenerator(2, random)),
80                                                      exponent,
81                                                      shareSphere,
82                                                      noInterpolationTolerance);
83  
84          // 2D interpolator.
85          final MultivariateInterpolator interpolator2D
86              = new MicrosphereProjectionInterpolator(new InterpolatingMicrosphere2D(16,
87                                                                                     darkFraction,
88                                                                                     darkThreshold,
89                                                                                     background),
90                                                      exponent,
91                                                      shareSphere,
92                                                      noInterpolationTolerance);
93  
94          final double min = -1;
95          final double max = 1;
96          final double range = max - min;
97          final int res = 5;
98          final int n = res * res; // Number of sample points.
99          final int dim = 2;
100         double[][] x = new double[n][dim];
101         double[] y = new double[n];
102         int index = 0;
103         for (int i = 0; i < res; i++) {
104             final double x1Val = toCoordinate(min, range, res, i);
105             for (int j = 0; j < res; j++) {
106                 final double x2Val = toCoordinate(min, range, res, j);
107                 x[index][0] = x1Val;
108                 x[index][1] = x2Val;
109                 y[index] = f.value(x[index]);
110                 ++index;
111             }
112         }
113 
114         final MultivariateFunction p = interpolator.interpolate(x, y);
115         final MultivariateFunction p2D = interpolator2D.interpolate(x, y);
116 
117         double[] c = new double[dim];
118         double expected, result, result2D;
119 
120         final int sampleIndex = 2;
121         c[0] = x[sampleIndex][0];
122         c[1] = x[sampleIndex][1];
123         expected = f.value(c);
124         result = p.value(c);
125         result2D = p2D.value(c);
126         assertEquals(expected, result2D, FastMath.ulp(1d), "on sample point (exact)");
127         assertEquals(result2D, result, FastMath.ulp(1d), "on sample point (ND vs 2D)");
128 
129         // Interpolation.
130         c[0] = 0.654321;
131         c[1] = -0.345678;
132         expected = f.value(c);
133         result = p.value(c);
134         result2D = p2D.value(c);
135         assertEquals(expected, result2D, 1e-1, "interpolation (exact)");
136         assertEquals(result2D, result, 1e-1, "interpolation (ND vs 2D)");
137 
138         // Extrapolation.
139         c[0] = 0 - 1e-2;
140         c[1] = 1 + 1e-2;
141         expected = f.value(c);
142         result = p.value(c);
143         result2D = p2D.value(c);
144         assertFalse(Double.isNaN(result));
145         assertFalse(Double.isNaN(result2D));
146         assertEquals(expected, result2D, 1e-1, "extrapolation (exact)");
147         assertEquals(result2D, result, 1e-2, "extrapolation (ND vs 2D)");
148 
149         // Far away.
150         c[0] = 20;
151         c[1] = -30;
152         result = p.value(c);
153         assertTrue(Double.isNaN(result), result + " should be NaN");
154         result2D = p2D.value(c);
155         assertTrue(Double.isNaN(result2D), result2D + " should be NaN");
156     }
157 
158     @Test
159     void testWrongDimensions() {
160         checkWrongArguments(0, 1, 0.5, 0.0, 0.0,
161                             LocalizedCoreFormats.NUMBER_TOO_SMALL_BOUND_EXCLUDED);
162         checkWrongArguments(1, 0, 0.5, 0.0, 0.0,
163                             LocalizedCoreFormats.NUMBER_TOO_SMALL_BOUND_EXCLUDED);
164         checkWrongArguments(1, 1, 0.5, -1.0, 0.0,
165                             LocalizedCoreFormats.NUMBER_TOO_SMALL);
166     }
167 
168     private void checkWrongArguments(int dimension,
169                                      int size,
170                                      double maxDarkFraction,
171                                      double darkThreshold,
172                                      double background,
173                                      LocalizedCoreFormats expected) {
174         try {
175             new InterpolatingMicrosphere(dimension, size, maxDarkFraction, darkThreshold, background, null);
176             fail("an exception should have been thrown");
177         } catch (MathIllegalArgumentException miae) {
178             assertEquals(expected, miae.getSpecifier());
179         }
180     }
181 
182     @Test
183     void testCopy() {
184         UnitSphereRandomVectorGenerator random =
185                         new UnitSphereRandomVectorGenerator(3, new Well19937a(0x265318ael));
186         InterpolatingMicrosphere original = new InterpolatingMicrosphere(3, 30, 0.5, 0.2, 0.0, random);
187         InterpolatingMicrosphere copy     = original.copy();
188         assertFalse(original == copy);
189         assertEquals(original.getDimension(), copy.getDimension());
190         assertEquals(original.getSize(),      copy.getSize());
191     }
192 
193     @Test
194     void testSizeLimit() {
195         UnitSphereRandomVectorGenerator random =
196                         new UnitSphereRandomVectorGenerator(3, new Well19937a(0x453l));
197         InterpolatingMicrosphere ims = new InterpolatingMicrosphere(3, 30, 0.5, 0.2, 0.0, random);
198         try {
199             Method add = InterpolatingMicrosphere.class.getDeclaredMethod("add", double[].class, Boolean.TYPE);
200             add.setAccessible(true);
201             try {
202                 add.invoke(ims, random.nextVector(), true);
203                 fail("an exception should have been thrown");
204             } catch (InvocationTargetException ite) {
205                 MathIllegalStateException miae = (MathIllegalStateException) ite.getCause();
206                 assertEquals(LocalizedCoreFormats.MAX_COUNT_EXCEEDED, miae.getSpecifier());
207             }
208         } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException e) {
209             fail(e.getLocalizedMessage());
210         }
211     }
212 
213     @Test
214     void testInconsistentDimensions() {
215         final int d1 = 5;
216         final int d2 = 3;
217         try {
218             UnitSphereRandomVectorGenerator random =
219                             new UnitSphereRandomVectorGenerator(d1, new Well19937a(0x1l));
220             new InterpolatingMicrosphere(d2, 30, 0.5, 0.2, 0.0, random);
221             fail("an exception should have been thrown");
222         } catch (MathIllegalArgumentException miae) {
223             assertEquals(LocalizedCoreFormats.DIMENSIONS_MISMATCH, miae.getSpecifier());
224             assertEquals(d1, ((Integer) miae.getParts()[0]).intValue());
225             assertEquals(d2, ((Integer) miae.getParts()[1]).intValue());
226         }
227     }
228 
229     /**
230      * @param min Minimum of the coordinate range.
231      * @param range Extent of the coordinate interval.
232      * @param res Number of pixels.
233      * @param pixel Pixel index.
234      */
235     private static double toCoordinate(double min,
236                                        double range,
237                                        int res,
238                                        int pixel) {
239         return pixel * range / (res - 1) + min;
240     }
241 }