FieldPolynomialSplineFunction.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 java.lang.reflect.Array;
  19. import java.util.Arrays;

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

  28. /**
  29.  * Represents a polynomial spline function.
  30.  * <p>
  31.  * A <strong>polynomial spline function</strong> consists of a set of
  32.  * <i>interpolating polynomials</i> and an ascending array of domain
  33.  * <i>knot points</i>, determining the intervals over which the spline function
  34.  * is defined by the constituent polynomials.  The polynomials are assumed to
  35.  * have been computed to match the values of another function at the knot
  36.  * points.  The value consistency constraints are not currently enforced by
  37.  * <code>PolynomialSplineFunction</code> itself, but are assumed to hold among
  38.  * the polynomials and knot points passed to the constructor.</p>
  39.  * <p>
  40.  * N.B.:  The polynomials in the <code>polynomials</code> property must be
  41.  * centered on the knot points to compute the spline function values.
  42.  * See below.</p>
  43.  * <p>
  44.  * The domain of the polynomial spline function is
  45.  * <code>[smallest knot, largest knot]</code>.  Attempts to evaluate the
  46.  * function at values outside of this range generate IllegalArgumentExceptions.
  47.  * </p>
  48.  * <p>
  49.  * The value of the polynomial spline function for an argument <code>x</code>
  50.  * is computed as follows:
  51.  * <ol>
  52.  * <li>The knot array is searched to find the segment to which <code>x</code>
  53.  * belongs.  If <code>x</code> is less than the smallest knot point or greater
  54.  * than the largest one, an <code>IllegalArgumentException</code>
  55.  * is thrown.</li>
  56.  * <li> Let <code>j</code> be the index of the largest knot point that is less
  57.  * than or equal to <code>x</code>.  The value returned is
  58.  * {@code polynomials[j](x - knot[j])}</li></ol>
  59.  *
  60.  * @param <T> the type of the field elements
  61.  * @since 1.5
  62.  */
  63. public class FieldPolynomialSplineFunction<T extends CalculusFieldElement<T>> implements CalculusFieldUnivariateFunction<T> {

  64.     /**
  65.      * Spline segment interval delimiters (knots).
  66.      * Size is n + 1 for n segments.
  67.      */
  68.     private final T[] knots;

  69.     /**
  70.      * The polynomial functions that make up the spline.  The first element
  71.      * determines the value of the spline over the first subinterval, the
  72.      * second over the second, etc.   Spline function values are determined by
  73.      * evaluating these functions at {@code (x - knot[i])} where i is the
  74.      * knot segment to which x belongs.
  75.      */
  76.     private final FieldPolynomialFunction<T>[] polynomials;

  77.     /**
  78.      * Number of spline segments. It is equal to the number of polynomials and
  79.      * to the number of partition points - 1.
  80.      */
  81.     private final int n;


  82.     /**
  83.      * Construct a polynomial spline function with the given segment delimiters
  84.      * and interpolating polynomials.
  85.      * The constructor copies both arrays and assigns the copies to the knots
  86.      * and polynomials properties, respectively.
  87.      *
  88.      * @param knots Spline segment interval delimiters.
  89.      * @param polynomials Polynomial functions that make up the spline.
  90.      * @throws NullArgumentException if either of the input arrays is {@code null}.
  91.      * @throws MathIllegalArgumentException if knots has length less than 2.
  92.      * @throws MathIllegalArgumentException if {@code polynomials.length != knots.length - 1}.
  93.      * @throws MathIllegalArgumentException if the {@code knots} array is not strictly increasing.
  94.      *
  95.      */
  96.     @SuppressWarnings("unchecked")
  97.     public FieldPolynomialSplineFunction(final T[] knots, final FieldPolynomialFunction<T>[] polynomials)
  98.         throws MathIllegalArgumentException, NullArgumentException {
  99.         if (knots == null ||
  100.             polynomials == null) {
  101.             throw new NullArgumentException();
  102.         }
  103.         if (knots.length < 2) {
  104.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NOT_ENOUGH_POINTS_IN_SPLINE_PARTITION,
  105.                                                    2, knots.length, false);
  106.         }
  107.         MathUtils.checkDimension(polynomials.length, knots.length - 1);
  108.         MathArrays.checkOrder(knots);

  109.         this.n = knots.length -1;
  110.         this.knots = knots.clone();
  111.         this.polynomials = (FieldPolynomialFunction<T>[]) Array.newInstance(FieldPolynomialFunction.class, n);
  112.         System.arraycopy(polynomials, 0, this.polynomials, 0, n);
  113.     }

  114.     /** Get the {@link Field} to which the instance belongs.
  115.      * @return {@link Field} to which the instance belongs
  116.      */
  117.     public Field<T> getField() {
  118.         return knots[0].getField();
  119.     }

  120.     /**
  121.      * Compute the value for the function.
  122.      * See {@link FieldPolynomialSplineFunction} for details on the algorithm for
  123.      * computing the value of the function.
  124.      *
  125.      * @param v Point for which the function value should be computed.
  126.      * @return the value.
  127.      * @throws MathIllegalArgumentException if {@code v} is outside of the domain of the
  128.      * spline function (smaller than the smallest knot point or larger than the
  129.      * largest knot point).
  130.      */
  131.     public T value(final double v) {
  132.         return value(getField().getZero().add(v));
  133.     }

  134.     /**
  135.      * Compute the value for the function.
  136.      * See {@link FieldPolynomialSplineFunction} for details on the algorithm for
  137.      * computing the value of the function.
  138.      *
  139.      * @param v Point for which the function value should be computed.
  140.      * @return the value.
  141.      * @throws MathIllegalArgumentException if {@code v} is outside of the domain of the
  142.      * spline function (smaller than the smallest knot point or larger than the
  143.      * largest knot point).
  144.      */
  145.     @Override
  146.     public T value(final T v) {
  147.         MathUtils.checkRangeInclusive(v.getReal(), knots[0].getReal(), knots[n].getReal());
  148.         int i = Arrays.binarySearch(Arrays.stream(knots).map(T::getReal).toArray(), v.getReal());
  149.         if (i < 0) {
  150.             i = -i - 2;
  151.         }
  152.         // This will handle the case where v is the last knot value
  153.         // There are only n-1 polynomials, so if v is the last knot
  154.         // then we will use the last polynomial to calculate the value.
  155.         if ( i >= polynomials.length ) {
  156.             i--;
  157.         }
  158.         return polynomials[i].value(v.subtract(knots[i]));
  159.     }

  160.     /**
  161.      * Get the number of spline segments.
  162.      * It is also the number of polynomials and the number of knot points - 1.
  163.      *
  164.      * @return the number of spline segments.
  165.      */
  166.     public int getN() {
  167.         return n;
  168.     }

  169.     /**
  170.      * Get a copy of the interpolating polynomials array.
  171.      * It returns a fresh copy of the array. Changes made to the copy will
  172.      * not affect the polynomials property.
  173.      *
  174.      * @return the interpolating polynomials.
  175.      */
  176.     public FieldPolynomialFunction<T>[] getPolynomials() {
  177.         return polynomials.clone();
  178.     }

  179.     /**
  180.      * Get an array copy of the knot points.
  181.      * It returns a fresh copy of the array. Changes made to the copy
  182.      * will not affect the knots property.
  183.      *
  184.      * @return the knot points.
  185.      */
  186.     public T[] getKnots() {
  187.         return knots.clone();
  188.     }

  189.     /**
  190.      * Indicates whether a point is within the interpolation range.
  191.      *
  192.      * @param x Point.
  193.      * @return {@code true} if {@code x} is a valid point.
  194.      */
  195.     public boolean isValidPoint(T x) {
  196.         if (x.getReal() < knots[0].getReal() ||
  197.             x.getReal() > knots[n].getReal()) {
  198.             return false;
  199.         } else {
  200.             return true;
  201.         }
  202.     }
  203.     /**
  204.      * Get the derivative of the polynomial spline function.
  205.      *
  206.      * @return the derivative function.
  207.      */
  208.     @SuppressWarnings("unchecked")
  209.     public FieldPolynomialSplineFunction<T> polynomialSplineDerivative() {
  210.         FieldPolynomialFunction<T>[] derivativePolynomials =
  211.                         (FieldPolynomialFunction<T>[]) Array.newInstance(FieldPolynomialFunction.class, n);
  212.         for (int i = 0; i < n; i++) {
  213.             derivativePolynomials[i] = polynomials[i].polynomialDerivative();
  214.         }
  215.         return new FieldPolynomialSplineFunction<>(knots, derivativePolynomials);
  216.     }


  217. }