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.transform;
23  
24  import java.util.Random;
25  
26  import org.hipparchus.analysis.UnivariateFunction;
27  import org.hipparchus.exception.MathIllegalArgumentException;
28  import org.hipparchus.util.FastMath;
29  import org.junit.Assert;
30  import org.junit.Test;
31  
32  /**
33   * Abstract test for classes implementing the {@link RealTransformer} interface.
34   * This abstract test handles the automatic generation of random data of various
35   * sizes. For each generated data array, actual values (returned by the
36   * transformer to be tested) are compared to expected values, returned by the
37   * {@link #transform(double[], TransformType)} (to be implemented by the user:
38   * a naive method may be used). Methods are also provided to test that invalid
39   * parameters throw the expected exceptions.
40   *
41   */
42  public abstract class RealTransformerAbstractTest {
43  
44      /** The common seed of all random number generators used in this test. */
45      private final static long SEED = 20110119L;
46  
47      /**
48       * Returns a new instance of the {@link RealTransformer} to be tested.
49       *
50       * @return a the transformer to be tested
51       */
52      abstract RealTransformer createRealTransformer();
53  
54      /**
55       * Returns an invalid data size. Transforms with this data size should
56       * trigger a {@link MathIllegalArgumentException}.
57       *
58       * @param i the index of the invalid data size ({@code 0 <= i <}
59       * {@link #getNumberOfInvalidDataSizes()}
60       * @return an invalid data size
61       */
62      abstract int getInvalidDataSize(int i);
63  
64      /**
65       * Returns the total number of invalid data sizes to be tested. If data
66       * array of any
67       * size can be handled by the {@link RealTransformer} to be tested, this
68       * method should return {@code 0}.
69       *
70       * @return the total number of invalid data sizes
71       */
72      abstract int getNumberOfInvalidDataSizes();
73  
74      /**
75       * Returns the total number of valid data sizes to be tested.
76       *
77       * @return the total number of valid data sizes
78       */
79      abstract int getNumberOfValidDataSizes();
80  
81      /**
82       * Returns the expected relative accuracy for data arrays of size
83       * {@code getValidDataSize(i)}.
84       *
85       * @param i the index of the valid data size
86       * @return the expected relative accuracy
87       */
88      abstract double getRelativeTolerance(int i);
89  
90      /**
91       * Returns a valid data size. This method allows for data arrays of various
92       * sizes to be automatically tested (by allowing multiple values of the
93       * specified index).
94       *
95       * @param i the index of the valid data size ({@code 0 <= i <}
96       * {@link #getNumberOfValidDataSizes()}
97       * @return a valid data size
98       */
99      abstract int getValidDataSize(int i);
100 
101     /**
102      * Returns a function for the accuracy check of
103      * {@link RealTransformer#transform(UnivariateFunction, double, double, int, TransformType)}.
104      * This function should be valid. In other words, none of the above methods
105      * should throw an exception when passed this function.
106      *
107      * @return a valid function
108      */
109     abstract UnivariateFunction getValidFunction();
110 
111     /**
112      * Returns a sampling lower bound for the accuracy check of
113      * {@link RealTransformer#transform(UnivariateFunction, double, double, int, TransformType)}.
114      * This lower bound should be valid. In other words, none of the above
115      * methods should throw an exception when passed this bound.
116      *
117      * @return a valid lower bound
118      */
119     abstract double getValidLowerBound();
120 
121     /**
122      * Returns a sampling upper bound for the accuracy check of
123      * {@link RealTransformer#transform(UnivariateFunction, double, double, int, TransformType)}.
124      * This upper bound should be valid. In other words, none of the above
125      * methods should throw an exception when passed this bound.
126      *
127      * @return a valid bound
128      */
129     abstract double getValidUpperBound();
130 
131     /**
132      * Returns the expected transform of the specified real data array.
133      *
134      * @param x the real data array to be transformed
135      * @param type the type of transform (forward, inverse) to be performed
136      * @return the expected transform
137      */
138     abstract double[] transform(double[] x, TransformType type);
139 
140     /*
141      * Check of preconditions.
142      */
143 
144     /**
145      * {@link RealTransformer#transform(double[], TransformType)} should throw a
146      * {@link MathIllegalArgumentException} if data size is invalid.
147      */
148     @Test
149     public void testTransformRealInvalidDataSize() {
150         final TransformType[] type = TransformType.values();
151         final RealTransformer transformer = createRealTransformer();
152         for (int i = 0; i < getNumberOfInvalidDataSizes(); i++) {
153             final int n = getInvalidDataSize(i);
154             for (int j = 0; j < type.length; j++) {
155                 try {
156                     transformer.transform(createRealData(n), type[j]);
157                     Assert.fail(type[j] + ", " + n);
158                 } catch (MathIllegalArgumentException e) {
159                     // Expected: do nothing
160                 }
161             }
162         }
163     }
164 
165     /**
166      * {@link RealTransformer#transform(UnivariateFunction, double, double, int, TransformType)}
167      * should throw a {@link MathIllegalArgumentException} if number of samples
168      * is invalid.
169      */
170     @Test
171     public void testTransformFunctionInvalidDataSize() {
172         final TransformType[] type = TransformType.values();
173         final RealTransformer transformer = createRealTransformer();
174         final UnivariateFunction f = getValidFunction();
175         final double a = getValidLowerBound();
176         final double b = getValidUpperBound();
177         for (int i = 0; i < getNumberOfInvalidDataSizes(); i++) {
178             final int n = getInvalidDataSize(i);
179             for (int j = 0; j < type.length; j++) {
180                 try {
181                     transformer.transform(f, a, b, n, type[j]);
182                     Assert.fail(type[j] + ", " + n);
183                 } catch (MathIllegalArgumentException e) {
184                     // Expected: do nothing
185                 }
186             }
187         }
188     }
189 
190     /**
191      * {@link RealTransformer#transform(UnivariateFunction, double, double, int, TransformType)}
192      * should throw a {@link MathIllegalArgumentException} if number of samples
193      * is not strictly positive.
194      */
195     @Test
196     public void testTransformFunctionNotStrictlyPositiveNumberOfSamples() {
197         final TransformType[] type = TransformType.values();
198         final RealTransformer transformer = createRealTransformer();
199         final UnivariateFunction f = getValidFunction();
200         final double a = getValidLowerBound();
201         final double b = getValidUpperBound();
202         for (int i = 0; i < getNumberOfValidDataSizes(); i++) {
203             final int n = getValidDataSize(i);
204             for (int j = 0; j < type.length; j++) {
205                 try {
206                     transformer.transform(f, a, b, -n, type[j]);
207                     Assert.fail(type[j] + ", " + (-n));
208                 } catch (MathIllegalArgumentException e) {
209                     // Expected: do nothing
210                 }
211             }
212         }
213     }
214 
215     /**
216      * {@link RealTransformer#transform(UnivariateFunction, double, double, int, TransformType)}
217      * should throw a {@link MathIllegalArgumentException} if sampling bounds are
218      * not correctly ordered.
219      */
220     @Test
221     public void testTransformFunctionInvalidBounds() {
222         final TransformType[] type = TransformType.values();
223         final RealTransformer transformer = createRealTransformer();
224         final UnivariateFunction f = getValidFunction();
225         final double a = getValidLowerBound();
226         final double b = getValidUpperBound();
227         for (int i = 0; i < getNumberOfValidDataSizes(); i++) {
228             final int n = getValidDataSize(i);
229             for (int j = 0; j < type.length; j++) {
230                 try {
231                     transformer.transform(f, b, a, n, type[j]);
232                     Assert.fail(type[j] + ", " + b + ", " + a);
233                 } catch (MathIllegalArgumentException e) {
234                     // Expected: do nothing
235                 }
236             }
237         }
238     }
239 
240     /*
241      * Accuracy tests of transform of valid data.
242      */
243 
244     /**
245      * Accuracy check of {@link RealTransformer#transform(double[], TransformType)}.
246      * For each valid data size returned by
247      * {@link #getValidDataSize(int) getValidDataSize(i)},
248      * a random data array is generated with
249      * {@link #createRealData(int) createRealData(i)}. The actual
250      * transform is computed and compared to the expected transform, return by
251      * {@link #transform(double[], TransformType)}. Actual and expected values
252      * should be equal to within the relative error returned by
253      * {@link #getRelativeTolerance(int) getRelativeTolerance(i)}.
254      */
255     @Test
256     public void testTransformReal() {
257         final TransformType[] type = TransformType.values();
258         for (int i = 0; i < getNumberOfValidDataSizes(); i++) {
259             final int n = getValidDataSize(i);
260             final double tol = getRelativeTolerance(i);
261             for (int j = 0; j < type.length; j++) {
262                 doTestTransformReal(n, tol, type[j]);
263             }
264         }
265     }
266 
267     /**
268      * Accuracy check of
269      * {@link RealTransformer#transform(UnivariateFunction, double, double, int, TransformType)}.
270      * For each valid data size returned by
271      * {@link #getValidDataSize(int) getValidDataSize(i)},
272      * the {@link UnivariateFunction} returned by {@link #getValidFunction()} is
273      * sampled. The actual transform is computed and compared to the expected
274      * transform, return by {@link #transform(double[], TransformType)}. Actual
275      * and expected values should be equal to within the relative error returned
276      * by {@link #getRelativeTolerance(int) getRelativeTolerance(i)}.
277      */
278     @Test
279     public void testTransformFunction() {
280         final TransformType[] type = TransformType.values();
281         for (int i = 0; i < getNumberOfValidDataSizes(); i++) {
282             final int n = getValidDataSize(i);
283             final double tol = getRelativeTolerance(i);
284             for (int j = 0; j < type.length; j++) {
285                 doTestTransformFunction(n, tol, type[j]);
286             }
287         }
288     }
289 
290     /*
291      * Utility methods.
292      */
293 
294     /**
295      * Returns a random array of doubles. Random generator always uses the same
296      * seed.
297      *
298      * @param n the size of the array to be returned
299      * @return a random array of specified size
300      */
301     double[] createRealData(final int n) {
302         final Random random = new Random(SEED);
303         final double[] data = new double[n];
304         for (int i = 0; i < n; i++) {
305             data[i] = 2.0 * random.nextDouble() - 1.0;
306         }
307         return data;
308     }
309 
310     /*
311      * The tests per se.
312      */
313 
314     private void doTestTransformReal(final int n, final double tol,
315         final TransformType type) {
316         final RealTransformer transformer = createRealTransformer();
317         final double[] x = createRealData(n);
318         final double[] expected = transform(x, type);
319         final double[] actual = transformer.transform(x, type);
320         for (int i = 0; i < n; i++) {
321             final String msg = String.format("%d, %d", n, i);
322             final double delta = tol * FastMath.abs(expected[i]);
323             Assert.assertEquals(msg, expected[i], actual[i], delta);
324         }
325     }
326 
327     private void doTestTransformFunction(final int n, final double tol,
328         final TransformType type) {
329         final RealTransformer transformer = createRealTransformer();
330         final UnivariateFunction f = getValidFunction();
331         final double a = getValidLowerBound();
332         final double b = getValidUpperBound();
333         final double[] x = createRealData(n);
334         for (int i = 0; i < n; i++) {
335             final double t = a + i * (b - a) / n;
336             x[i] = f.value(t);
337         }
338         final double[] expected = transform(x, type);
339         final double[] actual = transformer.transform(f, a, b, n, type);
340         for (int i = 0; i < n; i++) {
341             final String msg = String.format("%d, %d", n, i);
342             final double delta = tol * FastMath.abs(expected[i]);
343             Assert.assertEquals(msg, expected[i], actual[i], delta);
344         }
345     }
346 }