This forum has been archived. All content is frozen. Please use KDE Discuss instead.

Problems with linear solvers and customized classes

Tags: None
(comma "," separated)
mvukov
Registered Member
Posts
4
Karma
0
Hello,

I am extending the Eigen::Matrix class in order to conform API in my project. Following the instructions in the documentation I came up with the following code:

Code: Select all
template<typename T>
class GenericMatrix : public Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::AutoAlign>
{
public:
   /** Handy typedef for the base matrix class. */
   typedef Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::AutoAlign> Base;

   /** Constructors. */
   /** @{ */

   /** Constructor from any other Eigen class. */
   template<typename OtherDerived>
   inline GenericMatrix(const Eigen::MatrixBase<OtherDerived>& other) : Base( other ) {}

   /** Default ctor */
   GenericMatrix() : Base() {}

   GenericMatrix(   unsigned _nRows,
               unsigned _nCols
               )
      : Base(_nRows, _nCols)
   {}

   GenericMatrix(   unsigned _nRows,
               unsigned _nCols,
               const T* const _values
               )
      : Base(_nRows, _nCols)
   { std::copy(_values, _values + _nRows * _nCols, Base::data()); }

   GenericMatrix(   unsigned _nRows,
               unsigned _nCols,
               std::vector< T >& _values)
      : Base(_nRows, _nCols)
   { std::copy(_values.begin(), _values.end(), Base::data()); }

   GenericMatrix(   unsigned _nRows,
               unsigned _nCols,
               std::vector< std::vector< T > >& _values)
      : Base(_nRows, _nCols)
   {
      ASSERT( _values.size() > 0 )

      unsigned nRows = _values.size();
      unsigned nCols = _values[ 0 ].size();

      for (unsigned row = 0; row < nRows; ++row)
      {
         ASSERT( _values[ row ].size() == nCols );

         std::copy(_values[ row ].begin(), _values[ row ].end(), Base::data() + row * nCols);
      }
   }

   /** @} */

   /** Assign Eigen expressions to GenericMatrix. */
   template<typename OtherDerived>
   inline GenericMatrix& operator=(const Eigen::MatrixBase<OtherDerived>& other)
   {
      this->Base::operator=( other );

      return *this;
   }

   /** Destructor. */
   virtual ~GenericMatrix()
   {}

// ...
};


I also have GenericVector<T> which is derived from Eigen::Matrix<T, Eigen::Dynamic, 1>. Very similar constructors... The reason I am going this is way is because I am trying to use Eigen in a project with an existing matrix/vector API. I am aware that there is another, plugin based, approach, but this one looks more clean to me... (so far).

I am using Eigen 3.2.0 (OS X 10.7, gcc 4.7) and the following example code works:

Code: Select all
int main void
{
  GenericMatrix<double> A(3, 3), B(3, 4), C;
  A.setRandom(); B.setRandom();
  C = A * B;
}


When I want to solve a linear system with:
Code: Select all
GenericMatrix<double> A(4, 4);
GenericVector<double> b(4);

A.setRandom();
b.setRandom();

GenericVector<double> x;
x = A.fullPivHouseholderQr().solve(b);


I get a lot of some nasty errors, ending with
Code: Select all
"YOU_ARE_TRYING_TO_ACCESS_A_SINGLE_COEFFICIENT_IN_A_SPECIAL_EXPRESSION_WHERE_THAT_IS_NOT_ALLOWED_BECAUSE_THAT_WOULD_BE_INEFFICIENT".


Next, when I try to do Cholesky decomposition
Code: Select all
Eigen::LLT< GenericMatrix<double> > foo( A );


I also get a load of errors...

Since the error logs are quite long I can submit them upon request ;)

So, can you please help me to overcome these issues?

Thanx a lot in advance!
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
Please also paste the GenericVector class so that we can reproduce and also the first errors. If you can use c++11 features, then the best would be the plugin based approach with template typedefs to define GenericMatrix and GenericVector.
mvukov
Registered Member
Posts
4
Karma
0
Hi,

