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