PolynomialSplineFunction.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 java.util.Arrays;

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

  32. /**
  33.  * Represents a polynomial spline function.
  34.  * <p>
  35.  * A <strong>polynomial spline function</strong> consists of a set of
  36.  * <i>interpolating polynomials</i> and an ascending array of domain
  37.  * <i>knot points</i>, determining the intervals over which the spline function
  38.  * is defined by the constituent polynomials.  The polynomials are assumed to
  39.  * have been computed to match the values of another function at the knot
  40.  * points.  The value consistency constraints are not currently enforced by
  41.  * <code>PolynomialSplineFunction</code> itself, but are assumed to hold among
  42.  * the polynomials and knot points passed to the constructor.</p>
  43.  * <p>
  44.  * N.B.:  The polynomials in the <code>polynomials</code> property must be
  45.  * centered on the knot points to compute the spline function values.
  46.  * See below.</p>
  47.  * <p>
  48.  * The domain of the polynomial spline function is
  49.  * <code>[smallest knot, largest knot]</code>.  Attempts to evaluate the
  50.  * function at values outside of this range generate IllegalArgumentExceptions.
  51.  * </p>
  52.  * <p>
  53.  * The value of the polynomial spline function for an argument <code>x</code>
  54.  * is computed as follows:
  55.  * <ol>
  56.  * <li>The knot array is searched to find the segment to which <code>x</code>
  57.  * belongs.  If <code>x</code> is less than the smallest knot point or greater
  58.  * than the largest one, an <code>IllegalArgumentException</code>
  59.  * is thrown.</li>
  60.  * <li> Let <code>j</code> be the index of the largest knot point that is less
  61.  * than or equal to <code>x</code>.  The value returned is
  62.  * {@code polynomials[j](x - knot[j])}</li></ol>
  63.  *
  64.  */
  65. public class PolynomialSplineFunction implements UnivariateDifferentiableFunction, FieldUnivariateFunction {
  66.     /**
  67.      * Spline segment interval delimiters (knots).
  68.      * Size is n + 1 for n segments.
  69.      */
  70.     private final double[] knots;
  71.     /**
  72.      * The polynomial functions that make up the spline.  The first element
  73.      * determines the value of the spline over the first subinterval, the
  74.      * second over the second, etc.   Spline function values are determined by
  75.      * evaluating these functions at {@code (x - knot[i])} where i is the
  76.      * knot segment to which x belongs.
  77.      */
  78.     private final PolynomialFunction[] polynomials;
  79.     /**
  80.      * Number of spline segments. It is equal to the number of polynomials and
  81.      * to the number of partition points - 1.
  82.      */
  83.     private final int n;


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

  110.         this.n = knots.length -1;
  111.         this.knots = new double[n + 1];
  112.         System.arraycopy(knots, 0, this.knots, 0, n + 1);
  113.         this.polynomials = new PolynomialFunction[n];
  114.         System.arraycopy(polynomials, 0, this.polynomials, 0, n);
  115.     }

  116.     /**
  117.      * Compute the value for the function.
  118.      * See {@link PolynomialSplineFunction} for details on the algorithm for
  119.      * computing the value of the function.
  120.      *
  121.      * @param v Point for which the function value should be computed.
  122.      * @return the value.
  123.      * @throws MathIllegalArgumentException if {@code v} is outside of the domain of the
  124.      * spline function (smaller than the smallest knot point or larger than the
  125.      * largest knot point).
  126.      */
  127.     @Override
  128.     public double value(double v) {
  129.         MathUtils.checkRangeInclusive(v, knots[0], knots[n]);
  130.         int i = Arrays.binarySearch(knots, v);
  131.         if (i < 0) {
  132.             i = -i - 2;
  133.         }
  134.         // This will handle the case where v is the last knot value
  135.         // There are only n-1 polynomials, so if v is the last knot
  136.         // then we will use the last polynomial to calculate the value.
  137.         if ( i >= polynomials.length ) {
  138.             i--;
  139.         }
  140.         return polynomials[i].value(v - knots[i]);
  141.     }

  142.     /**
  143.      * Get the derivative of the polynomial spline function.
  144.      *
  145.      * @return the derivative function.
  146.      */
  147.     public PolynomialSplineFunction polynomialSplineDerivative() {
  148.         PolynomialFunction[] derivativePolynomials = new PolynomialFunction[n];
  149.         for (int i = 0; i < n; i++) {
  150.             derivativePolynomials[i] = polynomials[i].polynomialDerivative();
  151.         }
  152.         return new PolynomialSplineFunction(knots, derivativePolynomials);
  153.     }


  154.     /** {@inheritDoc}
  155.      */
  156.     @Override
  157.     public <T extends Derivative<T>> T value(final T t) {
  158.         final double t0 = t.getReal();
  159.         MathUtils.checkRangeInclusive(t0, knots[0], knots[n]);
  160.         int i = Arrays.binarySearch(knots, t0);
  161.         if (i < 0) {
  162.             i = -i - 2;
  163.         }
  164.         // This will handle the case where t is the last knot value
  165.         // There are only n-1 polynomials, so if t is the last knot
  166.         // then we will use the last polynomial to calculate the value.
  167.         if ( i >= polynomials.length ) {
  168.             i--;
  169.         }
  170.         return polynomials[i].value(t.subtract(knots[i]));
  171.     }

  172.     /**
  173.      * {@inheritDoc}
  174.      */
  175.     @Override
  176.     public <T extends CalculusFieldElement<T>> T value(final T t) {
  177.         final double t0 = t.getReal();
  178.         MathUtils.checkRangeInclusive(t0, knots[0], knots[n]);
  179.         int i = Arrays.binarySearch(knots, t0);
  180.         if (i < 0) {
  181.             i = -i - 2;
  182.         }
  183.         // This will handle the case where t is the last knot value
  184.         // There are only n-1 polynomials, so if t is the last knot
  185.         // then we will use the last polynomial to calculate the value.
  186.         if ( i >= polynomials.length ) {
  187.             i--;
  188.         }
  189.         return polynomials[i].value(t.subtract(knots[i]));
  190.     }

  191.     /**
  192.      * Get the number of spline segments.
  193.      * It is also the number of polynomials and the number of knot points - 1.
  194.      *
  195.      * @return the number of spline segments.
  196.      */
  197.     public int getN() {
  198.         return n;
  199.     }

  200.     /**
  201.      * Get a copy of the interpolating polynomials array.
  202.      * It returns a fresh copy of the array. Changes made to the copy will
  203.      * not affect the polynomials property.
  204.      *
  205.      * @return the interpolating polynomials.
  206.      */
  207.     public PolynomialFunction[] getPolynomials() {
  208.         PolynomialFunction[] p = new PolynomialFunction[n];
  209.         System.arraycopy(polynomials, 0, p, 0, n);
  210.         return p;
  211.     }

  212.     /**
  213.      * Get an array copy of the knot points.
  214.      * It returns a fresh copy of the array. Changes made to the copy
  215.      * will not affect the knots property.
  216.      *
  217.      * @return the knot points.
  218.      */
  219.     public double[] getKnots() {
  220.         double[] out = new double[n + 1];
  221.         System.arraycopy(knots, 0, out, 0, n + 1);
  222.         return out;
  223.     }

  224.     /**
  225.      * Indicates whether a point is within the interpolation range.
  226.      *
  227.      * @param x Point.
  228.      * @return {@code true} if {@code x} is a valid point.
  229.      */
  230.     public boolean isValidPoint(double x) {
  231.         return x >= knots[0] && x <= knots[n];
  232.     }
  233. }