Here is the vector class:

Code: Select all
template<typename T>
class GenericVector : public Eigen::Matrix<T, Eigen::Dynamic, 1>
{
public:
   /** Handy typedef for the base vector class. */
   typedef Eigen::Matrix<T, Eigen::Dynamic, 1> Base;

   /** Constructors. */
   /** @{ */

   /** Constructor from any other Eigen class. */
   template<typename OtherDerived>
   inline GenericVector(const Eigen::MatrixBase<OtherDerived>& other) : Base( other ) {}

   /** Default ctor */
   GenericVector() : Base() {}

   /** Ctor which accepts size of the vector. */
   GenericVector(   unsigned _dim ) : Base( _dim ) { Base::setZero(); }

   /** Ctor with an initializing C-like array. */
   GenericVector(   unsigned _dim,
               const T* const _values
               )
      : Base( _dim )
   { std::copy(_values, _values + _dim, Base::data()); }

   /** Ctor with an STL vector. */
   GenericVector(   std::vector< T > _values
               )
      : Base( _values.size() )
   { std::copy(_values.begin(), _values.end(), Base::data()); }

   /** @} */

   /** Assign Eigen expressions to GenericVector. */
   template<typename OtherDerived>
   inline GenericVector& operator=(const Eigen::MatrixBase<OtherDerived>& other)
   {
      this->Base::operator=( other );
      return *this;
   }

   /** Destructor. */
   virtual ~GenericVector()
   {}

// ...
};


Thanx for your reply btw ;) ATM, I am not using C++11, first I need to check for possible compatibility issues with compiler(s) and current project code. Most of my users are not C++ experts, so I look at derived classes from Eigen ones as an "intermediate upgrade" -- at least I will be able to document the classes with doxygen; for advanced features I would refer to the Eigen documentation of course. Plugin based approach would be the last resort if there is no other solution...

All in all, I feel I am missing some ctors and typedefs in my classes to make this work. I would appreciate greatly your help! :)
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
You are missing many operator= overloads. The best is to remove the one you have and add:

using Base::operator=;

You might also need to forward more ctor. Look at Matrix.h.
mvukov
Registered Member
Posts
4
Karma
0
OK, here is the solution:

Code: Select all
template<typename T>
class GenericMatrix : public Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::AutoAlign>
{
public:
   /** Handy typedef for the base matrix class. */
   typedef Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::AutoAlign> Base;
   using Base::operator=;

   /** Constructors. */
   /** @{ */

   /** Constructor from any other Eigen::MatrixBase derived class. */
   template<typename OtherDerived>
   inline GenericMatrix(const Eigen::MatrixBase<OtherDerived>& other) : Base( other ) {}

   /** Constructor from any other Eigen::ReturnByValue derived class. */
   template<typename OtherDerived>
   inline GenericMatrix(const Eigen::ReturnByValue<OtherDerived>& other) : Base( other ) {}

   /** Constructor from any other Eigen::EigenBase derived class. */
   template<typename OtherDerived>
   inline GenericMatrix(const Eigen::EigenBase<OtherDerived>& other) : Base( other ) {}

   /** Default ctor */
   GenericMatrix() : Base() {}

   GenericMatrix(   unsigned _nRows,
               unsigned _nCols
               )
      : Base(_nRows, _nCols)
   {}

   GenericMatrix(   unsigned _nRows,
               unsigned _nCols,
               const T* const _values
               )
      : Base(_nRows, _nCols)
   { std::copy(_values, _values + _nRows * _nCols, Base::data()); }

   GenericMatrix(   unsigned _nRows,
               unsigned _nCols,
               std::vector< T >& _values)
      : Base(_nRows, _nCols)
   { std::copy(_values.begin(), _values.end(), Base::data()); }

   GenericMatrix(   unsigned _nRows,
               unsigned _nCols,
               std::vector< std::vector< T > >& _values)
      : Base(_nRows, _nCols)
   {
      ASSERT( _values.size() > 0 )

      unsigned nRows = _values.size();
      unsigned nCols = _values[ 0 ].size();

      for (unsigned row = 0; row < nRows; ++row)
      {
         ASSERT( _values[ row ].size() == nCols );

         std::copy(_values[ row ].begin(), _values[ row ].end(), Base::data() + row * nCols);
      }
   }

