1

I am trying to use the Sundials ODE solver libraries in order to approximate the dynamics of an extension to the multi species Lotka-Volterra competition equations. E.g. in the case of two species

dx1/dt = r1 * x1 * (1 - (x1 + a12 * x2))

dx2/dt = r2 * x2 * (1 - (x2 + a21 * x1))

or...

dx/dt = r * x * (1 - A * x)

(where K = a11 = a22 = 1)

From what I understand the Sundials ODE solver expects an object of class ODE_vector, the elements of which represent the various state variables of the system, in this case x1 and x2 and I have no problem getting the solver to approximate the trajectories of the two species' populations if I program each parameter (r1, r2, a12, a21) as unique objects as in the following code:

int community::dynamics(ODE_vector const & state, ODE_vector & time_derivative) {

double r1 = 0.5;
double r2 = 0.2;
double a12 = 0.2;
double a21 = 0.2;

time_derivative[0] = r1 * state[0] * (1 - (state[0] + a12 * state[1]));
time_derivative[1] = r2 * state[1] * (1 - (state[1] + a21 * state[0]));
return 0;
};

... where time_derivative and state are members of the Sundials class ODE.

Can anyone show me how to adapt the code to get the system of ODEs into matrix form, i.e. so that r1, r2, a12 and a21 are in the form r and A? I've been told to convert from ODE_vector to armadillo vector/matrix objects using "Advanced constructors" (e.g. 1) but it's one thing to know that and another to successfully implement it!!

Thanks!

jack.l
  • 101
  • 5

1 Answers1

0

Where you thinking of something like this? I implemented that naively in 30 min. There is a lot of optimisation, but it will never be as performant as your stripped down code. Even if you used Armadillo, Blitz++ etc.

#include <algorithm>
#include <iostream>
#include <vector>

template<class T> class Matrix {
public:
  Matrix() {}
  Matrix(size_t m, size_t n) : m_(m), n_(n) { v_.resize(m*n); }

  Matrix& operator*= (const Matrix& other) {
    if (other.m_ != n_) {
      throw std::length_error("dimesions don't match");
    }
    Matrix<T> nv(m_,other.n_);
    for (size_t j = 0; j < other.n_; ++j) {
      for (size_t i = 0; i < m_; ++i) {
        for (size_t k = 0; k < n_; ++k) 
          nv(i,j) += operator()(i,k) * other(k,j);
      }
    }
    *this = nv;
    return *this;
  }
  Matrix operator* (const Matrix& other) const {
    Matrix ret = *this;
    return ret *= other;
  }

  Matrix& operator+= (const Matrix& other) {
    if (other.m_ != m_ || other.n_ != n_) {
      throw std::length_error("dimesions don't match");
    }
    std::transform(v_.begin(), v_.end(), other.v_.begin(), v_.begin(), std::plus<T>());
    return *this;
  }
  Matrix operator+ (const Matrix& other) const {
    Matrix ret = *this;
    return ret += other;
  }
  Matrix operator- () const {
    Matrix<T> res (m_,n_);
     std::transform (v_.begin(), v_.end(), res.v_.begin(), std::negate<T>());
     return res;
  }

  Matrix& operator-= (const Matrix& other) {
    return *this += -other;
  }
  Matrix operator- (const Matrix& other) const {
    Matrix ret = *this;
    return ret -= other;
  }

  Matrix operator->* (const Matrix& other) const {
    if (other.m_ != m_ || other.n_ != n_) {
      throw std::length_error("dimesions don't match");
    }
    Matrix res = *this;
    std::transform(res.v_.begin(), res.v_.end(), other.v_.begin(),
                   res.v_.begin(), std::multiplies<T>());
    return res;
  }



  Matrix operator=(std::vector<std::initializer_list<T>> const& other) {
    n_ = other.size();
    if (n_ == 0) {
      throw std::bad_alloc();
    }
    m_ = other.front().size();
    if (m_ == 0) {
      throw std::bad_alloc();
    }
    for (auto const& i : other) {
      if (i.size() != m_) {
        throw std::bad_alloc();
      }
    }
    v_.resize(m_*n_);
    size_t j = 0;
    for (const auto& i : other) {
      std::copy(i.begin(), i.end(), v_.begin()+j);
      j+=m_;
    }
    return *this;
  }

  T& operator() (size_t m, size_t n) {
    return v_[n*m_+m];
  }

  const T& operator() (size_t m, size_t n) const {
    return v_[n*m_+m];
  }



  size_t m() const {return m_;}
  size_t n() const {return n_;}

private:
  size_t m_, n_;
  std::vector<T> v_;
};

template<class T> inline std::ostream&
operator<< (std::ostream& os, const Matrix<T>& v) {
  size_t i,j;
  for (i = 0; i < v.m(); ++i) {
    for (j = 0; j < v.n(); ++j) {
      os  << v(i,j);
      os << " ";
    }
    if (i<v.m()-1)
    os << std::endl;
  }
  return os;
}

int main() {
  Matrix<double> A, r, x, one, dxdt;

  A = {{1,.2},
       {.2,1}};
  r = {{0.5,0.2}};
  x = {{0.1,-0.1}};
  one = {{1,1}};

  dxdt = (r ->* x ->* (one - A * r));

  std::cout << dxdt << std::endl;

  return 0;
}
Kaveh Vahedipour
  • 3,412
  • 1
  • 14
  • 22