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.exception.LocalizedCoreFormats;
25  import org.hipparchus.exception.MathIllegalArgumentException;
26  import org.hipparchus.util.FastMath;
27  import org.junit.Assert;
28  import org.junit.Test;
29  
30  /**
31   * Test of the LoessInterpolator class.
32   */
33  public class LoessInterpolatorTest {
34  
35      @Test
36      public void testOnOnePoint() {
37          double[] xval = {0.5};
38          double[] yval = {0.7};
39          double[] res = new LoessInterpolator().smooth(xval, yval);
40          Assert.assertEquals(1, res.length);
41          Assert.assertEquals(0.7, res[0], 0.0);
42      }
43  
44      @Test
45      public void testOnTwoPoints() {
46          double[] xval = {0.5, 0.6};
47          double[] yval = {0.7, 0.8};
48          double[] res = new LoessInterpolator().smooth(xval, yval);
49          Assert.assertEquals(2, res.length);
50          Assert.assertEquals(0.7, res[0], 0.0);
51          Assert.assertEquals(0.8, res[1], 0.0);
52      }
53  
54      @Test
55      public void testOnStraightLine() {
56          double[] xval = {1,2,3,4,5};
57          double[] yval = {2,4,6,8,10};
58          LoessInterpolator li = new LoessInterpolator(0.6, 2, 1e-12);
59          double[] res = li.smooth(xval, yval);
60          Assert.assertEquals(5, res.length);
61          for(int i = 0; i < 5; ++i) {
62              Assert.assertEquals(yval[i], res[i], 1e-8);
63          }
64      }
65  
66      @Test
67      public void testOnDistortedSine() {
68          int numPoints = 100;
69          double[] xval = new double[numPoints];
70          double[] yval = new double[numPoints];
71          double xnoise = 0.1;
72          double ynoise = 0.2;
73  
74          generateSineData(xval, yval, xnoise, ynoise);
75  
76          LoessInterpolator li = new LoessInterpolator(0.3, 4, 1e-12);
77  
78          double[] res = li.smooth(xval, yval);
79  
80          // Check that the resulting curve differs from
81          // the "real" sine less than the jittered one
82  
83          double noisyResidualSum = 0;
84          double fitResidualSum = 0;
85  
86          for(int i = 0; i < numPoints; ++i) {
87              double expected = FastMath.sin(xval[i]);
88              double noisy = yval[i];
89              double fit = res[i];
90  
91              noisyResidualSum += FastMath.pow(noisy - expected, 2);
92              fitResidualSum += FastMath.pow(fit - expected, 2);
93          }
94  
95          Assert.assertTrue(fitResidualSum < noisyResidualSum);
96      }
97  
98      @Test
99      public void testIncreasingBandwidthIncreasesSmoothness() {
100         int numPoints = 100;
101         double[] xval = new double[numPoints];
102         double[] yval = new double[numPoints];
103         double xnoise = 0.1;
104         double ynoise = 0.1;
105 
106         generateSineData(xval, yval, xnoise, ynoise);
107 
108         // Check that variance decreases as bandwidth increases
109 
110         double[] bandwidths = {0.1, 0.5, 1.0};
111         double[] variances = new double[bandwidths.length];
112         for (int i = 0; i < bandwidths.length; i++) {
113             double bw = bandwidths[i];
114 
115             LoessInterpolator li = new LoessInterpolator(bw, 4, 1e-12);
116 
117             double[] res = li.smooth(xval, yval);
118 
119             for (int j = 1; j < res.length; ++j) {
120                 variances[i] += FastMath.pow(res[j] - res[j-1], 2);
121             }
122         }
123 
124         for(int i = 1; i < variances.length; ++i) {
125             Assert.assertTrue(variances[i] < variances[i-1]);
126         }
127     }
128 
129     @Test
130     public void testIncreasingRobustnessItersIncreasesSmoothnessWithOutliers() {
131         int numPoints = 100;
132         double[] xval = new double[numPoints];
133         double[] yval = new double[numPoints];
134         double xnoise = 0.1;
135         double ynoise = 0.1;
136 
137         generateSineData(xval, yval, xnoise, ynoise);
138 
139         // Introduce a couple of outliers
140         yval[numPoints/3] *= 100;
141         yval[2 * numPoints/3] *= -100;
142 
143         // Check that variance decreases as the number of robustness
144         // iterations increases
145 
146         double[] variances = new double[4];
147         for (int i = 0; i < 4; i++) {
148             LoessInterpolator li = new LoessInterpolator(0.3, i, 1e-12);
149 
150             double[] res = li.smooth(xval, yval);
151 
152             for (int j = 1; j < res.length; ++j) {
153                 variances[i] += FastMath.abs(res[j] - res[j-1]);
154             }
155         }
156 
157         for(int i = 1; i < variances.length; ++i) {
158             Assert.assertTrue(variances[i] < variances[i-1]);
159         }
160     }
161 
162     @Test(expected=MathIllegalArgumentException.class)
163     public void testUnequalSizeArguments() {
164         new LoessInterpolator().smooth(new double[] {1,2,3}, new double[] {1,2,3,4});
165     }
166 
167     @Test(expected=MathIllegalArgumentException.class)
168     public void testEmptyData() {
169         new LoessInterpolator().smooth(new double[] {}, new double[] {});
170     }
171 
172     @Test(expected=MathIllegalArgumentException.class)
173     public void testNonStrictlyIncreasing1() {
174         new LoessInterpolator().smooth(new double[] {4,3,1,2}, new double[] {3,4,5,6});
175     }
176 
177     @Test(expected=MathIllegalArgumentException.class)
178     public void testNonStrictlyIncreasing2() {
179         new LoessInterpolator().smooth(new double[] {1,2,2,3}, new double[] {3,4,5,6});
180     }
181 
182     @Test
183     public void testNotAllFiniteReal1() {
184         try {
185             new LoessInterpolator().smooth(new double[] {1,2,Double.NaN}, new double[] {3,4,5});
186             Assert.fail("an exception should have been thrown");
187         } catch (MathIllegalArgumentException e) {
188             Assert.assertEquals(LocalizedCoreFormats.NOT_FINITE_NUMBER, e.getSpecifier());
189         }
190     }
191 
192     @Test
193     public void testNotAllFiniteReal2() {
194         try {
195             new LoessInterpolator().smooth(new double[] {1,2,Double.POSITIVE_INFINITY}, new double[] {3,4,5});
196         } catch (MathIllegalArgumentException e) {
197             Assert.assertEquals(LocalizedCoreFormats.NOT_FINITE_NUMBER, e.getSpecifier());
198         }
199     }
200 
201     @Test
202     public void testNotAllFiniteReal3() {
203         try {
204             new LoessInterpolator().smooth(new double[] {1,2,Double.NEGATIVE_INFINITY}, new double[] {3,4,5});
205         } catch (MathIllegalArgumentException e) {
206             Assert.assertEquals(LocalizedCoreFormats.NOT_FINITE_NUMBER, e.getSpecifier());
207         }
208     }
209 
210     @Test
211     public void testNotAllFiniteReal4() {
212         try {
213             new LoessInterpolator().smooth(new double[] {3,4,5}, new double[] {1,2,Double.NaN});
214         } catch (MathIllegalArgumentException e) {
215             Assert.assertEquals(LocalizedCoreFormats.NOT_FINITE_NUMBER, e.getSpecifier());
216         }
217     }
218 
219     @Test
220     public void testNotAllFiniteReal5() {
221         try {
222             new LoessInterpolator().smooth(new double[] {3,4,5}, new double[] {1,2,Double.POSITIVE_INFINITY});
223         } catch (MathIllegalArgumentException e) {
224             Assert.assertEquals(LocalizedCoreFormats.NOT_FINITE_NUMBER, e.getSpecifier());
225         }
226     }
227 
228     @Test
229     public void testNotAllFiniteReal6() {
230         try {
231             new LoessInterpolator().smooth(new double[] {3,4,5}, new double[] {1,2,Double.NEGATIVE_INFINITY});
232         } catch (MathIllegalArgumentException e) {
233             Assert.assertEquals(LocalizedCoreFormats.NOT_FINITE_NUMBER, e.getSpecifier());
234         }
235     }
236 
237     @Test(expected=MathIllegalArgumentException.class)
238     public void testInsufficientBandwidth() {
239         LoessInterpolator li = new LoessInterpolator(0.1, 3, 1e-12);
240         li.smooth(new double[] {1,2,3,4,5,6,7,8,9,10,11,12}, new double[] {1,2,3,4,5,6,7,8,9,10,11,12});
241     }
242 
243     @Test(expected=MathIllegalArgumentException.class)
244     public void testCompletelyIncorrectBandwidth1() {
245         new LoessInterpolator(-0.2, 3, 1e-12);
246     }
247 
248     @Test(expected=MathIllegalArgumentException.class)
249     public void testCompletelyIncorrectBandwidth2() {
250         new LoessInterpolator(1.1, 3, 1e-12);
251     }
252 
253     @Test
254     public void testMath296withoutWeights() {
255         double[] xval = {
256                 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
257                  1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0};
258         double[] yval = {
259                 0.47, 0.48, 0.55, 0.56, -0.08, -0.04, -0.07, -0.07,
260                 -0.56, -0.46, -0.56, -0.52, -3.03, -3.08, -3.09,
261                 -3.04, 3.54, 3.46, 3.36, 3.35};
262         // Output from R, rounded to .001
263         double[] yref = {
264                 0.461, 0.499, 0.541, 0.308, 0.175, -0.042, -0.072,
265                 -0.196, -0.311, -0.446, -0.557, -1.497, -2.133,
266                 -3.08, -3.09, -0.621, 0.982, 3.449, 3.389, 3.336
267         };
268         LoessInterpolator li = new LoessInterpolator(0.3, 4, 1e-12);
269         double[] res = li.smooth(xval, yval);
270         Assert.assertEquals(xval.length, res.length);
271         for(int i = 0; i < res.length; ++i) {
272             Assert.assertEquals(yref[i], res[i], 0.02);
273         }
274     }
275 
276     private void generateSineData(double[] xval, double[] yval, double xnoise, double ynoise) {
277         double dx = 2 * FastMath.PI / xval.length;
278         double x = 0;
279         for(int i = 0; i < xval.length; ++i) {
280             xval[i] = x;
281             yval[i] = FastMath.sin(x) + (2 * FastMath.random() - 1) * ynoise;
282             x += dx * (1 + (2 * FastMath.random() - 1) * xnoise);
283         }
284     }
285 }