Program Listing for File interpolation.cc

Return to documentation for file (numerics/interpolation.cc)

#include "lupnt/numerics/interpolation.h"

#include <string>

namespace lupnt {

  double LinearInterp1d(const VecXd& x, const VecXd& z, double xi) {
    if (x.size() != z.size()) throw std::runtime_error("Invalid size");
    if (xi < x(0) - EPS || xi > x(x.size() - 1) + EPS) throw std::runtime_error("Out of range");

    int i = 0;
    for (i = 0; i < x.size() - 1; ++i)
      if (xi <= x(i + 1) + EPS) break;

    double dx0 = (xi - x(i)) / (x(i + 1) - x(i));
    double dx1 = (x(i + 1) - xi) / (x(i + 1) - x(i));
    double result = z(i) * dx1 + z(i + 1) * dx0;
    return result;
  }

  double LinearInterp2d(const VecXd& x, const VecXd& y, const MatXd& z, double xi, double yi) {
    if (x.size() != z.rows() || y.size() != z.cols()) throw std::runtime_error("Invalid size");
    if (xi < x(0) - EPS || xi > x(x.size() - 1) + EPS || yi < y(0) - EPS
        || yi > y(y.size() - 1) + EPS)
      throw std::runtime_error("Out of range");

    int i = 0, j = 0;
    for (i = 0; i < x.size() - 1; ++i)
      if (xi <= x(i + 1) + EPS) break;
    for (j = 0; j < y.size() - 1; ++j)
      if (yi <= y(j + 1) + EPS) break;

    double dx0 = (xi - x(i)) / (x(i + 1) - x(i));
    double dx1 = (x(i + 1) - xi) / (x(i + 1) - x(i));
    double dy0 = (yi - y(j)) / (y(j + 1) - y(j));
    double dy1 = (y(j + 1) - yi) / (y(j + 1) - y(j));
    double result = z(i, j) * dx1 * dy1 + z(i, j + 1) * dx1 * dy0 + z(i + 1, j) * dx0 * dy1
                    + z(i + 1, j + 1) * dx0 * dy0;
    return result;
  }

  LagrangeInterpolator::LagrangeInterpolator(const VecXd& x, double xi, int order)
      : x_(x), xi_(xi), order_(order) {
    if (x.size() <= order) throw std::runtime_error("Invalid size");
    if (xi < x(0) - EPS || xi > x(x.size() - 1) + EPS) throw std::runtime_error("Out of range");
    ComputeFirstIndex();
    ComputeWeights();
  }

  void LagrangeInterpolator::ComputeFirstIndex() {
    // Minimize distance to mid point: |(x(i0) + x(i0+order))/ 2 - xi|
    i0_ = 0;
    double d_best = std::abs(x_(i0_) + x_(i0_ + order_) - 2 * xi_);
    for (int i = i0_ + 1; i < x_.size() - order_; ++i) {
      double d = std::abs(x_(i) + x_(i + order_) - 2 * xi_);
      if (d < d_best) {
        i0_ = i;
        d_best = d;
      } else {
        break;
      }
    }
  }

  void LagrangeInterpolator::ComputeWeights() {
    weights_ = VecXd::Zero(order_);
    for (int i = 0; i < order_; ++i) {
      weights_(i) = 1.0;
      for (int j = 0; j < order_; ++j) {
        if (j == i) continue;
        weights_(i) *= (xi_ - x_(i0_ + j)) / (x_(i0_ + i) - x_(i0_ + j));
      }
    }
  }

  double LagrangeInterpolator::Interpolate(const VecXd& z) {
    if (z.size() != x_.size()) throw std::runtime_error("Invalid input size");
    double result = 0;
    for (int i = 0; i < order_; ++i) {
      result += weights_(i) * z(i0_ + i);
    }
    return result;
  }

}  // namespace lupnt