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;
18  
19  import java.util.Arrays;
20  import junit.framework.TestCase;
21  
22  import org.apache.commons.math.FunctionEvaluationException;
23  
24  /**
25   * Tests the PolynomialSplineFunction implementation.
26   *
27   * @version $Revision: 566833 $
28   */
29  public class PolynomialSplineFunctionTest extends TestCase {
30  
31      /** Error tolerance for tests */
32      protected double tolerance = 1.0e-12;
33      
34      /** 
35       * Quadratic polynomials used in tests: 
36       * 
37       * x^2 + x            [-1, 0)
38       * x^2 + x + 2        [0, 1)
39       * x^2 + x + 4        [1, 2)
40       * 
41       * Defined so that evaluation using PolynomialSplineFunction evaluation
42       * algorithm agrees at knot point boundaries.
43       */
44      protected PolynomialFunction[] polynomials = {
45          new PolynomialFunction(new double[] {0d, 1d, 1d}), 
46          new PolynomialFunction(new double[] {2d, 1d, 1d}),
47          new PolynomialFunction(new double[] {4d, 1d, 1d})
48      };
49      
50      /** Knot points  */
51      protected double[] knots = {-1, 0, 1, 2};
52      
53      /** Derivative of test polynomials -- 2x + 1  */
54      protected PolynomialFunction dp = 
55          new PolynomialFunction(new double[] {1d, 2d});
56      
57      
58      public void testConstructor() {
59          PolynomialSplineFunction spline = 
60              new PolynomialSplineFunction(knots, polynomials);
61          assertTrue(Arrays.equals(knots, spline.getKnots()));
62          assertEquals(1d, spline.getPolynomials()[0].getCoefficients()[2], 0);
63          assertEquals(3, spline.getN());
64          
65          try { // too few knots
66              new PolynomialSplineFunction(new double[] {0}, polynomials);
67              fail("Expecting IllegalArgumentException");
68          } catch (IllegalArgumentException ex) {
69              // expected
70          }
71          
72          try { // too many knots
73              new PolynomialSplineFunction(new double[] {0,1,2,3,4}, polynomials);
74              fail("Expecting IllegalArgumentException");
75          } catch (IllegalArgumentException ex) {
76              // expected
77          }
78          
79          try { // knots not increasing
80              new PolynomialSplineFunction(new double[] {0,1, 3, 2}, polynomials);
81              fail("Expecting IllegalArgumentException");
82          } catch (IllegalArgumentException ex) {
83              // expected
84          }
85      }
86      
87      public void testValues() throws Exception {
88          PolynomialSplineFunction spline = 
89              new PolynomialSplineFunction(knots, polynomials);
90          UnivariateRealFunction dSpline = spline.derivative();
91          
92          /**
93           * interior points -- spline value at x should equal p(x - knot)
94           * where knot is the largest knot point less than or equal to x and p 
95           * is the polynomial defined over the knot segment to which x belongs.
96           */
97          double x = -1;
98          int index = 0;
99          for (int i = 0; i < 10; i++) {
100            x+=0.25;
101            index = findKnot(knots, x);
102            assertEquals("spline function evaluation failed for x=" + x, 
103                    polynomials[index].value(x - knots[index]), spline.value(x), tolerance);
104            assertEquals("spline derivative evaluation failed for x=" + x,
105                    dp.value(x - knots[index]), dSpline.value(x), tolerance);
106         }
107         
108         // knot points -- centering should zero arguments
109         for (int i = 0; i < 3; i++) {
110             assertEquals("spline function evaluation failed for knot=" + knots[i],
111                     polynomials[i].value(0), spline.value(knots[i]), tolerance);
112             assertEquals("spline function evaluation failed for knot=" + knots[i],
113                     dp.value(0), dSpline.value(knots[i]), tolerance);
114         }
115         
116         try { //outside of domain -- under min
117             x = spline.value(-1.5);
118             fail("Expecting IllegalArgumentException");
119         } catch (FunctionEvaluationException ex) {
120             // expected
121         }
122         
123         try { //outside of domain -- over max
124             x = spline.value(2.5);
125             fail("Expecting IllegalArgumentException");
126         } catch (FunctionEvaluationException ex) {
127             // expected
128         }         
129     }  
130     
131     /**
132      *  Do linear search to find largest knot point less than or equal to x.
133      *  Implementation does binary search.
134      */
135      protected int findKnot(double[] knots, double x) {
136          if (x < knots[0] || x >= knots[knots.length -1]) {
137              throw new IllegalArgumentException("x is out of range");
138          }
139          for (int i = 0; i < knots.length; i++) {
140              if (knots[i] > x) {
141                  return i -1;
142              }
143          }
144          throw new IllegalArgumentException("x is out of range");
145      }
146 }
147