Using Boost::odeint with Eigen::Matrix as state vector - c++

I'm trying to utilize the ODE integration capabilities of Boost using the Matrix class from Eigen 3 as my state vector, but I'm running into problems deep into Boost that I don't understand how to address.
A minimal example of what I'm trying to do:
#include <Eigen/Core>
#include <boost/numeric/odeint/stepper/runge_kutta_dopri5.hpp>
#include <iostream>
using namespace Eigen;
using namespace boost::numeric::odeint;
template<size_t N>
using vector = Matrix<double, N, 1>;
typedef vector<3> state;
int main() {
state X0;
X0 << 1., 2., 3.;
state xout = X0;
runge_kutta_dopri5<state> stepper;
// If I remove these lines, everything compiles fine
stepper.do_step([](const state x, state dxdt, const double t) -> void {
dxdt = x;
}, X0, 0.0, xout, 0.01);
std::cout << xout << std::endl;
}
If I coment out the call to stepper.do_step everything compiles and runs just fine, but of course doesn't do anything interesting. If I don't, Boost vomits compile errors over my terminal, the first of which is
In file included from /usr/include/boost/mpl/aux_/begin_end_impl.hpp:20:0,
from /usr/include/boost/mpl/begin_end.hpp:18,
from /usr/include/boost/mpl/is_sequence.hpp:19,
from /usr/include/boost/fusion/support/detail/is_mpl_sequence.hpp:12,
from /usr/include/boost/fusion/support/tag_of.hpp:13,
from /usr/include/boost/fusion/support/is_sequence.hpp:11,
from /usr/include/boost/fusion/sequence/intrinsic_fwd.hpp:12,
from /usr/include/boost/fusion/sequence/intrinsic/front.hpp:10,
from /usr/include/boost/fusion/include/front.hpp:10,
from /usr/include/boost/numeric/odeint/util/is_resizeable.hpp:26,
from /usr/include/boost/numeric/odeint/util/state_wrapper.hpp:25,
from /usr/include/boost/numeric/odeint/stepper/base/explicit_error_stepper_fsal_base.hpp:27,
from /usr/include/boost/numeric/odeint/stepper/runge_kutta_dopri5.hpp:24,
from /home/tlycken/exjobb/Code/alpha-orbit-follower/test/algebra/algebra-tests.cpp:2:
/usr/include/boost/mpl/eval_if.hpp: In instantiation of ‘struct boost::mpl::eval_if_c<true, boost::range_const_iterator<Eigen::Matrix<double, 3, 1> >, boost::range_mutable_iterator<const Eigen::Matrix<double, 3, 1> > >’:
/usr/include/boost/range/iterator.hpp:63:63: required from ‘struct boost::range_iterator<const Eigen::Matrix<double, 3, 1> >’
/usr/include/boost/range/begin.hpp:112:61: required by substitution of ‘template<class T> typename boost::range_iterator<const T>::type boost::range_adl_barrier::begin(const T&) [with T = Eigen::Matrix<double, 3, 1>]’
/usr/include/boost/numeric/odeint/algebra/range_algebra.hpp:52:45: required from ‘static void boost::numeric::odeint::range_algebra::for_each3(S1&, S2&, S3&, Op) [with S1 = Eigen::Matrix<double, 3, 1>; S2 = const Eigen::Matrix<double, 3, 1>; S3 = const Eigen::Matrix<double, 3, 1>; Op = boost::numeric::odeint::default_operations::scale_sum2<double, double>]’
/usr/include/boost/numeric/odeint/stepper/runge_kutta_dopri5.hpp:128:9: required from ‘void boost::numeric::odeint::runge_kutta_dopri5<State, Value, Deriv, Time, Algebra, Operations, Resizer>::do_step_impl(System, const StateIn&, const DerivIn&, boost::numeric::odeint::runge_kutta_dopri5<State, Value, Deriv, Time, Algebra, Operations, Resizer>::time_type, StateOut&, DerivOut&, boost::numeric::odeint::runge_kutta_dopri5<State, Value, Deriv, Time, Algebra, Operations, Resizer>::time_type) [with System = main()::__lambda0; StateIn = Eigen::Matrix<double, 3, 1>; DerivIn = Eigen::Matrix<double, 3, 1>; StateOut = Eigen::Matrix<double, 3, 1>; DerivOut = Eigen::Matrix<double, 3, 1>; State = Eigen::Matrix<double, 3, 1>; Value = double; Deriv = Eigen::Matrix<double, 3, 1>; Time = double; Algebra = boost::numeric::odeint::range_algebra; Operations = boost::numeric::odeint::default_operations; Resizer = boost::numeric::odeint::initially_resizer; boost::numeric::odeint::runge_kutta_dopri5<State, Value, Deriv, Time, Algebra, Operations, Resizer>::time_type = double]’
/usr/include/boost/numeric/odeint/stepper/base/explicit_error_stepper_fsal_base.hpp:167:9: required from ‘typename boost::disable_if<boost::is_same<StateInOut, Time>, void>::type boost::numeric::odeint::explicit_error_stepper_fsal_base<Stepper, Order, StepperOrder, ErrorOrder, State, Value, Deriv, Time, Algebra, Operations, Resizer>::do_step(System, const StateIn&, boost::numeric::odeint::explicit_error_stepper_fsal_base<Stepper, Order, StepperOrder, ErrorOrder, State, Value, Deriv, Time, Algebra, Operations, Resizer>::time_type, StateOut&, boost::numeric::odeint::explicit_error_stepper_fsal_base<Stepper, Order, StepperOrder, ErrorOrder, State, Value, Deriv, Time, Algebra, Operations, Resizer>::time_type) [with System = main()::__lambda0; StateIn = Eigen::Matrix<double, 3, 1>; StateOut = Eigen::Matrix<double, 3, 1>; Stepper = boost::numeric::odeint::runge_kutta_dopri5<Eigen::Matrix<double, 3, 1> >; short unsigned int Order = 5u; short unsigned int StepperOrder = 5u; short unsigned int ErrorOrder = 4u; State = Eigen::Matrix<double, 3, 1>; Value = double; Deriv = Eigen::Matrix<double, 3, 1>; Time = double; Algebra = boost::numeric::odeint::range_algebra; Operations = boost::numeric::odeint::default_operations; Resizer = boost::numeric::odeint::initially_resizer; typename boost::disable_if<boost::is_same<StateInOut, Time>, void>::type = void; boost::numeric::odeint::explicit_error_stepper_fsal_base<Stepper, Order, StepperOrder, ErrorOrder, State, Value, Deriv, Time, Algebra, Operations, Resizer>::time_type = double]’
/home/tlycken/exjobb/Code/alpha-orbit-follower/test/algebra/algebra-tests.cpp:21:137: required from here
/usr/include/boost/mpl/eval_if.hpp:60:31: error: no type named ‘type’ in ‘boost::mpl::eval_if_c<true, boost::range_const_iterator<Eigen::Matrix<double, 3, 1> >, boost::range_mutable_iterator<const Eigen::Matrix<double, 3, 1> > >::f_ {aka struct boost::range_const_iterator<Eigen::Matrix<double, 3, 1> >}’
typedef typename f_::type type;
I tried to dig into the Boost header where the error occurs, but I didn't understand enough of what's going on to be able to fix my code. Since the odeint library documentation clearly states
The main focus of odeint is to provide numerical methods implemented in a way where the algorithm is completely independent on the data structure used to represent the state x.
I believe this shouldn't be too hard to get working even if odeint doesn't support Eigen natively.

