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.interpolation;
23
24 import java.util.ArrayList;
25 import java.util.List;
26
27 import org.hipparchus.FieldElement;
28 import org.hipparchus.exception.LocalizedCoreFormats;
29 import org.hipparchus.exception.MathIllegalArgumentException;
30 import org.hipparchus.exception.MathRuntimeException;
31 import org.hipparchus.exception.NullArgumentException;
32 import org.hipparchus.util.MathArrays;
33 import org.hipparchus.util.MathUtils;
34
35 /** Polynomial interpolator using both sample values and sample derivatives.
36 * <p>
37 * The interpolation polynomials match all sample points, including both values
38 * and provided derivatives. There is one polynomial for each component of
39 * the values vector. All polynomials have the same degree. The degree of the
40 * polynomials depends on the number of points and number of derivatives at each
41 * point. For example the interpolation polynomials for n sample points without
42 * any derivatives all have degree n-1. The interpolation polynomials for n
43 * sample points with the two extreme points having value and first derivative
44 * and the remaining points having value only all have degree n+1. The
45 * interpolation polynomial for n sample points with value, first and second
46 * derivative for all points all have degree 3n-1.
47 * </p>
48 *
49 * @param <T> Type of the field elements.
50 *
51 */
52 public class FieldHermiteInterpolator<T extends FieldElement<T>> {
53
54 /** Sample abscissae. */
55 private final List<T> abscissae;
56
57 /** Top diagonal of the divided differences array. */
58 private final List<T[]> topDiagonal;
59
60 /** Bottom diagonal of the divided differences array. */
61 private final List<T[]> bottomDiagonal;
62
63 /** Create an empty interpolator.
64 */
65 public FieldHermiteInterpolator() {
66 this.abscissae = new ArrayList<>();
67 this.topDiagonal = new ArrayList<>();
68 this.bottomDiagonal = new ArrayList<>();
69 }
70
71 /** Add a sample point.
72 * <p>
73 * This method must be called once for each sample point. It is allowed to
74 * mix some calls with values only with calls with values and first
75 * derivatives.
76 * </p>
77 * <p>
78 * The point abscissae for all calls <em>must</em> be different.
79 * </p>
80 * @param x abscissa of the sample point
81 * @param value value and derivatives of the sample point
82 * (if only one row is passed, it is the value, if two rows are
83 * passed the first one is the value and the second the derivative
84 * and so on)
85 * @exception MathIllegalArgumentException if the abscissa difference between added point
86 * and a previous point is zero (i.e. the two points are at same abscissa)
87 * @exception MathRuntimeException if the number of derivatives is larger
88 * than 20, which prevents computation of a factorial
89 * @throws MathIllegalArgumentException if derivative structures are inconsistent
90 * @throws NullArgumentException if x is null
91 */
92 @SafeVarargs
93 public final void addSamplePoint(final T x, final T[] ... value)
94 throws MathRuntimeException,
95 NullArgumentException {
96
97 MathUtils.checkNotNull(x);
98 T factorial = x.getField().getOne();
99 for (int i = 0; i < value.length; ++i) {
100
101 final T[] y = value[i].clone();
102 if (i > 1) {
103 factorial = factorial.multiply(i);
104 final T inv = factorial.reciprocal();
105 for (int j = 0; j < y.length; ++j) {
106 y[j] = y[j].multiply(inv);
107 }
108 }
109
110 // update the bottom diagonal of the divided differences array
111 final int n = abscissae.size();
112 bottomDiagonal.add(n - i, y);
113 T[] bottom0 = y;
114 for (int j = i; j < n; ++j) {
115 final T[] bottom1 = bottomDiagonal.get(n - (j + 1));
116 if (x.equals(abscissae.get(n - (j + 1)))) {
117 throw new MathIllegalArgumentException(LocalizedCoreFormats.DUPLICATED_ABSCISSA_DIVISION_BY_ZERO, x);
118 }
119 final T inv = x.subtract(abscissae.get(n - (j + 1))).reciprocal();
120 for (int k = 0; k < y.length; ++k) {
121 bottom1[k] = inv.multiply(bottom0[k].subtract(bottom1[k]));
122 }
123 bottom0 = bottom1;
124 }
125
126 // update the top diagonal of the divided differences array
127 topDiagonal.add(bottom0.clone());
128
129 // update the abscissae array
130 abscissae.add(x);
131
132 }
133
134 }
135
136 /** Interpolate value at a specified abscissa.
137 * @param x interpolation abscissa
138 * @return interpolated value
139 * @exception MathIllegalArgumentException if sample is empty
140 * @throws NullArgumentException if x is null
141 */
142 public T[] value(T x) throws MathIllegalArgumentException, NullArgumentException {
143
144 // safety check
145 MathUtils.checkNotNull(x);
146 if (abscissae.isEmpty()) {
147 throw new MathIllegalArgumentException(LocalizedCoreFormats.EMPTY_INTERPOLATION_SAMPLE);
148 }
149
150 final T[] value = MathArrays.buildArray(x.getField(), topDiagonal.get(0).length);
151 T valueCoeff = x.getField().getOne();
152 for (int i = 0; i < topDiagonal.size(); ++i) {
153 T[] dividedDifference = topDiagonal.get(i);
154 for (int k = 0; k < value.length; ++k) {
155 value[k] = value[k].add(dividedDifference[k].multiply(valueCoeff));
156 }
157 final T deltaX = x.subtract(abscissae.get(i));
158 valueCoeff = valueCoeff.multiply(deltaX);
159 }
160
161 return value;
162
163 }
164
165 /** Interpolate value and first derivatives at a specified abscissa.
166 * @param x interpolation abscissa
167 * @param order maximum derivation order
168 * @return interpolated value and derivatives (value in row 0,
169 * 1<sup>st</sup> derivative in row 1, ... n<sup>th</sup> derivative in row n)
170 * @exception MathIllegalArgumentException if sample is empty
171 * @throws NullArgumentException if x is null
172 */
173 public T[][] derivatives(T x, int order) throws MathIllegalArgumentException, NullArgumentException {
174
175 // safety check
176 MathUtils.checkNotNull(x);
177 if (abscissae.isEmpty()) {
178 throw new MathIllegalArgumentException(LocalizedCoreFormats.EMPTY_INTERPOLATION_SAMPLE);
179 }
180
181 final T zero = x.getField().getZero();
182 final T one = x.getField().getOne();
183 final T[] tj = MathArrays.buildArray(x.getField(), order + 1);
184 tj[0] = zero;
185 for (int i = 0; i < order; ++i) {
186 tj[i + 1] = tj[i].add(one);
187 }
188
189 final T[][] derivatives =
190 MathArrays.buildArray(x.getField(), order + 1, topDiagonal.get(0).length);
191 final T[] valueCoeff = MathArrays.buildArray(x.getField(), order + 1);
192 valueCoeff[0] = x.getField().getOne();
193 for (int i = 0; i < topDiagonal.size(); ++i) {
194 T[] dividedDifference = topDiagonal.get(i);
195 final T deltaX = x.subtract(abscissae.get(i));
196 for (int j = order; j >= 0; --j) {
197 for (int k = 0; k < derivatives[j].length; ++k) {
198 derivatives[j][k] =
199 derivatives[j][k].add(dividedDifference[k].multiply(valueCoeff[j]));
200 }
201 valueCoeff[j] = valueCoeff[j].multiply(deltaX);
202 if (j > 0) {
203 valueCoeff[j] = valueCoeff[j].add(tj[j].multiply(valueCoeff[j - 1]));
204 }
205 }
206 }
207
208 return derivatives;
209
210 }
211
212 }