mirror of
https://gitlab.com/libeigen/eigen.git
synced 2025-09-12 17:33:15 +08:00
New doc page on implementing a new expression class.
This commit is contained in:
parent
6d0f0b8cec
commit
ce2035af86
@ -13,6 +13,7 @@ namespace Eigen {
|
|||||||
- \subpage TopicUsingIntelMKL
|
- \subpage TopicUsingIntelMKL
|
||||||
- \subpage TopicCUDA
|
- \subpage TopicCUDA
|
||||||
- \subpage TopicTemplateKeyword
|
- \subpage TopicTemplateKeyword
|
||||||
|
- \subpage TopicNewExpressionType
|
||||||
- \subpage UserManual_UnderstandingEigen
|
- \subpage UserManual_UnderstandingEigen
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
137
doc/NewExpressionType.dox
Normal file
137
doc/NewExpressionType.dox
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
namespace Eigen {
|
||||||
|
|
||||||
|
/** \page TopicNewExpressionType Adding a new expression type
|
||||||
|
|
||||||
|
This page describes with the help of an example how to implement a new
|
||||||
|
light-weight expression type in %Eigen. This consists of three parts:
|
||||||
|
the expression type itself, a traits class containing compile-time
|
||||||
|
information about the expression, and the evaluator class which is
|
||||||
|
used to evaluate the expression to a matrix.
|
||||||
|
|
||||||
|
\b TO \b DO: Write a page explaining the design, with details on
|
||||||
|
vectorization etc., and refer to that page here.
|
||||||
|
|
||||||
|
|
||||||
|
\eigenAutoToc
|
||||||
|
|
||||||
|
\section TopicSetting The setting
|
||||||
|
|
||||||
|
A circulant matrix is a matrix where each column is the same as the
|
||||||
|
column to the left, except that it is cyclically shifted downwards.
|
||||||
|
For example, here is a 4-by-4 circulant matrix:
|
||||||
|
\f[ \begin{bmatrix}
|
||||||
|
1 & 8 & 4 & 2 \\
|
||||||
|
2 & 1 & 8 & 4 \\
|
||||||
|
4 & 2 & 1 & 8 \\
|
||||||
|
8 & 4 & 2 & 1
|
||||||
|
\end{bmatrix} \f]
|
||||||
|
A circulant matrix is uniquely determined by its first column. We wish
|
||||||
|
to write a function \c makeCirculant which, given the first column,
|
||||||
|
returns an expression representing the circulant matrix.
|
||||||
|
|
||||||
|
For simplicity, we restrict the \c makeCirculant function to dense
|
||||||
|
matrices. It may make sense to also allow arrays, or sparse matrices,
|
||||||
|
but we will not do so here. We also do not want to support
|
||||||
|
vectorization.
|
||||||
|
|
||||||
|
|
||||||
|
\section TopicPreamble Getting started
|
||||||
|
|
||||||
|
We will present the file implementing the \c makeCirculant function
|
||||||
|
part by part. We start by including the appropriate header files and
|
||||||
|
forward declaring the expression class, which we will call
|
||||||
|
\c Circulant. The \c makeCirculant function will return an object of
|
||||||
|
this type. The class \c Circulant is in fact a class template; the
|
||||||
|
template argument \c ArgType refers to the type of the vector passed
|
||||||
|
to the \c makeCirculant function.
|
||||||
|
|
||||||
|
\include make_circulant.cpp.preamble
|
||||||
|
|
||||||
|
|
||||||
|
\section TopicTraits The traits class
|
||||||
|
|
||||||
|
For every expression class \c X, there should be a traits class
|
||||||
|
\c Traits<X> in the \c Eigen::internal namespace containing
|
||||||
|
information about \c X known as compile time.
|
||||||
|
|
||||||
|
As explained in \ref TopicSetting, we designed the \c Circulant
|
||||||
|
expression class to refer to dense matrices. The entries of the
|
||||||
|
circulant matrix have the same type as the entries of the vector
|
||||||
|
passed to the \c makeCirculant function. The type used to index the
|
||||||
|
entries is also the same. Again for simplicity, we will only return
|
||||||
|
column-major matrices. Finally, the circulant matrix is a square
|
||||||
|
matrix (number of rows equals number of columns), and the number of
|
||||||
|
rows equals the number of rows of the column vector passed to the
|
||||||
|
\c makeCirculant function. If this is a dynamic-size vector, then the
|
||||||
|
size of the circulant matrix is not known at compile-time.
|
||||||
|
|
||||||
|
This leads to the following code:
|
||||||
|
|
||||||
|
\include make_circulant.cpp.traits
|
||||||
|
|
||||||
|
|
||||||
|
\section TopicExpression The expression class
|
||||||
|
|
||||||
|
The next step is to define the expression class itself. In our case,
|
||||||
|
we want to inherit from \c MatrixBase in order to expose the interface
|
||||||
|
for dense matrices. In the constructor, we check that we are passed a
|
||||||
|
column vector (see \ref TopicAssertions) and we store the vector from
|
||||||
|
which we are going to build the circulant matrix in the member
|
||||||
|
variable \c m_arg. Finally, the expression class should compute the
|
||||||
|
size of the corresponding circulant matrix. As explained above, this
|
||||||
|
is a square matrix with as many columns as the vector used to
|
||||||
|
construct the matrix.
|
||||||
|
|
||||||
|
\b TO \b DO: What about the \c Nested typedef? It seems to be
|
||||||
|
necessary; is this only temporary?
|
||||||
|
|
||||||
|
\include make_circulant.cpp.expression
|
||||||
|
|
||||||
|
|
||||||
|
\section TopicEvaluator The evaluator
|
||||||
|
|
||||||
|
The last big fragment implements the evaluator for the \c Circulant
|
||||||
|
expression. The evaluator computes the entries of the circulant
|
||||||
|
matrix; this is done in the \c .coeff() member function. The entries
|
||||||
|
are computed by finding the corresponding entry of the vector from
|
||||||
|
which the circulant matrix is constructed. Getting this entry may
|
||||||
|
actually be non-trivial when the circulant matrix is constructed from
|
||||||
|
a vector which is given by a complicated expression, so we use the
|
||||||
|
evaluator which corresponds to the vector.
|
||||||
|
|
||||||
|
The \c CoeffReadCost constant records the cost of computing an entry
|
||||||
|
of the circulant matrix; we ignore the index computation and say that
|
||||||
|
this is the same as the cost of computing an entry of the vector from
|
||||||
|
which the circulant matrix is constructed.
|
||||||
|
|
||||||
|
In the constructor, we save the evaluator for the column vector which
|
||||||
|
defined the circulant matrix. We also save the size of that vector;
|
||||||
|
remember that we can query an expression object to find the size but
|
||||||
|
not the evaluator.
|
||||||
|
|
||||||
|
\include make_circulant.cpp.evaluator
|
||||||
|
|
||||||
|
|
||||||
|
\section TopicEntry The entry point
|
||||||
|
|
||||||
|
After all this, the \c makeCirculant function is very simple. It
|
||||||
|
simply creates an expression object and returns it.
|
||||||
|
|
||||||
|
\include make_circulant.cpp.entry
|
||||||
|
|
||||||
|
|
||||||
|
\section TopicMain A simple main function for testing
|
||||||
|
|
||||||
|
Finally, a short \c main function that shows how the \c makeCirculant
|
||||||
|
function can be called.
|
||||||
|
|
||||||
|
\include make_circulant.cpp.main
|
||||||
|
|
||||||
|
If all the fragments are combined, the following output is produced,
|
||||||
|
showing that the program works as expected:
|
||||||
|
|
||||||
|
\verbinclude make_circulant.out
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
11
doc/examples/make_circulant.cpp
Normal file
11
doc/examples/make_circulant.cpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
This program is presented in several fragments in the doc page.
|
||||||
|
Every fragment is in its own file; this file simply combines them.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "make_circulant.cpp.preamble"
|
||||||
|
#include "make_circulant.cpp.traits"
|
||||||
|
#include "make_circulant.cpp.expression"
|
||||||
|
#include "make_circulant.cpp.evaluator"
|
||||||
|
#include "make_circulant.cpp.entry"
|
||||||
|
#include "make_circulant.cpp.main"
|
5
doc/examples/make_circulant.cpp.entry
Normal file
5
doc/examples/make_circulant.cpp.entry
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
template <class ArgType>
|
||||||
|
Circulant<ArgType> makeCirculant(const Eigen::MatrixBase<ArgType>& arg)
|
||||||
|
{
|
||||||
|
return Circulant<ArgType>(arg.derived());
|
||||||
|
}
|
33
doc/examples/make_circulant.cpp.evaluator
Normal file
33
doc/examples/make_circulant.cpp.evaluator
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
namespace Eigen {
|
||||||
|
namespace internal {
|
||||||
|
template<typename ArgType>
|
||||||
|
struct evaluator<Circulant<ArgType> >
|
||||||
|
: evaluator_base<Circulant<ArgType> >
|
||||||
|
{
|
||||||
|
typedef Circulant<ArgType> XprType;
|
||||||
|
typedef typename nested_eval<ArgType, XprType::ColsAtCompileTime>::type ArgTypeNested;
|
||||||
|
typedef typename remove_all<ArgTypeNested>::type ArgTypeNestedCleaned;
|
||||||
|
typedef typename XprType::CoeffReturnType CoeffReturnType;
|
||||||
|
typedef typename XprType::Index Index;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CoeffReadCost = evaluator<ArgTypeNestedCleaned>::CoeffReadCost,
|
||||||
|
Flags = Eigen::ColMajor
|
||||||
|
};
|
||||||
|
|
||||||
|
evaluator(const XprType& xpr)
|
||||||
|
: m_argImpl(xpr.m_arg), m_rows(xpr.rows())
|
||||||
|
{ }
|
||||||
|
|
||||||
|
CoeffReturnType coeff(Index row, Index col) const
|
||||||
|
{
|
||||||
|
Index index = row - col;
|
||||||
|
if (index < 0) index += m_rows;
|
||||||
|
return m_argImpl.coeff(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
typename evaluator<ArgTypeNestedCleaned>::nestedType m_argImpl;
|
||||||
|
const Index m_rows;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
20
doc/examples/make_circulant.cpp.expression
Normal file
20
doc/examples/make_circulant.cpp.expression
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
template <class ArgType>
|
||||||
|
class Circulant : public Eigen::MatrixBase<Circulant<ArgType> >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Circulant(const ArgType& arg)
|
||||||
|
: m_arg(arg)
|
||||||
|
{
|
||||||
|
EIGEN_STATIC_ASSERT(ArgType::ColsAtCompileTime == 1,
|
||||||
|
YOU_TRIED_CALLING_A_VECTOR_METHOD_ON_A_MATRIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef typename Eigen::internal::ref_selector<Circulant>::type Nested;
|
||||||
|
|
||||||
|
typedef typename Eigen::internal::traits<Circulant>::Index Index;
|
||||||
|
Index rows() const { return m_arg.rows(); }
|
||||||
|
Index cols() const { return m_arg.rows(); }
|
||||||
|
|
||||||
|
typedef typename Eigen::internal::ref_selector<ArgType>::type ArgTypeNested;
|
||||||
|
ArgTypeNested m_arg;
|
||||||
|
};
|
8
doc/examples/make_circulant.cpp.main
Normal file
8
doc/examples/make_circulant.cpp.main
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
int main()
|
||||||
|
{
|
||||||
|
Eigen::VectorXd vec(4);
|
||||||
|
vec << 1, 2, 4, 8;
|
||||||
|
Eigen::MatrixXd mat;
|
||||||
|
mat = makeCirculant(vec);
|
||||||
|
std::cout << mat << std::endl;
|
||||||
|
}
|
4
doc/examples/make_circulant.cpp.preamble
Normal file
4
doc/examples/make_circulant.cpp.preamble
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#include <Eigen/Core>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
template <class ArgType> class Circulant;
|
19
doc/examples/make_circulant.cpp.traits
Normal file
19
doc/examples/make_circulant.cpp.traits
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
namespace Eigen {
|
||||||
|
namespace internal {
|
||||||
|
template <class ArgType>
|
||||||
|
struct traits<Circulant<ArgType> >
|
||||||
|
{
|
||||||
|
typedef Eigen::Dense StorageKind;
|
||||||
|
typedef Eigen::MatrixXpr XprKind;
|
||||||
|
typedef typename ArgType::Index Index;
|
||||||
|
typedef typename ArgType::Scalar Scalar;
|
||||||
|
enum {
|
||||||
|
Flags = Eigen::ColMajor,
|
||||||
|
RowsAtCompileTime = ArgType::RowsAtCompileTime,
|
||||||
|
ColsAtCompileTime = ArgType::RowsAtCompileTime,
|
||||||
|
MaxRowsAtCompileTime = ArgType::MaxRowsAtCompileTime,
|
||||||
|
MaxColsAtCompileTime = ArgType::MaxRowsAtCompileTime
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user