You only need to replace the definition of the stepper by
runge_kutta_dopri5<state,double,state,double,vector_space_algebra> stepper;
Eigen should work out of the Box with the vector_space_algebra but you need to specify them manually. In the next odeint version we have a mechanism for automatically detecting the algebra.
Btw. youe definition of the ODE is not correct, you need a reference for the derivatve
stepper.do_step([](const state &x, state &dxdt, const double t) -> void {
dxdt = x;
}, X0, 0.0, xout, 0.01);

This solution does not seem to work for adaptive integrator
typedef runge_kutta_dopri5<state,double,state,double,vector_space_algebra> stepper_type;
auto rhs = [](const state &x, state &dxdt, const double t) -> void { dxdt = x;}
integrate_adaptive( make_controlled<stepper_type>( 1E-12 , 1E-12 ), rhs , X0 , 0. , xout , 0.01;)

Related

assert() leads to maybe-uninitialized gcc warning

The following code compiles fine, with no warnings/errors:
#include <bitset>
#include <Eigen/Core>
using Array = Eigen::Array<float, -1, 1, 0, 3, 1>;
struct Tree {
Array array;
std::bitset<8> bitset;
};
auto func(Tree* tree) {
int c = tree->array.rows();
int d = tree->bitset.count();
//assert(c==d);
Array e(c);
for (int k = 0; k < d; ++k) e(k) = k;
return e.sum() / (e + 1);
}
int main() {
Tree tree;
func(&tree);
return 0;
}
Compilation cmd:
g++ -O3 -Wall -I anaconda3/envs/dev/include/eigen3/ test.cpp
However, when I uncomment the assert(), I get a maybe-uninitialized warning:
In file included from anaconda3/envs/dev/include/eigen3/Eigen/Core:253,
from test.cpp:2:
In member function ‘Eigen::internal::scalar_sum_op<LhsScalar, RhsScalar>::result_type Eigen::internal::scalar_sum_op<LhsScalar, RhsScalar>::operator()(const LhsScalar&, const RhsScalar&) const [with LhsScalar = float; RhsScalar = float]’,
inlined from ‘static Eigen::internal::redux_impl<Func, Evaluator, 3, 0>::Scalar Eigen::internal::redux_impl<Func, Evaluator, 3, 0>::run(const Evaluator&, const Func&, const XprType&) [with XprType = Eigen::Array<float, -1, 1, 0, 3, 1>; Func = Eigen::internal::scalar_sum_op<float, float>; Evaluator = Eigen::internal::redux_evaluator<Eigen::Array<float, -1, 1, 0, 3, 1> >]’ at anaconda3/envs/dev/include/eigen3/Eigen/src/Core/Redux.h:278:19,
inlined from ‘typename Eigen::internal::traits<T>::Scalar Eigen::DenseBase<Derived>::redux(const Func&) const [with BinaryOp = Eigen::internal::scalar_sum_op<float, float>; Derived = Eigen::Array<float, -1, 1, 0, 3, 1>]’ at anaconda3/envs/dev/include/eigen3/Eigen/src/Core/Redux.h:418:56,
inlined from ‘typename Eigen::internal::traits<T>::Scalar Eigen::DenseBase<Derived>::sum() const [with Derived = Eigen::Array<float, -1, 1, 0, 3, 1>]’ at anaconda3/envs/dev/include/eigen3/Eigen/src/Core/Redux.h:463:25,
inlined from ‘auto func(Tree*)’ at test.cpp:17:15:
anaconda3/envs/dev/include/eigen3/Eigen/src/Core/functors/BinaryFunctors.h:42:122: warning: ‘((const float*)((char*)&e + offsetof(Eigen::Array, Eigen::Array<float, -1, 1, 0, 3, 1>::<unnamed>.Eigen::PlainObjectBase<Eigen::Array<float, -1, 1, 0, 3, 1> >::m_storage)))[3]’ may be used uninitialized [-Wmaybe-uninitialized]
42 | EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type operator() (const LhsScalar& a, const RhsScalar& b) const { return a + b; }
| ~~^~~
test.cpp: In function ‘auto func(Tree*)’:
test.cpp:15:9: note: ‘e’ declared here
15 | Array e(c);
| ^
My question is this: why does the presence of the assert() affect whether or not gcc reports a maybe-uninitialized warning?
The example I show happens to use the eigen3 library, but I think my question is more general. How can the inclusion of an assert() that merely compares two int values lead to a compiler warning like this?
I want to note that this code is a minimal reproducible example for this assert-dependent-warning behavior. Removing the for-loop inside func(), for instance, makes the warning appear even without the assert. Replacing e.sum() / (e + 1) with a simpler expression like e + 1 makes the warning disappear with the assert. Replacing the std::bitset with something else also makes the warning disappear with the assert.
I am using gcc-11.3.0 and eigen-3.4.0.
This is apparently due to a gcc bug. The bug is present in gcc-12 or lower, and is fixed in gcc-13. A fix might be coming in gcc-12.
Bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108230

