BicubicInterpolatingFunction.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.  *      https://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. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.analysis.interpolation;

  22. import java.util.Arrays;

  23. import org.hipparchus.analysis.BivariateFunction;
  24. import org.hipparchus.exception.LocalizedCoreFormats;
  25. import org.hipparchus.exception.MathIllegalArgumentException;
  26. import org.hipparchus.util.MathArrays;
  27. import org.hipparchus.util.MathUtils;

  28. /**
  29.  * Function that implements the
  30.  * <a href="http://en.wikipedia.org/wiki/Bicubic_interpolation">
  31.  * bicubic spline interpolation</a>.
  32.  *
  33.  */
  34. public class BicubicInterpolatingFunction
  35.     implements BivariateFunction {
  36.     /** Number of coefficients. */
  37.     private static final int NUM_COEFF = 16;
  38.     /**
  39.      * Matrix to compute the spline coefficients from the function values
  40.      * and function derivatives values
  41.      */
  42.     private static final double[][] AINV = {
  43.         { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
  44.         { 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 },
  45.         { -3,3,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0 },
  46.         { 2,-2,0,0,1,1,0,0,0,0,0,0,0,0,0,0 },
  47.         { 0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0 },
  48.         { 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0 },
  49.         { 0,0,0,0,0,0,0,0,-3,3,0,0,-2,-1,0,0 },
  50.         { 0,0,0,0,0,0,0,0,2,-2,0,0,1,1,0,0 },
  51.         { -3,0,3,0,0,0,0,0,-2,0,-1,0,0,0,0,0 },
  52.         { 0,0,0,0,-3,0,3,0,0,0,0,0,-2,0,-1,0 },
  53.         { 9,-9,-9,9,6,3,-6,-3,6,-6,3,-3,4,2,2,1 },
  54.         { -6,6,6,-6,-3,-3,3,3,-4,4,-2,2,-2,-2,-1,-1 },
  55.         { 2,0,-2,0,0,0,0,0,1,0,1,0,0,0,0,0 },
  56.         { 0,0,0,0,2,0,-2,0,0,0,0,0,1,0,1,0 },
  57.         { -6,6,6,-6,-4,-2,4,2,-3,3,-3,3,-2,-1,-2,-1 },
  58.         { 4,-4,-4,4,2,2,-2,-2,2,-2,2,-2,1,1,1,1 }
  59.     };

  60.     /** Samples x-coordinates */
  61.     private final double[] xval;
  62.     /** Samples y-coordinates */
  63.     private final double[] yval;
  64.     /** Set of cubic splines patching the whole data grid */
  65.     private final BicubicFunction[][] splines;

  66.     /** Simple constructor.
  67.      * @param x Sample values of the x-coordinate, in increasing order.
  68.      * @param y Sample values of the y-coordinate, in increasing order.
  69.      * @param f Values of the function on every grid point.
  70.      * @param dFdX Values of the partial derivative of function with respect
  71.      * to x on every grid point.
  72.      * @param dFdY Values of the partial derivative of function with respect
  73.      * to y on every grid point.
  74.      * @param d2FdXdY Values of the cross partial derivative of function on
  75.      * every grid point.
  76.      * @throws MathIllegalArgumentException if the various arrays do not contain
  77.      * the expected number of elements.
  78.      * @throws MathIllegalArgumentException if {@code x} or {@code y} are
  79.      * not strictly increasing.
  80.      * @throws MathIllegalArgumentException if any of the arrays has zero length.
  81.      */
  82.     public BicubicInterpolatingFunction(double[] x,
  83.                                         double[] y,
  84.                                         double[][] f,
  85.                                         double[][] dFdX,
  86.                                         double[][] dFdY,
  87.                                         double[][] d2FdXdY)
  88.         throws MathIllegalArgumentException {
  89.         final int xLen = x.length;
  90.         final int yLen = y.length;

  91.         if (xLen == 0 || yLen == 0 || f.length == 0 || f[0].length == 0) {
  92.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NO_DATA);
  93.         }
  94.         MathUtils.checkDimension(xLen, f.length);
  95.         MathUtils.checkDimension(xLen, dFdX.length);
  96.         MathUtils.checkDimension(xLen, dFdY.length);
  97.         MathUtils.checkDimension(xLen, d2FdXdY.length);
  98.         MathArrays.checkOrder(x);
  99.         MathArrays.checkOrder(y);

  100.         xval = x.clone();
  101.         yval = y.clone();

  102.         final int lastI = xLen - 1;
  103.         final int lastJ = yLen - 1;
  104.         splines = new BicubicFunction[lastI][lastJ];

  105.         for (int i = 0; i < lastI; i++) {
  106.             MathUtils.checkDimension(f[i].length, yLen);
  107.             MathUtils.checkDimension(dFdX[i].length, yLen);
  108.             MathUtils.checkDimension(dFdY[i].length, yLen);
  109.             MathUtils.checkDimension(d2FdXdY[i].length, yLen);

  110.             final int ip1 = i + 1;
  111.             final double xR = xval[ip1] - xval[i];
  112.             for (int j = 0; j < lastJ; j++) {
  113.                 final int jp1 = j + 1;
  114.                 final double yR = yval[jp1] - yval[j];
  115.                 final double xRyR = xR * yR;
  116.                 final double[] beta = {
  117.                     f[i][j], f[ip1][j], f[i][jp1], f[ip1][jp1],
  118.                     dFdX[i][j] * xR, dFdX[ip1][j] * xR, dFdX[i][jp1] * xR, dFdX[ip1][jp1] * xR,
  119.                     dFdY[i][j] * yR, dFdY[ip1][j] * yR, dFdY[i][jp1] * yR, dFdY[ip1][jp1] * yR,
  120.                     d2FdXdY[i][j] * xRyR, d2FdXdY[ip1][j] * xRyR, d2FdXdY[i][jp1] * xRyR, d2FdXdY[ip1][jp1] * xRyR
  121.                 };

  122.                 splines[i][j] = new BicubicFunction(computeSplineCoefficients(beta));
  123.             }
  124.         }
  125.     }

  126.     /**
  127.      * {@inheritDoc}
  128.      */
  129.     @Override
  130.     public double value(double x, double y)
  131.         throws MathIllegalArgumentException {
  132.         final int i = searchIndex(x, xval);
  133.         final int j = searchIndex(y, yval);

  134.         final double xN = (x - xval[i]) / (xval[i + 1] - xval[i]);
  135.         final double yN = (y - yval[j]) / (yval[j + 1] - yval[j]);

  136.         return splines[i][j].value(xN, yN);
  137.     }

  138.     /**
  139.      * Indicates whether a point is within the interpolation range.
  140.      *
  141.      * @param x First coordinate.
  142.      * @param y Second coordinate.
  143.      * @return {@code true} if (x, y) is a valid point.
  144.      */
  145.     public boolean isValidPoint(double x, double y) {
  146.         if (x < xval[0] ||
  147.             x > xval[xval.length - 1] ||
  148.             y < yval[0] ||
  149.             y > yval[yval.length - 1]) {
  150.             return false;
  151.         } else {
  152.             return true;
  153.         }
  154.     }

  155.     /**
  156.      * @param c Coordinate.
  157.      * @param val Coordinate samples.
  158.      * @return the index in {@code val} corresponding to the interval
  159.      * containing {@code c}.
  160.      * @throws MathIllegalArgumentException if {@code c} is out of the
  161.      * range defined by the boundary values of {@code val}.
  162.      */
  163.     private int searchIndex(double c, double[] val) {
  164.         final int r = Arrays.binarySearch(val, c);

  165.         if (r == -1 ||
  166.             r == -val.length - 1) {
  167.             throw new MathIllegalArgumentException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE,
  168.                                                    c, val[0], val[val.length - 1]);
  169.         }

  170.         if (r < 0) {
  171.             // "c" in within an interpolation sub-interval: Return the
  172.             // index of the sample at the lower end of the sub-interval.
  173.             return -r - 2;
  174.         }
  175.         final int last = val.length - 1;
  176.         if (r == last) {
  177.             // "c" is the last sample of the range: Return the index
  178.             // of the sample at the lower end of the last sub-interval.
  179.             return last - 1;
  180.         }

  181.         // "c" is another sample point.
  182.         return r;
  183.     }

  184.     /**
  185.      * Compute the spline coefficients from the list of function values and
  186.      * function partial derivatives values at the four corners of a grid
  187.      * element. They must be specified in the following order:
  188.      * <ul>
  189.      *  <li>f(0,0)</li>
  190.      *  <li>f(1,0)</li>
  191.      *  <li>f(0,1)</li>
  192.      *  <li>f(1,1)</li>
  193.      *  <li>f<sub>x</sub>(0,0)</li>
  194.      *  <li>f<sub>x</sub>(1,0)</li>
  195.      *  <li>f<sub>x</sub>(0,1)</li>
  196.      *  <li>f<sub>x</sub>(1,1)</li>
  197.      *  <li>f<sub>y</sub>(0,0)</li>
  198.      *  <li>f<sub>y</sub>(1,0)</li>
  199.      *  <li>f<sub>y</sub>(0,1)</li>
  200.      *  <li>f<sub>y</sub>(1,1)</li>
  201.      *  <li>f<sub>xy</sub>(0,0)</li>
  202.      *  <li>f<sub>xy</sub>(1,0)</li>
  203.      *  <li>f<sub>xy</sub>(0,1)</li>
  204.      *  <li>f<sub>xy</sub>(1,1)</li>
  205.      * </ul>
  206.      * where the subscripts indicate the partial derivative with respect to
  207.      * the corresponding variable(s).
  208.      *
  209.      * @param beta List of function values and function partial derivatives
  210.      * values.
  211.      * @return the spline coefficients.
  212.      */
  213.     private double[] computeSplineCoefficients(double[] beta) {
  214.         final double[] a = new double[NUM_COEFF];

  215.         for (int i = 0; i < NUM_COEFF; i++) {
  216.             double result = 0;
  217.             final double[] row = AINV[i];
  218.             for (int j = 0; j < NUM_COEFF; j++) {
  219.                 result += row[j] * beta[j];
  220.             }
  221.             a[i] = result;
  222.         }

  223.         return a;
  224.     }
  225. }

  226. /**
  227.  * Bicubic function.
  228.  */
  229. class BicubicFunction implements BivariateFunction {
  230.     /** Number of points. */
  231.     private static final short N = 4;
  232.     /** Coefficients */
  233.     private final double[][] a;

  234.     /**
  235.      * Simple constructor.
  236.      *
  237.      * @param coeff Spline coefficients.
  238.      */
  239.     BicubicFunction(double[] coeff) {
  240.         a = new double[N][N];
  241.         for (int j = 0; j < N; j++) {
  242.             final double[] aJ = a[j];
  243.             for (int i = 0; i < N; i++) {
  244.                 aJ[i] = coeff[i * N + j];
  245.             }
  246.         }
  247.     }

  248.     /**
  249.      * {@inheritDoc}
  250.      */
  251.     @Override
  252.     public double value(double x, double y) {
  253.         MathUtils.checkRangeInclusive(x, 0, 1);
  254.         MathUtils.checkRangeInclusive(y, 0, 1);

  255.         final double x2 = x * x;
  256.         final double x3 = x2 * x;
  257.         final double[] pX = {1, x, x2, x3};

  258.         final double y2 = y * y;
  259.         final double y3 = y2 * y;
  260.         final double[] pY = {1, y, y2, y3};

  261.         return apply(pX, pY, a);
  262.     }

  263.     /**
  264.      * Compute the value of the bicubic polynomial.
  265.      *
  266.      * @param pX Powers of the x-coordinate.
  267.      * @param pY Powers of the y-coordinate.
  268.      * @param coeff Spline coefficients.
  269.      * @return the interpolated value.
  270.      */
  271.     private double apply(double[] pX, double[] pY, double[][] coeff) {
  272.         double result = 0;
  273.         for (int i = 0; i < N; i++) {
  274.             final double r = MathArrays.linearCombination(coeff[i], pY);
  275.             result += r * pX[i];
  276.         }

  277.         return result;
  278.     }
  279. }