Home > Enterprise >  Passing mutable Eigen::Ref<Eigen::VectorXd >& into function via reference
Passing mutable Eigen::Ref<Eigen::VectorXd >& into function via reference

Time:10-08

While trying to mimic a Python Code in CPP (With Eigen)

Python Code :

import numpy as np 
from numpy.linalg import inv
from matplotlib import pyplot as plt
from math import cos, sin, pi


def G(y,t): 
    a1d, a2d = y[0], y[1]
    a1, a2 = y[2], y[3]

    m11, m12 = (m1 m2)*l1, m2*l2*cos(a1-a2)
    m21, m22 = l1*cos(a1-a2), l2
    m = np.array([[m11, m12],[m21, m22]])

    f1 = -m2*l2*a2d*a2d*sin(a1-a2) - (m1 m2)*g*sin(a1)
    f2 = l1*a1d*a1d*sin(a1-a2) - g*sin(a2)
    f = np.array([f1, f2])

    accel = inv(m).dot(f)

    return np.array([accel[0], accel[1], a1d, a2d])


def RK4_step(y, t, dt):
    k1 = G(y,t)
    k2 = G(y 0.5*k1*dt, t 0.5*dt)
    k3 = G(y 0.5*k2*dt, t 0.5*dt)
    k4 = G(y k3*dt, t dt)

    return dt * (k1   2*k2   2*k3   k4) /6

# variables
m1, m2 = 2.0, 1.0
l1, l2 = 1.0, 2.0
g = 9.81

delta_t = 0.01
time = np.arange(0.0, 10.0, delta_t)

# initial state
y = np.array([0,0,0,1.0])   # [velocity, displacement]

Y1 = []
Y2 = []

# time-stepping solution
for t in time:
    y = y   RK4_step(y, t, delta_t) 

    Y1.append(y[2])
    Y2.append(y[3])


# plot the result
plt.plot(time,Y1)
plt.plot(time,Y2)
plt.grid(True)
plt.legend(['Angle1', 'Angle2'], loc='lower right')
plt.show()

Source : https://github.com/apf99/Double-Pendulum/blob/master/d_pen.py (https://www.youtube.com/watch?v=nBBQKZb6JZk)

Being a Noob to C and Eigen.

Transcribed Code in C :

#include<iostream>
#include<math.h>
#include<Eigen/Eigen>
#include<fstream>
// grc : gravitational constant

#define grc 9.81


// solving for acceleration
void G(const Eigen::Ref<const Eigen::VectorXd>& dummy2, const double& t, const double& m1, const double& m2, const double& l1, const double& l2, Eigen::Ref<Eigen::Vector4d>& k){
  // angular velocities (av1, av2) and displacements (a1, a2),
  double av1, av2, a1, a2;
  // angular velocities
  av1 = dummy2[0];
  av2 = dummy2[1];
  // angular position
  a1 = dummy2[3];
  a2 = dummy2[4];

  // Defiing the mass like matrix
  Eigen::Matrix2d A;
  A<<(m1 m2)*l1, m2*l2*std::cos(a1-a2),  l1*std::cos(a1-a2), l2;
  // Defining the forcing like function
  Eigen::Vector2d F, acceleration;
  F<<-m2 * l2 * av2*av2*std::sin(a1-a2) - (m1 m2)*grc*std::sin(a1), l1*av1*av1*std::sin(a1-a2) - grc*std::sin(a2);

  // product of the inverse mass and forcing function
  acceleration = (A.inverse()) * F;

  //assiging the values
  k(0) = acceleration(0);
  k(1) = acceleration(1);
  k(2) = av1;
  k(3) = av2;
   
}

// attempt to pass by refernce
// Ranga Kutta 4
void RK4(const Eigen::Ref<Eigen::VectorXd>& dummy, const double& t, const double& dt, const double& m1, const double& m2, const double& l1, const double& l2){
  Eigen::Vector4d k1 , k2 , k3 , k4 ;
  G(dummy, t, m1, m2, l1, l2, k1);
  G(dummy  0.5 * dt * k1, t 0.5*dt, m1, m2, l1, l2, k2);
  G(dummy  0.5 * dt * k2, t 0.5*dt, m1, m2, l1, l2, k3);
  G(dummy  dt * k3, t dt, m1, m2, l1, l2, k4);

  dummy = (dt/6.0) * (k1   2*k2   2* k3   k4);
}


int main(int argc, char** argv){

  // m1 and m2 are the masses of the pendulum 
  double m1 = 2.0, m2 = 1.0;
  // l1 and l2 are the lengths of the masses
  double l1 = 1.0, l2 = 2.0;
  // time delta
  double dt = 0.01;
  // Defining the angular displacement y(2), y(3) and  angular velocities y(0), y(1)
  Eigen::VectorXd y(4), y_dummy(4);
  y(0) = 0.0;
  y(1) = 0.0;
  y(2) = 0.0;
  y(3) = 1.0;

  // time as t
  double t = 0.0;

  //file opening to write data
  std::fstream pend("double_pendulum.dat",std::ios::out|std::ios::app);
  //position and velocity are written to the dat file
  pend<<"v(x)  v(y) p(x) p(y) \n";
  
  // time ; stepping
  for(unsigned i = 0; i<1000;i  ){
    t  = i/100;
    y_dummy = y;
    RK4(y_dummy,t,dt, m1, m2, l1, l2);
    y  = y_dummy;
    pend<<(y.transpose())<<std::endl;
  }

  pend.close();
  return 0;
  
}

When compiled with g -std=c 11 -I /usr/include/eigen3 -o dp dp.cpp

The following errors popup (The jist of the error is mentioned below)

error: cannot bind non-const lvalue reference of type ‘Eigen::Ref<Eigen::Matrix<double, 4, 1> >&’ to an rvalue of type ‘Eigen::Ref<Eigen::Matrix<double, 4, 1> >’
   43 |   G(dummy, t, m1, m2, l1, l2, k1);

etc .. etc.. etc.

for each non const reference and matrix passed.

When either or both are defined as const, they become immutable, hence the vector will not be updated.

Being completely flabbergasted by Eigen_tux documentation , what could be the solution in layman's terms.

Or, are there libraries that can perform this task in a simple manner?

PS : Couldn't compile the script completely

CodePudding user response:

A mutable reference, more precisely an l-value reference, only ever points at an existing, named object (the actual language rules are of course more complicated but that is the intention). The Eigen::Ref object is not one of those since it is created as a temporary object in the function call. Therefore the compiler refuses to create such a reference.

As a consequence, it is customary to pass inputs as const Eigen::Ref<const Matrix>& and input/outputs as Eigen::Ref<Matrix>. The function signature should look like this:

void G(const Eigen::Ref<const Eigen::VectorXd>& dummy2,
    double t, double m1, double m2, double l1, double l2,
    Eigen::Ref<Eigen::Vector4d> k);

Note that the Ref object functionally references the original matrix or matrix block. It doesn't need to be a reference in the C sense to do this. You could also pass the input Eigen::Ref<const Matrix> by value. However, this would introduce a slight loss in performance if you continue to pass that Ref to a sub-function, since it would create a new copy.

For mutable references we accept that cost for consistency and because mutable Refs are actually cheaper objects than Ref<const Matrix>. The reason being that const-Refs copy their inputs if the inner stride would not be 1, e.g. when you pass a single row of a matrix as a Ref<const Vector>. This is described in the Ref documentation.

Note also that passing simple doubles as const reference is pointless. Pass-by-value for scalars is more efficient.

  • Related