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 }