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.  *      http://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.apache.commons.math4.legacy.analysis.polynomials;

  18. import java.util.Arrays;

  19. import org.apache.commons.math4.legacy.analysis.differentiation.DerivativeStructure;
  20. import org.apache.commons.math4.legacy.analysis.differentiation.UnivariateDifferentiableFunction;
  21. import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
  22. import org.apache.commons.math4.legacy.exception.NonMonotonicSequenceException;
  23. import org.apache.commons.math4.legacy.exception.NullArgumentException;
  24. import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
  25. import org.apache.commons.math4.legacy.exception.OutOfRangeException;
  26. import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
  27. import org.apache.commons.math4.legacy.core.MathArrays;

  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.  */
  61. public class PolynomialSplineFunction implements UnivariateDifferentiableFunction {
  62.     /**
  63.      * Spline segment interval delimiters (knots).
  64.      * Size is n + 1 for n segments.
  65.      */
  66.     private final double[] knots;
  67.     /**
  68.      * The polynomial functions that make up the spline.  The first element
  69.      * determines the value of the spline over the first subinterval, the
  70.      * second over the second, etc.   Spline function values are determined by
  71.      * evaluating these functions at {@code (x - knot[i])} where i is the
  72.      * knot segment to which x belongs.
  73.      */
  74.     private final PolynomialFunction[] polynomials;
  75.     /**
  76.      * Number of spline segments. It is equal to the number of polynomials and
  77.      * to the number of partition points - 1.
  78.      */
  79.     private final int n;


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

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

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

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


  155.     /** {@inheritDoc}
  156.      * @since 3.1
  157.      */
  158.     @Override
  159.     public DerivativeStructure value(final DerivativeStructure t) {
  160.         final double t0 = t.getValue();
  161.         if (t0 < knots[0] || t0 > knots[n]) {
  162.             throw new OutOfRangeException(t0, knots[0], knots[n]);
  163.         }
  164.         int i = Arrays.binarySearch(knots, t0);
  165.         if (i < 0) {
  166.             i = -i - 2;
  167.         }
  168.         // This will handle the case where t is the last knot value
  169.         // There are only n-1 polynomials, so if t is the last knot
  170.         // then we will use the last polynomial to calculate the value.
  171.         if ( i >= polynomials.length ) {
  172.             i--;
  173.         }
  174.         return polynomials[i].value(t.subtract(knots[i]));
  175.     }

  176.     /**
  177.      * Get the number of spline segments.
  178.      * It is also the number of polynomials and the number of knot points - 1.
  179.      *
  180.      * @return the number of spline segments.
  181.      */
  182.     public int getN() {
  183.         return n;
  184.     }

  185.     /**
  186.      * Get a copy of the interpolating polynomials array.
  187.      * It returns a fresh copy of the array. Changes made to the copy will
  188.      * not affect the polynomials property.
  189.      *
  190.      * @return the interpolating polynomials.
  191.      */
  192.     public PolynomialFunction[] getPolynomials() {
  193.         PolynomialFunction[] p = new PolynomialFunction[n];
  194.         System.arraycopy(polynomials, 0, p, 0, n);
  195.         return p;
  196.     }

  197.     /**
  198.      * Get an array copy of the knot points.
  199.      * It returns a fresh copy of the array. Changes made to the copy
  200.      * will not affect the knots property.
  201.      *
  202.      * @return the knot points.
  203.      */
  204.     public double[] getKnots() {
  205.         double[] out = new double[n + 1];
  206.         System.arraycopy(knots, 0, out, 0, n + 1);
  207.         return out;
  208.     }

  209.     /**
  210.      * Indicates whether a point is within the interpolation range.
  211.      *
  212.      * @param x Point.
  213.      * @return {@code true} if {@code x} is a valid point.
  214.      */
  215.     public boolean isValidPoint(double x) {
  216.         return !(x < knots[0] ||
  217.             x > knots[n]);
  218.     }
  219. }