001 // Licensed to the Apache Software Foundation (ASF) under one
002 // or more contributor license agreements. See the NOTICE file
003 // distributed with this work for additional information
004 // regarding copyright ownership. The ASF licenses this file
005 // to you under the Apache License, Version 2.0 (the
006 // "License"); you may not use this file except in compliance
007 // with the License. You may obtain a copy of the License at
008 //
009 // http://www.apache.org/licenses/LICENSE-2.0
010 //
011 // Unless required by applicable law or agreed to in writing,
012 // software distributed under the License is distributed on an
013 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
014 // KIND, either express or implied. See the License for the
015 // specific language governing permissions and limitations
016 // under the License.
017
018 package org.apache.commons.math.optimization.fitting;
019
020 import static org.junit.Assert.assertEquals;
021 import static org.junit.Assert.assertTrue;
022
023 import java.util.Random;
024
025 import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
026 import org.apache.commons.math.optimization.DifferentiableMultivariateVectorialOptimizer;
027 import org.apache.commons.math.optimization.OptimizationException;
028 import org.apache.commons.math.optimization.general.GaussNewtonOptimizer;
029 import org.apache.commons.math.optimization.general.LevenbergMarquardtOptimizer;
030 import org.junit.Test;
031
032 public class PolynomialFitterTest {
033
034 @Test
035 public void testNoError() throws OptimizationException {
036 Random randomizer = new Random(64925784252l);
037 for (int degree = 1; degree < 10; ++degree) {
038 PolynomialFunction p = buildRandomPolynomial(degree, randomizer);
039
040 PolynomialFitter fitter =
041 new PolynomialFitter(degree, new LevenbergMarquardtOptimizer());
042 for (int i = 0; i <= degree; ++i) {
043 fitter.addObservedPoint(1.0, i, p.value(i));
044 }
045
046 PolynomialFunction fitted = fitter.fit();
047
048 for (double x = -1.0; x < 1.0; x += 0.01) {
049 double error = Math.abs(p.value(x) - fitted.value(x)) /
050 (1.0 + Math.abs(p.value(x)));
051 assertEquals(0.0, error, 1.0e-6);
052 }
053
054 }
055
056 }
057
058 @Test
059 public void testSmallError() throws OptimizationException {
060 Random randomizer = new Random(53882150042l);
061 double maxError = 0;
062 for (int degree = 0; degree < 10; ++degree) {
063 PolynomialFunction p = buildRandomPolynomial(degree, randomizer);
064
065 PolynomialFitter fitter =
066 new PolynomialFitter(degree, new LevenbergMarquardtOptimizer());
067 for (double x = -1.0; x < 1.0; x += 0.01) {
068 fitter.addObservedPoint(1.0, x,
069 p.value(x) + 0.1 * randomizer.nextGaussian());
070 }
071
072 PolynomialFunction fitted = fitter.fit();
073
074 for (double x = -1.0; x < 1.0; x += 0.01) {
075 double error = Math.abs(p.value(x) - fitted.value(x)) /
076 (1.0 + Math.abs(p.value(x)));
077 maxError = Math.max(maxError, error);
078 assertTrue(Math.abs(error) < 0.1);
079 }
080 }
081 assertTrue(maxError > 0.01);
082
083 }
084
085 @Test
086 public void testRedundantSolvable() {
087 // Levenberg-Marquardt should handle redundant information gracefully
088 checkUnsolvableProblem(new LevenbergMarquardtOptimizer(), true);
089 }
090
091 @Test
092 public void testRedundantUnsolvable() {
093 // Gauss-Newton should not be able to solve redundant information
094 DifferentiableMultivariateVectorialOptimizer optimizer =
095 new GaussNewtonOptimizer(true);
096 checkUnsolvableProblem(optimizer, false);
097 }
098
099 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 }