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.math.analysis.interpolation;
18  
19  import org.apache.commons.math.MathException;
20  import org.apache.commons.math.TestUtils;
21  import org.apache.commons.math.analysis.UnivariateRealFunction;
22  import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
23  import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
24  
25  import junit.framework.Test;
26  import junit.framework.TestCase;
27  import junit.framework.TestSuite;
28  
29  /**
30   * Test the SplineInterpolator.
31   *
32   * @version $Revision: 799857 $ $Date: 2009-08-01 09:07:12 -0400 (Sat, 01 Aug 2009) $ 
33   */
34  public class SplineInterpolatorTest extends TestCase {
35      
36      /** error tolerance for spline interpolator value at knot points */
37      protected double knotTolerance = 1E-12;
38     
39      /** error tolerance for interpolating polynomial coefficients */
40      protected double coefficientTolerance = 1E-6;
41      
42      /** error tolerance for interpolated values -- high value is from sin test */
43      protected double interpolationTolerance = 1E-2;
44  
45      public SplineInterpolatorTest(String name) {
46          super(name);
47      }
48  
49      public static Test suite() {
50          TestSuite suite = new TestSuite(SplineInterpolatorTest.class);
51          suite.setName("UnivariateRealInterpolator Tests");
52          return suite;
53      }
54  
55      public void testInterpolateLinearDegenerateTwoSegment()
56          throws Exception {
57          double x[] = { 0.0, 0.5, 1.0 };
58          double y[] = { 0.0, 0.5, 1.0 };
59          UnivariateRealInterpolator i = new SplineInterpolator();
60          UnivariateRealFunction f = i.interpolate(x, y);
61          verifyInterpolation(f, x, y);
62          verifyConsistency((PolynomialSplineFunction) f, x);
63          
64          // Verify coefficients using analytical values
65          PolynomialFunction polynomials[] = ((PolynomialSplineFunction) f).getPolynomials();
66          double target[] = {y[0], 1d};
67          TestUtils.assertEquals(polynomials[0].getCoefficients(), target, coefficientTolerance);
68          target = new double[]{y[1], 1d};
69          TestUtils.assertEquals(polynomials[1].getCoefficients(), target, coefficientTolerance);
70          
71          // Check interpolation
72          assertEquals(0.0,f.value(0.0), interpolationTolerance);
73          assertEquals(0.4,f.value(0.4), interpolationTolerance);
74          assertEquals(1.0,f.value(1.0), interpolationTolerance);
75      }
76  
77      public void testInterpolateLinearDegenerateThreeSegment()
78          throws Exception {
79          double x[] = { 0.0, 0.5, 1.0, 1.5 };
80          double y[] = { 0.0, 0.5, 1.0, 1.5 };
81          UnivariateRealInterpolator i = new SplineInterpolator();
82          UnivariateRealFunction f = i.interpolate(x, y);
83          verifyInterpolation(f, x, y);
84          
85          // Verify coefficients using analytical values
86          PolynomialFunction polynomials[] = ((PolynomialSplineFunction) f).getPolynomials();
87          double target[] = {y[0], 1d};
88          TestUtils.assertEquals(polynomials[0].getCoefficients(), target, coefficientTolerance);
89          target = new double[]{y[1], 1d};
90          TestUtils.assertEquals(polynomials[1].getCoefficients(), target, coefficientTolerance);
91          target = new double[]{y[2], 1d};
92          TestUtils.assertEquals(polynomials[2].getCoefficients(), target, coefficientTolerance);
93          
94          // Check interpolation
95          assertEquals(0,f.value(0), interpolationTolerance);
96          assertEquals(1.4,f.value(1.4), interpolationTolerance);
97          assertEquals(1.5,f.value(1.5), interpolationTolerance);
98      }
99  
100     public void testInterpolateLinear() throws Exception {
101         double x[] = { 0.0, 0.5, 1.0 };
102         double y[] = { 0.0, 0.5, 0.0 };
103         UnivariateRealInterpolator i = new SplineInterpolator();
104         UnivariateRealFunction f = i.interpolate(x, y);
105         verifyInterpolation(f, x, y);
106         verifyConsistency((PolynomialSplineFunction) f, x);
107         
108         // Verify coefficients using analytical values
109         PolynomialFunction polynomials[] = ((PolynomialSplineFunction) f).getPolynomials();
110         double target[] = {y[0], 1.5d, 0d, -2d};
111         TestUtils.assertEquals(polynomials[0].getCoefficients(), target, coefficientTolerance);
112         target = new double[]{y[1], 0d, -3d, 2d};
113         TestUtils.assertEquals(polynomials[1].getCoefficients(), target, coefficientTolerance);    
114     }
115     
116     public void testInterpolateSin() throws Exception {
117         double x[] =
118             {
119                 0.0,
120                 Math.PI / 6d,
121                 Math.PI / 2d,
122                 5d * Math.PI / 6d,
123                 Math.PI,
124                 7d * Math.PI / 6d,
125                 3d * Math.PI / 2d,
126                 11d * Math.PI / 6d,
127                 2.d * Math.PI };
128         double y[] = { 0d, 0.5d, 1d, 0.5d, 0d, -0.5d, -1d, -0.5d, 0d };
129         UnivariateRealInterpolator i = new SplineInterpolator();
130         UnivariateRealFunction f = i.interpolate(x, y);
131         verifyInterpolation(f, x, y);
132         verifyConsistency((PolynomialSplineFunction) f, x);
133         
134         /* Check coefficients against values computed using R (version 1.8.1, Red Hat Linux 9)
135          * 
136          * To replicate in R:
137          *     x[1] <- 0
138          *     x[2] <- pi / 6, etc, same for y[] (could use y <- scan() for y values)
139          *     g <- splinefun(x, y, "natural")
140          *     splinecoef <- eval(expression(z), envir = environment(g))
141          *     print(splinecoef) 
142          */
143         PolynomialFunction polynomials[] = ((PolynomialSplineFunction) f).getPolynomials();
144         double target[] = {y[0], 1.002676d, 0d, -0.17415829d};
145         TestUtils.assertEquals(polynomials[0].getCoefficients(), target, coefficientTolerance);
146         target = new double[]{y[1], 8.594367e-01, -2.735672e-01, -0.08707914};
147         TestUtils.assertEquals(polynomials[1].getCoefficients(), target, coefficientTolerance);
148         target = new double[]{y[2], 1.471804e-17,-5.471344e-01, 0.08707914};
149         TestUtils.assertEquals(polynomials[2].getCoefficients(), target, coefficientTolerance);
150         target = new double[]{y[3], -8.594367e-01, -2.735672e-01, 0.17415829};
151         TestUtils.assertEquals(polynomials[3].getCoefficients(), target, coefficientTolerance);
152         target = new double[]{y[4], -1.002676, 6.548562e-17, 0.17415829};
153         TestUtils.assertEquals(polynomials[4].getCoefficients(), target, coefficientTolerance);
154         target = new double[]{y[5], -8.594367e-01, 2.735672e-01, 0.08707914};
155         TestUtils.assertEquals(polynomials[5].getCoefficients(), target, coefficientTolerance);
156         target = new double[]{y[6], 3.466465e-16, 5.471344e-01, -0.08707914};
157         TestUtils.assertEquals(polynomials[6].getCoefficients(), target, coefficientTolerance);
158         target = new double[]{y[7], 8.594367e-01, 2.735672e-01, -0.17415829};
159         TestUtils.assertEquals(polynomials[7].getCoefficients(), target, coefficientTolerance); 
160         
161         //Check interpolation
162         assertEquals(Math.sqrt(2d) / 2d,f.value(Math.PI/4d),interpolationTolerance);
163         assertEquals(Math.sqrt(2d) / 2d,f.value(3d*Math.PI/4d),interpolationTolerance);     
164     }
165     
166 
167     public void testIllegalArguments() throws MathException {
168         // Data set arrays of different size.
169         UnivariateRealInterpolator i = new SplineInterpolator();
170         try {
171             double xval[] = { 0.0, 1.0 };
172             double yval[] = { 0.0, 1.0, 2.0 };
173             i.interpolate(xval, yval);
174             fail("Failed to detect data set array with different sizes.");
175         } catch (IllegalArgumentException iae) {
176         }
177         // X values not sorted.
178         try {
179             double xval[] = { 0.0, 1.0, 0.5 };
180             double yval[] = { 0.0, 1.0, 2.0 };
181             i.interpolate(xval, yval);
182             fail("Failed to detect unsorted arguments.");
183         } catch (IllegalArgumentException iae) {
184         }
185     }
186     
187     /**
188      * verifies that f(x[i]) = y[i] for i = 0..n-1 where n is common length.
189      */
190     protected void verifyInterpolation(UnivariateRealFunction f, double x[], double y[])  
191         throws Exception{
192         for (int i = 0; i < x.length; i++) {
193             assertEquals(f.value(x[i]), y[i], knotTolerance);
194         }     
195     }
196     
197     /**
198      * Verifies that interpolating polynomials satisfy consistency requirement:
199      *    adjacent polynomials must agree through two derivatives at knot points
200      */
201     protected void verifyConsistency(PolynomialSplineFunction f, double x[]) 
202         throws Exception {
203         PolynomialFunction polynomials[] = f.getPolynomials();
204         for (int i = 1; i < x.length - 2; i++) {
205             // evaluate polynomials and derivatives at x[i + 1]  
206             assertEquals(polynomials[i].value(x[i +1] - x[i]), polynomials[i + 1].value(0), 0.1); 
207             assertEquals(polynomials[i].derivative().value(x[i +1] - x[i]), 
208                     polynomials[i + 1].derivative().value(0), 0.5); 
209             assertEquals(polynomials[i].polynomialDerivative().derivative().value(x[i +1] - x[i]), 
210                     polynomials[i + 1].polynomialDerivative().derivative().value(0), 0.5); 
211         }
212     }
213     
214 }