PolynomialFunctionNewtonForm.java

  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.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.analysis.polynomials;

  22. import org.hipparchus.CalculusFieldElement;
  23. import org.hipparchus.analysis.FieldUnivariateFunction;
  24. import org.hipparchus.analysis.differentiation.Derivative;
  25. import org.hipparchus.analysis.differentiation.UnivariateDifferentiableFunction;
  26. import org.hipparchus.exception.LocalizedCoreFormats;
  27. import org.hipparchus.exception.MathIllegalArgumentException;
  28. import org.hipparchus.exception.NullArgumentException;
  29. import org.hipparchus.util.MathUtils;

  30. /**
  31.  * Implements the representation of a real polynomial function in
  32.  * Newton Form. For reference, see <b>Elementary Numerical Analysis</b>,
  33.  * ISBN 0070124477, chapter 2.
  34.  * <p>
  35.  * The formula of polynomial in Newton form is
  36.  *     p(x) = a[0] + a[1](x-c[0]) + a[2](x-c[0])(x-c[1]) + ... +
  37.  *            a[n](x-c[0])(x-c[1])...(x-c[n-1])
  38.  * Note that the length of a[] is one more than the length of c[]</p>
  39.  *
  40.  */
  41. public class PolynomialFunctionNewtonForm implements UnivariateDifferentiableFunction, FieldUnivariateFunction {

  42.     /**
  43.      * The coefficients of the polynomial, ordered by degree -- i.e.
  44.      * coefficients[0] is the constant term and coefficients[n] is the
  45.      * coefficient of x^n where n is the degree of the polynomial.
  46.      */
  47.     private double[] coefficients;

  48.     /**
  49.      * Centers of the Newton polynomial.
  50.      */
  51.     private final double[] c;

  52.     /**
  53.      * When all c[i] = 0, a[] becomes normal polynomial coefficients,
  54.      * i.e. a[i] = coefficients[i].
  55.      */
  56.     private final double[] a;

  57.     /**
  58.      * Whether the polynomial coefficients are available.
  59.      */
  60.     private boolean coefficientsComputed;

  61.     /**
  62.      * Construct a Newton polynomial with the given a[] and c[]. The order of
  63.      * centers are important in that if c[] shuffle, then values of a[] would
  64.      * completely change, not just a permutation of old a[].
  65.      * <p>
  66.      * The constructor makes copy of the input arrays and assigns them.</p>
  67.      *
  68.      * @param a Coefficients in Newton form formula.
  69.      * @param c Centers.
  70.      * @throws NullArgumentException if any argument is {@code null}.
  71.      * @throws MathIllegalArgumentException if any array has zero length.
  72.      * @throws MathIllegalArgumentException if the size difference between
  73.      * {@code a} and {@code c} is not equal to 1.
  74.      */
  75.     public PolynomialFunctionNewtonForm(double[] a, double[] c)
  76.         throws MathIllegalArgumentException, NullArgumentException {

  77.         verifyInputArray(a, c);
  78.         this.a = new double[a.length];
  79.         this.c = new double[c.length];
  80.         System.arraycopy(a, 0, this.a, 0, a.length);
  81.         System.arraycopy(c, 0, this.c, 0, c.length);
  82.         coefficientsComputed = false;
  83.     }

  84.     /**
  85.      * Calculate the function value at the given point.
  86.      *
  87.      * @param z Point at which the function value is to be computed.
  88.      * @return the function value.
  89.      */
  90.     @Override
  91.     public double value(double z) {
  92.        return evaluate(a, c, z);
  93.     }

  94.     /**
  95.      * {@inheritDoc}
  96.      */
  97.     @Override
  98.     public <T extends Derivative<T>> T value(final T t) {
  99.         verifyInputArray(a, c);

  100.         final int n = c.length;
  101.         T value = t.getField().getZero().add(a[n]);
  102.         for (int i = n - 1; i >= 0; i--) {
  103.             value = t.subtract(c[i]).multiply(value).add(a[i]);
  104.         }

  105.         return value;

  106.     }

  107.     /**
  108.      * {@inheritDoc}
  109.      */
  110.     @Override
  111.     public <T extends CalculusFieldElement<T>> T value(final T t) {
  112.         verifyInputArray(a, c);

  113.         final int n = c.length;
  114.         T value = t.getField().getZero().add(a[n]);
  115.         for (int i = n - 1; i >= 0; i--) {
  116.             value = t.subtract(c[i]).multiply(value).add(a[i]);
  117.         }

  118.         return value;

  119.     }

  120.     /**
  121.      * Returns the degree of the polynomial.
  122.      *
  123.      * @return the degree of the polynomial
  124.      */
  125.     public int degree() {
  126.         return c.length;
  127.     }

  128.     /**
  129.      * Returns a copy of coefficients in Newton form formula.
  130.      * <p>
  131.      * Changes made to the returned copy will not affect the polynomial.</p>
  132.      *
  133.      * @return a fresh copy of coefficients in Newton form formula
  134.      */
  135.     public double[] getNewtonCoefficients() {
  136.         double[] out = new double[a.length];
  137.         System.arraycopy(a, 0, out, 0, a.length);
  138.         return out;
  139.     }

  140.     /**
  141.      * Returns a copy of the centers array.
  142.      * <p>
  143.      * Changes made to the returned copy will not affect the polynomial.</p>
  144.      *
  145.      * @return a fresh copy of the centers array.
  146.      */
  147.     public double[] getCenters() {
  148.         double[] out = new double[c.length];
  149.         System.arraycopy(c, 0, out, 0, c.length);
  150.         return out;
  151.     }

  152.     /**
  153.      * Returns a copy of the coefficients array.
  154.      * <p>
  155.      * Changes made to the returned copy will not affect the polynomial.</p>
  156.      *
  157.      * @return a fresh copy of the coefficients array.
  158.      */
  159.     public double[] getCoefficients() {
  160.         if (!coefficientsComputed) {
  161.             computeCoefficients();
  162.         }
  163.         double[] out = new double[coefficients.length];
  164.         System.arraycopy(coefficients, 0, out, 0, coefficients.length);
  165.         return out;
  166.     }

  167.     /**
  168.      * Evaluate the Newton polynomial using nested multiplication. It is
  169.      * also called <a href="http://mathworld.wolfram.com/HornersRule.html">
  170.      * Horner's Rule</a> and takes O(N) time.
  171.      *
  172.      * @param a Coefficients in Newton form formula.
  173.      * @param c Centers.
  174.      * @param z Point at which the function value is to be computed.
  175.      * @return the function value.
  176.      * @throws NullArgumentException if any argument is {@code null}.
  177.      * @throws MathIllegalArgumentException if any array has zero length.
  178.      * @throws MathIllegalArgumentException if the size difference between
  179.      * {@code a} and {@code c} is not equal to 1.
  180.      */
  181.     public static double evaluate(double[] a, double[] c, double z)
  182.         throws MathIllegalArgumentException, NullArgumentException {
  183.         verifyInputArray(a, c);

  184.         final int n = c.length;
  185.         double value = a[n];
  186.         for (int i = n - 1; i >= 0; i--) {
  187.             value = a[i] + (z - c[i]) * value;
  188.         }

  189.         return value;
  190.     }

  191.     /**
  192.      * Calculate the normal polynomial coefficients given the Newton form.
  193.      * It also uses nested multiplication but takes O(N^2) time.
  194.      */
  195.     protected void computeCoefficients() {
  196.         final int n = degree();

  197.         coefficients = new double[n+1];
  198.         for (int i = 0; i <= n; i++) {
  199.             coefficients[i] = 0.0;
  200.         }

  201.         coefficients[0] = a[n];
  202.         for (int i = n-1; i >= 0; i--) {
  203.             for (int j = n-i; j > 0; j--) {
  204.                 coefficients[j] = coefficients[j-1] - c[i] * coefficients[j];
  205.             }
  206.             coefficients[0] = a[i] - c[i] * coefficients[0];
  207.         }

  208.         coefficientsComputed = true;
  209.     }

  210.     /**
  211.      * Verifies that the input arrays are valid.
  212.      * <p>
  213.      * The centers must be distinct for interpolation purposes, but not
  214.      * for general use. Thus it is not verified here.</p>
  215.      *
  216.      * @param a the coefficients in Newton form formula
  217.      * @param c the centers
  218.      * @throws NullArgumentException if any argument is {@code null}.
  219.      * @throws MathIllegalArgumentException if any array has zero length.
  220.      * @throws MathIllegalArgumentException if the size difference between
  221.      * {@code a} and {@code c} is not equal to 1.
  222.      */
  223.     protected static void verifyInputArray(double[] a, double[] c)
  224.         throws MathIllegalArgumentException, NullArgumentException {
  225.         MathUtils.checkNotNull(a);
  226.         MathUtils.checkNotNull(c);
  227.         if (a.length == 0 || c.length == 0) {
  228.             throw new MathIllegalArgumentException(LocalizedCoreFormats.EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY);
  229.         }
  230.         if (a.length != c.length + 1) {
  231.             throw new MathIllegalArgumentException(LocalizedCoreFormats.ARRAY_SIZES_SHOULD_HAVE_DIFFERENCE_1,
  232.                                                  a.length, c.length);
  233.         }
  234.     }

  235. }