|  | // This file is part of Eigen, a lightweight C++ template library | 
|  | // for linear algebra. | 
|  | // | 
|  | // Copyright (C) 2013 Christian Seiler <christian@iwakd.de> | 
|  | // | 
|  | // 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_TENSORSYMMETRY_SYMMETRY_H | 
|  | #define EIGEN_CXX11_TENSORSYMMETRY_SYMMETRY_H | 
|  |  | 
|  | namespace Eigen { | 
|  |  | 
|  | enum { | 
|  | NegationFlag           = 0x01, | 
|  | ConjugationFlag        = 0x02 | 
|  | }; | 
|  |  | 
|  | enum { | 
|  | GlobalRealFlag         = 0x01, | 
|  | GlobalImagFlag         = 0x02, | 
|  | GlobalZeroFlag         = 0x03 | 
|  | }; | 
|  |  | 
|  | namespace internal { | 
|  |  | 
|  | template<std::size_t NumIndices, typename... Sym>                   struct tensor_symmetry_pre_analysis; | 
|  | template<std::size_t NumIndices, typename... Sym>                   struct tensor_static_symgroup; | 
|  | template<bool instantiate, std::size_t NumIndices, typename... Sym> struct tensor_static_symgroup_if; | 
|  | template<typename Tensor_> struct tensor_symmetry_calculate_flags; | 
|  | template<typename Tensor_> struct tensor_symmetry_assign_value; | 
|  | template<typename... Sym> struct tensor_symmetry_num_indices; | 
|  |  | 
|  | } // end namespace internal | 
|  |  | 
|  | template<int One_, int Two_> | 
|  | struct Symmetry | 
|  | { | 
|  | static_assert(One_ != Two_, "Symmetries must cover distinct indices."); | 
|  | constexpr static int One = One_; | 
|  | constexpr static int Two = Two_; | 
|  | constexpr static int Flags = 0; | 
|  | }; | 
|  |  | 
|  | template<int One_, int Two_> | 
|  | struct AntiSymmetry | 
|  | { | 
|  | static_assert(One_ != Two_, "Symmetries must cover distinct indices."); | 
|  | constexpr static int One = One_; | 
|  | constexpr static int Two = Two_; | 
|  | constexpr static int Flags = NegationFlag; | 
|  | }; | 
|  |  | 
|  | template<int One_, int Two_> | 
|  | struct Hermiticity | 
|  | { | 
|  | static_assert(One_ != Two_, "Symmetries must cover distinct indices."); | 
|  | constexpr static int One = One_; | 
|  | constexpr static int Two = Two_; | 
|  | constexpr static int Flags = ConjugationFlag; | 
|  | }; | 
|  |  | 
|  | template<int One_, int Two_> | 
|  | struct AntiHermiticity | 
|  | { | 
|  | static_assert(One_ != Two_, "Symmetries must cover distinct indices."); | 
|  | constexpr static int One = One_; | 
|  | constexpr static int Two = Two_; | 
|  | constexpr static int Flags = ConjugationFlag | NegationFlag; | 
|  | }; | 
|  |  | 
|  | /** \class DynamicSGroup | 
|  | * \ingroup TensorSymmetry_Module | 
|  | * | 
|  | * \brief Dynamic symmetry group | 
|  | * | 
|  | * The %DynamicSGroup class represents a symmetry group that need not be known at | 
|  | * compile time. It is useful if one wants to support arbitrary run-time defineable | 
|  | * symmetries for tensors, but it is also instantiated if a symmetry group is defined | 
|  | * at compile time that would be either too large for the compiler to reasonably | 
|  | * generate (using templates to calculate this at compile time is very inefficient) | 
|  | * or that the compiler could generate the group but that it wouldn't make sense to | 
|  | * unroll the loop for setting coefficients anymore. | 
|  | */ | 
|  | class DynamicSGroup; | 
|  |  | 
|  | /** \internal | 
|  | * | 
|  | * \class DynamicSGroupFromTemplateArgs | 
|  | * \ingroup TensorSymmetry_Module | 
|  | * | 
|  | * \brief Dynamic symmetry group, initialized from template arguments | 
|  | * | 
|  | * This class is a child class of DynamicSGroup. It uses the template arguments | 
|  | * specified to initialize itself. | 
|  | */ | 
|  | template<typename... Gen> | 
|  | class DynamicSGroupFromTemplateArgs; | 
|  |  | 
|  | /** \class StaticSGroup | 
|  | * \ingroup TensorSymmetry_Module | 
|  | * | 
|  | * \brief Static symmetry group | 
|  | * | 
|  | * This class represents a symmetry group that is known and resolved completely | 
|  | * at compile time. Ideally, no run-time penalty is incurred compared to the | 
|  | * manual unrolling of the symmetry. | 
|  | * | 
|  | * <b><i>CAUTION:</i></b> | 
|  | * | 
|  | * Do not use this class directly for large symmetry groups. The compiler | 
|  | * may run into a limit, or segfault or in the very least will take a very, | 
|  | * very, very long time to compile the code. Use the SGroup class instead | 
|  | * if you want a static group. That class contains logic that will | 
|  | * automatically select the DynamicSGroup class instead if the symmetry | 
|  | * group becomes too large. (In that case, unrolling may not even be | 
|  | * beneficial.) | 
|  | */ | 
|  | template<typename... Gen> | 
|  | class StaticSGroup; | 
|  |  | 
|  | /** \class SGroup | 
|  | * \ingroup TensorSymmetry_Module | 
|  | * | 
|  | * \brief Symmetry group, initialized from template arguments | 
|  | * | 
|  | * This class represents a symmetry group whose generators are already | 
|  | * known at compile time. It may or may not be resolved at compile time, | 
|  | * depending on the estimated size of the group. | 
|  | * | 
|  | * \sa StaticSGroup | 
|  | * \sa DynamicSGroup | 
|  | */ | 
|  | template<typename... Gen> | 
|  | class SGroup : public internal::tensor_symmetry_pre_analysis<internal::tensor_symmetry_num_indices<Gen...>::value, Gen...>::root_type | 
|  | { | 
|  | public: | 
|  | constexpr static std::size_t NumIndices = internal::tensor_symmetry_num_indices<Gen...>::value; | 
|  | typedef typename internal::tensor_symmetry_pre_analysis<NumIndices, Gen...>::root_type Base; | 
|  |  | 
|  | // make standard constructors + assignment operators public | 
|  | inline SGroup() : Base() { } | 
|  | inline SGroup(const SGroup<Gen...>& other) : Base(other) { } | 
|  | inline SGroup(SGroup<Gen...>&& other) : Base(other) { } | 
|  | inline SGroup<Gen...>& operator=(const SGroup<Gen...>& other) { Base::operator=(other); return *this; } | 
|  | inline SGroup<Gen...>& operator=(SGroup<Gen...>&& other) { Base::operator=(other); return *this; } | 
|  |  | 
|  | // all else is defined in the base class | 
|  | }; | 
|  |  | 
|  | namespace internal { | 
|  |  | 
|  | template<typename... Sym> struct tensor_symmetry_num_indices | 
|  | { | 
|  | constexpr static std::size_t value = 1; | 
|  | }; | 
|  |  | 
|  | template<int One_, int Two_, typename... Sym> struct tensor_symmetry_num_indices<Symmetry<One_, Two_>, Sym...> | 
|  | { | 
|  | private: | 
|  | constexpr static std::size_t One = static_cast<std::size_t>(One_); | 
|  | constexpr static std::size_t Two = static_cast<std::size_t>(Two_); | 
|  | constexpr static std::size_t Three = tensor_symmetry_num_indices<Sym...>::value; | 
|  |  | 
|  | // don't use std::max, since it's not constexpr until C++14... | 
|  | constexpr static std::size_t maxOneTwoPlusOne = ((One > Two) ? One : Two) + 1; | 
|  | public: | 
|  | constexpr static std::size_t value = (maxOneTwoPlusOne > Three) ? maxOneTwoPlusOne : Three; | 
|  | }; | 
|  |  | 
|  | template<int One_, int Two_, typename... Sym> struct tensor_symmetry_num_indices<AntiSymmetry<One_, Two_>, Sym...> | 
|  | : public tensor_symmetry_num_indices<Symmetry<One_, Two_>, Sym...> {}; | 
|  | template<int One_, int Two_, typename... Sym> struct tensor_symmetry_num_indices<Hermiticity<One_, Two_>, Sym...> | 
|  | : public tensor_symmetry_num_indices<Symmetry<One_, Two_>, Sym...> {}; | 
|  | template<int One_, int Two_, typename... Sym> struct tensor_symmetry_num_indices<AntiHermiticity<One_, Two_>, Sym...> | 
|  | : public tensor_symmetry_num_indices<Symmetry<One_, Two_>, Sym...> {}; | 
|  |  | 
|  | /** \internal | 
|  | * | 
|  | * \class tensor_symmetry_pre_analysis | 
|  | * \ingroup TensorSymmetry_Module | 
|  | * | 
|  | * \brief Pre-select whether to use a static or dynamic symmetry group | 
|  | * | 
|  | * When a symmetry group could in principle be determined at compile time, | 
|  | * this template implements the logic whether to actually do that or whether | 
|  | * to rather defer that to runtime. | 
|  | * | 
|  | * The logic is as follows: | 
|  | * <dl> | 
|  | * <dt><b>No generators (trivial symmetry):</b></dt> | 
|  | * <dd>Use a trivial static group. Ideally, this has no performance impact | 
|  | *     compared to not using symmetry at all. In practice, this might not | 
|  | *     be the case.</dd> | 
|  | * <dt><b>More than 4 generators:</b></dt> | 
|  | * <dd>Calculate the group at run time, it is likely far too large for the | 
|  | *     compiler to be able to properly generate it in a realistic time.</dd> | 
|  | * <dt><b>Up to and including 4 generators:</b></dt> | 
|  | * <dd>Actually enumerate all group elements, but then check how many there | 
|  | *     are. If there are more than 16, it is unlikely that unrolling the | 
|  | *     loop (as is done in the static compile-time case) is sensible, so | 
|  | *     use a dynamic group instead. If there are at most 16 elements, actually | 
|  | *     use that static group. Note that the largest group with 4 generators | 
|  | *     still compiles with reasonable resources.</dd> | 
|  | * </dl> | 
|  | * | 
|  | * Note: Example compile time performance with g++-4.6 on an Intenl Core i5-3470 | 
|  | *       with 16 GiB RAM (all generators non-redundant and the subgroups don't | 
|  | *       factorize): | 
|  | * | 
|  | *          # Generators          -O0 -ggdb               -O2 | 
|  | *          ------------------------------------------------------------------- | 
|  | *          1                 0.5 s  /   250 MiB     0.45s /   230 MiB | 
|  | *          2                 0.5 s  /   260 MiB     0.5 s /   250 MiB | 
|  | *          3                 0.65s  /   310 MiB     0.62s /   310 MiB | 
|  | *          4                 2.2 s  /   860 MiB     1.7 s /   770 MiB | 
|  | *          5               130   s  / 13000 MiB   120   s / 11000 MiB | 
|  | * | 
|  | * It is clear that everything is still very efficient up to 4 generators, then | 
|  | * the memory and CPU requirements become unreasonable. Thus we only instantiate | 
|  | * the template group theory logic if the number of generators supplied is 4 or | 
|  | * lower, otherwise this will be forced to be done during runtime, where the | 
|  | * algorithm is reasonably fast. | 
|  | */ | 
|  | template<std::size_t NumIndices> | 
|  | struct tensor_symmetry_pre_analysis<NumIndices> | 
|  | { | 
|  | typedef StaticSGroup<> root_type; | 
|  | }; | 
|  |  | 
|  | template<std::size_t NumIndices, typename Gen_, typename... Gens_> | 
|  | struct tensor_symmetry_pre_analysis<NumIndices, Gen_, Gens_...> | 
|  | { | 
|  | constexpr static std::size_t max_static_generators = 4; | 
|  | constexpr static std::size_t max_static_elements = 16; | 
|  | typedef tensor_static_symgroup_if<(sizeof...(Gens_) + 1 <= max_static_generators), NumIndices, Gen_, Gens_...> helper; | 
|  | constexpr static std::size_t possible_size = helper::size; | 
|  |  | 
|  | typedef typename conditional< | 
|  | possible_size == 0 || possible_size >= max_static_elements, | 
|  | DynamicSGroupFromTemplateArgs<Gen_, Gens_...>, | 
|  | typename helper::type | 
|  | >::type root_type; | 
|  | }; | 
|  |  | 
|  | template<bool instantiate, std::size_t NumIndices, typename... Gens> | 
|  | struct tensor_static_symgroup_if | 
|  | { | 
|  | constexpr static std::size_t size = 0; | 
|  | typedef void type; | 
|  | }; | 
|  |  | 
|  | template<std::size_t NumIndices, typename... Gens> | 
|  | struct tensor_static_symgroup_if<true, NumIndices, Gens...> : tensor_static_symgroup<NumIndices, Gens...> {}; | 
|  |  | 
|  | template<typename Tensor_> | 
|  | struct tensor_symmetry_assign_value | 
|  | { | 
|  | typedef typename Tensor_::Index Index; | 
|  | typedef typename Tensor_::Scalar Scalar; | 
|  | constexpr static std::size_t NumIndices = Tensor_::NumIndices; | 
|  |  | 
|  | static inline int run(const std::array<Index, NumIndices>& transformed_indices, int transformation_flags, int dummy, Tensor_& tensor, const Scalar& value_) | 
|  | { | 
|  | Scalar value(value_); | 
|  | if (transformation_flags & ConjugationFlag) | 
|  | value = numext::conj(value); | 
|  | if (transformation_flags & NegationFlag) | 
|  | value = -value; | 
|  | tensor.coeffRef(transformed_indices) = value; | 
|  | return dummy; | 
|  | } | 
|  | }; | 
|  |  | 
|  | template<typename Tensor_> | 
|  | struct tensor_symmetry_calculate_flags | 
|  | { | 
|  | typedef typename Tensor_::Index Index; | 
|  | constexpr static std::size_t NumIndices = Tensor_::NumIndices; | 
|  |  | 
|  | static inline int run(const std::array<Index, NumIndices>& transformed_indices, int transform_flags, int current_flags, const std::array<Index, NumIndices>& orig_indices) | 
|  | { | 
|  | if (transformed_indices == orig_indices) { | 
|  | if (transform_flags & (ConjugationFlag | NegationFlag)) | 
|  | return current_flags | GlobalImagFlag; // anti-hermitian diagonal | 
|  | else if (transform_flags & ConjugationFlag) | 
|  | return current_flags | GlobalRealFlag; // hermitian diagonal | 
|  | else if (transform_flags & NegationFlag) | 
|  | return current_flags | GlobalZeroFlag; // anti-symmetric diagonal | 
|  | } | 
|  | return current_flags; | 
|  | } | 
|  | }; | 
|  |  | 
|  | template<typename Tensor_, typename Symmetry_, int Flags = 0> | 
|  | class tensor_symmetry_value_setter | 
|  | { | 
|  | public: | 
|  | typedef typename Tensor_::Index Index; | 
|  | typedef typename Tensor_::Scalar Scalar; | 
|  | constexpr static std::size_t NumIndices = Tensor_::NumIndices; | 
|  |  | 
|  | inline tensor_symmetry_value_setter(Tensor_& tensor, Symmetry_ const& symmetry, std::array<Index, NumIndices> const& indices) | 
|  | : m_tensor(tensor), m_symmetry(symmetry), m_indices(indices) { } | 
|  |  | 
|  | inline tensor_symmetry_value_setter<Tensor_, Symmetry_, Flags>& operator=(Scalar const& value) | 
|  | { | 
|  | doAssign(value); | 
|  | return *this; | 
|  | } | 
|  | private: | 
|  | Tensor_& m_tensor; | 
|  | Symmetry_ m_symmetry; | 
|  | std::array<Index, NumIndices> m_indices; | 
|  |  | 
|  | inline void doAssign(Scalar const& value) | 
|  | { | 
|  | #ifdef EIGEN_TENSOR_SYMMETRY_CHECK_VALUES | 
|  | int value_flags = m_symmetry.template apply<internal::tensor_symmetry_calculate_flags<Tensor_>, int>(m_indices, m_symmetry.globalFlags(), m_indices); | 
|  | if (value_flags & GlobalRealFlag) | 
|  | eigen_assert(numext::imag(value) == 0); | 
|  | if (value_flags & GlobalImagFlag) | 
|  | eigen_assert(numext::real(value) == 0); | 
|  | #endif | 
|  | m_symmetry.template apply<internal::tensor_symmetry_assign_value<Tensor_>, int>(m_indices, 0, m_tensor, value); | 
|  | } | 
|  | }; | 
|  |  | 
|  | } // end namespace internal | 
|  |  | 
|  | } // end namespace Eigen | 
|  |  | 
|  | #endif // EIGEN_CXX11_TENSORSYMMETRY_SYMMETRY_H | 
|  |  | 
|  | /* | 
|  | * kate: space-indent on; indent-width 2; mixedindent off; indent-mode cstyle; | 
|  | */ |