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