use coxeter notation parser for benchmark and tests

This commit is contained in:
David Allemang
2022-10-09 21:05:50 -04:00
parent 91045f2542
commit 164d4f6d29
8 changed files with 94 additions and 60 deletions

View File

@@ -8,8 +8,6 @@
#include <tc/core.hpp> #include <tc/core.hpp>
#include <tc/groups.hpp> #include <tc/groups.hpp>
#define NAMED(x) #x, x
void bench( void bench(
const std::string &group_expr, const std::string &group_expr,
const std::string &symbol, const std::string &symbol,

View File

@@ -10,6 +10,11 @@ namespace tc {
struct Group; struct Group;
struct SubGroup; struct SubGroup;
struct Graph {
size_t rank{};
std::vector<tc::Rel> edges{};
};
/** /**
* @brief Manage the presentation of a Coxeter group and enforce constraints * @brief Manage the presentation of a Coxeter group and enforce constraints
* on the multiplicities of its relations. * on the multiplicities of its relations.
@@ -28,15 +33,15 @@ namespace tc {
* <a href="https://en.wikipedia.org/wiki/Coxeter_group#Definition">Coxeter Group (Wikipedia)</a> * <a href="https://en.wikipedia.org/wiki/Coxeter_group#Definition">Coxeter Group (Wikipedia)</a>
*/ */
struct Group { struct Group {
int ngens; int rank;
tc::pair_map<int> _mults; tc::pair_map<int> _orders;
Group(const Group &) = default; Group(const Group &) = default;
explicit Group(int ngens, const std::vector<Rel> &rels = {}) explicit Group(int rank, const std::vector<Rel> &rels = {})
: ngens(ngens), _mults(ngens, 2) { : rank(rank), _orders(rank, 2) {
for (int i = 0; i < ngens; ++i) { for (int i = 0; i < rank; ++i) {
set(Rel{i, i, 1}); set(Rel{i, i, 1});
} }
@@ -45,14 +50,21 @@ namespace tc {
} }
} }
explicit Group(const Graph &graph)
: rank(graph.rank), _orders(graph.rank, 2) {
for (const auto &[i, j, order]: graph.edges) {
set({i, j, order});
}
}
void set(const Rel &r) { void set(const Rel &r) {
auto &[i, j, m] = r; auto &[i, j, m] = r;
assert(i != j || m == 1); assert(i != j || m == 1);
_mults(i, j) = m; _orders(i, j) = m;
} }
[[nodiscard]] int get(int i, int j) const { [[nodiscard]] int get(int i, int j) const {
return _mults(i, j); return _orders(i, j);
} }
[[nodiscard]] SubGroup subgroup(const std::vector<int> &gens) const; [[nodiscard]] SubGroup subgroup(const std::vector<int> &gens) const;

View File

@@ -13,7 +13,7 @@ namespace tc {
Group coxeter(const std::string &symbol); Group coxeter(const std::string &symbol);
Group vcoxeter(const std::string &symbol, std::vector<unsigned int> &values); Group vcoxeter(const std::string &symbol, const std::vector<unsigned int> &values);
template<typename ...Args> template<typename ...Args>
Group coxeter(const std::string &symbol, const Args &... args) { Group coxeter(const std::string &symbol, const Args &... args) {

View File

@@ -40,27 +40,31 @@ namespace tc {
} }
}; };
Cosets solve(const Group &group, const std::vector<Gen> &sub_gens, const Coset &bound) { Cosets solve(
auto ngens = group.ngens; const Group &group,
const std::vector<Gen> &sub_gens,
const Coset &bound
) {
auto rank = group.rank;
// region Initialize Cosets Table // region Initialize Cosets Table
Cosets cosets(ngens); Cosets cosets(rank);
cosets.add_row(); cosets.add_row();
if (ngens == 0) { if (rank == 0) {
cosets.complete = true; cosets.complete = true;
return cosets; return cosets;
} }
for (Coset g: sub_gens) { for (Coset g: sub_gens) {
if (g < ngens) if (g < rank)
cosets.put(0, g, 0); cosets.put(0, g, 0);
} }
// endregion // endregion
// region Initialize Relation Tables // region Initialize Relation Tables
std::vector<std::tuple<Gen, Gen, Mult>> rels; std::vector<std::tuple<Gen, Gen, Mult>> rels;
for (const auto &[i, j, m]: group._mults) { for (const auto &[i, j, m]: group._orders) {
// The algorithm only works for Coxeter groups; multiplicities m_ii=1 are assumed. Relation tables // 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. // _may_ be added for them, but they are redundant and hurt performance so are skipped.
if (i == j) continue; if (i == j) continue;
@@ -75,7 +79,7 @@ namespace tc {
} }
Tables rel_tables(rels); Tables rel_tables(rels);
std::vector<std::vector<size_t>> tables_for(ngens); std::vector<std::vector<size_t>> tables_for(rank);
int rel_idx = 0; int rel_idx = 0;
for (const auto &[i, j, m]: rels) { for (const auto &[i, j, m]: rels) {
tables_for[i].push_back(rel_idx); tables_for[i].push_back(rel_idx);
@@ -146,8 +150,8 @@ namespace tc {
cosets.put(fact_idx, target); cosets.put(fact_idx, target);
coset = fact_idx / ngens; coset = fact_idx / rank;
gen = fact_idx % ngens; gen = fact_idx % rank;
// If the product stays within the coset todo // If the product stays within the coset todo
for (size_t table_idx: tables_for[gen]) { for (size_t table_idx: tables_for[gen]) {
@@ -170,7 +174,7 @@ namespace tc {
if (trow.gnr == m) { if (trow.gnr == m) {
// loop is closed, but idempotent, so the target links to itself via the other generator. // loop is closed, but idempotent, so the target links to itself via the other generator.
// todo might be able to move this logic up into the (target == coset) block and avoid those computations. // todo might be able to move this logic up into the (target == coset) block and avoid those computations.
facts.push(target * ngens + other_gen); facts.push(target * rank + other_gen);
} }
} else { } else {
if (trow.gnr == m - 1) { if (trow.gnr == m - 1) {
@@ -180,7 +184,7 @@ namespace tc {
// loop is closed. We know the last element in the loop must link with this one. // loop is closed. We know the last element in the loop must link with this one.
lst = lst_vals[trow.lst_idx]; lst = lst_vals[trow.lst_idx];
// delete trow.lst_ptr; // delete trow.lst_ptr;
facts.push(lst * ngens + other_gen); facts.push(lst * rank + other_gen);
} }
} }
} }

View File

@@ -12,7 +12,7 @@ namespace tc {
return res; return res;
} }
Group vcoxeter(const std::string &symbol, std::vector<unsigned int> &values) { Group vcoxeter(const std::string &symbol, const std::vector<unsigned int> &values) {
fmt::dynamic_format_arg_store<fmt::format_context> ds; fmt::dynamic_format_arg_store<fmt::format_context> ds;
for (const auto &value: values) { for (const auto &value: values) {

View File

@@ -221,15 +221,10 @@ std::vector<Op> compile(const std::string &source) {
return cg.ops; return cg.ops;
} }
struct Graph { tc::Graph eval(const std::vector<Op> &ops) {
size_t rank{};
std::vector<tc::Rel> edges{};
};
Graph eval(const std::vector<Op> &ops) {
std::vector<std::stack<size_t>> stacks(1); std::vector<std::stack<size_t>> stacks(1);
Graph g; tc::Graph g;
stacks.back().emplace(g.rank++); stacks.back().emplace(g.rank++);
for (const auto &op: ops) { for (const auto &op: ops) {
@@ -287,20 +282,7 @@ Graph eval(const std::vector<Op> &ops) {
namespace tc { namespace tc {
Group coxeter(const std::string &symbol) { Group coxeter(const std::string &symbol) {
auto ops = compile(symbol); auto ops = compile(symbol);
// fmt::print("#ops: {}\n", ops.size());
// for (const auto &op: ops) {
// fmt::print(" {}\n", op);
// }
auto diagram = eval(ops); auto diagram = eval(ops);
return Group(diagram);
Group res((int) diagram.rank);
for (const auto &[i, j, order]: diagram.edges) {
res.set({i, j, order});
}
return res;
} }
} }

View File

@@ -20,7 +20,7 @@
TEST(coxeter, simple) { TEST(coxeter, simple) {
auto g = tc::coxeter("5 3 3"); auto g = tc::coxeter("5 3 3");
ASSERT_EQ(g.ngens, 4); ASSERT_EQ(g.rank, 4);
EXPECT_EQ(g.get(0, 1), 5); EXPECT_EQ(g.get(0, 1), 5);
EXPECT_EQ(g.get(1, 2), 3); EXPECT_EQ(g.get(1, 2), 3);
@@ -37,7 +37,7 @@ TEST(coxeter, simple) {
TEST(coxeter, looping) { TEST(coxeter, looping) {
auto g = tc::coxeter("{5 3 4}"); auto g = tc::coxeter("{5 3 4}");
ASSERT_EQ(g.ngens, 3); ASSERT_EQ(g.rank, 3);
EXPECT_EQ(g.get(0, 1), 5); EXPECT_EQ(g.get(0, 1), 5);
EXPECT_EQ(g.get(1, 2), 3); EXPECT_EQ(g.get(1, 2), 3);

View File

@@ -46,12 +46,50 @@ testing::AssertionResult AssertSolveOrder(
return res; return res;
} }
using namespace tc::group;
#define EXPECT_SOLVE_ORDER(group, sub_gens, expected_order) EXPECT_PRED_FORMAT3(AssertSolveOrder, group, sub_gens, expected_order); #define EXPECT_SOLVE_ORDER(group, sub_gens, expected_order) EXPECT_PRED_FORMAT3(AssertSolveOrder, group, sub_gens, expected_order);
using v = std::vector<tc::Gen>; using v = std::vector<tc::Gen>;
tc::Group A(unsigned int n) {
return tc::vcoxeter("3 * {}", {n - 1});
}
tc::Group B(unsigned int n) {
return tc::vcoxeter("4 3 * {}", {n - 2});
}
tc::Group D(unsigned int n) {
return tc::vcoxeter("3 * [1 1 {}]", {n - 3});
}
tc::Group E(unsigned int n) {
return tc::vcoxeter("3 * [1 2 {}]", {n - 4});
}
tc::Group F4() {
return tc::coxeter("3 4 3");
}
tc::Group G2() {
return tc::coxeter("6");
}
tc::Group H(unsigned int n) {
return tc::vcoxeter("5 3 * {}", {n - 2});
}
tc::Group I2(unsigned int n) {
return tc::vcoxeter("{}", {n});
}
tc::Group T(unsigned int m, unsigned int n) {
return tc::vcoxeter("{} 2 {}", {m, n});
}
tc::Group T(unsigned int n) {
return T(n, n);
}
// See the group orders here https://en.wikipedia.org/wiki/Coxeter_group#Properties // See the group orders here https://en.wikipedia.org/wiki/Coxeter_group#Properties
TEST(solve, A) { TEST(solve, A) {
@@ -95,27 +133,27 @@ TEST(solve, D) {
EXPECT_SOLVE_ORDER(D(4), v({0, 1, 3}), 8); EXPECT_SOLVE_ORDER(D(4), v({0, 1, 3}), 8);
EXPECT_SOLVE_ORDER(D(5), v({}), 1920); EXPECT_SOLVE_ORDER(D(5), v({}), 1920);
EXPECT_SOLVE_ORDER(D(5), v({0, 1}), 320); EXPECT_SOLVE_ORDER(D(5), v({0, 1}), 320);
EXPECT_SOLVE_ORDER(D(5), v({0, 1, 3}), 160); EXPECT_SOLVE_ORDER(D(5), v({0, 1, 3}), 80);
EXPECT_SOLVE_ORDER(D(5), v({0, 1, 3, 4}), 40); EXPECT_SOLVE_ORDER(D(5), v({0, 1, 3, 4}), 16);
EXPECT_SOLVE_ORDER(D(6), v({}), 23040); EXPECT_SOLVE_ORDER(D(6), v({}), 23040);
EXPECT_SOLVE_ORDER(D(6), v({0, 1}), 3840); EXPECT_SOLVE_ORDER(D(6), v({0, 1}), 3840);
EXPECT_SOLVE_ORDER(D(6), v({0, 1, 3}), 1920); EXPECT_SOLVE_ORDER(D(6), v({0, 1, 3}), 960);
EXPECT_SOLVE_ORDER(D(6), v({0, 1, 3, 5}), 480); EXPECT_SOLVE_ORDER(D(6), v({0, 1, 3, 5}), 480);
} }
TEST(solve, E) { TEST(solve, E) {
EXPECT_SOLVE_ORDER(E(4), v({}), 120); EXPECT_SOLVE_ORDER(E(4), v({}), 120);
EXPECT_SOLVE_ORDER(E(4), v({2}), 60); EXPECT_SOLVE_ORDER(E(4), v({2}), 60);
EXPECT_SOLVE_ORDER(E(4), v({2, 1}), 20); EXPECT_SOLVE_ORDER(E(4), v({2, 1}), 30);
EXPECT_SOLVE_ORDER(E(4), v({2, 1, 3}), 5); EXPECT_SOLVE_ORDER(E(4), v({2, 1, 3}), 10);
EXPECT_SOLVE_ORDER(E(5), v({}), 1920); EXPECT_SOLVE_ORDER(E(5), v({}), 1920);
EXPECT_SOLVE_ORDER(E(5), v({2}), 960); EXPECT_SOLVE_ORDER(E(5), v({2}), 960);
EXPECT_SOLVE_ORDER(E(5), v({2, 1}), 320); EXPECT_SOLVE_ORDER(E(5), v({2, 1}), 480);
EXPECT_SOLVE_ORDER(E(5), v({2, 1, 3}), 80); EXPECT_SOLVE_ORDER(E(5), v({2, 1, 3}), 160);
EXPECT_SOLVE_ORDER(E(6), v({}), 51840); EXPECT_SOLVE_ORDER(E(6), v({}), 51840);
EXPECT_SOLVE_ORDER(E(6), v({2}), 25920); EXPECT_SOLVE_ORDER(E(6), v({2}), 25920);
EXPECT_SOLVE_ORDER(E(6), v({2, 1}), 8640); EXPECT_SOLVE_ORDER(E(6), v({2, 1}), 12960);
EXPECT_SOLVE_ORDER(E(6), v({2, 1, 3}), 2160); EXPECT_SOLVE_ORDER(E(6), v({2, 1, 3}), 4320);
} }
TEST(solve, F) { TEST(solve, F) {