   /** @} */

   /** Destructor. */
   virtual ~GenericMatrix()
   {}
// ...
};

namespace Eigen
{
namespace internal
{
template<typename T>
struct traits< GenericMatrix< T > >
{
   typedef T Scalar;
   typedef Dense StorageKind;
   typedef DenseIndex Index;
   typedef MatrixXpr XprKind;
   enum
   {
      RowsAtCompileTime = Dynamic,
      ColsAtCompileTime = Dynamic,
      MaxRowsAtCompileTime = Dynamic,
      MaxColsAtCompileTime = Dynamic,
      Options = RowMajor | AutoAlign,
      Flags = compute_matrix_flags<T, Dynamic, Dynamic, Options, Dynamic, Dynamic>::ret,
      CoeffReadCost = NumTraits<Scalar>::ReadCost,
      InnerStrideAtCompileTime = 1,
      OuterStrideAtCompileTime = (Options & RowMajor) ? ColsAtCompileTime : RowsAtCompileTime
   };
};

} // namespace internal
} // namespace Eigen


and

Code: Select all
template<typename T>
class GenericVector : public Eigen::Matrix<T, Eigen::Dynamic, 1>
{
public:
   /** Handy typedef for the base vector class. */
   typedef Eigen::Matrix<T, Eigen::Dynamic, 1> Base;
   using Base::operator=;

   /** Constructors. */
   /** @{ */

   /** Constructor from any other Eigen::MatrixBase derived class. */
   template<typename OtherDerived>
   inline GenericVector(const Eigen::MatrixBase<OtherDerived>& other) : Base( other ) {}

   /** Constructor from any other Eigen::ReturnByValue derived class. */
   template<typename OtherDerived>
   inline GenericVector(const Eigen::ReturnByValue<OtherDerived>& other) : Base( other ) {}

   /** Constructor from any other Eigen::EigenBase derived class. */
   template<typename OtherDerived>
   inline GenericVector(const Eigen::EigenBase<OtherDerived>& other) : Base( other ) {}

   /** Default ctor */
   GenericVector() : Base() {}

   /** Ctor which accepts size of the vector. */
   GenericVector(   unsigned _dim ) : Base( _dim ) { Base::setZero(); }

   /** Ctor with an initializing C-like array. */
   GenericVector(   unsigned _dim,
               const T* const _values
               )
      : Base( _dim )
   { std::copy(_values, _values + _dim, Base::data()); }

   /** Ctor with an STL vector. */
   GenericVector(   std::vector< T > _values
               )
      : Base( _values.size() )
   { std::copy(_values.begin(), _values.end(), Base::data()); }

   /** @} */

   /** Destructor. */
   virtual ~GenericVector()
   {}
// ...
};


I removed my own namespace for clarity... This compiles and works. The key was to use:

- using Base::operator=
- define traits (for Matrix)
- forward a few ctors

Here are test cases that work:

Code: Select all
GenericMatrix<double> A(4, 4);
GenericVector<double> b(4);
A.setRandom(); b.setRandom();
GenericVector<double> x = A.fullPivHouseholderQr().solve(b);


and

Code: Select all
Eigen::LLT< GenericMatrix<double> > foo( A );
Matrix L = foo.matrixL();


Thanx for the help! :) In case there are some pieces can be done better, please let me know. After all, this is not so complex extension, please consider to put it in the manual ;)
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
For the traits:
Code: Select all
template<typename T>
struct traits< GenericMatrix< T > > : traits<Matrix<T,Dynamic,Dynamic,Eigen::RowMajor | Eigen::AutoAlign> {};
mvukov
Registered Member
Posts
4
Karma
0
Thanx a lot!


Bookmarks



Who is online

Registered users: Bing [Bot], Google [Bot], Yahoo [Bot]