|
| 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.interpolation; |
| 18 | + |
| 19 | +import org.apache.commons.math4.legacy.analysis.polynomials.PolynomialFunction; |
| 20 | +import org.apache.commons.math4.legacy.analysis.polynomials.PolynomialSplineFunction; |
| 21 | +import org.apache.commons.math4.legacy.core.MathArrays; |
| 22 | +import org.apache.commons.math4.legacy.exception.DimensionMismatchException; |
| 23 | +import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException; |
| 24 | +import org.apache.commons.math4.legacy.exception.util.LocalizedFormats; |
| 25 | + |
| 26 | +/** |
| 27 | + * Clamped cubic spline interpolator. |
| 28 | + * The interpolating function consists in cubic polynomial functions defined over the |
| 29 | + * subintervals determined by the "knot points". |
| 30 | + * |
| 31 | + * The interpolating polynomials satisfy: |
| 32 | + * <ol> |
| 33 | + * <li>The value of the interpolating function at each of the input {@code x} values |
| 34 | + * equals the corresponding input {@code y} value.</li> |
| 35 | + * <li>Adjacent polynomials are equal through two derivatives at the knot points |
| 36 | + * (i.e., adjacent polynomials "match up" at the knot points, as do their first and |
| 37 | + * second derivatives).</li> |
| 38 | + * <li>The clamped boundary condition, i.e. the interpolating function takes "a |
| 39 | + * specific direction" at both its start point and its end point by providing the |
| 40 | + * desired first derivative values (slopes) as function parameters to |
| 41 | + * {@link #interpolate(double[], double[], double, double)}.</li> |
| 42 | + * </ol> |
| 43 | + * |
| 44 | + * The algorithm is implemented as described in |
| 45 | + * <blockquote> |
| 46 | + * R.L. Burden, J.D. Faires, |
| 47 | + * <em>Numerical Analysis</em>, 9th Ed., 2010, Cengage Learning, ISBN 0-538-73351-9, pp 153-156. |
| 48 | + * </blockquote> |
| 49 | + * |
| 50 | + */ |
| 51 | +public class ClampedSplineInterpolator implements UnivariateInterpolator { |
| 52 | + /** |
| 53 | + * |
| 54 | + * The first derivatives evaluated at the first and last knot points are |
| 55 | + * approximated from a natural/unclamped spline that passes through the same |
| 56 | + * set of points. |
| 57 | + * |
| 58 | + * @param x Arguments for the interpolation points. |
| 59 | + * @param y Values for the interpolation points. |
| 60 | + * @return the interpolating function. |
| 61 | + * @throws DimensionMismatchException if {@code x} and {@code y} have different sizes. |
| 62 | + * @throws NumberIsTooSmallException if the size of {@code x < 3}. |
| 63 | + * @throws org.apache.commons.math4.legacy.exception.NonMonotonicSequenceException if {@code x} is not sorted in strict increasing order. |
| 64 | + */ |
| 65 | + @Override |
| 66 | + public PolynomialSplineFunction interpolate(final double[] x, |
| 67 | + final double[] y) { |
| 68 | + final SplineInterpolator spliner = new SplineInterpolator(); |
| 69 | + final PolynomialSplineFunction spline = spliner.interpolate(x, y); |
| 70 | + final PolynomialSplineFunction derivativeSpline = spline.polynomialSplineDerivative(); |
| 71 | + final double fpStart = derivativeSpline.value(x[0]); |
| 72 | + final double fpEnd = derivativeSpline.value(x[x.length - 1]); |
| 73 | + |
| 74 | + return this.interpolate(x, y, fpStart, fpEnd); |
| 75 | + } |
| 76 | + |
| 77 | + /** |
| 78 | + * Computes an interpolating function for the data set with defined |
| 79 | + * boundary conditions. |
| 80 | + * |
| 81 | + * @param x Arguments for the interpolation points. |
| 82 | + * @param y Values for the interpolation points. |
| 83 | + * @param fpStart First derivative at the starting point of the returned |
| 84 | + * spline function (starting slope). |
| 85 | + * @param fpEnd First derivative at the ending point of the returned |
| 86 | + * spline function (ending slope). |
| 87 | + * @return the interpolating function. |
| 88 | + * @throws DimensionMismatchException if {@code x} and {@code y} have different sizes. |
| 89 | + * @throws NumberIsTooSmallException if the size of {@code x < 3}. |
| 90 | + * @throws org.apache.commons.math4.legacy.exception.NonMonotonicSequenceException if {@code x} is not sorted in strict increasing order. |
| 91 | + */ |
| 92 | + public PolynomialSplineFunction interpolate(final double[] x, |
| 93 | + final double[] y, |
| 94 | + final double fpStart, |
| 95 | + final double fpEnd) { |
| 96 | + if (x.length != y.length) { |
| 97 | + throw new DimensionMismatchException(x.length, y.length); |
| 98 | + } |
| 99 | + |
| 100 | + if (x.length < 3) { |
| 101 | + throw new NumberIsTooSmallException(LocalizedFormats.NUMBER_OF_POINTS, |
| 102 | + x.length, 3, true); |
| 103 | + } |
| 104 | + |
| 105 | + // Number of intervals. The number of data points is n + 1. |
| 106 | + final int n = x.length - 1; |
| 107 | + |
| 108 | + MathArrays.checkOrder(x); |
| 109 | + |
| 110 | + // Differences between knot points. |
| 111 | + final double[] h = new double[n]; |
| 112 | + for (int i = 0; i < n; i++) { |
| 113 | + h[i] = x[i + 1] - x[i]; |
| 114 | + } |
| 115 | + |
| 116 | + final double[] mu = new double[n]; |
| 117 | + final double[] z = new double[n + 1]; |
| 118 | + |
| 119 | + final double alpha0 = 3 * ((y[1] - y[0]) / h[0] - fpStart); |
| 120 | + final double alphaN = 3 * (fpEnd - (y[n] - y[n - 1]) / h[n - 1]); |
| 121 | + |
| 122 | + mu[0] = 0.5; |
| 123 | + final double ell0 = 2 * h[0]; |
| 124 | + z[0] = alpha0 / ell0; |
| 125 | + |
| 126 | + for (int i = 1; i < n; i++) { |
| 127 | + final double alpha = 3 * ((y[i + 1] - y[i]) / h[i] - (y[i] - y[i - 1]) / h[i - 1]); |
| 128 | + final double ell = 2 * (x[i + 1] - x[i - 1]) - h[i - 1] * mu[i - 1]; |
| 129 | + mu[i] = h[i] / ell; |
| 130 | + z[i] = (alpha - h[i - 1] * z[i - 1]) / ell; |
| 131 | + } |
| 132 | + |
| 133 | + // Cubic spline coefficients. |
| 134 | + final double[] b = new double[n]; // Linear. |
| 135 | + final double[] c = new double[n + 1]; // Quadratic. |
| 136 | + final double[] d = new double[n]; // Cubic. |
| 137 | + |
| 138 | + final double ellN = h[n - 1] * (2 - mu[n - 1]); |
| 139 | + z[n] = (alphaN - h[n - 1] * z[n - 1]) / ellN; |
| 140 | + c[n] = z[n]; |
| 141 | + |
| 142 | + for (int j = n - 1; j >= 0; j--) { |
| 143 | + c[j] = z[j] - mu[j] * c[j + 1]; |
| 144 | + b[j] = ((y[j + 1] - y[j]) / h[j]) - h[j] * (c[j + 1] + 2 * c[j]) / 3; |
| 145 | + d[j] = (c[j + 1] - c[j]) / (3 * h[j]); |
| 146 | + } |
| 147 | + |
| 148 | + final PolynomialFunction[] polynomials = new PolynomialFunction[n]; |
| 149 | + final double[] coefficients = new double[4]; |
| 150 | + for (int i = 0; i < n; i++) { |
| 151 | + coefficients[0] = y[i]; |
| 152 | + coefficients[1] = b[i]; |
| 153 | + coefficients[2] = c[i]; |
| 154 | + coefficients[3] = d[i]; |
| 155 | + polynomials[i] = new PolynomialFunction(coefficients); |
| 156 | + } |
| 157 | + |
| 158 | + return new PolynomialSplineFunction(x, polynomials); |
| 159 | + } |
| 160 | +} |
0 commit comments