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.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 MathIllegalArgumentException, 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 }