| // 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 |
| |
| #include "./InternalHeaderCheck.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 std::conditional_t< |
| possible_size == 0 || possible_size >= max_static_elements, |
| DynamicSGroupFromTemplateArgs<Gen_, Gens_...>, |
| typename helper::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; |
| */ |