From 230f5c3aa91b843cf1864258386e3902ffb6370a Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Mon, 25 Nov 2013 15:20:31 +0100 Subject: [PATCH] Evaluator: introduce the main Assignment class, add call_assignment to bypass NoAlias and AssumeAliasing, and some bits of cleaning --- Eigen/src/Core/AssignEvaluator.h | 244 +++++++++---------------------- Eigen/src/Core/NoAlias.h | 3 +- Eigen/src/Core/Swap.h | 72 +++++++++ Eigen/src/Core/util/XprHelper.h | 10 ++ test/evaluators.cpp | 76 +++++++++- 5 files changed, 226 insertions(+), 179 deletions(-) diff --git a/Eigen/src/Core/AssignEvaluator.h b/Eigen/src/Core/AssignEvaluator.h index 5b5d29ca9..158dc7842 100644 --- a/Eigen/src/Core/AssignEvaluator.h +++ b/Eigen/src/Core/AssignEvaluator.h @@ -514,7 +514,7 @@ struct dense_assignment_loop }; /*************************************************************************** -* Part 4 : Generic Assignment routine +* Part 4 : Generic dense assignment kernel ***************************************************************************/ // This class generalize the assignment of a coefficient (or packet) from one dense evaluator @@ -617,6 +617,10 @@ protected: DstXprType& m_dstExpr; }; +/*************************************************************************** +* Part 5 : Entry point for dense rectangular assignment +***************************************************************************/ + template void call_dense_assignment_loop(const DstXprType& dst, const SrcXprType& src, const Functor &func) { @@ -645,195 +649,81 @@ void call_dense_assignment_loop(const DstXprType& dst, const SrcXprType& src) } /*************************************************************************** -* Part 5 : Entry points +* Part 6 : Generic assignment ***************************************************************************/ -// Based on DenseBase::LazyAssign() -// The following functions are just for testing and they are meant to be moved to operator= and the likes. -template class StorageBase, typename SrcXprType> -EIGEN_STRONG_INLINE -const DstXprType& copy_using_evaluator(const NoAlias& dst, - const EigenBase& src) +// An evaluator must define its shape. It can be one of the following: +struct DenseShape {}; +struct DiagonalShape {}; +struct BandShape {}; +struct TriangularShape {}; +struct SelfAdjointShape {}; +struct SparseShape {}; + + +// Based on the respective shapes of the destination and source, +// the class AssignmentKind determine the kind of assignment mechanism. +// AssignmentKind must define a Kind typedef. +template struct AssignmentKind; + +// AssignmentKind<.,.>::Kind can be one of the following: + struct Dense2Dense {}; + struct Triangular2Triangular {}; +// struct Diagonal2Diagonal {}; // <=> Dense2Dense + struct Sparse2Dense {}; + struct Sparse2Sparse {}; + +// This is the main assignment class +template< typename DstXprType, typename SrcXprType, typename Functor, + typename Kind = Dense2Dense,//AssignmentKind< evaluator::Shape , evaluator::Shape >::Kind, + typename Scalar = typename DstXprType::Scalar> +struct Assignment; + + +// The only purpose of this call_assignment() function is to deal with noalias() / AssumeAliasing. +// Indeed, I (Gael) think that this concept of AssumeAliasing was a mistake, and it makes thing quite complicated. +// So this intermediate function removes everything related to AssumeAliasing such that Assignment +// does not has to bother about these annoying details. + +template +void call_assignment(Dst& dst, const Src& src, const Func& func) { - return noalias_copy_using_evaluator(dst.expression(), src.derived(), internal::assign_op()); + typedef typename internal::conditional::AssumeAliasing==1, EvalToTemp, Src>::type ActualSrc; + Assignment::run(dst, src, func); } -template::AssumeAliasing> -struct AddEvalIfAssumingAliasing; - -template -struct AddEvalIfAssumingAliasing +// by-pass AssumeAliasing +template class StorageBase, typename Src, typename Func> +void call_assignment(const NoAlias& dst, const Src& src, const Func& func) { - static const XprType& run(const XprType& xpr) - { - return xpr; - } -}; - -template -struct AddEvalIfAssumingAliasing -{ - static const EvalToTemp run(const XprType& xpr) - { - return EvalToTemp(xpr); - } -}; - -template -EIGEN_STRONG_INLINE -const DstXprType& copy_using_evaluator(const EigenBase& dst, const EigenBase& src, const Functor &func) -{ - return noalias_copy_using_evaluator(dst.const_cast_derived(), - AddEvalIfAssumingAliasing::run(src.derived()), - func - ); + Assignment::run(dst.expression(), src, func); } -// this mimics operator= -template -EIGEN_STRONG_INLINE -const DstXprType& copy_using_evaluator(const EigenBase& dst, const EigenBase& src) +// Generic Dense to Dense assignment +template< typename DstXprType, typename SrcXprType, typename Functor, typename Scalar> +struct Assignment { - return copy_using_evaluator(dst.const_cast_derived(), src.derived(), internal::assign_op()); -} - -template -EIGEN_STRONG_INLINE -const DstXprType& noalias_copy_using_evaluator(const PlainObjectBase& dst, const EigenBase& src, const Functor &func) -{ -#ifdef EIGEN_DEBUG_ASSIGN - internal::copy_using_evaluator_traits::debug(); -#endif -#ifdef EIGEN_NO_AUTOMATIC_RESIZING - eigen_assert((dst.size()==0 || (IsVectorAtCompileTime ? (dst.size() == src.size()) - : (dst.rows() == src.rows() && dst.cols() == src.cols()))) - && "Size mismatch. Automatic resizing is disabled because EIGEN_NO_AUTOMATIC_RESIZING is defined"); -#else - dst.const_cast_derived().resizeLike(src.derived()); -#endif - call_dense_assignment_loop(dst.const_cast_derived(), src.derived(), func); - return dst.derived(); -} - -template -EIGEN_STRONG_INLINE -const DstXprType& noalias_copy_using_evaluator(const EigenBase& dst, const EigenBase& src, const Functor &func) -{ - call_dense_assignment_loop(dst.const_cast_derived(), src.derived(), func); - return dst.derived(); -} - -// Based on DenseBase::swap() -// TODO: Check whether we need to do something special for swapping two -// Arrays or Matrices. (Jitse) - -// Overload default assignPacket behavior for swapping them -template -class swap_kernel : public generic_dense_assignment_kernel > -{ - typedef generic_dense_assignment_kernel > Base; - typedef typename DstEvaluatorTypeT::PacketScalar PacketScalar; - using Base::m_dst; - using Base::m_src; - using Base::m_functor; - -public: - typedef typename Base::Scalar Scalar; - typedef typename Base::Index Index; - typedef typename Base::DstXprType DstXprType; - - swap_kernel(DstEvaluatorTypeT &dst, const SrcEvaluatorTypeT &src, DstXprType& dstExpr) - : Base(dst, src, swap_assign_op(), dstExpr) - {} - - template - void assignPacket(Index row, Index col) + static void run(DstXprType &dst, const SrcXprType &src, const Functor &func) { - m_functor.template swapPacket(&m_dst.coeffRef(row,col), &const_cast(m_src).coeffRef(row,col)); - } - - template - void assignPacket(Index index) - { - m_functor.template swapPacket(&m_dst.coeffRef(index), &const_cast(m_src).coeffRef(index)); - } - - // TODO find a simple way not to have to copy/paste this function from generic_dense_assignment_kernel, by simple I mean no CRTP (Gael) - template - void assignPacketByOuterInner(Index outer, Index inner) - { - Index row = Base::rowIndexByOuterInner(outer, inner); - Index col = Base::colIndexByOuterInner(outer, inner); - assignPacket(row, col); - } -}; - -template -void swap_using_evaluator(const DstXprType& dst, const SrcXprType& src) -{ - // TODO there is too much redundancy with call_dense_assignment_loop - - eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols()); - - typedef typename evaluator::type DstEvaluatorType; - typedef typename evaluator::type SrcEvaluatorType; + // TODO check whether this is the right place to perform these checks: + enum{ + SameType = internal::is_same::value + }; - DstEvaluatorType dstEvaluator(dst); - SrcEvaluatorType srcEvaluator(src); + EIGEN_STATIC_ASSERT_LVALUE(DstXprType) + EIGEN_STATIC_ASSERT_SAME_MATRIX_SIZE(DstXprType,SrcXprType) + EIGEN_STATIC_ASSERT(SameType,YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY) - typedef swap_kernel Kernel; - Kernel kernel(dstEvaluator, srcEvaluator, dst.const_cast_derived()); - - dense_assignment_loop::run(kernel); -} - -// Based on MatrixBase::operator+= (in CwiseBinaryOp.h) -template -void add_assign_using_evaluator(const MatrixBase& dst, const MatrixBase& src) -{ - typedef typename DstXprType::Scalar Scalar; - copy_using_evaluator(dst.derived(), src.derived(), add_assign_op()); -} - -// Based on ArrayBase::operator+= -template -void add_assign_using_evaluator(const ArrayBase& dst, const ArrayBase& src) -{ - typedef typename DstXprType::Scalar Scalar; - copy_using_evaluator(dst.derived(), src.derived(), add_assign_op()); -} - -// TODO: Add add_assign_using_evaluator for EigenBase ? (Jitse) - -template -void subtract_assign_using_evaluator(const MatrixBase& dst, const MatrixBase& src) -{ - typedef typename DstXprType::Scalar Scalar; - copy_using_evaluator(dst.derived(), src.derived(), sub_assign_op()); -} - -template -void subtract_assign_using_evaluator(const ArrayBase& dst, const ArrayBase& src) -{ - typedef typename DstXprType::Scalar Scalar; - copy_using_evaluator(dst.derived(), src.derived(), sub_assign_op()); -} - -template -void multiply_assign_using_evaluator(const ArrayBase& dst, const ArrayBase& src) -{ - typedef typename DstXprType::Scalar Scalar; - copy_using_evaluator(dst.derived(), src.derived(), mul_assign_op()); -} - -template -void divide_assign_using_evaluator(const ArrayBase& dst, const ArrayBase& src) -{ - typedef typename DstXprType::Scalar Scalar; - copy_using_evaluator(dst.derived(), src.derived(), div_assign_op()); -} - + eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols()); + + #ifdef EIGEN_DEBUG_ASSIGN + internal::copy_using_evaluator_traits::debug(); + #endif + + call_dense_assignment_loop(dst, src, func); + } +}; } // namespace internal diff --git a/Eigen/src/Core/NoAlias.h b/Eigen/src/Core/NoAlias.h index 0a1c32743..65117a806 100644 --- a/Eigen/src/Core/NoAlias.h +++ b/Eigen/src/Core/NoAlias.h @@ -30,8 +30,9 @@ namespace Eigen { template class StorageBase> class NoAlias { - typedef typename ExpressionType::Scalar Scalar; public: + typedef typename ExpressionType::Scalar Scalar; + NoAlias(ExpressionType& expression) : m_expression(expression) {} /** Behaves like MatrixBase::lazyAssign(other) diff --git a/Eigen/src/Core/Swap.h b/Eigen/src/Core/Swap.h index d602fba65..8fd94b3c7 100644 --- a/Eigen/src/Core/Swap.h +++ b/Eigen/src/Core/Swap.h @@ -12,6 +12,8 @@ namespace Eigen { +// #ifndef EIGEN_TEST_EVALUATORS + /** \class SwapWrapper * \ingroup Core_Module * @@ -135,6 +137,76 @@ template class SwapWrapper ExpressionType& m_expression; }; +// #endif + +#ifdef EIGEN_TEST_EVALUATORS + +namespace internal { + +// Overload default assignPacket behavior for swapping them +template +class dense_swap_kernel : public generic_dense_assignment_kernel > +{ + typedef generic_dense_assignment_kernel > Base; + typedef typename DstEvaluatorTypeT::PacketScalar PacketScalar; + using Base::m_dst; + using Base::m_src; + using Base::m_functor; + +public: + typedef typename Base::Scalar Scalar; + typedef typename Base::Index Index; + typedef typename Base::DstXprType DstXprType; + + dense_swap_kernel(DstEvaluatorTypeT &dst, const SrcEvaluatorTypeT &src, DstXprType& dstExpr) + : Base(dst, src, swap_assign_op(), dstExpr) + {} + + template + void assignPacket(Index row, Index col) + { + m_functor.template swapPacket(&m_dst.coeffRef(row,col), &const_cast(m_src).coeffRef(row,col)); + } + + template + void assignPacket(Index index) + { + m_functor.template swapPacket(&m_dst.coeffRef(index), &const_cast(m_src).coeffRef(index)); + } + + // TODO find a simple way not to have to copy/paste this function from generic_dense_assignment_kernel, by simple I mean no CRTP (Gael) + template + void assignPacketByOuterInner(Index outer, Index inner) + { + Index row = Base::rowIndexByOuterInner(outer, inner); + Index col = Base::colIndexByOuterInner(outer, inner); + assignPacket(row, col); + } +}; + +template +void call_dense_swap_loop(const DstXprType& dst, const SrcXprType& src) +{ + // TODO there is too much redundancy with call_dense_assignment_loop + + eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols()); + + typedef typename evaluator::type DstEvaluatorType; + typedef typename evaluator::type SrcEvaluatorType; + + DstEvaluatorType dstEvaluator(dst); + SrcEvaluatorType srcEvaluator(src); + + typedef dense_swap_kernel Kernel; + Kernel kernel(dstEvaluator, srcEvaluator, dst.const_cast_derived()); + + dense_assignment_loop::run(kernel); +} + +} // namespace internal + +#endif + } // end namespace Eigen #endif // EIGEN_SWAP_H diff --git a/Eigen/src/Core/util/XprHelper.h b/Eigen/src/Core/util/XprHelper.h index 195d9e2e1..189928c8f 100644 --- a/Eigen/src/Core/util/XprHelper.h +++ b/Eigen/src/Core/util/XprHelper.h @@ -293,6 +293,15 @@ struct transfer_constness >::type type; }; +#ifdef EIGEN_TEST_EVALUATORS + +// When using evaluators, we never evaluate when assembling the expression!! +template::type> struct nested +{ + typedef typename ref_selector::type type; +}; + +#else /** \internal Determines how a given expression should be nested into another one. * For example, when you do a * (b+c), Eigen will determine how the expression b+c should be * nested into the bigger product expression. The choice is between nesting the expression b+c as-is, or @@ -339,6 +348,7 @@ template::type> str typename ref_selector::type >::type type; }; +#endif // EIGEN_TEST_EVALUATORS template EIGEN_DEVICE_FUNC diff --git a/test/evaluators.cpp b/test/evaluators.cpp index e3922c1be..3fb0d9896 100644 --- a/test/evaluators.cpp +++ b/test/evaluators.cpp @@ -1,7 +1,81 @@ + +#ifndef EIGEN_ENABLE_EVALUATORS #define EIGEN_ENABLE_EVALUATORS +#endif + #include "main.h" -using internal::copy_using_evaluator; +namespace Eigen { + + template + EIGEN_STRONG_INLINE + DstXprType& copy_using_evaluator(const EigenBase &dst, const SrcXprType &src) + { + call_assignment(dst.const_cast_derived(), src.derived(), internal::assign_op()); + return dst.const_cast_derived(); + } + + template class StorageBase, typename SrcXprType> + EIGEN_STRONG_INLINE + const DstXprType& copy_using_evaluator(const NoAlias& dst, const SrcXprType &src) + { + call_assignment(dst, src.derived(), internal::assign_op()); + return dst.expression(); + } + + template + EIGEN_STRONG_INLINE + DstXprType& copy_using_evaluator(const PlainObjectBase &dst, const SrcXprType &src) + { + #ifdef EIGEN_NO_AUTOMATIC_RESIZING + eigen_assert((dst.size()==0 || (IsVectorAtCompileTime ? (dst.size() == src.size()) + : (dst.rows() == src.rows() && dst.cols() == src.cols()))) + && "Size mismatch. Automatic resizing is disabled because EIGEN_NO_AUTOMATIC_RESIZING is defined"); + #else + dst.const_cast_derived().resizeLike(src.derived()); + #endif + + call_assignment(dst.const_cast_derived(), src.derived(), internal::assign_op()); + return dst.const_cast_derived(); + } + + template + void add_assign_using_evaluator(const DstXprType& dst, const SrcXprType& src) + { + typedef typename DstXprType::Scalar Scalar; + call_assignment(dst.const_cast_derived(), src.derived(), internal::add_assign_op()); + } + + template + void subtract_assign_using_evaluator(const DstXprType& dst, const SrcXprType& src) + { + typedef typename DstXprType::Scalar Scalar; + call_assignment(dst.const_cast_derived(), src.derived(), internal::sub_assign_op()); + } + + template + void multiply_assign_using_evaluator(const DstXprType& dst, const SrcXprType& src) + { + typedef typename DstXprType::Scalar Scalar; + call_assignment(dst.const_cast_derived(), src.derived(), internal::mul_assign_op()); + } + + template + void divide_assign_using_evaluator(const DstXprType& dst, const SrcXprType& src) + { + typedef typename DstXprType::Scalar Scalar; + call_assignment(dst.const_cast_derived(), src.derived(), internal::div_assign_op()); + } + + template + void swap_using_evaluator(const DstXprType& dst, const SrcXprType& src) + { + call_dense_swap_loop(dst.const_cast_derived(), src.const_cast_derived()); + } + +} + + using namespace std; #define VERIFY_IS_APPROX_EVALUATOR(DEST,EXPR) VERIFY_IS_APPROX(copy_using_evaluator(DEST,(EXPR)), (EXPR).eval());