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.polynomials;
18  
19  
20  import org.apache.commons.math4.legacy.analysis.UnivariateFunction;
21  import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
22  import org.apache.commons.math4.legacy.exception.MathIllegalStateException;
23  import org.apache.commons.math4.legacy.exception.OutOfRangeException;
24  import org.junit.Assert;
25  import org.junit.Test;
26  
27  /**
28   * Tests the PolynomialSplineFunction implementation.
29   *
30   */
31  public class PolynomialSplineFunctionTest {
32  
33      /** Error tolerance for tests */
34      protected double tolerance = 1.0e-12;
35  
36      /**
37       * Quadratic polynomials used in tests:
38       *
39       * x^2 + x            [-1, 0)
40       * x^2 + x + 2        [0, 1)
41       * x^2 + x + 4        [1, 2)
42       *
43       * Defined so that evaluation using PolynomialSplineFunction evaluation
44       * algorithm agrees at knot point boundaries.
45       */
46      protected PolynomialFunction[] polynomials = {
47          new PolynomialFunction(new double[] {0d, 1d, 1d}),
48          new PolynomialFunction(new double[] {2d, 1d, 1d}),
49          new PolynomialFunction(new double[] {4d, 1d, 1d})
50      };
51  
52      /** Knot points  */
53      protected double[] knots = {-1, 0, 1, 2};
54  
55      /** Derivative of test polynomials -- 2x + 1  */
56      protected PolynomialFunction dp =
57          new PolynomialFunction(new double[] {1d, 2d});
58  
59  
60      @Test
61      public void testConstructor() {
62          PolynomialSplineFunction spline =
63              new PolynomialSplineFunction(knots, polynomials);
64          Assert.assertArrayEquals(knots, spline.getKnots(), 0.0);
65          Assert.assertEquals(1d, spline.getPolynomials()[0].getCoefficients()[2], 0);
66          Assert.assertEquals(3, spline.getN());
67  
68          try { // too few knots
69              new PolynomialSplineFunction(new double[] {0}, polynomials);
70              Assert.fail("Expecting MathIllegalArgumentException");
71          } catch (MathIllegalArgumentException ex) {
72              // expected
73          }
74  
75          try { // too many knots
76              new PolynomialSplineFunction(new double[] {0,1,2,3,4}, polynomials);
77              Assert.fail("Expecting MathIllegalArgumentException");
78          } catch (MathIllegalArgumentException ex) {
79              // expected
80          }
81  
82          try { // knots not increasing
83              new PolynomialSplineFunction(new double[] {0,1, 3, 2}, polynomials);
84              Assert.fail("Expecting MathIllegalArgumentException");
85          } catch (MathIllegalArgumentException ex) {
86              // expected
87          }
88      }
89  
90      @Test
91      public void testValues() {
92          PolynomialSplineFunction spline =
93              new PolynomialSplineFunction(knots, polynomials);
94          UnivariateFunction dSpline = spline.polynomialSplineDerivative();
95  
96          /**
97           * interior points -- spline value at x should equal p(x - knot)
98           * where knot is the largest knot point less than or equal to x and p
99           * is the polynomial defined over the knot segment to which x belongs.
100          */
101         double x = -1;
102         int index = 0;
103         for (int i = 0; i < 10; i++) {
104            x+=0.25;
105            index = findKnot(knots, x);
106            Assert.assertEquals("spline function evaluation failed for x=" + x,
107                    polynomials[index].value(x - knots[index]), spline.value(x), tolerance);
108            Assert.assertEquals("spline derivative evaluation failed for x=" + x,
109                    dp.value(x - knots[index]), dSpline.value(x), tolerance);
110         }
111 
112         // knot points -- centering should zero arguments
113         for (int i = 0; i < 3; i++) {
114             Assert.assertEquals("spline function evaluation failed for knot=" + knots[i],
115                     polynomials[i].value(0), spline.value(knots[i]), tolerance);
116             Assert.assertEquals("spline function evaluation failed for knot=" + knots[i],
117                     dp.value(0), dSpline.value(knots[i]), tolerance);
118         }
119 
120         try { //outside of domain -- under min
121             x = spline.value(-1.5);
122             Assert.fail("Expecting OutOfRangeException");
123         } catch (OutOfRangeException ex) {
124             // expected
125         }
126 
127         try { //outside of domain -- over max
128             x = spline.value(2.5);
129             Assert.fail("Expecting OutOfRangeException");
130         } catch (OutOfRangeException ex) {
131             // expected
132         }
133     }
134 
135     @Test
136     public void testIsValidPoint() {
137         final PolynomialSplineFunction spline =
138             new PolynomialSplineFunction(knots, polynomials);
139         final double xMin = knots[0];
140         final double xMax = knots[knots.length - 1];
141 
142         double x;
143 
144         x = xMin;
145         Assert.assertTrue(spline.isValidPoint(x));
146         // Ensure that no exception is thrown.
147         spline.value(x);
148 
149         x = xMax;
150         Assert.assertTrue(spline.isValidPoint(x));
151         // Ensure that no exception is thrown.
152         spline.value(x);
153 
154         final double xRange = xMax - xMin;
155         x = xMin + xRange / 3.4;
156         Assert.assertTrue(spline.isValidPoint(x));
157         // Ensure that no exception is thrown.
158         spline.value(x);
159 
160         final double small = 1e-8;
161         x = xMin - small;
162         Assert.assertFalse(spline.isValidPoint(x));
163         // Ensure that an exception would have been thrown.
164         try {
165             spline.value(x);
166             Assert.fail("OutOfRangeException expected");
167         } catch (OutOfRangeException expected) {}
168     }
169 
170     /**
171      *  Do linear search to find largest knot point less than or equal to x.
172      *  Implementation does binary search.
173      */
174      protected int findKnot(double[] knots, double x) {
175          if (x < knots[0] || x >= knots[knots.length -1]) {
176              throw new OutOfRangeException(x, knots[0], knots[knots.length -1]);
177          }
178          for (int i = 0; i < knots.length; i++) {
179              if (knots[i] > x) {
180                  return i - 1;
181              }
182          }
183          throw new MathIllegalStateException();
184      }
185 }
186