View Javadoc
1   /*
2    * Licensed to the Hipparchus project 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 Hipparchus project 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  package org.hipparchus.analysis.polynomials;
18  
19  import java.lang.reflect.Array;
20  import java.util.Arrays;
21  
22  import org.hipparchus.Field;
23  import org.hipparchus.CalculusFieldElement;
24  import org.hipparchus.analysis.CalculusFieldUnivariateFunction;
25  import org.hipparchus.exception.LocalizedCoreFormats;
26  import org.hipparchus.exception.MathIllegalArgumentException;
27  import org.hipparchus.exception.NullArgumentException;
28  import org.hipparchus.util.MathArrays;
29  import org.hipparchus.util.MathUtils;
30  
31  /**
32   * Represents a polynomial spline function.
33   * <p>
34   * A <strong>polynomial spline function</strong> consists of a set of
35   * <i>interpolating polynomials</i> and an ascending array of domain
36   * <i>knot points</i>, determining the intervals over which the spline function
37   * is defined by the constituent polynomials.  The polynomials are assumed to
38   * have been computed to match the values of another function at the knot
39   * points.  The value consistency constraints are not currently enforced by
40   * <code>PolynomialSplineFunction</code> itself, but are assumed to hold among
41   * the polynomials and knot points passed to the constructor.</p>
42   * <p>
43   * N.B.:  The polynomials in the <code>polynomials</code> property must be
44   * centered on the knot points to compute the spline function values.
45   * See below.</p>
46   * <p>
47   * The domain of the polynomial spline function is
48   * <code>[smallest knot, largest knot]</code>.  Attempts to evaluate the
49   * function at values outside of this range generate IllegalArgumentExceptions.
50   * </p>
51   * <p>
52   * The value of the polynomial spline function for an argument <code>x</code>
53   * is computed as follows:
54   * <ol>
55   * <li>The knot array is searched to find the segment to which <code>x</code>
56   * belongs.  If <code>x</code> is less than the smallest knot point or greater
57   * than the largest one, an <code>IllegalArgumentException</code>
58   * is thrown.</li>
59   * <li> Let <code>j</code> be the index of the largest knot point that is less
60   * than or equal to <code>x</code>.  The value returned is
61   * {@code polynomials[j](x - knot[j])}</li></ol>
62   *
63   * @param <T> the type of the field elements
64   * @since 1.5
65   */
66  public class FieldPolynomialSplineFunction<T extends CalculusFieldElement<T>> implements CalculusFieldUnivariateFunction<T> {
67  
68      /**
69       * Spline segment interval delimiters (knots).
70       * Size is n + 1 for n segments.
71       */
72      private final T[] knots;
73  
74      /**
75       * The polynomial functions that make up the spline.  The first element
76       * determines the value of the spline over the first subinterval, the
77       * second over the second, etc.   Spline function values are determined by
78       * evaluating these functions at {@code (x - knot[i])} where i is the
79       * knot segment to which x belongs.
80       */
81      private final FieldPolynomialFunction<T>[] polynomials;
82  
83      /**
84       * Number of spline segments. It is equal to the number of polynomials and
85       * to the number of partition points - 1.
86       */
87      private final int n;
88  
89  
90      /**
91       * Construct a polynomial spline function with the given segment delimiters
92       * and interpolating polynomials.
93       * The constructor copies both arrays and assigns the copies to the knots
94       * and polynomials properties, respectively.
95       *
96       * @param knots Spline segment interval delimiters.
97       * @param polynomials Polynomial functions that make up the spline.
98       * @throws NullArgumentException if either of the input arrays is {@code null}.
99       * @throws MathIllegalArgumentException if knots has length less than 2.
100      * @throws MathIllegalArgumentException if {@code polynomials.length != knots.length - 1}.
101      * @throws MathIllegalArgumentException if the {@code knots} array is not strictly increasing.
102      *
103      */
104     @SuppressWarnings("unchecked")
105     public FieldPolynomialSplineFunction(final T[] knots, final FieldPolynomialFunction<T>[] polynomials)
106         throws MathIllegalArgumentException, NullArgumentException {
107         if (knots == null ||
108             polynomials == null) {
109             throw new NullArgumentException();
110         }
111         if (knots.length < 2) {
112             throw new MathIllegalArgumentException(LocalizedCoreFormats.NOT_ENOUGH_POINTS_IN_SPLINE_PARTITION,
113                                                    2, knots.length, false);
114         }
115         MathUtils.checkDimension(polynomials.length, knots.length - 1);
116         MathArrays.checkOrder(knots);
117 
118         this.n = knots.length -1;
119         this.knots = knots.clone();
120         this.polynomials = (FieldPolynomialFunction<T>[]) Array.newInstance(FieldPolynomialFunction.class, n);
121         System.arraycopy(polynomials, 0, this.polynomials, 0, n);
122     }
123 
124     /** Get the {@link Field} to which the instance belongs.
125      * @return {@link Field} to which the instance belongs
126      */
127     public Field<T> getField() {
128         return knots[0].getField();
129     }
130 
131     /**
132      * Compute the value for the function.
133      * See {@link FieldPolynomialSplineFunction} for details on the algorithm for
134      * computing the value of the function.
135      *
136      * @param v Point for which the function value should be computed.
137      * @return the value.
138      * @throws MathIllegalArgumentException if {@code v} is outside of the domain of the
139      * spline function (smaller than the smallest knot point or larger than the
140      * largest knot point).
141      */
142     public T value(final double v) {
143         return value(getField().getZero().add(v));
144     }
145 
146     /**
147      * Compute the value for the function.
148      * See {@link FieldPolynomialSplineFunction} for details on the algorithm for
149      * computing the value of the function.
150      *
151      * @param v Point for which the function value should be computed.
152      * @return the value.
153      * @throws MathIllegalArgumentException if {@code v} is outside of the domain of the
154      * spline function (smaller than the smallest knot point or larger than the
155      * largest knot point).
156      */
157     @Override
158     public T value(final T v) {
159         MathUtils.checkRangeInclusive(v.getReal(), knots[0].getReal(), knots[n].getReal());
160         int i = Arrays.binarySearch(Arrays.stream(knots).map(T::getReal).toArray(), v.getReal());
161         if (i < 0) {
162             i = -i - 2;
163         }
164         // This will handle the case where v is the last knot value
165         // There are only n-1 polynomials, so if v is the last knot
166         // then we will use the last polynomial to calculate the value.
167         if ( i >= polynomials.length ) {
168             i--;
169         }
170         return polynomials[i].value(v.subtract(knots[i]));
171     }
172 
173     /**
174      * Get the number of spline segments.
175      * It is also the number of polynomials and the number of knot points - 1.
176      *
177      * @return the number of spline segments.
178      */
179     public int getN() {
180         return n;
181     }
182 
183     /**
184      * Get a copy of the interpolating polynomials array.
185      * It returns a fresh copy of the array. Changes made to the copy will
186      * not affect the polynomials property.
187      *
188      * @return the interpolating polynomials.
189      */
190     public FieldPolynomialFunction<T>[] getPolynomials() {
191         return polynomials.clone();
192     }
193 
194     /**
195      * Get an array copy of the knot points.
196      * It returns a fresh copy of the array. Changes made to the copy
197      * will not affect the knots property.
198      *
199      * @return the knot points.
200      */
201     public T[] getKnots() {
202         return knots.clone();
203     }
204 
205     /**
206      * Indicates whether a point is within the interpolation range.
207      *
208      * @param x Point.
209      * @return {@code true} if {@code x} is a valid point.
210      */
211     public boolean isValidPoint(T x) {
212         if (x.getReal() < knots[0].getReal() ||
213             x.getReal() > knots[n].getReal()) {
214             return false;
215         } else {
216             return true;
217         }
218     }
219     /**
220      * Get the derivative of the polynomial spline function.
221      *
222      * @return the derivative function.
223      */
224     @SuppressWarnings("unchecked")
225     public FieldPolynomialSplineFunction<T> polynomialSplineDerivative() {
226         FieldPolynomialFunction<T>[] derivativePolynomials =
227                         (FieldPolynomialFunction<T>[]) Array.newInstance(FieldPolynomialFunction.class, n);
228         for (int i = 0; i < n; i++) {
229             derivativePolynomials[i] = polynomials[i].polynomialDerivative();
230         }
231         return new FieldPolynomialSplineFunction<>(knots, derivativePolynomials);
232     }
233 
234 
235 }