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 org.hipparchus.analysis.UnivariateFunction;
25  import org.hipparchus.analysis.function.Sin;
26  import org.hipparchus.analysis.function.Sinc;
27  import org.hipparchus.exception.LocalizedCoreFormats;
28  import org.hipparchus.exception.MathIllegalArgumentException;
29  import org.hipparchus.exception.MathIllegalStateException;
30  import org.hipparchus.exception.MathRuntimeException;
31  import org.hipparchus.util.FastMath;
32  import org.junit.jupiter.params.ParameterizedTest;
33  import org.junit.jupiter.params.provider.MethodSource;
34  
35  import java.util.Arrays;
36  import java.util.Collection;
37  
38  import static org.junit.jupiter.api.Assertions.assertEquals;
39  import static org.junit.jupiter.api.Assertions.fail;
40  
41  /**
42   * Test case for fast sine transformer.
43   * <p>
44   * FST algorithm is exact, the small tolerance number is used only
45   * to account for round-off errors.
46   *
47   */
48  public final class FastSineTransformerTest extends RealTransformerAbstractTest<DstNormalization> {
49  
50      private DstNormalization normalization;
51  
52      private int[] invalidDataSize;
53  
54      private double[] relativeTolerance;
55  
56      private int[] validDataSize;
57  
58      public void initFastSineTransformerTest(final DstNormalization normalization) {
59          this.normalization = normalization;
60          this.validDataSize = new int[] {
61              1, 2, 4, 8, 16, 32, 64, 128
62          };
63          this.invalidDataSize = new int[] {
64              129
65          };
66          this.relativeTolerance = new double[] {
67              1E-15, 1E-15, 1E-14, 1E-14, 1E-13, 1E-12, 1E-11, 1E-11
68          };
69      }
70  
71      /**
72       * Returns an array containing {@code true, false} in order to check both
73       * standard and orthogonal DSTs.
74       *
75       * @return an array of parameters for this parameterized test
76       */
77      public static Collection<Object[]> data() {
78          final DstNormalization[] normalization = DstNormalization.values();
79          final Object[][] data = new DstNormalization[normalization.length][1];
80          for (int i = 0; i < normalization.length; i++) {
81              data[i][0] = normalization[i];
82          }
83          return Arrays.asList(data);
84      }
85  
86      /**
87       * {@inheritDoc}
88       *
89       * Overriding the default implementation allows to ensure that the first
90       * element of the data set is zero.
91       */
92      @Override
93      double[] createRealData(final int n) {
94          final double[] data = super.createRealData(n);
95          data[0] = 0.0;
96          return data;
97      }
98  
99      @Override
100     RealTransformer createRealTransformer() {
101         return new FastSineTransformer(normalization);
102     }
103 
104     @Override
105     int getInvalidDataSize(final int i) {
106         return invalidDataSize[i];
107     }
108 
109     @Override
110     int getNumberOfInvalidDataSizes() {
111         return invalidDataSize.length;
112     }
113 
114     @Override
115     int getNumberOfValidDataSizes() {
116         return validDataSize.length;
117     }
118 
119     @Override
120     double getRelativeTolerance(final int i) {
121         return relativeTolerance[i];
122     }
123 
124     @Override
125     int getValidDataSize(final int i) {
126         return validDataSize[i];
127     }
128 
129     @Override
130     UnivariateFunction getValidFunction() {
131         return new Sinc();
132     }
133 
134     @Override
135     double getValidLowerBound() {
136         return 0.0;
137     }
138 
139     @Override
140     double getValidUpperBound() {
141         return FastMath.PI;
142     }
143 
144     @Override
145     double[] transform(final double[] x, final TransformType type) {
146         final int n = x.length;
147         final double[] y = new double[n];
148         final double[] sin = new double[2 * n];
149         for (int i = 0; i < sin.length; i++) {
150             sin[i] = FastMath.sin(FastMath.PI * i / n);
151         }
152         for (int j = 0; j < n; j++) {
153             double yj = 0.0;
154             for (int i = 0; i < n; i++) {
155                 yj += x[i] * sin[(i * j) % sin.length];
156             }
157             y[j] = yj;
158         }
159         final double s;
160         if (type == TransformType.FORWARD) {
161             if (normalization == DstNormalization.STANDARD_DST_I) {
162                 s = 1.0;
163             } else if (normalization == DstNormalization.ORTHOGONAL_DST_I) {
164                 s = FastMath.sqrt(2.0 / n);
165             } else {
166                 throw new MathIllegalStateException(LocalizedCoreFormats.ILLEGAL_STATE);
167             }
168         } else if (type == TransformType.INVERSE) {
169             if (normalization == DstNormalization.STANDARD_DST_I) {
170                 s = 2.0 / n;
171             } else if (normalization == DstNormalization.ORTHOGONAL_DST_I) {
172                 s = FastMath.sqrt(2.0 / n);
173             } else {
174                 throw new MathIllegalStateException(LocalizedCoreFormats.ILLEGAL_STATE);
175             }
176         } else {
177              // Should never occur. This clause is a safeguard in case other
178              // types are used to TransformType (which should not be done).
179             throw MathRuntimeException.createInternalError();
180         }
181         TransformUtils.scaleArray(y, s);
182         return y;
183     }
184 
185     @Override
186     void init(DstNormalization normalization) {
187         initFastSineTransformerTest(normalization);
188     }
189 
190     /*
191      * Additional tests.
192      */
193     @MethodSource("data")
194     @ParameterizedTest
195     void testTransformRealFirstElementNotZero(final DstNormalization normalization) {
196         initFastSineTransformerTest(normalization);
197         final TransformType[] type = TransformType.values();
198         final double[] data = new double[] {
199             1.0, 1.0, 1.0, 1.0
200         };
201         final RealTransformer transformer = createRealTransformer();
202         for (int j = 0; j < type.length; j++) {
203             try {
204                 transformer.transform(data, type[j]);
205                 fail(type[j].toString());
206             } catch (MathIllegalArgumentException e) {
207                 // Expected: do nothing
208             }
209         }
210     }
211 
212     /*
213      * Additional (legacy) tests.
214      */
215 
216     /**
217      * Test of transformer for the ad hoc data.
218      */
219     @MethodSource("data")
220     @ParameterizedTest
221     void testAdHocData(final DstNormalization normalization) {
222         initFastSineTransformerTest(normalization);
223         FastSineTransformer transformer;
224         transformer = new FastSineTransformer(DstNormalization.STANDARD_DST_I);
225         double[] result;
226         double tolerance = 1E-12;
227 
228         double[] x = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0 };
229         double[] y = { 0.0, 20.1093579685034, -9.65685424949238,
230                        5.98642305066196, -4.0, 2.67271455167720,
231                       -1.65685424949238, 0.795649469518633 };
232 
233         result = transformer.transform(x, TransformType.FORWARD);
234         for (int i = 0; i < result.length; i++) {
235             assertEquals(y[i], result[i], tolerance);
236         }
237 
238         result = transformer.transform(y, TransformType.INVERSE);
239         for (int i = 0; i < result.length; i++) {
240             assertEquals(x[i], result[i], tolerance);
241         }
242 
243         TransformUtils.scaleArray(x, FastMath.sqrt(x.length / 2.0));
244         transformer = new FastSineTransformer(DstNormalization.ORTHOGONAL_DST_I);
245 
246         result = transformer.transform(y, TransformType.FORWARD);
247         for (int i = 0; i < result.length; i++) {
248             assertEquals(x[i], result[i], tolerance);
249         }
250 
251         result = transformer.transform(x, TransformType.INVERSE);
252         for (int i = 0; i < result.length; i++) {
253             assertEquals(y[i], result[i], tolerance);
254         }
255     }
256 
257     /**
258      * Test of transformer for the sine function.
259      */
260     @MethodSource("data")
261     @ParameterizedTest
262     void testSinFunction(final DstNormalization normalization) {
263         initFastSineTransformerTest(normalization);
264         UnivariateFunction f = new Sin();
265         FastSineTransformer transformer;
266         transformer = new FastSineTransformer(DstNormalization.STANDARD_DST_I);
267         double min;
268         double max;
269         double[] result;
270         double tolerance = 1E-12;
271         int N = 1 << 8;
272 
273         min = 0.0; max = 2.0 * FastMath.PI;
274         result = transformer.transform(f, min, max, N, TransformType.FORWARD);
275         assertEquals(N >> 1, result[2], tolerance);
276         for (int i = 0; i < N; i += (i == 1 ? 2 : 1)) {
277             assertEquals(0.0, result[i], tolerance);
278         }
279 
280         min = -FastMath.PI; max = FastMath.PI;
281         result = transformer.transform(f, min, max, N, TransformType.FORWARD);
282         assertEquals(-(N >> 1), result[2], tolerance);
283         for (int i = 0; i < N; i += (i == 1 ? 2 : 1)) {
284             assertEquals(0.0, result[i], tolerance);
285         }
286     }
287 
288     /**
289      * Test of parameters for the transformer.
290      */
291     @MethodSource("data")
292     @ParameterizedTest
293     void testParameters(final DstNormalization normalization) throws Exception {
294         initFastSineTransformerTest(normalization);
295         UnivariateFunction f = new Sin();
296         FastSineTransformer transformer;
297         transformer = new FastSineTransformer(DstNormalization.STANDARD_DST_I);
298 
299         try {
300             // bad interval
301             transformer.transform(f, 1, -1, 64, TransformType.FORWARD);
302             fail("Expecting MathIllegalArgumentException - bad interval");
303         } catch (MathIllegalArgumentException ex) {
304             // expected
305         }
306         try {
307             // bad samples number
308             transformer.transform(f, -1, 1, 0, TransformType.FORWARD);
309             fail("Expecting MathIllegalArgumentException - bad samples number");
310         } catch (MathIllegalArgumentException ex) {
311             // expected
312         }
313         try {
314             // bad samples number
315             transformer.transform(f, -1, 1, 100, TransformType.FORWARD);
316             fail("Expecting MathIllegalArgumentException - bad samples number");
317         } catch (MathIllegalArgumentException ex) {
318             // expected
319         }
320     }
321 }