From c7c6784643011ee4e8686d1dbce846ce40eafc79 Mon Sep 17 00:00:00 2001 From: David Allemang Date: Fri, 16 Sep 2022 11:49:09 -0400 Subject: [PATCH] add bound to support for infinite groups and free relations special values tc::UNBOUND and tc::FREE used for this. --- tc/bench/benchmark.cpp | 24 ++++++++++++++++++------ tc/include/tc/core.hpp | 4 +++- tc/include/tc/cosets.hpp | 5 +++-- tc/include/tc/group.hpp | 34 ++++++++++++++++++++++++---------- tc/src/core.cpp | 25 ++++++++++++++++++++++--- 5 files changed, 70 insertions(+), 22 deletions(-) diff --git a/tc/bench/benchmark.cpp b/tc/bench/benchmark.cpp index e6a8878..eb47fca 100644 --- a/tc/bench/benchmark.cpp +++ b/tc/bench/benchmark.cpp @@ -10,19 +10,26 @@ #define NAMED(x) #x, x -void bench(std::string group_expr, tc::Group group, const std::vector &gens) { +void bench( + std::string group_expr, + const tc::Group &group, + const std::vector &gens, + const tc::Coset bound = tc::UNBOUNDED +) { std::clock_t s = std::clock(); - tc::Cosets cosets = tc::solve(group, gens); + tc::Cosets cosets = tc::solve(group, gens, bound); std::clock_t e = std::clock(); - double time = (double) (e - s) / CLOCKS_PER_SEC; + auto time = (double) (e - s) / CLOCKS_PER_SEC; tc::Coset order = cosets.size(); - size_t cos_s = (size_t) (order / time); + auto cos_s = (size_t) (order / time); + + bool complete = cosets.complete; std::string name = fmt::format("{}/{}", group_expr, gens); std::string row = fmt::format( - "{:>24},{:>10},{:>8.3f}s,{:>10L}", - name, order, time, cos_s + "{:>24},{:>10},{:>6},{:>8.3f}s,{:>10L}", + name, order, complete, time, cos_s ); fmt::print("{}\n", row); } @@ -40,6 +47,11 @@ int main(int argc, char *argv[]) { fmt::print("{:>24},{:>10},{:>9},{:>10}\n", "NAME", "ORDER", "TIME", "COS/S"); + auto g = tc::group::A(4); + g.set(tc::Rel{0, 3, 3}); + bench("~A(3)", g, {}, 4385964); + bench("~I(1)", sch(tc::FREE), {}, 4385964); + bench(NAMED(H(2)), {}); bench(NAMED(H(3)), {}); bench(NAMED(H(4)), {}); diff --git a/tc/include/tc/core.hpp b/tc/include/tc/core.hpp index 268cb3b..e65d4fe 100644 --- a/tc/include/tc/core.hpp +++ b/tc/include/tc/core.hpp @@ -10,5 +10,7 @@ #include "group.hpp" namespace tc { - Cosets solve(const Group &group, const std::vector &sub_gens); + constexpr Coset UNBOUNDED = (Coset) (-1); + + Cosets solve(const Group &group, const std::vector &sub_gens, const Coset &bound = UNBOUNDED); } diff --git a/tc/include/tc/cosets.hpp b/tc/include/tc/cosets.hpp index 4e9bfde..541447d 100644 --- a/tc/include/tc/cosets.hpp +++ b/tc/include/tc/cosets.hpp @@ -8,15 +8,16 @@ namespace tc { using Coset = uint32_t; using Gen = uint8_t; using Mult = uint16_t; + constexpr Mult FREE = Mult(-1); + constexpr Coset UNSET = Coset(-1); using Rel = std::tuple; struct Cosets { - const Coset UNSET = Coset(-1); - Gen ngens; std::vector data; Path path; + bool complete = false; Cosets(const Cosets &) = default; diff --git a/tc/include/tc/group.hpp b/tc/include/tc/group.hpp index 55b93a5..a850e41 100644 --- a/tc/include/tc/group.hpp +++ b/tc/include/tc/group.hpp @@ -8,6 +8,23 @@ namespace tc { struct Group; struct SubGroup; + /** + * @brief Manage the presentation of a Coxeter group and enforce constraints + * on the multiplicities of its relations. + *
    + *
  • + * m_ij = 1 iff i != j + *
  • + *
  • + * m_ij = m_ji + *
  • + *
  • + * If m_ij == inf (tc::FREE) then no relation is imposed. + *
  • + *
+ * @see + * Coxeter Group (Wikipedia) + */ struct Group { int ngens; tc::pair_map _mults; @@ -17,6 +34,10 @@ namespace tc { explicit Group(int ngens, const std::vector &rels = {}) : ngens(ngens), _mults(ngens, 2) { + for (int i = 0; i < ngens; ++i) { + set(Rel{i, i, 1}); + } + for (const auto &rel: rels) { set(rel); } @@ -24,6 +45,9 @@ namespace tc { void set(const Rel &r) { auto &[i, j, m] = r; + if (i == j && m != 1) { + throw std::runtime_error("Coxeter groups must satisfy m_ii=1."); + } _mults(i, j) = m; } @@ -31,16 +55,6 @@ namespace tc { return _mults(i, j); } - [[nodiscard]] std::vector rels() const { - std::vector res; - for (int i = 0; i < ngens - 1; ++i) { - for (int j = i + 1; j < ngens; ++j) { - res.emplace_back(i, j, get(i, j)); - } - } - return res; - } - [[nodiscard]] SubGroup subgroup(const std::vector &gens) const; }; diff --git a/tc/src/core.cpp b/tc/src/core.cpp index d2a2e27..64410a0 100644 --- a/tc/src/core.cpp +++ b/tc/src/core.cpp @@ -40,7 +40,7 @@ namespace tc { } }; - Cosets solve(const Group &group, const std::vector &sub_gens) { + Cosets solve(const Group &group, const std::vector &sub_gens, const Coset &bound) { auto ngens = group.ngens; // region Initialize Cosets Table @@ -48,6 +48,7 @@ namespace tc { cosets.add_row(); if (ngens == 0) { + cosets.complete = true; return cosets; } @@ -57,9 +58,22 @@ namespace tc { } // endregion - auto rels = group.rels(); - // region Initialize Relation Tables + std::vector> rels; + for (const auto &[i, j, m]: group._mults) { + // The algorithm only works for Coxeter groups; multiplicities m_ii=1 are assumed. Relation tables + // _may_ be added for them, but they are redundant and hurt performance so are skipped. + if (i == j) continue; + + // Coxeter groups admit infinite multiplicities, represented by contexpr tc::FREE. Relation tables + // for these should be skipped. + if (m == FREE) { + continue; + } + + rels.emplace_back(i, j, m); + } + Tables rel_tables(rels); std::vector> tables_for(ngens); int rel_idx = 0; @@ -97,6 +111,10 @@ namespace tc { while (idx < cosets.data.size() and cosets.isset(idx)) idx++; + if (cosets.size() >= bound) { + return cosets; + } + // if there are none, then return if (idx == cosets.data.size()) { // todo unrolled linked list interval @@ -191,6 +209,7 @@ namespace tc { } } + cosets.complete = true; return cosets; } }