Repository: commons-math
Updated Branches:
  refs/heads/master 0802df0f7 -> e11c00085


MATH-1166: Bicubic interpolation

New classes to replace "BicubicSplineInterpolatingFunction" and
"BicubicSplineInterpolator".


Project: http://git-wip-us.apache.org/repos/asf/commons-math/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-math/commit/136bf342
Tree: http://git-wip-us.apache.org/repos/asf/commons-math/tree/136bf342
Diff: http://git-wip-us.apache.org/repos/asf/commons-math/diff/136bf342

Branch: refs/heads/master
Commit: 136bf34297a9ac8049e451800df819ebf092cdf2
Parents: 8aec465
Author: Gilles <er...@apache.org>
Authored: Fri Nov 28 15:16:51 2014 +0100
Committer: Gilles <er...@apache.org>
Committed: Fri Nov 28 15:16:51 2014 +0100

----------------------------------------------------------------------
 .../BicubicInterpolatingFunction.java           | 325 ++++++++++++++++
 .../interpolation/BicubicInterpolator.java      | 112 ++++++
 .../BicubicInterpolatingFunctionTest.java       | 383 +++++++++++++++++++
 .../interpolation/BicubicInterpolatorTest.java  | 175 +++++++++
 4 files changed, 995 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-math/blob/136bf342/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolatingFunction.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolatingFunction.java
 
