view src/interpolator.cpp @ 40:eaa99e09607d

Remove indentation due to namespace scope
author Jordi Gutiérrez Hermoso <jordigh@gmail.com>
date Fri, 12 Mar 2010 09:21:07 -0600
parents 7f31f9e2d196
children 3f8311cbf602
line wrap: on
line source

#include <vector>
#include <set>

//debug
#include <iostream>

#include <utility>

#include "include/interpolator.hpp"
#include "include/rbf.hpp"
#include "include/func.hpp"
#include "include/error.hpp"
#include "include/utils.hpp"

#include <boost/functional/hash.hpp>
#include <boost/shared_ptr.hpp>

namespace kwantix{ 
using boost::shared_ptr;

template<typename RBF>
interpolator<RBF>::interpolator()
{
  initted = false;
}

template<typename RBF>
interpolator<RBF>::interpolator(shared_ptr<linear_BVP2> bvp)
{
  //Workaround because gdb can't break inside constructors. :-(
  init(bvp);
}

template<typename RBF>
interpolator<RBF>::interpolator(shared_ptr<domain> Omega, 
                                const map<point, double>& Xi)
{
  //Check that Xi matches the domain in size
  size_t Omega_size = Omega -> get_interior().size() 
    + Omega -> get_boundary().size();
  if(Xi.size() != Omega_size)
  {
    badArgument exc;
    if(Xi.size() < Omega_size)
      exc.reason = "Did not provide enough interpolation data for every " 
        "point in the given domain.";
    else
      exc.reason = "Provided more interpolation data than points in the " 
        "given domain.";
    exc.line = __LINE__;
    exc.file = __FILE__;
    throw exc;
  }

  //Create a trivial bvp taking into account the given domain.
  shared_ptr<Id_op> Id(new Id_op);
  shared_ptr<dirichlet_op> D(new dirichlet_op);
  map<point, double> f, g;

  //Extract f and g from given information
  for(map<point, double>::const_iterator I = Xi.begin();
      I != Xi.end(); I++)
  {
    if(kwantix::contains(Omega -> get_interior(), I -> first))
      f[I -> first] = I -> second;
    else if(kwantix::contains(Omega -> get_boundary(), I -> first))
      g[I -> first] = I -> second;
    else
    {
      badArgument exc;
      exc.reason = "The interpolation data contains points not in "
        "the given domain.";
      exc.line = __LINE__;
      exc.file = __FILE__;
      throw exc;
    }
  } 
    
  shared_ptr<linear_BVP2> new_bvp(new linear_BVP2(Omega, Id, D, f,g));
    
  interpolate(new_bvp);
}

template<typename RBF>
interpolator<RBF>::interpolator(const map<point, double>& Xi)
{
  interpolate(Xi);
}

template<typename RBF>
void interpolator<RBF>::interpolate(const map<point, double>& Xi)
{
    
  if(Xi.empty()){//Dude, wtf?
    badArgument exc;
    exc.reason = "Cannot interpolate if no data is given.";
    exc.line = __LINE__;
    exc.file = __FILE__;
    throw exc;
  }

  //Create a trivial bvp.
  shared_ptr<Id_op> Id(new Id_op);
  shared_ptr<dirichlet_op> D(new dirichlet_op);
  set<point> intr;
  set<point> bdry; //empty
  map<point, point> nrml; //empty
  map<point, double> g; //empty
    
  bool dim_set = false;
  size_t dimension = 0;
  for(map<point,double>::const_iterator i = Xi.begin(); i != Xi.end(); i++){
    if(!dim_set){
      dimension = (i -> first).size();
      dim_set = true;
    }
    else if(dimension != (i -> first).size()){
      badArgument exc;
      exc.reason = "Inconformant dimensions in interpolation data.";
      exc.line = __LINE__;
      exc.file = __FILE__;
      throw exc;
    }
    intr.insert( i->first);
  }
  shared_ptr<domain> Omega(new domain(dimension, intr,bdry,nrml));
  shared_ptr<linear_BVP2> bvp(new linear_BVP2(Omega, Id, D, Xi, g));
    
  init(bvp);
}

template<typename RBF>
void interpolator<RBF>::interpolate(shared_ptr<linear_BVP2> bvp)
{
  init(bvp);
}

template<typename RBF>
void interpolator<RBF>::interpolate(const interp_values& values)
{
  if(!initted){
    badArgument exc;
    exc.reason = "Cannot interpolate with other interpolator's "
      "values without defining the domain.";
    exc.line = __LINE__;
    exc.file = __FILE__;
    throw exc;
  }
  if(rbfs_hash != values.rbfs_hash){
    badArgument exc;
    exc.reason = "Cannot interpolate with values from incompatible "
      "interpolator." ;
    exc.line = __LINE__;
    exc.file = __FILE__;
    throw exc;
  }
  coeffs = M.inv(values.v);
   
  //Precompute the relevant values
  recompute_values_vec();
}

template<typename RBF>
void interpolator<RBF>::init(shared_ptr<linear_BVP2> bvp)
{
  thebvp = bvp;

  using std::set;
    
  shared_ptr<const domain> Omega = bvp -> get_domain();
  set<point> interior = Omega -> get_interior();
  set<point> boundary = Omega -> get_boundary();
  map<point, vector> normals = Omega -> get_normals();
  n = interior.size();
  m = boundary.size();
    
  vector temp(n+m);
  coeffs = temp;
  rbfs.reserve(n+m);

    
  RBF::set_dimension(Omega -> get_dimension());
    
  set<point>::iterator I;
  //Define all the rbfs...
  for(I = interior.begin(); I != interior.end(); I++){
    RBF r(*I);
    rbfs.push_back(r);
  }
  for(I = boundary.begin(); I != boundary.end(); I++){
    RBF r(*I);
    rbfs.push_back(r);
  }
    
  //Now define the matrix to be inverted...
  matrix Mtemp(n+m,n+m);
  M = Mtemp;
  shared_ptr<const linear_diff_op2> L = thebvp -> get_linear_diff_op2(); 

  shared_ptr<const bdry_diff_op> B = thebvp -> get_bdry_diff_op();

  I = interior.begin();
  for(size_t i = 1; i <= n; i++){
    for(size_t j = 1; j <= n+m; j++)
      M(i,j) = L -> at(rbfs[j-1], *I);
    I++;
  }
    
  map<point, vector>::iterator J;
  J = normals.begin();
  for(size_t i = n+1; i <= n+m; i++){
    for(size_t j = 1; j <= n+m; j++)
      M(i,j) = B -> at(rbfs[j-1], J->first, J->second);
    J++;
  }
   
  computecoeffs();
    
  ev_precomp = false;
  d1_precomp = false;
  d2_precomp = false;

  rbfs_hash = hash_value(rbfs);

  kwantix::normals nrmls_tmp(rbfs_hash, normals, n);  
  nrmls = nrmls_tmp;

  initted = true;
}

template<typename RBF>
size_t interpolator<RBF>::hash_value(const std::vector<RBF>& rbfs_in)
{
  size_t seed = 0;  

  for(size_t i = 0; i < rbfs_in.size(); i++)
    boost::hash_combine(seed,rbfs[i]);

  return seed;
}
  
// ******************** Evaluations, derivatives ********************
 
//  ---------- Precomputation ---------------------------
  
template<typename RBF>
void interpolator<RBF>::precompute_ev()
{
  if(!initted){
    throw not_initted(__LINE__, __FILE__);
  }

  if(!ev_precomp){
    std::vector<size_t> alpha; //empty vector
      
    //Precompute the RBFs at all points of the domain
    matrix phis(n+m,n+m);
    set<point>::iterator I;
    size_t i;
    for(I = thebvp -> get_domain () -> get_interior().begin(), i = 1; 
        i <= n; i++, I++)
      for(size_t j = 0; j < n+m; j++)
        phis(i,j+1) = rbfs[j].at(*I);
      
    for(I = thebvp -> get_domain() -> get_boundary().begin(), i=n+1; 
        i <= n+m; i++, I++)
      for(size_t j = 0; j < n+m; j++)
        phis(i,j+1) = rbfs[j].at(*I);
      
    precomp_rbfs[alpha] = phis;
    computecoeffs();
      
    ev_precomp = true;
  }
}

template<typename RBF>
void interpolator<RBF>::precompute_d1()
{
  if(!initted){
    throw not_initted(__LINE__, __FILE__);
  }

  //Have the RBFs been precomputed yet?
  if(!d1_precomp){
    //No, so precompute the RBFs first derivatives at all points of
    //the domain.
    for(size_t k = 1; k <= thebvp -> get_domain() -> get_dimension(); 
        k++){
      std::vector<size_t> alpha(k); alpha[k-1]++;
      matrix phis(n+m,n+m);
      {
        set<point>::iterator I;
        size_t i;
        for(I = thebvp -> get_domain() -> get_interior().begin(), 
              i = 1; i <= n; i++, I++)
          for(size_t j = 0; j < n+m; j++)
            phis(i,j+1) = rbfs[j].d(*I,k);
      
        for(I = thebvp -> get_domain() -> get_boundary().begin(), 
              i=n+1; i <= n+m; i++, I++)
          for(size_t j = 0; j < n+m; j++)
            phis(i,j+1) = rbfs[j].d(*I,k);
      }

      precomp_rbfs[alpha] = phis;
      computecoeffs();
    }
  }
  d1_precomp = true;
}

template<typename RBF>
void interpolator<RBF>::precompute_d2()
{
  if(!initted)
    throw not_initted(__LINE__, __FILE__);
  if(!d2_precomp){
    size_t dim = thebvp -> get_domain() -> get_dimension();
    //precompute all second derivatives
    for(size_t k1 = 1; k1 <= dim; k1++){
      for(size_t k2 = k1; k2 <= dim; k2++){
        std::vector<size_t> alpha(k1>k2?k1:k2); alpha[k1-1]++; alpha[k2-1]++;
        matrix phis(n+m,n+m);
        set<point>::iterator I;
        size_t i;
        for(I = thebvp -> get_domain () -> get_interior().begin(), i = 1; 
            i <= n; i++, I++)
          for(size_t j = 0; j < n+m; j++)
            phis(i,j+1) = rbfs[j].d2(*I,k1,k2);
      
        for(I = thebvp -> get_domain() -> get_boundary().begin(), i=n+1; 
            i <= n+m; i++, I++)
          for(size_t j = 0; j < n+m; j++)
            phis(i,j+1) = rbfs[j].d2(*I,k1,k2);
      
        precomp_rbfs[alpha] = phis;
        computecoeffs();	  
      }
    }
  }
  d2_precomp = true;
}

// ----------------- Point evaluation ---------------------------

template<typename RBF>
double interpolator<RBF>::operator()(const point& p) const
{
  return at(p);
}

template<typename RBF>
double interpolator<RBF>::at(const point& p) const
{
  if(!initted){
    throw not_initted(__LINE__, __FILE__);
  }
  std::vector<size_t> alpha; //empty vector

  //Are we evaluating at a precomputed point in the domain?
  if(ev_precomp and 
     precomp_values[alpha].find(p) != precomp_values[alpha].end())
    return precomp_values[alpha][p];
     
  //Else, must compute the value
  double result = 0;
  for(size_t i = 1; i <= coeffs.size(); i++)
    result += coeffs(i)*rbfs[i-1].at(p);
        
  return result;
}

template<typename RBF>
double interpolator<RBF>::d(const point& p, size_t k) const{
  if(!initted){
    throw not_initted(__LINE__, __FILE__);
  }
  std::vector<size_t> alpha(k); alpha[k-1]++;
    
  //Are we evaluating at a precomputed point in the domain?
  if(d1_precomp and 
     precomp_values[alpha].find(p) != precomp_values[alpha].end())
    return precomp_values[alpha][p];
    
  //Else, must compute the value
  double result = 0;
  for(size_t i = 1; i <= coeffs.size(); i++)
    result += coeffs(i)*rbfs[i-1].d(p,k);
   
  return result;
}

template<typename RBF>
double interpolator<RBF>::d2(const point& p, size_t k1, size_t k2) const{
  if(!initted){
    throw not_initted(__LINE__, __FILE__);
  }
  std::vector<size_t> alpha(k1>k2?k1:k2); alpha[k1-1]++; alpha[k2-1]++;
    
  //Are we evaluating at a precomputed point in the domain?
  if(d2_precomp and 
     precomp_values[alpha].find(p) != precomp_values[alpha].end())
    return precomp_values[alpha][p];
    
  //Else, must compute the value
  double result = 0;
  for(size_t i = 1; i <= coeffs.size(); i++)
    result += coeffs(i)*rbfs[i-1].d2(p,k1,k2);

  return result;
}

// ------------------- Whole domain evaluations ----------------

template<typename RBF>
kwantix::badArgument
interpolator<RBF>::not_precomputed(int line, string file) const
{
  badArgument exc;
  exc.reason = "Cannot do whole domain evaluations without "
    "precomputed RBFs.";
  exc.line = line;
  exc.file = file;
  return exc;
}

template<typename RBF>
interp_values interpolator<RBF>::operator()() const
{
  return at();
}
  
template<typename RBF>
interp_values interpolator<RBF>::at() const
{
  if(!ev_precomp)
    throw not_precomputed(__LINE__, __FILE__);
     
  std::vector<size_t> alpha;
  return interp_values(precomp_values_vec[alpha],rbfs_hash,n,m);
}
   
template<typename RBF>
interp_values interpolator<RBF>::d(size_t k) const
{
  if(!d1_precomp)
    throw not_precomputed(__LINE__, __FILE__);
    
  std::vector<size_t> alpha(k); alpha[k-1]++;
  return interp_values(precomp_values_vec[alpha],rbfs_hash,n,m);
}

template<typename RBF>
interp_values interpolator<RBF>::d2(size_t k1, size_t k2) const
{
  if(!d2_precomp)
    throw not_precomputed(__LINE__, __FILE__);
    
  std::vector<size_t> alpha(k1>k2?k1:k2); alpha[k1-1]++; alpha[k2-1]++;
  return interp_values(precomp_values_vec[alpha],rbfs_hash,n,m);
}

//***************** linear operations ***************************

template<typename RBF>
interpolator<RBF> interpolator<RBF>::
operator+(const interpolator<RBF>& u) const
{    
  if(this -> rbfs_hash != u.rbfs_hash){
    badArgument exc;
    exc.reason = 
      "Cannot add interpolators on different domains (interior and boundary must match)."; 
    exc.line = __LINE__;
    exc.file = __FILE__;
    throw exc;	
  }
    
  interpolator<RBF> out = *this;
  out.coeffs = (this -> coeffs) + u.coeffs;
  out.recompute_values_vec();
       
  return out;
}

template<typename RBF>
interpolator<RBF> interpolator<RBF>::
operator-(const interpolator<RBF>& u) const
{    
  if(this -> rbfs_hash != u.rbfs_hash){
    badArgument exc;
    exc.reason = 
      "Cannot subtract interpolators on different domains (interior and boundary must match)."; 
    exc.line = __LINE__;
    exc.file = __FILE__;
    throw exc;	
  }

  interpolator<RBF> out = *this;
  out.coeffs = (this -> coeffs) - u.coeffs;
  out.recompute_values_vec();
       
  return out;
}
  
template<typename RBF>
interpolator<RBF> interpolator<RBF>::operator*(double a) const
{
  interpolator<RBF> u = *this;
  u.coeffs = (this -> coeffs)*a;
  u.recompute_values_vec();
  return u;
}

template<typename RBF>
interpolator<RBF> interpolator<RBF>::operator/(double a) const
{
  interpolator<RBF> u = *this;
  u.coeffs = (this -> coeffs)*(1/a);
  u.recompute_values_vec();
  return u;
}

template<typename RBF>
normals interpolator<RBF>::get_normals() const{
  if(!initted){
    throw badArgument("Interpolator not initialised, so there are "
                      "no normals!",__FILE__,__LINE__);
  }
  return nrmls;
}
  
//******************* data manipulation **************************

template<typename RBF>
void interpolator<RBF>::set_f(const intr_values& f){
  if(!initted){
    throw not_initted(__LINE__, __FILE__);
  }

  std::vector<size_t> alpha;
  slice s(1,n);
  precomp_values_vec[alpha](s) = f.v;
  //precomp_values[alpha] = valsvec2map(precomp_values_vec[alpha]);

  computecoeffs(true);
}

template<typename RBF>
void interpolator<RBF>::set_g(const bdry_values& g){
  if(!initted){
    throw not_initted(__LINE__, __FILE__);
  }
    
  std::vector<size_t> alpha;
  slice s(n+1,n+m);
  precomp_values_vec[alpha](s) = g.v;
  //precomp_values[alpha] = valsvec2map(precomp_values_vec[alpha]);
    
  computecoeffs(true);
}

template<typename RBF>
void interpolator<RBF>::set_f(const realfunc& f){
  if(!initted){
    throw not_initted(__LINE__, __FILE__);
  }
  thebvp -> set_f(f);
  computecoeffs();
}

template<typename RBF>
void interpolator<RBF>::set_g(const realfunc& g)
{
  if(!initted){
    throw not_initted(__LINE__, __FILE__);
  }
  thebvp -> set_g(g);
  computecoeffs();
}

template<typename RBF>
void interpolator<RBF>::set_f(const map<point, double>& f)
{
  if(!initted){
    throw not_initted(__LINE__, __FILE__);
  }
  thebvp -> set_f(f);
  computecoeffs();
}

template<typename RBF>
void interpolator<RBF>::set_g(const map<point, double>& g)
{
  if(!initted){
    throw not_initted(__LINE__, __FILE__);
  }
  thebvp -> set_g(g);
  computecoeffs();
}

template<typename RBF>
void interpolator<RBF>::set_bdry_values(const bdry_values& b_new){
  if(!initted){
    throw not_initted(__LINE__, __FILE__);
  }
  std::vector<size_t> alpha; //empty, corresponds to evaluation
  if(precomp_values_vec.find(alpha) == precomp_values_vec.end())
    at();
  kwantix::vector rhs = precomp_values_vec[alpha];
  slice s1(1,n), s2(n+1,n+m);
  rhs(s2) = b_new.v;
  coeffs = precomp_rbfs[alpha].inv(rhs);
    
  recompute_values_vec();
}

template<typename RBF>
void interpolator<RBF>::recompute_values_vec(){
  typename 
    map<std::vector<size_t>, matrix>::const_iterator I;
  for(I = precomp_rbfs.begin(); I != precomp_rbfs.end(); I++)
    precomp_values_vec[I -> first] = (I -> second)*coeffs;
      
  //Pointwise map evaluations we will not compute again.
  precomp_values.clear();
}

template<typename RBF>
kwantix::badArgument
interpolator<RBF>::not_initted(int line, string file) const
{
  badArgument exc;
  exc.reason = 
    "Interpolator can't interpolate without initialisation data.";
  exc.line = line;
  exc.file = file;
  return exc;
}

template<typename RBF>
void interpolator<RBF>::computecoeffs(bool rhs_defined)
{
  kwantix::vector rhs(n+m);

  //Compute the RBF coefficients
  if(rhs_defined){
    std::vector<size_t> alpha;
    rhs = precomp_values_vec[alpha];
  }
  else
  {
    map<point, double>::const_iterator I;

    I = (thebvp -> get_f()).begin();
    for(size_t i = 1; i <= n; i++){
      rhs(i) = I->second;
      I++;
    }
    I = (thebvp -> get_g()).begin();   
    for(size_t i = n+1; i <= n+m; i++){
      rhs(i) = I->second;
      I++;
    }
  }
  coeffs = M.inv(rhs);
    
  //Precompute the values for the derivatives and evaluations that
  //have already been computed.
  {				
    typename map<std::vector<size_t>, matrix>::const_iterator I;
      
    for(I = precomp_rbfs.begin(); I != precomp_rbfs.end(); I++){
      kwantix::vector vals = (I->second)*coeffs;
      precomp_values_vec[I -> first] = vals;
      precomp_values[I -> first] = valsvec2map(vals);
    }
  }
}

template<typename RBF>
map<point, double> interpolator<RBF>::
valsvec2map(const kwantix::vector& values) const
{
  map<point, double> out;
  {
    set<point>::const_iterator I;
    size_t i;
    for(I = thebvp -> get_domain() -> get_interior().begin(), i = 1;
        i <= n; I++, i++)
      out[*I] = values(i);
      
    for(I = thebvp -> get_domain() -> get_boundary().begin(), i = n+1;
        i <= n+m; I++, i++)
      out[*I] = values(i);
  }
  return out;
}
  
//****************** I/O *************************************
  
template<typename RBF>
void interpolator<RBF>::into_os(std::ostream& os) const
{
  kwantix::vector values;
  size_t i;
  set<point>::const_iterator I;
    
  //If we already have precomputed the values...
  std::vector<size_t> alpha;
  if(precomp_values_vec.count(alpha)){
    values = precomp_values_vec[alpha];
  }
  else{
    kwantix::vector tmp(n+m);
    for(i = 1, I=thebvp->get_domain()->get_interior().begin() ; 
        i<=n; i++, I++)
    {
      tmp(i) = at(*I);
    }

    for(i = n+1, I=thebvp->get_domain()->get_boundary().begin() ; 
        i<=n+m; i++, I++)
    {
      tmp(i) = at(*I);
    }

    values = tmp;
  }
    
  for(i = 1, I=thebvp->get_domain()->get_interior().begin() ; 
      i<=n; i++, I++)
  {
    os << *I << " " << values(i) << std::endl;
  }

  for(i = n+1, I=thebvp->get_domain()->get_boundary().begin() ; 
      i<=n+m; i++, I++)
  {
    os << *I << " " << values(i) << std::endl;
  }
 
}

//Instantiations
template class interpolator<piecewise_polynomial>;
template class interpolator<thin_plate_spline>;
template class interpolator<multiquadric>;
template class interpolator<inverse_multiquadric>;
template class interpolator<inverse_quadratic>;
template class interpolator<gaussian>;


}

/*! \mainpage Outline of the RBF-DDM code

  Thank you for your interest in my code. Above you can browse the
  structure of my code. You can also read here a technical design
  document that also outlines certain difficulties I have been facing
  with it:

  http://inversethought.com/jordi/design.pdf
*/