FieldPolynomialFunction.java

  1. /*
  2.  * Licensed to the Hipparchus project 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 Hipparchus project 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. package org.hipparchus.analysis.polynomials;

  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.Field;
  20. import org.hipparchus.analysis.CalculusFieldUnivariateFunction;
  21. import org.hipparchus.exception.LocalizedCoreFormats;
  22. import org.hipparchus.exception.MathIllegalArgumentException;
  23. import org.hipparchus.exception.NullArgumentException;
  24. import org.hipparchus.util.FastMath;
  25. import org.hipparchus.util.MathArrays;
  26. import org.hipparchus.util.MathUtils;

  27. /**
  28.  * Immutable representation of a real polynomial function with real coefficients.
  29.  * <p>
  30.  * <a href="http://mathworld.wolfram.com/HornersMethod.html">Horner's Method</a>
  31.  * is used to evaluate the function.</p>
  32.  * @param <T> the type of the field elements
  33.  * @since 1.5
  34.  *
  35.  */
  36. public class FieldPolynomialFunction<T extends CalculusFieldElement<T>> implements CalculusFieldUnivariateFunction<T> {

  37.     /**
  38.      * The coefficients of the polynomial, ordered by degree -- i.e.,
  39.      * coefficients[0] is the constant term and coefficients[n] is the
  40.      * coefficient of x^n where n is the degree of the polynomial.
  41.      */
  42.     private final T[] coefficients;

  43.     /**
  44.      * Construct a polynomial with the given coefficients.  The first element
  45.      * of the coefficients array is the constant term.  Higher degree
  46.      * coefficients follow in sequence.  The degree of the resulting polynomial
  47.      * is the index of the last non-null element of the array, or 0 if all elements
  48.      * are null.
  49.      * <p>
  50.      * The constructor makes a copy of the input array and assigns the copy to
  51.      * the coefficients property.</p>
  52.      *
  53.      * @param c Polynomial coefficients.
  54.      * @throws NullArgumentException if {@code c} is {@code null}.
  55.      * @throws MathIllegalArgumentException if {@code c} is empty.
  56.      */
  57.     public FieldPolynomialFunction(final T[] c)
  58.         throws MathIllegalArgumentException, NullArgumentException {
  59.         super();
  60.         MathUtils.checkNotNull(c);
  61.         int n = c.length;
  62.         if (n == 0) {
  63.             throw new MathIllegalArgumentException(LocalizedCoreFormats.EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY);
  64.         }
  65.         while ((n > 1) && (c[n - 1].isZero())) {
  66.             --n;
  67.         }
  68.         this.coefficients = MathArrays.buildArray(c[0].getField(), n);
  69.         System.arraycopy(c, 0, this.coefficients, 0, n);
  70.     }

  71.     /**
  72.      * Compute the value of the function for the given argument.
  73.      * <p>
  74.      *  The value returned is </p><p>
  75.      *  {@code coefficients[n] * x^n + ... + coefficients[1] * x  + coefficients[0]}
  76.      * </p>
  77.      *
  78.      * @param x Argument for which the function value should be computed.
  79.      * @return the value of the polynomial at the given point.
  80.      *
  81.      * @see org.hipparchus.analysis.UnivariateFunction#value(double)
  82.      */
  83.     public T value(double x) {
  84.        return evaluate(coefficients, getField().getZero().add(x));
  85.     }

  86.     /**
  87.      * Compute the value of the function for the given argument.
  88.      * <p>
  89.      *  The value returned is </p><p>
  90.      *  {@code coefficients[n] * x^n + ... + coefficients[1] * x  + coefficients[0]}
  91.      * </p>
  92.      *
  93.      * @param x Argument for which the function value should be computed.
  94.      * @return the value of the polynomial at the given point.
  95.      *
  96.      * @see org.hipparchus.analysis.UnivariateFunction#value(double)
  97.      */
  98.     @Override
  99.     public T value(T x) {
  100.        return evaluate(coefficients, x);
  101.     }

  102.     /** Get the {@link Field} to which the instance belongs.
  103.      * @return {@link Field} to which the instance belongs
  104.      */
  105.     public Field<T> getField() {
  106.         return coefficients[0].getField();
  107.     }

  108.     /**
  109.      * Returns the degree of the polynomial.
  110.      *
  111.      * @return the degree of the polynomial.
  112.      */
  113.     public int degree() {
  114.         return coefficients.length - 1;
  115.     }

  116.     /**
  117.      * Returns a copy of the coefficients array.
  118.      * <p>
  119.      * Changes made to the returned copy will not affect the coefficients of
  120.      * the polynomial.</p>
  121.      *
  122.      * @return a fresh copy of the coefficients array.
  123.      */
  124.     public T[] getCoefficients() {
  125.         return coefficients.clone();
  126.     }

  127.     /**
  128.      * Uses Horner's Method to evaluate the polynomial with the given coefficients at
  129.      * the argument.
  130.      *
  131.      * @param coefficients Coefficients of the polynomial to evaluate.
  132.      * @param argument Input value.
  133.      * @param <T> the type of the field elements
  134.      * @return the value of the polynomial.
  135.      * @throws MathIllegalArgumentException if {@code coefficients} is empty.
  136.      * @throws NullArgumentException if {@code coefficients} is {@code null}.
  137.      */
  138.     protected static <T extends CalculusFieldElement<T>> T evaluate(T[] coefficients, T argument)
  139.         throws MathIllegalArgumentException, NullArgumentException {
  140.         MathUtils.checkNotNull(coefficients);
  141.         int n = coefficients.length;
  142.         if (n == 0) {
  143.             throw new MathIllegalArgumentException(LocalizedCoreFormats.EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY);
  144.         }
  145.         T result = coefficients[n - 1];
  146.         for (int j = n - 2; j >= 0; j--) {
  147.             result = argument.multiply(result).add(coefficients[j]);
  148.         }
  149.         return result;
  150.     }

  151.     /**
  152.      * Add a polynomial to the instance.
  153.      *
  154.      * @param p Polynomial to add.
  155.      * @return a new polynomial which is the sum of the instance and {@code p}.
  156.      */
  157.     public FieldPolynomialFunction<T> add(final FieldPolynomialFunction<T> p) {
  158.         // identify the lowest degree polynomial
  159.         final int lowLength  = FastMath.min(coefficients.length, p.coefficients.length);
  160.         final int highLength = FastMath.max(coefficients.length, p.coefficients.length);

  161.         // build the coefficients array
  162.         T[] newCoefficients = MathArrays.buildArray(getField(), highLength);
  163.         for (int i = 0; i < lowLength; ++i) {
  164.             newCoefficients[i] = coefficients[i].add(p.coefficients[i]);
  165.         }
  166.         System.arraycopy((coefficients.length < p.coefficients.length) ?
  167.                          p.coefficients : coefficients,
  168.                          lowLength,
  169.                          newCoefficients, lowLength,
  170.                          highLength - lowLength);

  171.         return new FieldPolynomialFunction<>(newCoefficients);
  172.     }

  173.     /**
  174.      * Subtract a polynomial from the instance.
  175.      *
  176.      * @param p Polynomial to subtract.
  177.      * @return a new polynomial which is the instance minus {@code p}.
  178.      */
  179.     public FieldPolynomialFunction<T> subtract(final FieldPolynomialFunction<T> p) {
  180.         // identify the lowest degree polynomial
  181.         int lowLength  = FastMath.min(coefficients.length, p.coefficients.length);
  182.         int highLength = FastMath.max(coefficients.length, p.coefficients.length);

  183.         // build the coefficients array
  184.         T[] newCoefficients = MathArrays.buildArray(getField(), highLength);
  185.         for (int i = 0; i < lowLength; ++i) {
  186.             newCoefficients[i] = coefficients[i].subtract(p.coefficients[i]);
  187.         }
  188.         if (coefficients.length < p.coefficients.length) {
  189.             for (int i = lowLength; i < highLength; ++i) {
  190.                 newCoefficients[i] = p.coefficients[i].negate();
  191.             }
  192.         } else {
  193.             System.arraycopy(coefficients, lowLength, newCoefficients, lowLength,
  194.                              highLength - lowLength);
  195.         }

  196.         return new FieldPolynomialFunction<>(newCoefficients);
  197.     }

  198.     /**
  199.      * Negate the instance.
  200.      *
  201.      * @return a new polynomial with all coefficients negated
  202.      */
  203.     public FieldPolynomialFunction<T> negate() {
  204.         final T[] newCoefficients = MathArrays.buildArray(getField(), coefficients.length);
  205.         for (int i = 0; i < coefficients.length; ++i) {
  206.             newCoefficients[i] = coefficients[i].negate();
  207.         }
  208.         return new FieldPolynomialFunction<>(newCoefficients);
  209.     }

  210.     /**
  211.      * Multiply the instance by a polynomial.
  212.      *
  213.      * @param p Polynomial to multiply by.
  214.      * @return a new polynomial equal to this times {@code p}
  215.      */
  216.     public FieldPolynomialFunction<T> multiply(final FieldPolynomialFunction<T> p) {
  217.         final Field<T> field = getField();
  218.         final T[] newCoefficients = MathArrays.buildArray(field, coefficients.length + p.coefficients.length - 1);

  219.         for (int i = 0; i < newCoefficients.length; ++i) {
  220.             newCoefficients[i] = field.getZero();
  221.             for (int j = FastMath.max(0, i + 1 - p.coefficients.length);
  222.                  j < FastMath.min(coefficients.length, i + 1);
  223.                  ++j) {
  224.                 newCoefficients[i] = newCoefficients[i].add(coefficients[j].multiply(p.coefficients[i-j]));
  225.             }
  226.         }

  227.         return new FieldPolynomialFunction<>(newCoefficients);
  228.     }

  229.     /**
  230.      * Returns the coefficients of the derivative of the polynomial with the given coefficients.
  231.      *
  232.      * @param coefficients Coefficients of the polynomial to differentiate.
  233.      * @param <T> the type of the field elements
  234.      * @return the coefficients of the derivative or {@code null} if coefficients has length 1.
  235.      * @throws MathIllegalArgumentException if {@code coefficients} is empty.
  236.      * @throws NullArgumentException if {@code coefficients} is {@code null}.
  237.      */
  238.     protected static <T extends CalculusFieldElement<T>> T[] differentiate(T[] coefficients)
  239.         throws MathIllegalArgumentException, NullArgumentException {
  240.         MathUtils.checkNotNull(coefficients);
  241.         int n = coefficients.length;
  242.         if (n == 0) {
  243.             throw new MathIllegalArgumentException(LocalizedCoreFormats.EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY);
  244.         }
  245.         final Field<T> field = coefficients[0].getField();
  246.         final T[] result = MathArrays.buildArray(field, FastMath.max(1, n - 1));
  247.         if (n == 1) {
  248.             result[0] = field.getZero();
  249.         } else {
  250.             for (int i = n - 1; i > 0; i--) {
  251.                 result[i - 1] = coefficients[i].multiply(i);
  252.             }
  253.         }
  254.         return result;
  255.     }

  256.     /**
  257.      * Returns an anti-derivative of this polynomial, with 0 constant term.
  258.      *
  259.      * @return a polynomial whose derivative has the same coefficients as this polynomial
  260.      */
  261.     public FieldPolynomialFunction<T> antiDerivative() {
  262.         final Field<T> field = getField();
  263.         final int d = degree();
  264.         final T[] anti = MathArrays.buildArray(field, d + 2);
  265.         anti[0] = field.getZero();
  266.         for (int i = 1; i <= d + 1; i++) {
  267.             anti[i] = coefficients[i - 1].multiply(1.0 / i);
  268.         }
  269.         return new FieldPolynomialFunction<>(anti);
  270.     }

  271.     /**
  272.      * Returns the definite integral of this polymomial over the given interval.
  273.      * <p>
  274.      * [lower, upper] must describe a finite interval (neither can be infinite
  275.      * and lower must be less than or equal to upper).
  276.      *
  277.      * @param lower lower bound for the integration
  278.      * @param upper upper bound for the integration
  279.      * @return the integral of this polymomial over the given interval
  280.      * @throws MathIllegalArgumentException if the bounds do not describe a finite interval
  281.      */
  282.     public T integrate(final double lower, final double upper) {
  283.         final T zero = getField().getZero();
  284.         return integrate(zero.add(lower), zero.add(upper));
  285.     }

  286.     /**
  287.      * Returns the definite integral of this polymomial over the given interval.
  288.      * <p>
  289.      * [lower, upper] must describe a finite interval (neither can be infinite
  290.      * and lower must be less than or equal to upper).
  291.      *
  292.      * @param lower lower bound for the integration
  293.      * @param upper upper bound for the integration
  294.      * @return the integral of this polymomial over the given interval
  295.      * @throws MathIllegalArgumentException if the bounds do not describe a finite interval
  296.      */
  297.     public T integrate(final T lower, final T upper) {
  298.         if (Double.isInfinite(lower.getReal()) || Double.isInfinite(upper.getReal())) {
  299.             throw new MathIllegalArgumentException(LocalizedCoreFormats.INFINITE_BOUND);
  300.         }
  301.         if (lower.getReal() > upper.getReal()) {
  302.             throw new MathIllegalArgumentException(LocalizedCoreFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND);
  303.         }
  304.         final FieldPolynomialFunction<T> anti = antiDerivative();
  305.         return anti.value(upper).subtract(anti.value(lower));
  306.     }

  307.     /**
  308.      * Returns the derivative as a {@link FieldPolynomialFunction}.
  309.      *
  310.      * @return the derivative polynomial.
  311.      */
  312.     public FieldPolynomialFunction<T> polynomialDerivative() {
  313.         return new FieldPolynomialFunction<>(differentiate(coefficients));
  314.     }

  315. }