Search code examples
c++c++11boostarmadilloodeint

Armadillo's cx_mat and Boost's odeint compilation error


I was going to solve several matrix differential equations, of the form d/dt (X) = F(X), where X is a large complex matrix, and F denotes some function of it. I tried to use Boost's odeint with state_type as Armadillo's cx_mat. But it produces compilation error for controlled stepper type. My sample code is as follows

#include <armadillo>
#include <iostream>
#include <boost/numeric/odeint.hpp>

using namespace std;
using namespace arma;
using namespace boost::numeric::odeint;

using state_type = arma::cx_mat;


void ode(const state_type& X, state_type& derr, double) {
  derr =  X;  // sample derivative, can be anything else
}

// define resizable and norm_inf
namespace boost { namespace numeric { namespace odeint {

template <>
struct is_resizeable<arma::cx_mat> {
    typedef boost::true_type type;
    const static bool value = type::value;
};

template <>
struct same_size_impl<arma::cx_mat, arma::cx_mat> {
    static bool same_size(const arma::cx_mat& x, const arma::cx_mat& y)
    {
      return arma::size(x) == arma::size(y);
    }
};

template<>
struct resize_impl<arma::cx_mat, arma::cx_mat> {
      static void resize(arma::cx_mat& v1, const arma::cx_mat& v2) {
      v1.resize(arma::size(v2));
    }
};


template<>
 struct vector_space_norm_inf<state_type> {
    typedef double result_type;
    result_type operator()(const state_type& p) const
    {
      return arma::norm(p, "inf");
    }
};



} } } // namespace boost::numeric::odeint





using stepper =  runge_kutta_dopri5<state_type, double, state_type, double, vector_space_algebra>;

int main () {

  cx_mat A = randu<cx_mat>(4, 4);


  integrate_adaptive( make_controlled<stepper>(1E-10, 1E-10),  ode, A, 0.0 , 10.0, 0.1);
}  

This code gives the following compilation error:

/usr/include/armadillo_bits/Mat_meat.hpp:5153:3: error: static assertion failed: error: incorrect or unsupported type
   arma_type_check(( is_same_type< eT, typename T1::elem_type >::no ));

What I can understand that Armadillo does not support copying a real matrix (mat) into a complex one (cx_mat), like

mat Z = something;
cx_mat Y = Z;  // ERROR

Which happens somewhere in odeint. Right now I am overcoming this by copying whole matrix into std::vector<std::complex<double> > put it into the function ode, then inside the function again copy whole std::vector<std::complex<double> > into cx_mat, calculate F(X), then copy it into std::vector<std::complex<double> > and return. Obviously, this is very slow and inefficient.

Any simple solution to the problem?? If possible, I may want to shift to Eigen, if that helps.


Solution

  • Hi I know this is quite old but as I did stumble across it facing the same problem I wanted to share my solution to this. I used Eigen and with some minor additions similar to yours in Armadillo it worked for me(tested compilation with icpc and g++). Here a minimal code snipped:

    #include<Eigen/Eigen>
    #include<boost/numeric/odeint.hpp>
    #include<iostream>
    #include<complex>
    #include<boost/numeric/odeint/external/eigen/eigen_algebra.hpp>
    
    typedef std::complex<double> cdoub;
    typedef Eigen::MatrixXcd state_type;
    
    namespace boost {
    namespace numeric {
    namespace odeint {
    
    template<typename B,int S1,int S2,int O, int M1, int M2>
    struct algebra_dispatcher< Eigen::Matrix<B,S1,S2,O,M1,M2> >
    {
        typedef vector_space_algebra algebra_type;
    };
    
    template<>
     struct vector_space_norm_inf<state_type> {
        typedef double result_type;
        result_type operator()(const state_type& p) const
        {
          return p.lpNorm<Eigen::Infinity>();
        }
    };
    }}}
    
    namespace Eigen {
    template<typename D, int Rows, int Cols>
    Matrix<D, Rows, Cols> operator/(const Matrix<D, Rows, Cols>& v1, const Matrix<D, Rows, Cols>& v2)
    {
        return v1.array()/v2.array();
    }   
    
    template<typename D, int Rows, int Cols>
    Matrix<D, Rows, Cols>
    abs(Matrix<D, Rows, Cols> const& m) {
        return m.cwiseAbs();
    }
    }
    
    /* The rhs of x' = f(x) */
    void harmonic_oscillator( const state_type &x , state_type &dxdt , const double /* t */ )
    {
        dxdt = x;
    }
    //]
    
    int main() {
        using namespace boost::numeric::odeint;
        state_type X0(3,3);
        X0 = state_type::Random(3,3);
        state_type xout = X0;
    
        typedef runge_kutta_dopri5<state_type,double,state_type,double,vector_space_algebra> error_stepper_type;
        typedef controlled_runge_kutta< error_stepper_type > controlled_stepper_type;
        controlled_stepper_type controlled_stepper;
    
        double t =0.0;
        double dt = 0.1;
    
        controlled_stepper.try_step(harmonic_oscillator, X0, t, dt);
    
        std::cout << X0 << std::endl;
    }
    

    Perhaps the problem has already been solved but as the question came up again here https://gist.github.com/jefftrull/5625b77c0f86c439f29f five days ago I wanted to share it, hopfully it helps :)