| // 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_DYNAMICSYMMETRY_H | |
| #define EIGEN_CXX11_TENSORSYMMETRY_DYNAMICSYMMETRY_H | |
| namespace Eigen { | |
| class DynamicSGroup | |
| { | |
| public: | |
| inline explicit DynamicSGroup() : m_numIndices(1), m_elements(), m_generators(), m_globalFlags(0) { m_elements.push_back(ge(Generator(0, 0, 0))); } | |
| inline DynamicSGroup(const DynamicSGroup& o) : m_numIndices(o.m_numIndices), m_elements(o.m_elements), m_generators(o.m_generators), m_globalFlags(o.m_globalFlags) { } | |
| inline DynamicSGroup(DynamicSGroup&& o) : m_numIndices(o.m_numIndices), m_elements(), m_generators(o.m_generators), m_globalFlags(o.m_globalFlags) { std::swap(m_elements, o.m_elements); } | |
| inline DynamicSGroup& operator=(const DynamicSGroup& o) { m_numIndices = o.m_numIndices; m_elements = o.m_elements; m_generators = o.m_generators; m_globalFlags = o.m_globalFlags; return *this; } | |
| inline DynamicSGroup& operator=(DynamicSGroup&& o) { m_numIndices = o.m_numIndices; std::swap(m_elements, o.m_elements); m_generators = o.m_generators; m_globalFlags = o.m_globalFlags; return *this; } | |
| void add(int one, int two, int flags = 0); | |
| template<typename Gen_> | |
| inline void add(Gen_) { add(Gen_::One, Gen_::Two, Gen_::Flags); } | |
| inline void addSymmetry(int one, int two) { add(one, two, 0); } | |
| inline void addAntiSymmetry(int one, int two) { add(one, two, NegationFlag); } | |
| inline void addHermiticity(int one, int two) { add(one, two, ConjugationFlag); } | |
| inline void addAntiHermiticity(int one, int two) { add(one, two, NegationFlag | ConjugationFlag); } | |
| template<typename Op, typename RV, typename Index, std::size_t N, typename... Args> | |
| inline RV apply(const std::array<Index, N>& idx, RV initial, Args&&... args) const | |
| { | |
| eigen_assert(N >= m_numIndices && "Can only apply symmetry group to objects that have at least the required amount of indices."); | |
| for (std::size_t i = 0; i < size(); i++) | |
| initial = Op::run(h_permute(i, idx, typename internal::gen_numeric_list<int, N>::type()), m_elements[i].flags, initial, std::forward<Args>(args)...); | |
| return initial; | |
| } | |
| template<typename Op, typename RV, typename Index, typename... Args> | |
| inline RV apply(const std::vector<Index>& idx, RV initial, Args&&... args) const | |
| { | |
| eigen_assert(idx.size() >= m_numIndices && "Can only apply symmetry group to objects that have at least the required amount of indices."); | |
| for (std::size_t i = 0; i < size(); i++) | |
| initial = Op::run(h_permute(i, idx), m_elements[i].flags, initial, std::forward<Args>(args)...); | |
| return initial; | |
| } | |
| inline int globalFlags() const { return m_globalFlags; } | |
| inline std::size_t size() const { return m_elements.size(); } | |
| template<typename Tensor_, typename... IndexTypes> | |
| inline internal::tensor_symmetry_value_setter<Tensor_, DynamicSGroup> operator()(Tensor_& tensor, typename Tensor_::Index firstIndex, IndexTypes... otherIndices) const | |
| { | |
| static_assert(sizeof...(otherIndices) + 1 == Tensor_::NumIndices, "Number of indices used to access a tensor coefficient must be equal to the rank of the tensor."); | |
| return operator()(tensor, std::array<typename Tensor_::Index, Tensor_::NumIndices>{{firstIndex, otherIndices...}}); | |
| } | |
| template<typename Tensor_> | |
| inline internal::tensor_symmetry_value_setter<Tensor_, DynamicSGroup> operator()(Tensor_& tensor, std::array<typename Tensor_::Index, Tensor_::NumIndices> const& indices) const | |
| { | |
| return internal::tensor_symmetry_value_setter<Tensor_, DynamicSGroup>(tensor, *this, indices); | |
| } | |
| private: | |
| struct GroupElement { | |
| std::vector<int> representation; | |
| int flags; | |
| bool isId() const | |
| { | |
| for (std::size_t i = 0; i < representation.size(); i++) | |
| if (i != (size_t)representation[i]) | |
| return false; | |
| return true; | |
| } | |
| }; | |
| struct Generator { | |
| int one; | |
| int two; | |
| int flags; | |
| constexpr inline Generator(int one_, int two_, int flags_) : one(one_), two(two_), flags(flags_) {} | |
| }; | |
| std::size_t m_numIndices; | |
| std::vector<GroupElement> m_elements; | |
| std::vector<Generator> m_generators; | |
| int m_globalFlags; | |
| template<typename Index, std::size_t N, int... n> | |
| inline std::array<Index, N> h_permute(std::size_t which, const std::array<Index, N>& idx, internal::numeric_list<int, n...>) const | |
| { | |
| return std::array<Index, N>{{ idx[n >= m_numIndices ? n : m_elements[which].representation[n]]... }}; | |
| } | |
| template<typename Index> | |
| inline std::vector<Index> h_permute(std::size_t which, std::vector<Index> idx) const | |
| { | |
| std::vector<Index> result; | |
| result.reserve(idx.size()); | |
| for (auto k : m_elements[which].representation) | |
| result.push_back(idx[k]); | |
| for (std::size_t i = m_numIndices; i < idx.size(); i++) | |
| result.push_back(idx[i]); | |
| return result; | |
| } | |
| inline GroupElement ge(Generator const& g) const | |
| { | |
| GroupElement result; | |
| result.representation.reserve(m_numIndices); | |
| result.flags = g.flags; | |
| for (std::size_t k = 0; k < m_numIndices; k++) { | |
| if (k == (std::size_t)g.one) | |
| result.representation.push_back(g.two); | |
| else if (k == (std::size_t)g.two) | |
| result.representation.push_back(g.one); | |
| else | |
| result.representation.push_back(int(k)); | |
| } | |
| return result; | |
| } | |
| GroupElement mul(GroupElement, GroupElement) const; | |
| inline GroupElement mul(Generator g1, GroupElement g2) const | |
| { | |
| return mul(ge(g1), g2); | |
| } | |
| inline GroupElement mul(GroupElement g1, Generator g2) const | |
| { | |
| return mul(g1, ge(g2)); | |
| } | |
| inline GroupElement mul(Generator g1, Generator g2) const | |
| { | |
| return mul(ge(g1), ge(g2)); | |
| } | |
| inline int findElement(GroupElement e) const | |
| { | |
| for (auto ee : m_elements) { | |
| if (ee.representation == e.representation) | |
| return ee.flags ^ e.flags; | |
| } | |
| return -1; | |
| } | |
| void updateGlobalFlags(int flagDiffOfSameGenerator); | |
| }; | |
| // dynamic symmetry group that auto-adds the template parameters in the constructor | |
| template<typename... Gen> | |
| class DynamicSGroupFromTemplateArgs : public DynamicSGroup | |
| { | |
| public: | |
| inline DynamicSGroupFromTemplateArgs() : DynamicSGroup() | |
| { | |
| add_all(internal::type_list<Gen...>()); | |
| } | |
| inline DynamicSGroupFromTemplateArgs(DynamicSGroupFromTemplateArgs const& other) : DynamicSGroup(other) { } | |
| inline DynamicSGroupFromTemplateArgs(DynamicSGroupFromTemplateArgs&& other) : DynamicSGroup(other) { } | |
| inline DynamicSGroupFromTemplateArgs<Gen...>& operator=(const DynamicSGroupFromTemplateArgs<Gen...>& o) { DynamicSGroup::operator=(o); return *this; } | |
| inline DynamicSGroupFromTemplateArgs<Gen...>& operator=(DynamicSGroupFromTemplateArgs<Gen...>&& o) { DynamicSGroup::operator=(o); return *this; } | |
| private: | |
| template<typename Gen1, typename... GenNext> | |
| inline void add_all(internal::type_list<Gen1, GenNext...>) | |
| { | |
| add(Gen1()); | |
| add_all(internal::type_list<GenNext...>()); | |
| } | |
| inline void add_all(internal::type_list<>) | |
| { | |
| } | |
| }; | |
| inline DynamicSGroup::GroupElement DynamicSGroup::mul(GroupElement g1, GroupElement g2) const | |
| { | |
| eigen_internal_assert(g1.representation.size() == m_numIndices); | |
| eigen_internal_assert(g2.representation.size() == m_numIndices); | |
| GroupElement result; | |
| result.representation.reserve(m_numIndices); | |
| for (std::size_t i = 0; i < m_numIndices; i++) { | |
| int v = g2.representation[g1.representation[i]]; | |
| eigen_assert(v >= 0); | |
| result.representation.push_back(v); | |
| } | |
| result.flags = g1.flags ^ g2.flags; | |
| return result; | |
| } | |
| inline void DynamicSGroup::add(int one, int two, int flags) | |
| { | |
| eigen_assert(one >= 0); | |
| eigen_assert(two >= 0); | |
| eigen_assert(one != two); | |
| if ((std::size_t)one >= m_numIndices || (std::size_t)two >= m_numIndices) { | |
| std::size_t newNumIndices = (one > two) ? one : two + 1; | |
| for (auto& gelem : m_elements) { | |
| gelem.representation.reserve(newNumIndices); | |
| for (std::size_t i = m_numIndices; i < newNumIndices; i++) | |
| gelem.representation.push_back(i); | |
| } | |
| m_numIndices = newNumIndices; | |
| } | |
| Generator g{one, two, flags}; | |
| GroupElement e = ge(g); | |
| /* special case for first generator */ | |
| if (m_elements.size() == 1) { | |
| while (!e.isId()) { | |
| m_elements.push_back(e); | |
| e = mul(e, g); | |
| } | |
| if (e.flags > 0) | |
| updateGlobalFlags(e.flags); | |
| // only add in case we didn't have identity | |
| if (m_elements.size() > 1) | |
| m_generators.push_back(g); | |
| return; | |
| } | |
| int p = findElement(e); | |
| if (p >= 0) { | |
| updateGlobalFlags(p); | |
| return; | |
| } | |
| std::size_t coset_order = m_elements.size(); | |
| m_elements.push_back(e); | |
| for (std::size_t i = 1; i < coset_order; i++) | |
| m_elements.push_back(mul(m_elements[i], e)); | |
| m_generators.push_back(g); | |
| std::size_t coset_rep = coset_order; | |
| do { | |
| for (auto g : m_generators) { | |
| e = mul(m_elements[coset_rep], g); | |
| p = findElement(e); | |
| if (p < 0) { | |
| // element not yet in group | |
| m_elements.push_back(e); | |
| for (std::size_t i = 1; i < coset_order; i++) | |
| m_elements.push_back(mul(m_elements[i], e)); | |
| } else if (p > 0) { | |
| updateGlobalFlags(p); | |
| } | |
| } | |
| coset_rep += coset_order; | |
| } while (coset_rep < m_elements.size()); | |
| } | |
| inline void DynamicSGroup::updateGlobalFlags(int flagDiffOfSameGenerator) | |
| { | |
| switch (flagDiffOfSameGenerator) { | |
| case 0: | |
| default: | |
| // nothing happened | |
| break; | |
| case NegationFlag: | |
| // every element is it's own negative => whole tensor is zero | |
| m_globalFlags |= GlobalZeroFlag; | |
| break; | |
| case ConjugationFlag: | |
| // every element is it's own conjugate => whole tensor is real | |
| m_globalFlags |= GlobalRealFlag; | |
| break; | |
| case (NegationFlag | ConjugationFlag): | |
| // every element is it's own negative conjugate => whole tensor is imaginary | |
| m_globalFlags |= GlobalImagFlag; | |
| break; | |
| /* NOTE: | |
| * since GlobalZeroFlag == GlobalRealFlag | GlobalImagFlag, if one generator | |
| * causes the tensor to be real and the next one to be imaginary, this will | |
| * trivially give the correct result | |
| */ | |
| } | |
| } | |
| } // end namespace Eigen | |
| #endif // EIGEN_CXX11_TENSORSYMMETRY_DYNAMICSYMMETRY_H | |
| /* | |
| * kate: space-indent on; indent-width 2; mixedindent off; indent-mode cstyle; | |
| */ |