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