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 java.lang.reflect.Array;
25  import java.util.Arrays;
26  
27  import org.hipparchus.analysis.differentiation.Gradient;
28  import org.hipparchus.analysis.differentiation.GradientField;
29  import org.hipparchus.analysis.interpolation.LinearInterpolator;
30  import org.hipparchus.exception.LocalizedCoreFormats;
31  import org.hipparchus.exception.MathIllegalArgumentException;
32  import org.hipparchus.exception.MathIllegalStateException;
33  import org.hipparchus.util.Binary64;
34  import org.junit.Assert;
35  import org.junit.Before;
36  import org.junit.Test;
37  
38  /**
39   * Tests the FieldFieldPolynomialSplineFunction implementation.
40   *
41   */
42  public class FieldPolynomialSplineFunctionTest {
43  
44      /** Error tolerance for tests */
45      private double tolerance;
46  
47      /**
48       * Quadratic polynomials used in tests:
49       *
50       * x^2 + x            [-1, 0)
51       * x^2 + x + 2        [0, 1)
52       * x^2 + x + 4        [1, 2)
53       *
54       * Defined so that evaluation using FieldPolynomialSplineFunction evaluation
55       * algorithm agrees at knot point boundaries.
56       */
57      private FieldPolynomialFunction<Binary64>[] polynomials;
58  
59      /** Knot points  */
60      private Binary64[] knots;
61  
62      /** Derivative of test polynomials -- 2x + 1  */
63      protected PolynomialFunction dp =
64          new PolynomialFunction(new double[] {1d, 2d});
65  
66  
67      @Test
68      public void testConstructor() {
69          FieldPolynomialSplineFunction<Binary64> spline =
70              new FieldPolynomialSplineFunction<>(knots, polynomials);
71          Assert.assertTrue(Arrays.equals(knots, spline.getKnots()));
72          Assert.assertEquals(1d, spline.getPolynomials()[0].getCoefficients()[2].getReal(), 0);
73          Assert.assertEquals(3, spline.getN());
74  
75          try { // too few knots
76              new FieldPolynomialSplineFunction<>(new Binary64[] { new Binary64(0) }, polynomials);
77              Assert.fail("Expecting MathIllegalArgumentException");
78          } catch (MathIllegalArgumentException ex) {
79              // expected
80          }
81  
82          try { // too many knots
83              new FieldPolynomialSplineFunction<>(new Binary64[] {
84                  new Binary64(0), new Binary64(1), new Binary64(2),
85                  new Binary64(3), new Binary64(4)
86              }, polynomials);
87              Assert.fail("Expecting MathIllegalArgumentException");
88          } catch (MathIllegalArgumentException ex) {
89              // expected
90          }
91  
92          try { // knots not increasing
93              new FieldPolynomialSplineFunction<>(new Binary64[] {
94                  new Binary64(0), new Binary64(1), new Binary64(3), new Binary64(2)
95              }, polynomials);
96              Assert.fail("Expecting MathIllegalArgumentException");
97          } catch (MathIllegalArgumentException ex) {
98              // expected
99          }
100     }
101 
102     @Test
103     public void testValues() {
104         FieldPolynomialSplineFunction<Binary64> spline =
105             new FieldPolynomialSplineFunction<>(knots, polynomials);
106         FieldPolynomialSplineFunction<Binary64> dSpline = spline.polynomialSplineDerivative();
107 
108         /**
109          * interior points -- spline value at x should equal p(x - knot)
110          * where knot is the largest knot point less than or equal to x and p
111          * is the polynomial defined over the knot segment to which x belongs.
112          */
113         double x = -1;
114         int index = 0;
115         for (int i = 0; i < 10; i++) {
116            x += 0.25;
117            index = findKnot(knots, x);
118            Assert.assertEquals("spline function evaluation failed for x=" + x,
119                                polynomials[index].value(new Binary64(x).subtract(knots[index])).getReal(),
120                                spline.value(x).getReal(),
121                                tolerance);
122            Assert.assertEquals("spline derivative evaluation failed for x=" + x,
123                                dp.value(new Binary64(x).subtract(knots[index])).getReal(),
124                                dSpline.value(x).getReal(),
125                                tolerance);
126         }
127 
128         // knot points -- centering should zero arguments
129         for (int i = 0; i < 3; i++) {
130             Assert.assertEquals("spline function evaluation failed for knot=" + knots[i].getReal(),
131                                 polynomials[i].value(0).getReal(),
132                                 spline.value(knots[i]).getReal(),
133                                 tolerance);
134             Assert.assertEquals("spline function evaluation failed for knot=" + knots[i],
135                                 dp.value(0),
136                                 dSpline.value(knots[i]).getReal(),
137                                 tolerance);
138         }
139 
140         try { //outside of domain -- under min
141             spline.value(-1.5);
142             Assert.fail("Expecting MathIllegalArgumentException");
143         } catch (MathIllegalArgumentException ex) {
144             // expected
145         }
146 
147         try { //outside of domain -- over max
148             spline.value(2.5);
149             Assert.fail("Expecting MathIllegalArgumentException");
150         } catch (MathIllegalArgumentException ex) {
151             // expected
152         }
153     }
154 
155     @Test
156     public void testIsValidPoint() {
157         final FieldPolynomialSplineFunction<Binary64> spline =
158             new FieldPolynomialSplineFunction<>(knots, polynomials);
159         final Binary64 xMin = knots[0];
160         final Binary64 xMax = knots[knots.length - 1];
161 
162         Binary64 x;
163 
164         x = xMin;
165         Assert.assertTrue(spline.isValidPoint(x));
166         // Ensure that no exception is thrown.
167         spline.value(x);
168 
169         x = xMax;
170         Assert.assertTrue(spline.isValidPoint(x));
171         // Ensure that no exception is thrown.
172         spline.value(x);
173 
174         final Binary64 xRange = xMax.subtract(xMin);
175         x = xMin.add(xRange.divide(3.4));
176         Assert.assertTrue(spline.isValidPoint(x));
177         // Ensure that no exception is thrown.
178         spline.value(x);
179 
180         final Binary64 small = new Binary64(1e-8);
181         x = xMin.subtract(small);
182         Assert.assertFalse(spline.isValidPoint(x));
183         // Ensure that an exception would have been thrown.
184         try {
185             spline.value(x);
186             Assert.fail("MathIllegalArgumentException expected");
187         } catch (MathIllegalArgumentException expected) {}
188     }
189 
190     /**
191      *  Do linear search to find largest knot point less than or equal to x.
192      *  Implementation does binary search.
193      */
194      protected int findKnot(Binary64[] knots, double x) {
195          if (x < knots[0].getReal() || x >= knots[knots.length -1].getReal()) {
196              throw new MathIllegalArgumentException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE,
197                                                     x, knots[0], knots[knots.length -1]);
198          }
199          for (int i = 0; i < knots.length; i++) {
200              if (knots[i].getReal() > x) {
201                  return i - 1;
202              }
203          }
204          throw new MathIllegalStateException(LocalizedCoreFormats.ILLEGAL_STATE);
205      }
206 
207      private FieldPolynomialFunction<Binary64> buildD64(double...c) {
208          Binary64[] array = new Binary64[c.length];
209          for (int i = 0; i < c.length; ++i) {
210              array[i] = new Binary64(c[i]);
211          }
212          return new FieldPolynomialFunction<>(array);
213      }
214 
215      @Before
216      @SuppressWarnings("unchecked")
217      public void setUp() {
218          tolerance = 1.0e-12;
219          polynomials = (FieldPolynomialFunction<Binary64>[]) Array.newInstance(FieldPolynomialFunction.class, 3);
220          polynomials[0] = buildD64(0, 1, 1);
221          polynomials[1] = buildD64(2, 1, 1);
222          polynomials[2] = buildD64(4, 1, 1);
223          knots = new Binary64[] {
224              new Binary64(-1), new Binary64(0), new Binary64(1), new Binary64(2)
225          };
226      }
227 
228      @Test
229      public void testValueGradient() {
230          // issue #257
231          // Given
232          final int freeParameters = 1;
233          final GradientField field = GradientField.getField(freeParameters);
234          final Gradient zero = field.getZero();
235          final Gradient time1 = zero.add(1.0);
236          final Gradient time2 = zero.add(2.0);
237          final Gradient time3 = zero.add(4.0);
238          final Gradient x1 = zero.add(4.0);
239          final Gradient x2 = Gradient.variable(freeParameters, 0, -2.0);
240          final Gradient x3 = zero.add(0.0);
241          final Gradient[] times = new Gradient[] {time1, time2, time3};
242          final Gradient[] xs = new Gradient[] {x1, x2, x3};
243          // When
244          final FieldPolynomialSplineFunction<Gradient> spline = new LinearInterpolator().interpolate(times, xs);
245          final Gradient actualEvaluation = spline.value(zero.add(3.));
246          // Then
247          final Gradient expectedEvaluation = Gradient.variable(freeParameters, 0, 0).multiply(0.5).add(-1.0);
248          Assert.assertEquals(expectedEvaluation, actualEvaluation);
249      }
250 
251 }
252