Expression templates toy example: user-defined cast not applied for complex types

I am trying to build a toy example for expression trees using modern C++17 based on this blogpost. The code works fine for primitive types like int or double. However, when I try it with something like an Eigen::Vector3d the build fails because the user-defined cast operator result_t() const is not applied correctly (I assume).
Can someone give me a hint what exactly is the reason for the failure?
#include <Eigen/Dense>
template <class T, class U, class Callable>
struct BinaryExpression {
const T* left {nullptr};
const U* right {nullptr};
Callable callable;
BinaryExpression(const T& t, const U& u, const Callable c)
: left(&t), right(&u), callable(c) {}
auto operator()() const
{
return callable(*left, *right);
}
using result_t = decltype(std::declval<Callable>()(std::declval<const T&>(), std::declval<const U&>()));
operator result_t() const
{
return this->operator()();
}
};
struct Plus {
template <class T, class U>
auto operator()(const T& left, const U& right) const
{
return left + right;
}
};
int main()
{
Eigen::Vector3d v1 (1, 2, 3);
Eigen::Vector3d v2 (1, 2, 3);
int num1 = 1;
double num2 = 2.4;
BinaryExpression expr(num1, num2, Plus{});
BinaryExpression expr2(num2, expr, Plus{}); // this works fine
BinaryExpression expr3(v1, v2, Plus{});
BinaryExpression expr4(expr3, v1, Plus{}); // why does this give an error?
}
Compiler error message
../main.cpp: In instantiation of ‘auto Plus::operator()(const T&, const U&) const [with T = BinaryExpression<Eigen::Matrix<double, 3, 1>, Eigen::Matrix<double, 3, 1>, Plus>; U = Eigen::Matrix<double, 3, 1>]’:
.../main.cpp:36:55: required from ‘struct BinaryExpression<BinaryExpression<Eigen::Matrix<double, 3, 1>, Eigen::Matrix<double, 3, 1>, Plus>, Eigen::Matrix<double, 3, 1>, Plus>’
.../main.cpp:63:45: required from here
.../main.cpp:47:21: error: no match for ‘operator+’ (operand types are ‘const BinaryExpression<Eigen::Matrix<double, 3, 1>, Eigen::Matrix<double, 3, 1>, Plus>’ and ‘const Eigen::Matrix<double, 3, 1>’)
return left + right;
~~~~~^~~~~~~
I understand that operator+ is not defined for adding a BinaryExpression with a Matrix type. However, I expect the code to apply the user-defined cast of BinaryExpression which evaluates it to a Matrix and therefore calls the operator+ for adding two Matrix types.
line 47 is that they dont know the type of variable "left" and "right".
If you add an int with a string, it will produces error.

