Deal with automatic transposition in call_assignment, fix a few shortcomings

This commit is contained in:
Gael Guennebaud 2014-02-17 16:11:55 +01:00
parent bffa15142c
commit d595fd31f5
5 changed files with 102 additions and 11 deletions

View File

@ -531,6 +531,23 @@ EIGEN_STRONG_INLINE Derived& DenseBase<Derived>
namespace internal { namespace internal {
#ifdef EIGEN_TEST_EVALUATORS
// TODO remove this class which is now useless
template<typename Derived, typename OtherDerived>
struct assign_selector {
EIGEN_DEVICE_FUNC
static EIGEN_STRONG_INLINE Derived& run(Derived& dst, const OtherDerived& other) {
call_assignment(dst, other.derived(), internal::assign_op<typename OtherDerived::Scalar>());
return dst;
}
template<typename ActualDerived, typename ActualOtherDerived>
EIGEN_DEVICE_FUNC
static EIGEN_STRONG_INLINE Derived& evalTo(ActualDerived& dst, const ActualOtherDerived& other) { other.evalTo(dst); return dst; }
};
#else // EIGEN_TEST_EVALUATORS
template<typename Derived, typename OtherDerived, template<typename Derived, typename OtherDerived,
bool EvalBeforeAssigning = (int(internal::traits<OtherDerived>::Flags) & EvalBeforeAssigningBit) != 0, bool EvalBeforeAssigning = (int(internal::traits<OtherDerived>::Flags) & EvalBeforeAssigningBit) != 0,
bool NeedToTranspose = ((int(Derived::RowsAtCompileTime) == 1 && int(OtherDerived::ColsAtCompileTime) == 1) bool NeedToTranspose = ((int(Derived::RowsAtCompileTime) == 1 && int(OtherDerived::ColsAtCompileTime) == 1)
@ -566,7 +583,7 @@ struct assign_selector<Derived,OtherDerived,true,true> {
EIGEN_DEVICE_FUNC EIGEN_DEVICE_FUNC
static EIGEN_STRONG_INLINE Derived& run(Derived& dst, const OtherDerived& other) { return dst.lazyAssign(other.transpose().eval()); } static EIGEN_STRONG_INLINE Derived& run(Derived& dst, const OtherDerived& other) { return dst.lazyAssign(other.transpose().eval()); }
}; };
#endif // EIGEN_TEST_EVALUATORS
} // end namespace internal } // end namespace internal
template<typename Derived> template<typename Derived>
@ -604,7 +621,11 @@ template <typename OtherDerived>
EIGEN_DEVICE_FUNC EIGEN_DEVICE_FUNC
EIGEN_STRONG_INLINE Derived& MatrixBase<Derived>::operator=(const EigenBase<OtherDerived>& other) EIGEN_STRONG_INLINE Derived& MatrixBase<Derived>::operator=(const EigenBase<OtherDerived>& other)
{ {
#ifdef EIGEN_TEST_EVALUATORS
return internal::assign_selector<Derived,OtherDerived>::evalTo(derived(), other.derived());
#else
return internal::assign_selector<Derived,OtherDerived,false>::evalTo(derived(), other.derived()); return internal::assign_selector<Derived,OtherDerived,false>::evalTo(derived(), other.derived());
#endif
} }
template<typename Derived> template<typename Derived>
@ -612,7 +633,11 @@ template<typename OtherDerived>
EIGEN_DEVICE_FUNC EIGEN_DEVICE_FUNC
EIGEN_STRONG_INLINE Derived& MatrixBase<Derived>::operator=(const ReturnByValue<OtherDerived>& other) EIGEN_STRONG_INLINE Derived& MatrixBase<Derived>::operator=(const ReturnByValue<OtherDerived>& other)
{ {
#ifdef EIGEN_TEST_EVALUATORS
return internal::assign_selector<Derived,OtherDerived>::evalTo(derived(), other.derived());
#else
return internal::assign_selector<Derived,OtherDerived,false>::evalTo(derived(), other.derived()); return internal::assign_selector<Derived,OtherDerived,false>::evalTo(derived(), other.derived());
#endif
} }
} // end namespace Eigen } // end namespace Eigen

View File

@ -656,28 +656,75 @@ template< typename DstXprType, typename SrcXprType, typename Functor,
struct Assignment; struct Assignment;
// The only purpose of this call_assignment() function is to deal with noalias() / AssumeAliasing. // The only purpose of this call_assignment() function is to deal with noalias() / AssumeAliasing and automatic transposition.
// Indeed, I (Gael) think that this concept of AssumeAliasing was a mistake, and it makes thing quite complicated. // 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 // So this intermediate function removes everything related to AssumeAliasing such that Assignment
// does not has to bother about these annoying details. // does not has to bother about these annoying details.
template<typename Dst, typename Src, typename Func> template<typename Dst, typename Src> struct transpose_to_match
void call_assignment(Dst& dst, const Src& src, const Func& func)
{ {
typedef typename internal::conditional<evaluator_traits<Src>::AssumeAliasing==1, EvalToTemp<Src>, Src>::type ActualSrc; enum {
Assignment<Dst,ActualSrc,Func>::run(dst, src, func); NeedToTranspose = ( (int(Dst::RowsAtCompileTime) == 1 && int(Src::ColsAtCompileTime) == 1)
| // FIXME | instead of || to please GCC 4.4.0 stupid warning "suggest parentheses around &&".
// revert to || as soon as not needed anymore.
(int(Dst::ColsAtCompileTime) == 1 && int(Src::RowsAtCompileTime) == 1))
&& int(Dst::SizeAtCompileTime) != 1
};
typedef typename internal::conditional<NeedToTranspose, Transpose<Dst>, Dst>::type type;
};
template<typename Dst, typename Src>
void call_assignment(Dst& dst, const Src& src)
{
call_assignment(dst, src, internal::assign_op<typename Dst::Scalar>());
}
template<typename Dst, typename Src>
void call_assignment(const Dst& dst, const Src& src)
{
call_assignment(dst, src, internal::assign_op<typename Dst::Scalar>());
}
// Deal with AssumeAliasing
template<typename Dst, typename Src, typename Func>
void call_assignment(Dst& dst, const Src& src, const Func& func, typename enable_if<evaluator_traits<Src>::AssumeAliasing==1, void*>::type = 0)
{
// The following initial implementation through an EvalToTemp object does not permit to
// perform deferred resizing as in 'A = A * B' when the size of 'A' as to be changed
// typedef typename internal::conditional<evaluator_traits<Src>::AssumeAliasing==1, EvalToTemp<Src>, Src>::type ActualSrc;
// Assignment<Dst,ActualSrc,Func>::run(dst, src, func);
// TODO we should simply do tmp(src);
#ifdef EIGEN_TEST_EVALUATORS
typename Src::PlainObject tmp(src);
#else
typename Src::PlainObject tmp(src.rows(), src.cols());
call_assignment(tmp.noalias(), src);
#endif
// resizing
dst.resize(tmp.rows(), tmp.cols());
call_assignment(dst.noalias(), tmp, func);
TRACK;
}
template<typename Dst, typename Src, typename Func>
void call_assignment(Dst& dst, const Src& src, const Func& func, typename enable_if<evaluator_traits<Src>::AssumeAliasing==0, void*>::type = 0)
{
Assignment<typename transpose_to_match<Dst,Src>::type,Src,Func>::run(dst, src, func);
} }
// by-pass AssumeAliasing // by-pass AssumeAliasing
// FIXME the const version should probably not be needed
template<typename Dst, template <typename> class StorageBase, typename Src, typename Func> template<typename Dst, template <typename> class StorageBase, typename Src, typename Func>
void call_assignment(const NoAlias<Dst,StorageBase>& dst, const Src& src, const Func& func) void call_assignment(const NoAlias<Dst,StorageBase>& dst, const Src& src, const Func& func)
{ {
Assignment<Dst,Src,Func>::run(dst.expression(), src, func); Assignment<typename transpose_to_match<Dst,Src>::type,Src,Func>::run(dst.expression(), src, func);
} }
template<typename Dst, template <typename> class StorageBase, typename Src, typename Func> template<typename Dst, template <typename> class StorageBase, typename Src, typename Func>
void call_assignment(NoAlias<Dst,StorageBase>& dst, const Src& src, const Func& func) void call_assignment(NoAlias<Dst,StorageBase>& dst, const Src& src, const Func& func)
{ {
Assignment<Dst,Src,Func>::run(dst.expression(), src, func); Assignment<typename transpose_to_match<Dst,Src>::type,Src,Func>::run(dst.expression(), src, func);
} }
// Generic Dense to Dense assignment // Generic Dense to Dense assignment

View File

@ -639,6 +639,18 @@ class PlainObjectBase : public internal::dense_xpr_base<Derived>::type
* *
* \internal * \internal
*/ */
#ifdef EIGEN_TEST_EVALUATORS
// aliasing is dealt once in internall::call_assignment
// so at this stage we have to assume aliasing... and resising has to be done later.
template<typename OtherDerived>
EIGEN_DEVICE_FUNC
EIGEN_STRONG_INLINE Derived& _set(const DenseBase<OtherDerived>& other)
{
internal::call_assignment(this->derived(), other.derived());
return this->derived();
return this->derived();
}
#else
template<typename OtherDerived> template<typename OtherDerived>
EIGEN_DEVICE_FUNC EIGEN_DEVICE_FUNC
EIGEN_STRONG_INLINE Derived& _set(const DenseBase<OtherDerived>& other) EIGEN_STRONG_INLINE Derived& _set(const DenseBase<OtherDerived>& other)
@ -654,7 +666,7 @@ class PlainObjectBase : public internal::dense_xpr_base<Derived>::type
template<typename OtherDerived> template<typename OtherDerived>
EIGEN_DEVICE_FUNC EIGEN_DEVICE_FUNC
EIGEN_STRONG_INLINE void _set_selector(const OtherDerived& other, const internal::false_type&) { _set_noalias(other); } EIGEN_STRONG_INLINE void _set_selector(const OtherDerived& other, const internal::false_type&) { _set_noalias(other); }
#endif
/** \internal Like _set() but additionally makes the assumption that no aliasing effect can happen (which /** \internal Like _set() but additionally makes the assumption that no aliasing effect can happen (which
* is the case when creating a new matrix) so one can enforce lazy evaluation. * is the case when creating a new matrix) so one can enforce lazy evaluation.
* *
@ -669,7 +681,12 @@ class PlainObjectBase : public internal::dense_xpr_base<Derived>::type
//_resize_to_match(other); //_resize_to_match(other);
// the 'false' below means to enforce lazy evaluation. We don't use lazyAssign() because // the 'false' below means to enforce lazy evaluation. We don't use lazyAssign() because
// it wouldn't allow to copy a row-vector into a column-vector. // it wouldn't allow to copy a row-vector into a column-vector.
#ifdef EIGEN_TEST_EVALUATORS
internal::call_assignment(this->noalias(), other.derived());
return this->derived();
#else
return internal::assign_selector<Derived,OtherDerived,false>::run(this->derived(), other.derived()); return internal::assign_selector<Derived,OtherDerived,false>::run(this->derived(), other.derived());
#endif
} }
template<typename T0, typename T1> template<typename T0, typename T1>

View File

@ -1235,6 +1235,7 @@ struct triangular_assignment_loop<Kernel, Mode, Dynamic, SetOpposite>
} // end namespace internal } // end namespace internal
#ifdef EIGEN_TEST_EVALUATORS
/** Assigns a triangular or selfadjoint matrix to a dense matrix. /** Assigns a triangular or selfadjoint matrix to a dense matrix.
* If the matrix is triangular, the opposite part is set to zero. */ * If the matrix is triangular, the opposite part is set to zero. */
template<typename Derived> template<typename Derived>
@ -1244,6 +1245,7 @@ void TriangularBase<Derived>::evalToLazy(MatrixBase<DenseDerived> &other) const
other.derived().resize(this->rows(), this->cols()); other.derived().resize(this->rows(), this->cols());
internal::call_triangular_assignment_loop<Derived::Mode,(Derived::Mode&SelfAdjoint)==0 /* SetOpposite */>(other.derived(), derived().nestedExpression()); internal::call_triangular_assignment_loop<Derived::Mode,(Derived::Mode&SelfAdjoint)==0 /* SetOpposite */>(other.derived(), derived().nestedExpression());
} }
#endif
#endif // EIGEN_ENABLE_EVALUATORS #endif // EIGEN_ENABLE_EVALUATORS

View File

@ -47,8 +47,8 @@ struct traits<CoeffBasedProduct<LhsNested,RhsNested,NestingFlags> >
typename traits<_RhsNested>::Index>::type Index; typename traits<_RhsNested>::Index>::type Index;
enum { enum {
LhsCoeffReadCost = _LhsNested::CoeffReadCost, LhsCoeffReadCost = traits<_LhsNested>::CoeffReadCost,
RhsCoeffReadCost = _RhsNested::CoeffReadCost, RhsCoeffReadCost = traits<_RhsNested>::CoeffReadCost,
LhsFlags = traits<_LhsNested>::Flags, LhsFlags = traits<_LhsNested>::Flags,
RhsFlags = traits<_RhsNested>::Flags, RhsFlags = traits<_RhsNested>::Flags,