This is an automated email from the ASF dual-hosted git repository. erans pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-math.git
commit 4c4c227092e9182609d91e1e88a32ee8edc2f8ce Author: Gilles Sadowski <gillese...@gmail.com> AuthorDate: Mon May 12 17:25:48 2025 +0200 MATH-1650: Add unit tests file. Thanks to Michael Scholz. --- .../ClampedSplineInterpolatorTest.java | 175 +++++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/analysis/interpolation/ClampedSplineInterpolatorTest.java b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/analysis/interpolation/ClampedSplineInterpolatorTest.java new file mode 100644 index 000000000..de233cac6 --- /dev/null +++ b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/analysis/interpolation/ClampedSplineInterpolatorTest.java @@ -0,0 +1,175 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.legacy.analysis.interpolation; + +import org.apache.commons.math4.legacy.TestUtils; +import org.apache.commons.math4.legacy.analysis.integration.SimpsonIntegrator; +import org.apache.commons.math4.legacy.analysis.polynomials.PolynomialFunction; +import org.apache.commons.math4.legacy.analysis.polynomials.PolynomialSplineFunction; +import org.apache.commons.math4.legacy.exception.DimensionMismatchException; +import org.apache.commons.math4.legacy.exception.NonMonotonicSequenceException; +import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException; +import org.junit.Assert; +import org.junit.Test; + +/** + * Test the ClampedSplineInterpolator. + */ +public class ClampedSplineInterpolatorTest { + /** Error tolerance for spline interpolator value at knot points. */ + private static final double KNOT_TOL = 1e-14; + /** Error tolerance for interpolating polynomial coefficients. */ + private static final double COEF_TOL = 1e-14; + + @Test + public void testInterpolateLinearDegenerateTwoSegment() { + final double[] x = {0, 0.5, 1}; + final double[] y = {1, Math.exp(0.5), Math.exp(1)}; + final double fpo = 1; + final double fpn = Math.exp(1); + final ClampedSplineInterpolator i = new ClampedSplineInterpolator(); + final PolynomialSplineFunction f = i.interpolate(x, y, fpo, fpn); + verifyInterpolation(f, x, y); + verifyConsistency(f, x); + + // Verify coefficients using analytical values + final PolynomialFunction[] polynomials = f.getPolynomials(); + + final double[] target0 = {1, 1, 0.4889506772539256, 0.21186881109317435}; + final double[] target1 = {1.6487212707001282, 1.6478522855738063, 0.8067538938936871, 0.35156753198873575}; + TestUtils.assertEquals(polynomials[0].getCoefficients(), target0, COEF_TOL); + TestUtils.assertEquals(polynomials[1].getCoefficients(), target1, COEF_TOL); + } + + @Test + public void testInterpolateLinearDegenerateThreeSegment() { + final double[] x = {0, 1, 2, 3}; + final double[] y = {1, Math.exp(1), Math.exp(2), Math.exp(3)}; + final double fpo = 1; + final double fpn = Math.exp(3); + final ClampedSplineInterpolator i = new ClampedSplineInterpolator(); + final PolynomialSplineFunction f = i.interpolate(x, y, fpo, fpn); + verifyInterpolation(f, x, y); + verifyConsistency(f, x); + + // Verify coefficients using analytical values + final PolynomialFunction[] polynomials = f.getPolynomials(); + + final double[] target0 = {1, 0.9999999999999999, 0.4446824969658283, 0.27359933149321697}; + final double[] target1 = {2.718281828459045, 2.710162988411307, 1.2654804914454791, 0.6951307906148195}; + final double[] target2 = {7.38905609893065, 7.326516343146723, 3.3508728632899376, 2.019091617820356}; + TestUtils.assertEquals(polynomials[0].getCoefficients(), target0, COEF_TOL); + TestUtils.assertEquals(polynomials[1].getCoefficients(), target1, COEF_TOL); + TestUtils.assertEquals(polynomials[2].getCoefficients(), target2, COEF_TOL); + } + + @Test(expected=DimensionMismatchException.class) + public void testArrayLengthMismatch() { + // Data set arrays of different size. + new ClampedSplineInterpolator().interpolate(new double[] {1, 2, 3, 4}, + new double[] {2, 3, 5}, + 2, 1); + } + @Test(expected=NonMonotonicSequenceException.class) + public void testUnsortedArray() { + // Knot values not sorted. + new ClampedSplineInterpolator().interpolate(new double[] {1, 3, 2 }, + new double[] {2, 3, 5}, + 2, 1); + } + @Test(expected=NumberIsTooSmallException.class) + public void testInsufficientData() { + // Not enough data to interpolate. + new ClampedSplineInterpolator().interpolate(new double[] {1, 2 }, + new double[] {2, 3}, + 2, 1); + } + + /** + * Verifies that a clamped spline <i>without</i> specified boundary conditions behaves similar to a natural + * ("unclamped") spline. + * + * <p> + * Using the exponential function <code>e<sup>x</sup></code> over the interval <code>[0, 3]</code>, the test + * evaluates: + * <ol> + * <li>The integral of a clamped spline with specified boundary conditions (endpoint slopes/derivatives).</li> + * <li>The integral of a clamped spline without specified boundary conditions.</li> + * <li>The integral of a natural spline (without boundary conditions by default).</li> + * </ol> + * + * These integrals are compared against the direct integral of the function <code>e<sup>x</sup></code> over the same + * interval.</p> + * + * <p> + * This test is based on "Example 4" in R.L. Burden, J.D. Faires, + * <u>Numerical Analysis</u>, 9th Ed., 2010, Cengage Learning, ISBN 0-538-73351-9, pp 156-157. + * </p> + */ + @Test + public void testIntegral() { + final double[] x = {0, 1, 2, 3}; + final double[] y = {1, Math.exp(1), Math.exp(2), Math.exp(3)}; + final double fpo = 1; + final double fpn = Math.exp(3); + + final ClampedSplineInterpolator clampedSplineInterpolator = new ClampedSplineInterpolator(); + final PolynomialSplineFunction clampedSpline = clampedSplineInterpolator.interpolate(x, y, fpo, fpn); + final PolynomialSplineFunction clampedSplineAsNaturalSpline = clampedSplineInterpolator.interpolate(x, y); + + final SplineInterpolator naturalSplineInterpolator = new SplineInterpolator(); + final PolynomialSplineFunction naturalSpline = naturalSplineInterpolator.interpolate(x, y); + + final SimpsonIntegrator integrator = new SimpsonIntegrator(); + + final double clampedSplineIntegral = integrator.integrate(1000, clampedSpline, 0, 3); + final double clampedSplineAsNaturalSplineIntegral = integrator.integrate(1000, clampedSplineAsNaturalSpline, 0, 3); + final double naturalSplineIntegral = integrator.integrate(1000, naturalSpline, 0, 3); + final double exponentialFunctionIntegral = integrator.integrate(1000, arg -> Math.exp(arg), 0, 3); + + Assert.assertEquals(Math.abs(clampedSplineAsNaturalSplineIntegral - naturalSplineIntegral), 0, 0); + Assert.assertEquals(Math.abs(exponentialFunctionIntegral - clampedSplineIntegral), 0.02589, 0.1); + Assert.assertEquals(Math.abs(exponentialFunctionIntegral - naturalSplineIntegral), 0.46675, 0.1); + } + + /** + * Verifies that f(x[i]) = y[i] for i = 0, ..., n-1 (where n is common length). + */ + private void verifyInterpolation(PolynomialSplineFunction f, + double[] x, double[] y) { + for (int i = 0; i < x.length; i++) { + Assert.assertEquals(f.value(x[i]), y[i], KNOT_TOL); + } + } + + /** + * Verifies that interpolating polynomials satisfy consistency requirement: adjacent polynomials must agree through + * two derivatives at knot points. + */ + private void verifyConsistency(PolynomialSplineFunction f, + double[] x) { + PolynomialFunction polynomials[] = f.getPolynomials(); + for (int i = 1; i < x.length - 2; i++) { + // evaluate polynomials and derivatives at x[i + 1] + Assert.assertEquals(polynomials[i].value(x[i + 1] - x[i]), polynomials[i + 1].value(0), 0.1); + Assert.assertEquals(polynomials[i].polynomialDerivative().value(x[i + 1] - x[i]), + polynomials[i + 1].polynomialDerivative().value(0), 0.5); + Assert.assertEquals(polynomials[i].polynomialDerivative().polynomialDerivative().value(x[i + 1] - x[i]), + polynomials[i + 1].polynomialDerivative().polynomialDerivative().value(0), 0.5); + } + } +}