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  *      http://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 package org.apache.commons.math3.fitting;
18 
19 import java.util.Random;
20 
21 import org.apache.commons.math3.optim.nonlinear.vector.jacobian.LevenbergMarquardtOptimizer;
22 import org.apache.commons.math3.analysis.function.HarmonicOscillator;
23 import org.apache.commons.math3.exception.NumberIsTooSmallException;
24 import org.apache.commons.math3.exception.MathIllegalStateException;
25 import org.apache.commons.math3.util.FastMath;
26 import org.apache.commons.math3.util.MathUtils;
27 import org.junit.Test;
28 import org.junit.Assert;
29 
30 @Deprecated
31 public class HarmonicFitterTest {
32     @Test(expected=NumberIsTooSmallException.class)
testPreconditions1()33     public void testPreconditions1() {
34         HarmonicFitter fitter =
35             new HarmonicFitter(new LevenbergMarquardtOptimizer());
36 
37         fitter.fit();
38     }
39 
40     @Test
testNoError()41     public void testNoError() {
42         final double a = 0.2;
43         final double w = 3.4;
44         final double p = 4.1;
45         HarmonicOscillator f = new HarmonicOscillator(a, w, p);
46 
47         HarmonicFitter fitter =
48             new HarmonicFitter(new LevenbergMarquardtOptimizer());
49         for (double x = 0.0; x < 1.3; x += 0.01) {
50             fitter.addObservedPoint(1, x, f.value(x));
51         }
52 
53         final double[] fitted = fitter.fit();
54         Assert.assertEquals(a, fitted[0], 1.0e-13);
55         Assert.assertEquals(w, fitted[1], 1.0e-13);
56         Assert.assertEquals(p, MathUtils.normalizeAngle(fitted[2], p), 1e-13);
57 
58         HarmonicOscillator ff = new HarmonicOscillator(fitted[0], fitted[1], fitted[2]);
59 
60         for (double x = -1.0; x < 1.0; x += 0.01) {
61             Assert.assertTrue(FastMath.abs(f.value(x) - ff.value(x)) < 1e-13);
62         }
63     }
64 
65     @Test
66     public void test1PercentError() {
67         Random randomizer = new Random(64925784252l);
68         final double a = 0.2;
69         final double w = 3.4;
70         final double p = 4.1;
71         HarmonicOscillator f = new HarmonicOscillator(a, w, p);
72 
73         HarmonicFitter fitter =
74             new HarmonicFitter(new LevenbergMarquardtOptimizer());
75         for (double x = 0.0; x < 10.0; x += 0.1) {
76             fitter.addObservedPoint(1, x,
77                                     f.value(x) + 0.01 * randomizer.nextGaussian());
78         }
79 
80         final double[] fitted = fitter.fit();
81         Assert.assertEquals(a, fitted[0], 7.6e-4);
82         Assert.assertEquals(w, fitted[1], 2.7e-3);
83         Assert.assertEquals(p, MathUtils.normalizeAngle(fitted[2], p), 1.3e-2);
84     }
85 
86     @Test
87     public void testTinyVariationsData() {
88         Random randomizer = new Random(64925784252l);
89 
90         HarmonicFitter fitter =
91             new HarmonicFitter(new LevenbergMarquardtOptimizer());
92         for (double x = 0.0; x < 10.0; x += 0.1) {
93             fitter.addObservedPoint(1, x, 1e-7 * randomizer.nextGaussian());
94         }
95 
96         fitter.fit();
97         // This test serves to cover the part of the code of "guessAOmega"
98         // when the algorithm using integrals fails.
99     }
100 
101     @Test
102     public void testInitialGuess() {
103         Random randomizer = new Random(45314242l);
104         final double a = 0.2;
105         final double w = 3.4;
106         final double p = 4.1;
107         HarmonicOscillator f = new HarmonicOscillator(a, w, p);
108 
109         HarmonicFitter fitter =
110             new HarmonicFitter(new LevenbergMarquardtOptimizer());
111         for (double x = 0.0; x < 10.0; x += 0.1) {
112             fitter.addObservedPoint(1, x,
113                                     f.value(x) + 0.01 * randomizer.nextGaussian());
114         }
115 
116         final double[] fitted = fitter.fit(new double[] { 0.15, 3.6, 4.5 });
117         Assert.assertEquals(a, fitted[0], 1.2e-3);
118         Assert.assertEquals(w, fitted[1], 3.3e-3);
119         Assert.assertEquals(p, MathUtils.normalizeAngle(fitted[2], p), 1.7e-2);
120     }
121 
122     @Test
123     public void testUnsorted() {
124         Random randomizer = new Random(64925784252l);
125         final double a = 0.2;
126         final double w = 3.4;
127         final double p = 4.1;
128         HarmonicOscillator f = new HarmonicOscillator(a, w, p);
129 
130         HarmonicFitter fitter =
131             new HarmonicFitter(new LevenbergMarquardtOptimizer());
132 
133         // build a regularly spaced array of measurements
134         int size = 100;
135         double[] xTab = new double[size];
136         double[] yTab = new double[size];
137         for (int i = 0; i < size; ++i) {
138             xTab[i] = 0.1 * i;
139             yTab[i] = f.value(xTab[i]) + 0.01 * randomizer.nextGaussian();
140         }
141 
142         // shake it
143         for (int i = 0; i < size; ++i) {
144             int i1 = randomizer.nextInt(size);
145             int i2 = randomizer.nextInt(size);
146             double xTmp = xTab[i1];
147             double yTmp = yTab[i1];
148             xTab[i1] = xTab[i2];
149             yTab[i1] = yTab[i2];
150             xTab[i2] = xTmp;
151             yTab[i2] = yTmp;
152         }
153 
154         // pass it to the fitter
155         for (int i = 0; i < size; ++i) {
156             fitter.addObservedPoint(1, xTab[i], yTab[i]);
157         }
158 
159         final double[] fitted = fitter.fit();
160         Assert.assertEquals(a, fitted[0], 7.6e-4);
161         Assert.assertEquals(w, fitted[1], 3.5e-3);
162         Assert.assertEquals(p, MathUtils.normalizeAngle(fitted[2], p), 1.5e-2);
163     }
164 
165     @Test(expected=MathIllegalStateException.class)
166     public void testMath844() {
167         final double[] y = { 0, 1, 2, 3, 2, 1,
168                              0, -1, -2, -3, -2, -1,
169                              0, 1, 2, 3, 2, 1,
170                              0, -1, -2, -3, -2, -1,
171                              0, 1, 2, 3, 2, 1, 0 };
172         final int len = y.length;
173         final WeightedObservedPoint[] points = new WeightedObservedPoint[len];
174         for (int i = 0; i < len; i++) {
175             points[i] = new WeightedObservedPoint(1, i, y[i]);
176         }
177 
178         // The guesser fails because the function is far from an harmonic
179         // function: It is a triangular periodic function with amplitude 3
180         // and period 12, and all sample points are taken at integer abscissae
181         // so function values all belong to the integer subset {-3, -2, -1, 0,
182         // 1, 2, 3}.
183         new HarmonicFitter.ParameterGuesser(points);
184     }
185 }
186