1   // Licensed to the Apache Software Foundation (ASF) under one
2   // or more contributor license agreements.  See the NOTICE file
3   // distributed with this work for additional information
4   // regarding copyright ownership.  The ASF licenses this file
5   // to you under the Apache License, Version 2.0 (the
6   // "License"); you may not use this file except in compliance
7   // with 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,
12  // software distributed under the License is distributed on an
13  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14  // KIND, either express or implied.  See the License for the
15  // specific language governing permissions and limitations
16  // under the License.
17  
18  package org.apache.commons.math.optimization.fitting;
19  
20  import static org.junit.Assert.assertEquals;
21  import static org.junit.Assert.assertTrue;
22  
23  import java.util.Random;
24  
25  import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
26  import org.apache.commons.math.optimization.DifferentiableMultivariateVectorialOptimizer;
27  import org.apache.commons.math.optimization.OptimizationException;
28  import org.apache.commons.math.optimization.general.GaussNewtonOptimizer;
29  import org.apache.commons.math.optimization.general.LevenbergMarquardtOptimizer;
30  import org.junit.Test;
31  
32  public class PolynomialFitterTest {
33  
34      @Test
35      public void testNoError() throws OptimizationException {
36          Random randomizer = new Random(64925784252l);
37          for (int degree = 1; degree < 10; ++degree) {
38              PolynomialFunction p = buildRandomPolynomial(degree, randomizer);
39  
40              PolynomialFitter fitter =
41                  new PolynomialFitter(degree, new LevenbergMarquardtOptimizer());
42              for (int i = 0; i <= degree; ++i) {
43                  fitter.addObservedPoint(1.0, i, p.value(i));
44              }
45  
46              PolynomialFunction fitted = fitter.fit();
47  
48              for (double x = -1.0; x < 1.0; x += 0.01) {
49                  double error = Math.abs(p.value(x) - fitted.value(x)) /
50                                 (1.0 + Math.abs(p.value(x)));
51                  assertEquals(0.0, error, 1.0e-6);
52              }
53  
54          }
55  
56      }
57  
58      @Test
59      public void testSmallError() throws OptimizationException {
60          Random randomizer = new Random(53882150042l);
61          double maxError = 0;
62          for (int degree = 0; degree < 10; ++degree) {
63              PolynomialFunction p = buildRandomPolynomial(degree, randomizer);
64  
65              PolynomialFitter fitter =
66                  new PolynomialFitter(degree, new LevenbergMarquardtOptimizer());
67              for (double x = -1.0; x < 1.0; x += 0.01) {
68                  fitter.addObservedPoint(1.0, x,
69                                          p.value(x) + 0.1 * randomizer.nextGaussian());
70              }
71  
72              PolynomialFunction fitted = fitter.fit();
73  
74              for (double x = -1.0; x < 1.0; x += 0.01) {
75                  double error = Math.abs(p.value(x) - fitted.value(x)) /
76                                (1.0 + Math.abs(p.value(x)));
77                  maxError = Math.max(maxError, error);
78                  assertTrue(Math.abs(error) < 0.1);
79              }
80          }
81          assertTrue(maxError > 0.01);
82  
83      }
84  
85      @Test
86      public void testRedundantSolvable() {
87          // Levenberg-Marquardt should handle redundant information gracefully
88          checkUnsolvableProblem(new LevenbergMarquardtOptimizer(), true);
89      }
90  
91      @Test
92      public void testRedundantUnsolvable() {
93          // Gauss-Newton should not be able to solve redundant information
94          DifferentiableMultivariateVectorialOptimizer optimizer =
95              new GaussNewtonOptimizer(true);
96          checkUnsolvableProblem(optimizer, false);
97      }
98  
99      private void checkUnsolvableProblem(DifferentiableMultivariateVectorialOptimizer optimizer,
100                                         boolean solvable) {
101         Random randomizer = new Random(1248788532l);
102         for (int degree = 0; degree < 10; ++degree) {
103             PolynomialFunction p = buildRandomPolynomial(degree, randomizer);
104 
105             PolynomialFitter fitter = new PolynomialFitter(degree, optimizer);
106 
107             // reusing the same point over and over again does not bring
108             // information, the problem cannot be solved in this case for
109             // degrees greater than 1 (but one point is sufficient for
110             // degree 0)
111             for (double x = -1.0; x < 1.0; x += 0.01) {
112                 fitter.addObservedPoint(1.0, 0.0, p.value(0.0));
113             }
114 
115             try {
116                 fitter.fit();
117                 assertTrue(solvable || (degree == 0));
118             } catch(OptimizationException e) {
119                 assertTrue((! solvable) && (degree > 0));
120             }
121 
122         }
123 
124     }
125 
126     private PolynomialFunction buildRandomPolynomial(int degree, Random randomizer) {
127         final double[] coefficients = new double[degree + 1];
128         for (int i = 0; i <= degree; ++i) {
129             coefficients[i] = randomizer.nextGaussian();
130         }
131         return new PolynomialFunction(coefficients);
132     }
133 
134 }