Tensorflow C++ Placeholder initialization

I was trying to perform image captioning with tensorflow on cpp. I was following the direction on How to import an saved Tensorflow model train using tf.estimator and predict on input data and tried
int main(int argc, char**) {
....
auto x = tf::Tensor(tf::DT_UINT8, tf::TensorShape({height, width, channel}));
auto matrix = x.matrix<char>();
....
}
When I compile the program, the code
> In file included from /usr/local/include/tensorflow/core/framework/tensor.h:26:0,
from /usr/local/include/tensorflow/core/public/session.h:23,
from /usr/local/include/tensorflow/cc/saved_model/loader.h:26,
from server.cpp:22:
/usr/local/include/tensorflow/core/framework/types.h: In instantiation of ‘struct tensorflow::DataTypeToEnum<char>’:
/usr/local/include/tensorflow/core/framework/tensor.h:530:46: required from ‘typename tensorflow::TTypes<T, NDIMS>::Tensor tensorflow::Tensor::tensor() [with T = char; long unsigned int NDIMS = 2ul; typename tensorflow::TTypes<T, NDIMS>::Tensor = Eigen::TensorMap<Eigen::Tensor<char, 2, 1, long int>, 16, Eigen::MakePointer>]’
/usr/local/include/tensorflow/core/framework/tensor.h:240:25: required from ‘typename tensorflow::TTypes<T>::Matrix tensorflow::Tensor::matrix() [with T = char; typename tensorflow::TTypes<T>::Matrix = Eigen::TensorMap<Eigen::Tensor<char, 2, 1, long int>, 16, Eigen::MakePointer>]’
server.cpp:121:34: required from here
/usr/local/include/tensorflow/core/framework/types.h:138:3: error: static assertion failed: Specified Data Type not supported
static_assert(IsValidDataType<T>::value, "Specified Data Type not supported");In file included from /usr/local/include/tensorflow/core/public/session.h:23:0,
from /usr/local/include/tensorflow/cc/saved_model/loader.h:26,
from server.cpp:22:
/usr/local/include/tensorflow/core/framework/tensor.h: In instantiation of ‘typename tensorflow::TTypes<T, NDIMS>::Tensor tensorflow::Tensor::tensor() [with T = char; long unsigned int NDIMS = 2ul; typename tensorflow::TTypes<T, NDIMS>::Tensor = Eigen::TensorMap<Eigen::Tensor<char, 2, 1, long int>, 16, Eigen::MakePointer>]’:
/usr/local/include/tensorflow/core/framework/tensor.h:240:25: required from ‘typename tensorflow::TTypes<T>::Matrix tensorflow::Tensor::matrix() [with T = char; typename tensorflow::TTypes<T>::Matrix = Eigen::TensorMap<Eigen::Tensor<char, 2, 1, long int>, 16, Eigen::MakePointer>]’
server.cpp:121:34: required from here
/usr/local/include/tensorflow/core/framework/tensor.h:530:46: error: ‘v’ is not a member of ‘tensorflow::DataTypeToEnum<char>’
CheckTypeAndIsAligned(DataTypeToEnum<T>::v());
Did anybody came across the same problem?
Found a solution. It seems like two issues were present.
(1) DT_UINT8 is uchar in c++
(2) use tensor instead of matrix
I changed the code to
auto x = tf::Tensor(tf::DT_UINT8, tf::TensorShape(
{height, width,3}));
auto matrix = x.tensor<uchar, 3>();
and worked. Got the idea from https://github.com/tensorflow/tensorflow/issues/19909
Even though you declare your TensorShape with an initialization list, you should still specify the number of dimensions of your matrix, as it cannot be deduced at compilation time in C++.
auto x = tf::Tensor(tf::DT_UINT8, tf::TensorShape({height, width, channel}));
auto matrix = x.matrix<char, 3>();

