// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2014 Benoit Steiner // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. #ifndef EIGEN_CXX11_TENSOR_TENSOR_CONTRACTION_H #define EIGEN_CXX11_TENSOR_TENSOR_CONTRACTION_H namespace Eigen { /** \class TensorContraction * \ingroup CXX11_Tensor_Module * * \brief Tensor contraction class. * * */ namespace internal { template struct traits > { // Type promotion to handle the case where the types of the lhs and the rhs are different. typedef typename internal::promote_storage_type::ret Scalar; typedef typename internal::packet_traits::type Packet; typedef typename promote_storage_type::StorageKind, typename traits::StorageKind>::ret StorageKind; typedef typename promote_index_type::Index, typename traits::Index>::type Index; typedef typename LhsXprType::Nested LhsNested; typedef typename RhsXprType::Nested RhsNested; typedef typename remove_reference::type _LhsNested; typedef typename remove_reference::type _RhsNested; enum { Flags = 0, }; }; template struct eval, Eigen::Dense> { typedef const TensorContractionOp& type; }; template struct nested, 1, typename eval >::type> { typedef TensorContractionOp type; }; } // end namespace internal template class TensorContractionOp : public TensorBase > { public: typedef typename Eigen::internal::traits::Scalar Scalar; typedef typename Eigen::internal::traits::Packet Packet; typedef typename Eigen::NumTraits::Real RealScalar; typedef typename internal::promote_storage_type::ret CoeffReturnType; typedef typename internal::promote_storage_type::ret PacketReturnType; typedef typename Eigen::internal::nested::type Nested; typedef typename Eigen::internal::traits::StorageKind StorageKind; typedef typename Eigen::internal::traits::Index Index; EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorContractionOp(const LhsXprType& lhs, const RhsXprType& rhs, const Indices& dims) : m_lhs_xpr(lhs), m_rhs_xpr(rhs), m_indices(dims) {} EIGEN_DEVICE_FUNC const Indices& indices() const { return m_indices; } /** \returns the nested expressions */ EIGEN_DEVICE_FUNC const typename internal::remove_all::type& lhsExpression() const { return m_lhs_xpr; } EIGEN_DEVICE_FUNC const typename internal::remove_all::type& rhsExpression() const { return m_rhs_xpr; } protected: typename LhsXprType::Nested m_lhs_xpr; typename RhsXprType::Nested m_rhs_xpr; const Indices m_indices; }; template struct max_n_1 { static const size_t size = n; }; template <> struct max_n_1<0> { static const size_t size = 1; }; template struct TensorEvaluator, Device> { typedef TensorContractionOp XprType; static const int NumDims = max_n_1::Dimensions::count + TensorEvaluator::Dimensions::count - 2 * internal::array_size::value>::size; typedef typename XprType::Index Index; typedef DSizes Dimensions; enum { IsAligned = TensorEvaluator::IsAligned & TensorEvaluator::IsAligned, PacketAccess = /*TensorEvaluator::PacketAccess & TensorEvaluator::PacketAccess */ false, }; TensorEvaluator(const XprType& op, const Device& device) : m_leftImpl(op.lhsExpression(), device), m_rightImpl(op.rhsExpression(), device) { Index index = 0; Index stride = 1; m_shiftright = 1; int skipped = 0; const typename TensorEvaluator::Dimensions& left_dims = m_leftImpl.dimensions(); for (int i = 0; i < TensorEvaluator::Dimensions::count; ++i) { bool skip = false; for (int j = 0; j < internal::array_size::value; ++j) { if (op.indices()[j].first == i) { skip = true; m_leftOffsets[2*skipped] = stride; m_leftOffsets[2*skipped+1] = stride * left_dims[i]; m_stitchsize[skipped] = left_dims[i]; break; } } if (!skip) { m_dimensions[index++] = left_dims[i]; m_shiftright *= left_dims[i]; } else { ++skipped; } stride *= left_dims[i]; } stride = 1; skipped = 0; const typename TensorEvaluator::Dimensions& right_dims = m_rightImpl.dimensions(); for (int i = 0; i < TensorEvaluator::Dimensions::count; ++i) { bool skip = false; for (int j = 0; j < internal::array_size::value; ++j) { if (op.indices()[j].second == i) { skip = true; m_rightOffsets[2*skipped] = stride; m_rightOffsets[2*skipped+1] = stride * right_dims[i]; break; } } if (!skip) { m_dimensions[index++] = right_dims[i]; } else { ++skipped; } stride *= right_dims[i]; } // Scalar case if (TensorEvaluator::Dimensions::count + TensorEvaluator::Dimensions::count == 2 * internal::array_size::value) { m_dimensions[0] = 1; } } typedef typename XprType::Scalar Scalar; typedef typename XprType::CoeffReturnType CoeffReturnType; typedef typename XprType::PacketReturnType PacketReturnType; const Dimensions& dimensions() const { return m_dimensions; } void evalTo(typename XprType::Scalar* buffer) const { for (int i = 0; i < dimensions().TotalSize(); ++i) { buffer[i] += coeff(i); } } EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool evalSubExprsIfNeeded(Scalar*) { m_leftImpl.evalSubExprsIfNeeded(NULL); m_rightImpl.evalSubExprsIfNeeded(NULL); return true; } EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void cleanup() { m_leftImpl.cleanup(); m_rightImpl.cleanup(); } EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType coeff(Index index) const { const Index startLeft = index % m_shiftright; const Index startRight = index / m_shiftright; CoeffReturnType result = CoeffReturnType(0); partialStitch(startLeft, startRight, 0, result); return result; } /* TODO: vectorization template EIGEN_DEVICE_FUNC PacketReturnType packet(Index index) const { assert(false); }*/ private: EIGEN_DEVICE_FUNC void partialStitch(Index startLeft, Index startRight, int StitchIndex, CoeffReturnType& accum) const { Index firstLeft = (startLeft / m_leftOffsets[2*StitchIndex]) * m_leftOffsets[2*StitchIndex+1] + (startLeft % m_leftOffsets[2*StitchIndex]); Index firstRight = (startRight / m_rightOffsets[2*StitchIndex]) * m_rightOffsets[2*StitchIndex+1] + (startRight % m_rightOffsets[2*StitchIndex]); for (int j = 0; j < m_stitchsize[StitchIndex]; ++j) { const Index left = firstLeft+j*m_leftOffsets[2*StitchIndex]; const Index right = firstRight+j*m_rightOffsets[2*StitchIndex]; if (StitchIndex < internal::array_size::value-1) { partialStitch(left, right, StitchIndex+1, accum); } else { accum += m_leftImpl.coeff(left) * m_rightImpl.coeff(right); } } } Scalar* data() const { return NULL; } private: array::value> m_leftOffsets; array::value> m_rightOffsets; array::value> m_stitchsize; Index m_shiftright; Dimensions m_dimensions; TensorEvaluator m_leftImpl; TensorEvaluator m_rightImpl; }; } // end namespace Eigen #endif // EIGEN_CXX11_TENSOR_TENSOR_CONTRACTION_H