b/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolatingFunction.java
new file mode 100644
index 0000000..7fe947f
--- /dev/null
+++ 
b/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolatingFunction.java
@@ -0,0 +1,325 @@
+/*
+ * 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.math3.analysis.interpolation;
+
+import java.util.Arrays;
+import org.apache.commons.math3.analysis.BivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Function that implements the
+ * <a href="http://en.wikipedia.org/wiki/Bicubic_interpolation";>
+ * bicubic spline interpolation</a>.
+ *
+ * @since 3.4
+ */
+public class BicubicInterpolatingFunction
+    implements BivariateFunction {
+    /** Number of coefficients. */
+    private static final int NUM_COEFF = 16;
+    /**
+     * Matrix to compute the spline coefficients from the function values
+     * and function derivatives values
+     */
+    private static final double[][] AINV = {
+        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 },
+        { -3,3,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0 },
+        { 2,-2,0,0,1,1,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0 },
+        { 0,0,0,0,0,0,0,0,-3,3,0,0,-2,-1,0,0 },
+        { 0,0,0,0,0,0,0,0,2,-2,0,0,1,1,0,0 },
+        { -3,0,3,0,0,0,0,0,-2,0,-1,0,0,0,0,0 },
+        { 0,0,0,0,-3,0,3,0,0,0,0,0,-2,0,-1,0 },
+        { 9,-9,-9,9,6,3,-6,-3,6,-6,3,-3,4,2,2,1 },
+        { -6,6,6,-6,-3,-3,3,3,-4,4,-2,2,-2,-2,-1,-1 },
+        { 2,0,-2,0,0,0,0,0,1,0,1,0,0,0,0,0 },
+        { 0,0,0,0,2,0,-2,0,0,0,0,0,1,0,1,0 },
+        { -6,6,6,-6,-4,-2,4,2,-3,3,-3,3,-2,-1,-2,-1 },
+        { 4,-4,-4,4,2,2,-2,-2,2,-2,2,-2,1,1,1,1 }
+    };
+
+    /** Samples x-coordinates */
+    private final double[] xval;
+    /** Samples y-coordinates */
+    private final double[] yval;
+    /** Set of cubic splines patching the whole data grid */
+    private final BicubicFunction[][] splines;
+
+    /**
+     * @param x Sample values of the x-coordinate, in increasing order.
+     * @param y Sample values of the y-coordinate, in increasing order.
+     * @param f Values of the function on every grid point.
+     * @param dFdX Values of the partial derivative of function with respect
+     * to x on every grid point.
+     * @param dFdY Values of the partial derivative of function with respect
+     * to y on every grid point.
+     * @param d2FdXdY Values of the cross partial derivative of function on
+     * every grid point.
+     * @throws DimensionMismatchException if the various arrays do not contain
+     * the expected number of elements.
+     * @throws NonMonotonicSequenceException if {@code x} or {@code y} are
+     * not strictly increasing.
+     * @throws NoDataException if any of the arrays has zero length.
+     */
+    public BicubicInterpolatingFunction(double[] x,
+                                        double[] y,
+                                        double[][] f,
+                                        double[][] dFdX,
+                                        double[][] dFdY,
+                                        double[][] d2FdXdY)
+        throws DimensionMismatchException,
+               NoDataException,
+               NonMonotonicSequenceException {
+        final int xLen = x.length;
+        final int yLen = y.length;
+
+        if (xLen == 0 || yLen == 0 || f.length == 0 || f[0].length == 0) {
+            throw new NoDataException();
+        }
+        if (xLen != f.length) {
+            throw new DimensionMismatchException(xLen, f.length);
+        }
+        if (xLen != dFdX.length) {
+            throw new DimensionMismatchException(xLen, dFdX.length);
+        }
+        if (xLen != dFdY.length) {
+            throw new DimensionMismatchException(xLen, dFdY.length);
+        }
+        if (xLen != d2FdXdY.length) {
+            throw new DimensionMismatchException(xLen, d2FdXdY.length);
+        }
+
+        MathArrays.checkOrder(x);
+        MathArrays.checkOrder(y);
+
+        xval = x.clone();
+        yval = y.clone();
+
+        final int lastI = xLen - 1;
+        final int lastJ = yLen - 1;
+        splines = new BicubicFunction[lastI][lastJ];
+
+        for (int i = 0; i < lastI; i++) {
+            if (f[i].length != yLen) {
+                throw new DimensionMismatchException(f[i].length, yLen);
+            }
+            if (dFdX[i].length != yLen) {
+                throw new DimensionMismatchException(dFdX[i].length, yLen);
+            }
+            if (dFdY[i].length != yLen) {
+                throw new DimensionMismatchException(dFdY[i].length, yLen);
+            }
+            if (d2FdXdY[i].length != yLen) {
+                throw new DimensionMismatchException(d2FdXdY[i].length, yLen);
+            }
+            final int ip1 = i + 1;
+            final double xR = xval[ip1] - xval[i];
+            for (int j = 0; j < lastJ; j++) {
+                final int jp1 = j + 1;
+                final double yR = yval[jp1] - yval[j];
+                final double xRyR = xR * yR;
+                final double[] beta = new double[] {
+                    f[i][j], f[ip1][j], f[i][jp1], f[ip1][jp1],
+                    dFdX[i][j] * xR, dFdX[ip1][j] * xR, dFdX[i][jp1] * xR, 
dFdX[ip1][jp1] * xR,
+                    dFdY[i][j] * yR, dFdY[ip1][j] * yR, dFdY[i][jp1] * yR, 
dFdY[ip1][jp1] * yR,
+                    d2FdXdY[i][j] * xRyR, d2FdXdY[ip1][j] * xRyR, 
d2FdXdY[i][jp1] * xRyR, d2FdXdY[ip1][jp1] * xRyR
+                };
+
+                splines[i][j] = new 
BicubicFunction(computeSplineCoefficients(beta));
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public double value(double x, double y)
+        throws OutOfRangeException {
+        final int i = searchIndex(x, xval);
+        final int j = searchIndex(y, yval);
+
+        final double xN = (x - xval[i]) / (xval[i + 1] - xval[i]);
+        final double yN = (y - yval[j]) / (yval[j + 1] - yval[j]);
+
+        return splines[i][j].value(xN, yN);
+    }
+
+    /**
+     * Indicates whether a point is within the interpolation range.
+     *
+     * @param x First coordinate.
+     * @param y Second coordinate.
+     * @return {@code true} if (x, y) is a valid point.
+     */
+    public boolean isValidPoint(double x, double y) {
+        if (x < xval[0] ||
+            x > xval[xval.length - 1] ||
+            y < yval[0] ||
+            y > yval[yval.length - 1]) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * @param c Coordinate.
+     * @param val Coordinate samples.
+     * @return the index in {@code val} corresponding to the interval
+     * containing {@code c}.
+     * @throws OutOfRangeException if {@code c} is out of the
+     * range defined by the boundary values of {@code val}.
+     */
+    private int searchIndex(double c, double[] val) {
+        final int r = Arrays.binarySearch(val, c);
+
+        if (r == -1 ||
+            r == -val.length - 1) {
+            throw new OutOfRangeException(c, val[0], val[val.length - 1]);
+        }
+
+        if (r < 0) {
+            // "c" in within an interpolation sub-interval: Return the
+            // index of the sample at the lower end of the sub-interval.
+            return -r - 2;
+        }
+        final int last = val.length - 1;
+        if (r == last) {
+            // "c" is the last sample of the range: Return the index
+            // of the sample at the lower end of the last sub-interval.
+            return last - 1;
+        }
+
+        // "c" is another sample point.
+        return r;
+    }
+
+    /**
+     * Compute the spline coefficients from the list of function values and
+     * function partial derivatives values at the four corners of a grid
+     * element. They must be specified in the following order:
+     * <ul>
+     *  <li>f(0,0)</li>
+     *  <li>f(1,0)</li>
+     *  <li>f(0,1)</li>
+     *  <li>f(1,1)</li>
+     *  <li>f<sub>x</sub>(0,0)</li>
+     *  <li>f<sub>x</sub>(1,0)</li>
+     *  <li>f<sub>x</sub>(0,1)</li>
+     *  <li>f<sub>x</sub>(1,1)</li>
+     *  <li>f<sub>y</sub>(0,0)</li>
+     *  <li>f<sub>y</sub>(1,0)</li>
+     *  <li>f<sub>y</sub>(0,1)</li>
+     *  <li>f<sub>y</sub>(1,1)</li>
+     *  <li>f<sub>xy</sub>(0,0)</li>
+     *  <li>f<sub>xy</sub>(1,0)</li>
+     *  <li>f<sub>xy</sub>(0,1)</li>
+     *  <li>f<sub>xy</sub>(1,1)</li>
+     * </ul>
+     * where the subscripts indicate the partial derivative with respect to
+     * the corresponding variable(s).
+     *
+     * @param beta List of function values and function partial derivatives
+     * values.
+     * @return the spline coefficients.
+     */
+    private double[] computeSplineCoefficients(double[] beta) {
+        final double[] a = new double[NUM_COEFF];
+
+        for (int i = 0; i < NUM_COEFF; i++) {
+            double result = 0;
+            final double[] row = AINV[i];
+            for (int j = 0; j < NUM_COEFF; j++) {
+                result += row[j] * beta[j];
+            }
+            a[i] = result;
+        }
+
+        return a;
+    }
+}
+
+/**
+ * Bicubic function.
+ */
+class BicubicFunction implements BivariateFunction {
+    /** Number of points. */
+    private static final short N = 4;
+    /** Coefficients */
+    private final double[][] a;
+
+    /**
+     * Simple constructor.
+     *
+     * @param coeff Spline coefficients.
+     */
+    public BicubicFunction(double[] coeff) {
+        a = new double[N][N];
+        for (int j = 0; j < N; j++) {
+            final double[] aJ = a[j];
+            for (int i = 0; i < N; i++) {
+                aJ[i] = coeff[i * N + j];
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public double value(double x, double y) {
+        if (x < 0 || x > 1) {
+            throw new OutOfRangeException(x, 0, 1);
+        }
+        if (y < 0 || y > 1) {
+            throw new OutOfRangeException(y, 0, 1);
+        }
+
+        final double x2 = x * x;
+        final double x3 = x2 * x;
+        final double[] pX = {1, x, x2, x3};
+
+        final double y2 = y * y;
+        final double y3 = y2 * y;
+        final double[] pY = {1, y, y2, y3};
+
+        return apply(pX, pY, a);
+    }
+
+    /**
+     * Compute the value of the bicubic polynomial.
+     *
+     * @param pX Powers of the x-coordinate.
+     * @param pY Powers of the y-coordinate.
+     * @param coeff Spline coefficients.
+     * @return the interpolated value.
+     */
+    private double apply(double[] pX, double[] pY, double[][] coeff) {
+        double result = 0;
+        for (int i = 0; i < N; i++) {
+            final double r = MathArrays.linearCombination(coeff[i], pY);
+            result += r * pX[i];
+        }
+
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-math/blob/136bf342/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolator.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolator.java
 
b/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolator.java
new file mode 100644
index 0000000..7a3f948
--- /dev/null
+++ 
b/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolator.java
@@ -0,0 +1,112 @@
+/*
+ * 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.math3.analysis.interpolation;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Generates a {@link BicubicInterpolatingFunction bicubic interpolating
+ * function}.
+ * <p>
+ *  Caveat: Because the interpolation scheme requires that derivatives be
+ *  specified at the sample points, those are approximated with finite
+ *  differences (using the 2-points symmetric formulae).
+ *  Since their values are undefined at the borders of the provided
+ *  interpolation ranges, the interpolated values will be wrong at the
+ *  edges of the patch.
+ *  The {@code interpolate} method will return a function that overrides
+ *  {@link isValidPoint(double,double)} to indicate points where the
+ *  interpolation will be inaccurate.
+ * </p>
+ *
+ * @since 3.4
+ */
+public class BicubicInterpolator
+    implements BivariateGridInterpolator {
+    /**
+     * {@inheritDoc}
+     */
+    public BicubicInterpolatingFunction interpolate(final double[] xval,
+                                                    final double[] yval,
+                                                    final double[][] fval)
+        throws NoDataException, DimensionMismatchException,
+               NonMonotonicSequenceException, NumberIsTooSmallException {
+        if (xval.length == 0 || yval.length == 0 || fval.length == 0) {
+            throw new NoDataException();
+        }
+        if (xval.length != fval.length) {
+            throw new DimensionMismatchException(xval.length, fval.length);
+        }
+
+        MathArrays.checkOrder(xval);
+        MathArrays.checkOrder(yval);
+
+        final int xLen = xval.length;
+        final int yLen = yval.length;
+
+        // Approximation to the partial derivatives using finite differences.
+        final double[][] dFdX = new double[xLen][yLen];
+        final double[][] dFdY = new double[xLen][yLen];
+        final double[][] d2FdXdY = new double[xLen][yLen];
+        for (int i = 1; i < xLen - 1; i++) {
+            final int nI = i + 1;
+            final int pI = i - 1;
+
+            final double nX = xval[nI];
+            final double pX = xval[pI];
+
+            final double deltaX = nX - pX;
+
+            for (int j = 1; j < yLen - 1; j++) {
+                final int nJ = j + 1;
+                final int pJ = j - 1;
+
+                final double nY = yval[nJ];
+                final double pY = yval[pJ];
+
+                final double deltaY = nY - pY;
+
+                dFdX[i][j] = (fval[nI][j] - fval[pI][j]) / deltaX;
+                dFdY[i][j] = (fval[i][nJ] - fval[i][pJ]) / deltaY;
+
+                final double deltaXY = deltaX * deltaY;
+
+                d2FdXdY[i][j] = (fval[nI][nJ] - fval[nI][pJ] - fval[pI][nJ] + 
fval[pI][pJ]) / deltaXY;
+            }
+        }
+
+        // Create the interpolating function.
+        return new BicubicInterpolatingFunction(xval, yval, fval,
+                                                dFdX, dFdY, d2FdXdY) {
+            @Override
+            public boolean isValidPoint(double x, double y) {
+                if (x < xval[1] ||
+                    x > xval[xval.length - 2] ||
+                    y < yval[1] ||
+                    y > yval[yval.length - 2]) {
+                    return false;
+                } else {
+                    return true;
+                }
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-math/blob/136bf342/src/test/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolatingFunctionTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolatingFunctionTest.java
 
b/src/test/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolatingFunctionTest.java
new file mode 100644
index 0000000..6d56ed8
--- /dev/null
+++ 
b/src/test/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolatingFunctionTest.java
@@ -0,0 +1,383 @@
+/*
+ * 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.math3.analysis.interpolation;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.analysis.BivariateFunction;
+import org.apache.commons.math3.distribution.UniformRealDistribution;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.Ignore;
+
+/**
+ * Test case for the bicubic function.
+ */
+public final class BicubicInterpolatingFunctionTest {
+    /**
+     * Test preconditions.
+     */
+    @Test
+    public void testPreconditions() {
+        double[] xval = new double[] {3, 4, 5, 6.5};
+        double[] yval = new double[] {-4, -3, -1, 2.5};
+        double[][] zval = new double[xval.length][yval.length];
+
+        @SuppressWarnings("unused")
+        BivariateFunction bcf = new BicubicInterpolatingFunction(xval, yval, 
zval,
+                                                                 zval, zval, 
zval);
+
+        double[] wxval = new double[] {3, 2, 5, 6.5};
+        try {
+            bcf = new BicubicInterpolatingFunction(wxval, yval, zval, zval, 
zval, zval);
+            Assert.fail("an exception should have been thrown");
+        } catch (MathIllegalArgumentException e) {
+            // Expected
+        }
+        double[] wyval = new double[] {-4, -1, -1, 2.5};
+        try {
+            bcf = new BicubicInterpolatingFunction(xval, wyval, zval, zval, 
zval, zval);
+            Assert.fail("an exception should have been thrown");
+        } catch (MathIllegalArgumentException e) {
+            // Expected
+        }
+        double[][] wzval = new double[xval.length][yval.length - 1];
+        try {
+            bcf = new BicubicInterpolatingFunction(xval, yval, wzval, zval, 
zval, zval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            bcf = new BicubicInterpolatingFunction(xval, yval, zval, wzval, 
zval, zval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            bcf = new BicubicInterpolatingFunction(xval, yval, zval, zval, 
wzval, zval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            bcf = new BicubicInterpolatingFunction(xval, yval, zval, zval, 
zval, wzval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+
+        wzval = new double[xval.length - 1][yval.length];
+        try {
+            bcf = new BicubicInterpolatingFunction(xval, yval, wzval, zval, 
zval, zval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            bcf = new BicubicInterpolatingFunction(xval, yval, zval, wzval, 
zval, zval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            bcf = new BicubicInterpolatingFunction(xval, yval, zval, zval, 
wzval, zval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            bcf = new BicubicInterpolatingFunction(xval, yval, zval, zval, 
zval, wzval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public void testIsValidPoint() {
+        final double xMin = -12;
+        final double xMax = 34;
+        final double yMin = 5;
+        final double yMax = 67;
+        final double[] xval = new double[] { xMin, xMax };
+        final double[] yval = new double[] { yMin, yMax };
+        final double[][] f = new double[][] { { 1, 2 },
+                                              { 3, 4 } };
+        final double[][] dFdX = f;
+        final double[][] dFdY = f;
+        final double[][] dFdXdY = f;
+
+        final BicubicInterpolatingFunction bcf
+            = new BicubicInterpolatingFunction(xval, yval, f,
+                                                     dFdX, dFdY, dFdXdY);
+
+        double x, y;
+
+        x = xMin;
+        y = yMin;
+        Assert.assertTrue(bcf.isValidPoint(x, y));
+        // Ensure that no exception is thrown.
+        bcf.value(x, y);
+
+        x = xMax;
+        y = yMax;
+        Assert.assertTrue(bcf.isValidPoint(x, y));
+        // Ensure that no exception is thrown.
+        bcf.value(x, y);
+
+        final double xRange = xMax - xMin;
+        final double yRange = yMax - yMin;
+        x = xMin + xRange / 3.4;
+        y = yMin + yRange / 1.2;
+        Assert.assertTrue(bcf.isValidPoint(x, y));
+        // Ensure that no exception is thrown.
+        bcf.value(x, y);
+
+        final double small = 1e-8;
+        x = xMin - small;
+        y = yMax;
+        Assert.assertFalse(bcf.isValidPoint(x, y));
+        // Ensure that an exception would have been thrown.
+        try {
+            bcf.value(x, y);
+            Assert.fail("OutOfRangeException expected");
+        } catch (OutOfRangeException expected) {}
+
+        x = xMin;
+        y = yMax + small;
+        Assert.assertFalse(bcf.isValidPoint(x, y));
+        // Ensure that an exception would have been thrown.
+        try {
+            bcf.value(x, y);
+            Assert.fail("OutOfRangeException expected");
+        } catch (OutOfRangeException expected) {}
+    }
+
+    /**
+     * Interpolating a plane.
+     * <p>
+     * z = 2 x - 3 y + 5
+     */
+    @Test
+    public void testPlane() {
+        final int numberOfElements = 10;
+        final double minimumX = -10;
+        final double maximumX = 10;
+        final double minimumY = -10;
+        final double maximumY = 10;
+        final int numberOfSamples = 1000;
+
+        final double interpolationTolerance = 1e-15;
+        final double maxTolerance = 1e-14;
+
+        // Function values
+        BivariateFunction f = new BivariateFunction() {
+                public double value(double x, double y) {
+                    return 2 * x - 3 * y + 5;
+                }
+            };
+        BivariateFunction dfdx = new BivariateFunction() {
+                public double value(double x, double y) {
+                    return 2;
+                }
+            };
+        BivariateFunction dfdy = new BivariateFunction() {
+                public double value(double x, double y) {
+                    return -3;
+                }
+            };
+        BivariateFunction d2fdxdy = new BivariateFunction() {
+                public double value(double x, double y) {
+                    return 0;
+                }
+            };
+
+        testInterpolation(minimumX,
+                          maximumX,
+                          minimumY,
+                          maximumY,
+                          numberOfElements,
+                          numberOfSamples,
+                          f,
+                          dfdx,
+                          dfdy,
+                          d2fdxdy,
+                          interpolationTolerance,
+                          maxTolerance,
+                          false);
+    }
+
+    /**
+     * Interpolating a paraboloid.
+     * <p>
+     * z = 2 x<sup>2</sup> - 3 y<sup>2</sup> + 4 x y - 5
+     */
+    @Test
+    public void testParaboloid() {
+        final int numberOfElements = 10;
+        final double minimumX = -10;
+        final double maximumX = 10;
+        final double minimumY = -10;
+        final double maximumY = 10;
+        final int numberOfSamples = 1000;
+
+        final double interpolationTolerance = 2e-14;
+        final double maxTolerance = 1e-12;
+
+        // Function values
+        BivariateFunction f = new BivariateFunction() {
+                public double value(double x, double y) {
+                    return 2 * x * x - 3 * y * y + 4 * x * y - 5;
+                }
+            };
+        BivariateFunction dfdx = new BivariateFunction() {
+                public double value(double x, double y) {
+                    return 4 * (x + y);
+                }
+            };
+        BivariateFunction dfdy = new BivariateFunction() {
+                public double value(double x, double y) {
+                    return 4 * x - 6 * y;
+                }
+            };
+        BivariateFunction d2fdxdy = new BivariateFunction() {
+                public double value(double x, double y) {
+                    return 4;
+                }
+            };
+
+        testInterpolation(minimumX,
+                          maximumX,
+                          minimumY,
+                          maximumY,
+                          numberOfElements,
+                          numberOfSamples,
+                          f,
+                          dfdx,
+                          dfdy,
+                          d2fdxdy,
+                          interpolationTolerance,
+                          maxTolerance,
+                          false);
+    }
+
+    /**
+     * @param minimumX Lower bound of interpolation range along the 
x-coordinate.
+     * @param maximumX Higher bound of interpolation range along the 
x-coordinate.
+     * @param minimumY Lower bound of interpolation range along the 
y-coordinate.
+     * @param maximumY Higher bound of interpolation range along the 
y-coordinate.
+     * @param numberOfElements Number of data points (along each dimension).
+     * @param numberOfSamples Number of test points.
+     * @param f Function to test.
+     * @param dfdx Partial derivative w.r.t. x of the function to test.
+     * @param dfdy Partial derivative w.r.t. y of the function to test.
+     * @param d2fdxdy Second partial cross-derivative of the function to test.
+     * @param meanTolerance Allowed average error (mean error on all 
interpolated values).
+     * @param maxTolerance Allowed error on each interpolated value.
+     */
+    private void testInterpolation(double minimumX,
+                                   double maximumX,
+                                   double minimumY,
+                                   double maximumY,
+                                   int numberOfElements,
+                                   int numberOfSamples,
+                                   BivariateFunction f,
+                                   BivariateFunction dfdx,
+                                   BivariateFunction dfdy,
+                                   BivariateFunction d2fdxdy,
+                                   double meanTolerance,
+                                   double maxTolerance,
+                                   boolean print) {
+        double expected;
+        double actual;
+        double currentX;
+        double currentY;
+        final double deltaX = (maximumX - minimumX) / numberOfElements;
+        final double deltaY = (maximumY - minimumY) / numberOfElements;
+        final double[] xValues = new double[numberOfElements];
+        final double[] yValues = new double[numberOfElements];
+        final double[][] zValues = new 
double[numberOfElements][numberOfElements];
+        final double[][] dzdx = new double[numberOfElements][numberOfElements];
+        final double[][] dzdy = new double[numberOfElements][numberOfElements];
+        final double[][] d2zdxdy = new 
double[numberOfElements][numberOfElements];
+
+        for (int i = 0; i < numberOfElements; i++) {
+            xValues[i] = minimumX + deltaX * i;
+            final double x = xValues[i];
+            for (int j = 0; j < numberOfElements; j++) {
+                yValues[j] = minimumY + deltaY * j;
+                final double y = yValues[j];
+                zValues[i][j] = f.value(x, y);
+                dzdx[i][j] = dfdx.value(x, y);
+                dzdy[i][j] = dfdy.value(x, y);
+                d2zdxdy[i][j] = d2fdxdy.value(x, y);
+            }
+        }
+
+        final BivariateFunction interpolation
+            = new BicubicInterpolatingFunction(xValues,
+                                               yValues,
+                                               zValues,
+                                               dzdx,
+                                               dzdy,
+                                               d2zdxdy);
+
+        for (int i = 0; i < numberOfElements; i++) {
+            currentX = xValues[i];
+            for (int j = 0; j < numberOfElements; j++) {
+                currentY = yValues[j];
+                expected = f.value(currentX, currentY);
+                actual = interpolation.value(currentX, currentY);
+                Assert.assertTrue("On data point: " + expected + " != " + 
actual,
+                                  Precision.equals(expected, actual));
+            }
+        }
+
+        final RandomGenerator rng = new Well19937c(1234567L);
+        final UniformRealDistribution distX = new UniformRealDistribution(rng, 
xValues[0], xValues[xValues.length - 1]);
+        final UniformRealDistribution distY = new UniformRealDistribution(rng, 
yValues[0], yValues[yValues.length - 1]);
+
+        double sumError = 0;
+        for (int i = 0; i < numberOfSamples; i++) {
+            currentX = distX.sample();
+            currentY = distY.sample();
+            expected = f.value(currentX, currentY);
+
+            if (print) {
+                System.out.println(currentX + " " + currentY + " -> ");
+            }
+
+            actual = interpolation.value(currentX, currentY);
+            sumError += FastMath.abs(actual - expected);
+
+            if (print) {
+                System.out.println(actual + " (diff=" + (expected - actual) + 
")");
+            }
+
+            Assert.assertEquals(expected, actual, maxTolerance);
+        }
+
+        final double meanError = sumError / numberOfSamples;
+        Assert.assertEquals(0, meanError, meanTolerance);
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-math/blob/136bf342/src/test/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolatorTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolatorTest.java
 
b/src/test/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolatorTest.java
new file mode 100644
index 0000000..e2a3550
--- /dev/null
+++ 
b/src/test/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolatorTest.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.math3.analysis.interpolation;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.analysis.BivariateFunction;
+import org.apache.commons.math3.distribution.UniformRealDistribution;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test case for the bicubic interpolator.
+ */
+public final class BicubicInterpolatorTest {
+    /**
+     * Test preconditions.
+     */
+    @Test
+    public void testPreconditions() {
+        double[] xval = new double[] {3, 4, 5, 6.5};
+        double[] yval = new double[] {-4, -3, -1, 2.5};
+        double[][] zval = new double[xval.length][yval.length];
+
+        BivariateGridInterpolator interpolator = new BicubicInterpolator();
+
+        @SuppressWarnings("unused")
+        BivariateFunction p = interpolator.interpolate(xval, yval, zval);
+
+        double[] wxval = new double[] {3, 2, 5, 6.5};
+        try {
+            p = interpolator.interpolate(wxval, yval, zval);
+            Assert.fail("an exception should have been thrown");
+        } catch (MathIllegalArgumentException e) {
+            // Expected
+        }
+
+        double[] wyval = new double[] {-4, -3, -1, -1};
+        try {
+            p = interpolator.interpolate(xval, wyval, zval);
+            Assert.fail("an exception should have been thrown");
+        } catch (MathIllegalArgumentException e) {
+            // Expected
+        }
+
+        double[][] wzval = new double[xval.length][yval.length + 1];
+        try {
+            p = interpolator.interpolate(xval, yval, wzval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        wzval = new double[xval.length - 1][yval.length];
+        try {
+            p = interpolator.interpolate(xval, yval, wzval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+    }
+
+    /**
+     * Interpolating a plane.
+     * <p>
+     * z = 2 x - 3 y + 5
+     */
+    @Test
+    public void testPlane() {
+        BivariateFunction f = new BivariateFunction() {
+                public double value(double x, double y) {
+                    return 2 * x - 3 * y + 5;
+                }
+            };
+
+        testInterpolation(3000,
+                          1e-13,
+                          f,
+                          false);
+    }
+
+    /**
+     * Interpolating a paraboloid.
+     * <p>
+     * z = 2 x<sup>2</sup> - 3 y<sup>2</sup> + 4 x y - 5
+     */
+    @Test
+    public void testParaboloid() {
+        BivariateFunction f = new BivariateFunction() {
+                public double value(double x, double y) {
+                    return 2 * x * x - 3 * y * y + 4 * x * y - 5;
+                }
+            };
+
+        testInterpolation(3000,
+                          1e-12,
+                          f,
+                          false);
+    }
+
+    /**
+     * @param numSamples Number of test samples.
+     * @param tolerance Allowed tolerance on the interpolated value.
+     * @param f Test function.
+     * @param print Whether to print debugging output to the console.
+     */
+    private void testInterpolation(int numSamples,
+                                   double tolerance,
+                                   BivariateFunction f,
+                                   boolean print) {
+        final int sz = 21;
+        final double[] xval = new double[sz];
+        final double[] yval = new double[sz];
+        // Coordinate values
+        final double delta = 1d / (sz - 1);
+        for (int i = 0; i < sz; i++) {
+            xval[i] = -1 + 15 * i * delta;
+            yval[i] = -20 + 30 * i * delta;
+        }
+
+        final double[][] zval = new double[xval.length][yval.length];
+        for (int i = 0; i < xval.length; i++) {
+            for (int j = 0; j < yval.length; j++) {
+                zval[i][j] = f.value(xval[i], yval[j]);
+            }
+        }
+
+        final BicubicInterpolator interpolator = new BicubicInterpolator();
+        final BicubicInterpolatingFunction p = interpolator.interpolate(xval, 
yval, zval);
+        double x, y;
+
+        final RandomGenerator rng = new Well19937c();
+        final UniformRealDistribution distX = new UniformRealDistribution(rng, 
xval[0], xval[xval.length - 1]);
+        final UniformRealDistribution distY = new UniformRealDistribution(rng, 
yval[0], yval[yval.length - 1]);
+
+        int count = 0;
+        while (true) {
+            x = distX.sample();
+            y = distY.sample();
+            if (!p.isValidPoint(x, y)) {
+                if (print) {
+                    System.out.println("# " + x + " " + y);
+                }
+                continue;
+            }
+
+            if (count++ > numSamples) {
+                break;
+            }
+            final double expected = f.value(x, y);
+            final double actual = p.value(x, y);
+
+            if (print) {
+                System.out.println(x + " " + y + " " + expected + " " + 
actual);
+            }
+
+            Assert.assertEquals(expected, actual, tolerance);
+        }
+    }
+}

Reply via email to