Combining Eigen and CppAD

I want to use automatic differentiation mechanism provided by
CppAD inside Eigen linear algebra. An example type is
Eigen::Matrix< CppAD::AD,-1,-1>. As CppAD::AD is a custom numeric type
the NumTraits for this type have to be provided. CppAD provides
those in the file cppad/example/cppad_eigen.hpp. This makes the
following minimal example compile:
#include <cppad/cppad.hpp>
#include <cppad/example/cppad_eigen.hpp>
int main() {
typedef double Scalar;
typedef CppAD::AD<Scalar> AD;
// independent variable vector
Eigen::Matrix<AD,Eigen::Dynamic,1> x(4);
CppAD::Independent(x);
// dependent variable vector
Eigen::Matrix<AD,Eigen::Dynamic,1> y(4);
Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic> m(4,4);
m.setIdentity();
y = 1. * x;
// THIS DOES NOT WORK
// y = m * x;
CppAD::ADFun<Scalar> fun(x, y);
}
As soon as some more complex expression is used, e.g. the
mentioned
y = m * x;
the code fails to compile:
PATH/Eigen/latest/include/Eigen/src/Core/Product.h:29:116: error:
no type named 'ReturnType' in 'Eigen::ScalarBinaryOpTraits<double, CppAD::AD<double>,
Eigen::internal::scalar_product_op<double, CppAD::AD<double> > >'
...typename ScalarBinaryOpTraits<typename traits<LhsCleaned>::Scalar, typename traits<RhsCleaned>::Scalar>::ReturnType...
If i manually cast the double Matrix to AD, it works. However this
is not a solution because the type promotion is virtually used
everywhere in Eigen.
It looks to me as if the NumTraits provided by CppAD are not sufficient for this case. This is supported by a followup error message:
PATH/Eigen/latest/include/Eigen/src/Core/Product.h:155:5: error:
no type named 'CoeffReturnType' in
'Eigen::internal::dense_product_base<Eigen::Matrix<double, -1, -1, 0, -1, -1>,
Eigen::Matrix<CppAD::AD<double>, -1, 1, 0, -1, 1>, 0, 7>'
EIGEN_DENSE_PUBLIC_INTERFACE(Derived)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Other use cases lead to error messages like:
PATH/Eigen/src/Core/functors/Binary
Functors.h:78:92: error: no type named ‘ReturnType’ in ‘struct Eigen::ScalarBinaryOpTraits<dou
ble, CppAD::AD<double>, Eigen::internal::scalar_product_op<double, CppAD::AD<double> > >’
Can anybody point me in the correct direction? It is possible that the NumTraits are for old Eigen Versions. I'm using 3.3.2 and CppAD from the
current master branch.
If you want to multiply a Matrix<CppAD::AD<double>, ...> by a Matrix<double, ...> you also need to specialize the corresponding ScalarBinaryOpTraits:
namespace Eigen {
template<typename X, typename BinOp>
struct ScalarBinaryOpTraits<CppAD::AD<X>,X,BinOp>
{
typedef CppAD::AD<X> ReturnType;
};
template<typename X, typename BinOp>
struct ScalarBinaryOpTraits<X,CppAD::AD<X>,BinOp>
{
typedef CppAD::AD<X> ReturnType;
};
} // namespace Eigen
This requires that CppAD::AD<X>() * X() is implemented.
Alternatively, you need to cast your matrix m to AD:
// should work:
y = m.cast<CppAD::AD<double> >() * x;

