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 }