BicubicInterpolatingFunction.java
- /*
- * 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
- *
- * https://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.
- */
- /*
- * This is not the original file distributed by the Apache Software Foundation
- * It has been modified by the Hipparchus project
- */
- package org.hipparchus.analysis.interpolation;
- import java.util.Arrays;
- import org.hipparchus.analysis.BivariateFunction;
- import org.hipparchus.exception.LocalizedCoreFormats;
- import org.hipparchus.exception.MathIllegalArgumentException;
- import org.hipparchus.util.MathArrays;
- import org.hipparchus.util.MathUtils;
- /**
- * Function that implements the
- * <a href="http://en.wikipedia.org/wiki/Bicubic_interpolation">
- * bicubic spline interpolation</a>.
- *
- */
- 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;
- /** Simple constructor.
- * @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 MathIllegalArgumentException if the various arrays do not contain
- * the expected number of elements.
- * @throws MathIllegalArgumentException if {@code x} or {@code y} are
- * not strictly increasing.
- * @throws MathIllegalArgumentException if any of the arrays has zero length.
- */
- public BicubicInterpolatingFunction(double[] x,
- double[] y,
- double[][] f,
- double[][] dFdX,
- double[][] dFdY,
- double[][] d2FdXdY)
- throws MathIllegalArgumentException {
- final int xLen = x.length;
- final int yLen = y.length;
- if (xLen == 0 || yLen == 0 || f.length == 0 || f[0].length == 0) {
- throw new MathIllegalArgumentException(LocalizedCoreFormats.NO_DATA);
- }
- MathUtils.checkDimension(xLen, f.length);
- MathUtils.checkDimension(xLen, dFdX.length);
- MathUtils.checkDimension(xLen, dFdY.length);
- MathUtils.checkDimension(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++) {
- MathUtils.checkDimension(f[i].length, yLen);
- MathUtils.checkDimension(dFdX[i].length, yLen);
- MathUtils.checkDimension(dFdY[i].length, yLen);
- MathUtils.checkDimension(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 = {
- 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}
- */
- @Override
- public double value(double x, double y)
- throws MathIllegalArgumentException {
- 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 MathIllegalArgumentException 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 MathIllegalArgumentException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE,
- 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.
- */
- 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}
- */
- @Override
- public double value(double x, double y) {
- MathUtils.checkRangeInclusive(x, 0, 1);
- MathUtils.checkRangeInclusive(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;
- }
- }