Eigen's AutoDiffJacobian, need some help getting a learning example to work

I have been using Eigen's AutoDiffScalar with much success and would now like to go over to the AutoDiffJacobian instead of doing this over by myself. Hence I created a learning example after have studied the AutoDiffJacobian.h, but something is wrong.
Functor:
template <typename Scalar>
struct adFunctor
{
typedef Eigen::Matrix<Scalar, 3, 1> InputType;
typedef Eigen::Matrix<Scalar, 2, 1> ValueType;
typedef Eigen::Matrix<Scalar,
ValueType::RowsAtCompileTime,
InputType::RowsAtCompileTime> JacobianType;
enum {
InputsAtCompileTime = InputType::RowsAtCompileTime,
ValuesAtCompileTime = ValueType::RowsAtCompileTime
};
adFunctor() {}
size_t inputs() const { return InputsAtCompileTime; }
void operator() (const InputType &input,
ValueType *output) const
{
Scalar s1 = Scalar(0), s2 = Scalar(0);
/* Some operations to test the AD. */
for (int i = 0; i < 3; i++)
{
s1 += log(input(i));
s2 += sqrt(input(i));
}
(*output)(0) = s1;
(*output)(1) = s2;
}
};
Usage:
Eigen::Matrix<double, 3, 1> in;
in << 1,2,3;
Eigen::Matrix<double, 2, 1> out;
Eigen::AutoDiffJacobian< adFunctor<double> > adjac;
adjac(in, &out);
The error that is received from this as is follows:
/usr/include/eigen3/unsupported/Eigen/src/AutoDiff/AutoDiffJacobian.h: In instantiation of ‘void Eigen::AutoDiffJacobian<Functor>::operator()(const InputType&, Eigen::AutoDiffJacobian<Functor>::ValueType*, Eigen::AutoDiffJacobian<Functor>::JacobianType*) const [with Functor = adFunctor<double>; Eigen::AutoDiffJacobian<Functor>::InputType = Eigen::Matrix<double, 3, 1>; Eigen::AutoDiffJacobian<Functor>::ValueType = Eigen::Matrix<double, 2, 1>; Eigen::AutoDiffJacobian<Functor>::JacobianType = Eigen::Matrix<double, 2, 3, 0, 2, 3>]’:
/home/emifre/Git/autodiff-test/src/autodiff_test.cpp:55:17: required from here
/usr/include/eigen3/unsupported/Eigen/src/AutoDiff/AutoDiffJacobian.h:69:24: error: no matching function for call to ‘Eigen::AutoDiffJacobian<adFunctor<double> >::operator()(Eigen::AutoDiffJacobian<adFunctor<double> >::ActiveInput&, Eigen::AutoDiffJacobian<adFunctor<double> >::ActiveValue*) const’
Functor::operator()(ax, &av);
~~~~~~~~~~~~~~~~~~~^~~~~~~~~
/home/emifre/Git/autodiff-test/src/autodiff_test.cpp:27:8: note: candidate: void adFunctor<Scalar>::operator()(const InputType&, adFunctor<Scalar>::ValueType*) const [with Scalar = double; adFunctor<Scalar>::InputType = Eigen::Matrix<double, 3, 1>; adFunctor<Scalar>::ValueType = Eigen::Matrix<double, 2, 1>]
void operator() (const InputType &input,
^~~~~~~~
/home/emifre/Git/autodiff-test/src/autodiff_test.cpp:27:8: note: no known conversion for argument 2 from ‘Eigen::AutoDiffJacobian<adFunctor<double> >::ActiveValue* {aka Eigen::Matrix<Eigen::AutoDiffScalar<Eigen::Matrix<double, 3, 1> >, 2, 1, 0, 2, 1>*}’ to ‘adFunctor<double>::ValueType* {aka Eigen::Matrix<double, 2, 1>*}’
From this error it seems that I am somehow not having the correct types for my functor for the second call to the functor in AutoDiffJacobian.h, but the first call to it works.
I hope someone here has an idea why and can help, maybe I have just misunderstood the usage.
EDIT: A compilable example that shows the problem:
#include <Eigen/Dense>
#include <unsupported/Eigen/AutoDiff>
/*
* Testing differentiation that will produce a Jacobian, using functors and the
* AutoDiffJacobian helper.
*/
template <typename Scalar>
struct adFunctor
{
typedef Eigen::Matrix<Scalar, 3, 1> InputType;
typedef Eigen::Matrix<Scalar, 2, 1> ValueType;
typedef Eigen::Matrix<Scalar,
ValueType::RowsAtCompileTime,
InputType::RowsAtCompileTime> JacobianType;
enum {
InputsAtCompileTime = InputType::RowsAtCompileTime,
ValuesAtCompileTime = ValueType::RowsAtCompileTime
};
adFunctor() {}
size_t inputs() const { return InputsAtCompileTime; }
void operator() (const InputType &input,
ValueType *output) const
{
Scalar s1 = Scalar(0), s2 = Scalar(0);
/* Some operations to test the AD. */
for (int i = 0; i < 3; i++)
{
s1 += log(input(i));
s2 += sqrt(input(i));
}
(*output)(0) = s1;
(*output)(1) = s2;
}
};
int main(int argc, char *argv[])
{
Eigen::Matrix<double, 3, 1> in;
in << 1,2,3;
Eigen::Matrix<double, 2, 1> out;
Eigen::AutoDiffJacobian< adFunctor<double> > adjac;
adjac(in, &out);
return 0;
}
Okey, after a lot of testing I got it to work.
It was just me misunderstanding the error from the compiler that said it straight out, I was missing the template for the operator itself.
It just needs to be changed to:
template <typename T1, typename T2>
void operator() (const T1 &input, T2 *output) const
Now it works like a treat! I hope someone more than me has usage of this.
Maybe that is too late for a reply.
I post my solution here.
The reason why compile failed is that inside AutoDiffScalar template class, it requires the custom function to implement the operator() with arguments are Vectors with ActiveScalar as scalars. Calling custom function inside AutoDiffJacobian - ActiveType overload
The ActiveScalar is a AutoDiffScalar<...> type.
Therefore, the custom class must provide an overload of operators () that accept such arguments.
Besides, they also require an overload that accepts raw InputType/ValueType. Calling custom function inside AutoDiffJacobian - OriginalType overload
Then, to have both of them, it is better to have a template implementation, e.g.,
template<typename T>
void operator()(const Eigen::Matrix<T, InputsAtCompileTime, 1>& x,
Eigen::Matrix<T, ValuesAtCompileTime,1>* v) const{
// Computation codes here ...
}
This is better than the solution from #Korken which accepts any type.