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 org.hipparchus.CalculusFieldElement;
25  import org.hipparchus.analysis.FieldUnivariateFunction;
26  import org.hipparchus.analysis.differentiation.Derivative;
27  import org.hipparchus.analysis.differentiation.UnivariateDifferentiableFunction;
28  import org.hipparchus.exception.LocalizedCoreFormats;
29  import org.hipparchus.exception.MathIllegalArgumentException;
30  import org.hipparchus.exception.NullArgumentException;
31  import org.hipparchus.util.MathUtils;
32  
33  /**
34   * Implements the representation of a real polynomial function in
35   * Newton Form. For reference, see <b>Elementary Numerical Analysis</b>,
36   * ISBN 0070124477, chapter 2.
37   * <p>
38   * The formula of polynomial in Newton form is
39   *     p(x) = a[0] + a[1](x-c[0]) + a[2](x-c[0])(x-c[1]) + ... +
40   *            a[n](x-c[0])(x-c[1])...(x-c[n-1])
41   * Note that the length of a[] is one more than the length of c[]</p>
42   *
43   */
44  public class PolynomialFunctionNewtonForm implements UnivariateDifferentiableFunction, FieldUnivariateFunction {
45  
46      /**
47       * The coefficients of the polynomial, ordered by degree -- i.e.
48       * coefficients[0] is the constant term and coefficients[n] is the
49       * coefficient of x^n where n is the degree of the polynomial.
50       */
51      private double[] coefficients;
52  
53      /**
54       * Centers of the Newton polynomial.
55       */
56      private final double[] c;
57  
58      /**
59       * When all c[i] = 0, a[] becomes normal polynomial coefficients,
60       * i.e. a[i] = coefficients[i].
61       */
62      private final double[] a;
63  
64      /**
65       * Whether the polynomial coefficients are available.
66       */
67      private boolean coefficientsComputed;
68  
69      /**
70       * Construct a Newton polynomial with the given a[] and c[]. The order of
71       * centers are important in that if c[] shuffle, then values of a[] would
72       * completely change, not just a permutation of old a[].
73       * <p>
74       * The constructor makes copy of the input arrays and assigns them.</p>
75       *
76       * @param a Coefficients in Newton form formula.
77       * @param c Centers.
78       * @throws NullArgumentException if any argument is {@code null}.
79       * @throws MathIllegalArgumentException if any array has zero length.
80       * @throws MathIllegalArgumentException if the size difference between
81       * {@code a} and {@code c} is not equal to 1.
82       */
83      public PolynomialFunctionNewtonForm(double[] a, double[] c)
84          throws MathIllegalArgumentException, NullArgumentException {
85  
86          verifyInputArray(a, c);
87          this.a = new double[a.length];
88          this.c = new double[c.length];
89          System.arraycopy(a, 0, this.a, 0, a.length);
90          System.arraycopy(c, 0, this.c, 0, c.length);
91          coefficientsComputed = false;
92      }
93  
94      /**
95       * Calculate the function value at the given point.
96       *
97       * @param z Point at which the function value is to be computed.
98       * @return the function value.
99       */
100     @Override
101     public double value(double z) {
102        return evaluate(a, c, z);
103     }
104 
105     /**
106      * {@inheritDoc}
107      */
108     @Override
109     public <T extends Derivative<T>> T value(final T t) {
110         verifyInputArray(a, c);
111 
112         final int n = c.length;
113         T value = t.getField().getZero().add(a[n]);
114         for (int i = n - 1; i >= 0; i--) {
115             value = t.subtract(c[i]).multiply(value).add(a[i]);
116         }
117 
118         return value;
119 
120     }
121 
122     /**
123      * {@inheritDoc}
124      */
125     @Override
126     public <T extends CalculusFieldElement<T>> T value(final T t) {
127         verifyInputArray(a, c);
128 
129         final int n = c.length;
130         T value = t.getField().getZero().add(a[n]);
131         for (int i = n - 1; i >= 0; i--) {
132             value = t.subtract(c[i]).multiply(value).add(a[i]);
133         }
134 
135         return value;
136 
137     }
138 
139     /**
140      * Returns the degree of the polynomial.
141      *
142      * @return the degree of the polynomial
143      */
144     public int degree() {
145         return c.length;
146     }
147 
148     /**
149      * Returns a copy of coefficients in Newton form formula.
150      * <p>
151      * Changes made to the returned copy will not affect the polynomial.</p>
152      *
153      * @return a fresh copy of coefficients in Newton form formula
154      */
155     public double[] getNewtonCoefficients() {
156         double[] out = new double[a.length];
157         System.arraycopy(a, 0, out, 0, a.length);
158         return out;
159     }
160 
161     /**
162      * Returns a copy of the centers array.
163      * <p>
164      * Changes made to the returned copy will not affect the polynomial.</p>
165      *
166      * @return a fresh copy of the centers array.
167      */
168     public double[] getCenters() {
169         double[] out = new double[c.length];
170         System.arraycopy(c, 0, out, 0, c.length);
171         return out;
172     }
173 
174     /**
175      * Returns a copy of the coefficients array.
176      * <p>
177      * Changes made to the returned copy will not affect the polynomial.</p>
178      *
179      * @return a fresh copy of the coefficients array.
180      */
181     public double[] getCoefficients() {
182         if (!coefficientsComputed) {
183             computeCoefficients();
184         }
185         double[] out = new double[coefficients.length];
186         System.arraycopy(coefficients, 0, out, 0, coefficients.length);
187         return out;
188     }
189 
190     /**
191      * Evaluate the Newton polynomial using nested multiplication. It is
192      * also called <a href="http://mathworld.wolfram.com/HornersRule.html">
193      * Horner's Rule</a> and takes O(N) time.
194      *
195      * @param a Coefficients in Newton form formula.
196      * @param c Centers.
197      * @param z Point at which the function value is to be computed.
198      * @return the function value.
199      * @throws NullArgumentException if any argument is {@code null}.
200      * @throws MathIllegalArgumentException if any array has zero length.
201      * @throws MathIllegalArgumentException if the size difference between
202      * {@code a} and {@code c} is not equal to 1.
203      */
204     public static double evaluate(double[] a, double[] c, double z)
205         throws MathIllegalArgumentException, NullArgumentException {
206         verifyInputArray(a, c);
207 
208         final int n = c.length;
209         double value = a[n];
210         for (int i = n - 1; i >= 0; i--) {
211             value = a[i] + (z - c[i]) * value;
212         }
213 
214         return value;
215     }
216 
217     /**
218      * Calculate the normal polynomial coefficients given the Newton form.
219      * It also uses nested multiplication but takes O(N^2) time.
220      */
221     protected void computeCoefficients() {
222         final int n = degree();
223 
224         coefficients = new double[n+1];
225         for (int i = 0; i <= n; i++) {
226             coefficients[i] = 0.0;
227         }
228 
229         coefficients[0] = a[n];
230         for (int i = n-1; i >= 0; i--) {
231             for (int j = n-i; j > 0; j--) {
232                 coefficients[j] = coefficients[j-1] - c[i] * coefficients[j];
233             }
234             coefficients[0] = a[i] - c[i] * coefficients[0];
235         }
236 
237         coefficientsComputed = true;
238     }
239 
240     /**
241      * Verifies that the input arrays are valid.
242      * <p>
243      * The centers must be distinct for interpolation purposes, but not
244      * for general use. Thus it is not verified here.</p>
245      *
246      * @param a the coefficients in Newton form formula
247      * @param c the centers
248      * @throws NullArgumentException if any argument is {@code null}.
249      * @throws MathIllegalArgumentException if any array has zero length.
250      * @throws MathIllegalArgumentException if the size difference between
251      * {@code a} and {@code c} is not equal to 1.
252      */
253     protected static void verifyInputArray(double[] a, double[] c)
254         throws MathIllegalArgumentException, NullArgumentException {
255         MathUtils.checkNotNull(a);
256         MathUtils.checkNotNull(c);
257         if (a.length == 0 || c.length == 0) {
258             throw new MathIllegalArgumentException(LocalizedCoreFormats.EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY);
259         }
260         if (a.length != c.length + 1) {
261             throw new MathIllegalArgumentException(LocalizedCoreFormats.ARRAY_SIZES_SHOULD_HAVE_DIFFERENCE_1,
262                                                  a.length, c.length);
263         }
264     }
265 
266 }