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 }