mirror of
https://github.com/allemangD/toddcox-visualize.git
synced 2025-11-10 03:52:48 -05:00
Improve tc::Group, tc::Cosets API.
This is a combination of many commits: --------------------------------------------------------------------------- final. add public/private for core classes. wip - big cleanup cosets. index<>, cosets<>, and group<> all defined transparent <void> overrides that operate directly on indices, not names inheritance hierarchy means one can drop or change generator names still missing path support. wip - named cosets fix lang test wip - named generators Move Group docstring away from specialization. Remove tc::Gen; use Group<void> remove tc::Rel add thousands separators for benchmark limits add generators list to tc::group remove pair_map
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
add_library(tc
|
||||
include/tc/core.hpp
|
||||
include/tc/cosets.hpp
|
||||
include/tc/group.hpp
|
||||
include/tc/groups.hpp
|
||||
include/tc/pair_map.hpp
|
||||
include/tc/util.hpp
|
||||
src/core.cpp
|
||||
|
||||
src/cosets.cpp
|
||||
src/group.cpp
|
||||
src/groups.cpp
|
||||
src/lang.cpp
|
||||
src/solve.cpp
|
||||
)
|
||||
target_link_libraries(tc peglib::peglib fmt::fmt)
|
||||
target_include_directories(tc PUBLIC include)
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
add_executable(benchmark benchmark.cpp)
|
||||
target_link_libraries(benchmark PUBLIC tc fmt::fmt)
|
||||
|
||||
add_executable(named named.cpp)
|
||||
target_link_libraries(named PUBLIC tc fmt::fmt)
|
||||
|
||||
@@ -11,20 +11,20 @@
|
||||
void bench(
|
||||
const std::string &group_expr,
|
||||
const std::string &symbol,
|
||||
const std::vector<tc::Gen> &gens,
|
||||
const tc::Coset bound = tc::UNBOUNDED
|
||||
const std::vector<size_t> &gens,
|
||||
const size_t bound = SIZE_MAX
|
||||
) {
|
||||
tc::Group group = tc::coxeter(symbol);
|
||||
tc::Group<> group = tc::coxeter(symbol);
|
||||
|
||||
std::clock_t s = std::clock();
|
||||
tc::Cosets cosets = tc::solve(group, gens, bound);
|
||||
tc::Cosets<> cosets = group.solve(gens, bound);
|
||||
std::clock_t e = std::clock();
|
||||
|
||||
auto time = (double) (e - s) / CLOCKS_PER_SEC;
|
||||
tc::Coset order = cosets.size();
|
||||
size_t order = cosets.order();
|
||||
auto cos_s = (size_t) (order / time);
|
||||
|
||||
bool complete = cosets.complete;
|
||||
bool complete = cosets.complete();
|
||||
|
||||
std::string name = fmt::format("{}/{}", group_expr, gens);
|
||||
std::string row = fmt::format(
|
||||
@@ -80,59 +80,59 @@ int main(int argc, char *argv[]) {
|
||||
// Affine Groups
|
||||
|
||||
// ~A_n: {3 * `n+1`}
|
||||
bench("~A_5", "{3 * 6}", {}, 1000000);
|
||||
bench("~A_6", "{3 * 7}", {}, 1000000);
|
||||
bench("~A_7", "{3 * 8}", {}, 1000000);
|
||||
bench("~A_8", "{3 * 9}", {}, 1000000);
|
||||
bench("~A_5", "{3 * 6}", {}, 10'000'000);
|
||||
bench("~A_6", "{3 * 7}", {}, 10'000'000);
|
||||
bench("~A_7", "{3 * 8}", {}, 10'000'000);
|
||||
bench("~A_8", "{3 * 9}", {}, 10'000'000);
|
||||
// ~B_n: 4 3 * `n-3` 3 * [1 1]
|
||||
bench("~B_5", "4 3 * 2 3 * [1 1]", {}, 1000000);
|
||||
bench("~B_6", "4 3 * 3 3 * [1 1]", {}, 1000000);
|
||||
bench("~B_7", "4 3 * 4 3 * [1 1]", {}, 1000000);
|
||||
bench("~B_8", "4 3 * 5 3 * [1 1]", {}, 1000000);
|
||||
bench("~B_5", "4 3 * 2 3 * [1 1]", {}, 10'000'000);
|
||||
bench("~B_6", "4 3 * 3 3 * [1 1]", {}, 10'000'000);
|
||||
bench("~B_7", "4 3 * 4 3 * [1 1]", {}, 10'000'000);
|
||||
bench("~B_8", "4 3 * 5 3 * [1 1]", {}, 10'000'000);
|
||||
// ~B_n: 4 3 * `n-2` 4
|
||||
bench("~C_5", "4 3 * 3 4", {}, 1000000);
|
||||
bench("~C_6", "4 3 * 4 4", {}, 1000000);
|
||||
bench("~C_7", "4 3 * 5 4", {}, 1000000);
|
||||
bench("~C_8", "4 3 * 6 4", {}, 1000000);
|
||||
bench("~C_5", "4 3 * 3 4", {}, 10'000'000);
|
||||
bench("~C_6", "4 3 * 4 4", {}, 10'000'000);
|
||||
bench("~C_7", "4 3 * 5 4", {}, 10'000'000);
|
||||
bench("~C_8", "4 3 * 6 4", {}, 10'000'000);
|
||||
// ~D_n: 3 * [1 1] 3 * `n-4` 3 * [1 1]
|
||||
bench("~D_5", "3 * [1 1] 3 * 1 3 * [1 1]", {}, 1000000);
|
||||
bench("~D_6", "3 * [1 1] 3 * 2 3 * [1 1]", {}, 1000000);
|
||||
bench("~D_7", "3 * [1 1] 3 * 3 3 * [1 1]", {}, 1000000);
|
||||
bench("~D_8", "3 * [1 1] 3 * 4 3 * [1 1]", {}, 1000000);
|
||||
bench("~D_5", "3 * [1 1] 3 * 1 3 * [1 1]", {}, 10'000'000);
|
||||
bench("~D_6", "3 * [1 1] 3 * 2 3 * [1 1]", {}, 10'000'000);
|
||||
bench("~D_7", "3 * [1 1] 3 * 3 3 * [1 1]", {}, 10'000'000);
|
||||
bench("~D_8", "3 * [1 1] 3 * 4 3 * [1 1]", {}, 10'000'000);
|
||||
// grid: `p` `q` ; 2(p+q) = pq
|
||||
// triangle: `p` `q` `r` ; 1/p + 1/q + 1/r = 1
|
||||
|
||||
// Special Affine Groups
|
||||
bench("~I_1", "-", {}, 1000000);
|
||||
bench("~E_6", "3 * [2 2 2]", {}, 1000000);
|
||||
bench("~E_7", "3 * [1 3 3]", {}, 1000000);
|
||||
bench("~E_8", "3 * [1 2 5]", {}, 1000000);
|
||||
// bench("E_9", "3 * [1 2 5]", {}, 1000000); // ~E_8 == E_9
|
||||
bench("~F_4", "3 4 3 3", {}, 1000000);
|
||||
bench("~G_2", "6 3", {}, 1000000);
|
||||
bench("~I_1", "-", {}, 10'000'000);
|
||||
bench("~E_6", "3 * [2 2 2]", {}, 10'000'000);
|
||||
bench("~E_7", "3 * [1 3 3]", {}, 10'000'000);
|
||||
bench("~E_8", "3 * [1 2 5]", {}, 10'000'000);
|
||||
// bench("E_9", "3 * [1 2 5]", {}, 10'000'000); // ~E_8 == E_9
|
||||
bench("~F_4", "3 4 3 3", {}, 10'000'000);
|
||||
bench("~G_2", "6 3", {}, 10'000'000);
|
||||
|
||||
// Hyperbolic Groups
|
||||
// grid: `p` `q` ; 2(p+q) < pq
|
||||
// triangle: `p` `q` `r` ; 1/p + 1/q + 1/r < 1
|
||||
|
||||
// Special Hyperbolic Groups
|
||||
bench("-BH_3", "4 3 5", {}, 1000000);
|
||||
bench("-K_3", "5 3 5", {}, 1000000);
|
||||
bench("-J_3", "3 5 3", {}, 1000000);
|
||||
// bench("~H_3", "3 5 3", {}, 1000000); // -J_3 == ~H_3
|
||||
bench("-DH_3", "5 3 * [1 1]", {}, 1000000);
|
||||
bench("^AB_3", "{3 3 3 4}", {}, 1000000);
|
||||
bench("^AH_3", "{3 3 3 5}", {}, 1000000);
|
||||
bench("^BB_3", "{3 4 3 4}", {}, 1000000);
|
||||
bench("^BH_3", "{3 4 3 5}", {}, 1000000);
|
||||
bench("^HH_3", "{3 5 3 5}", {}, 1000000);
|
||||
bench("-H_4", "5 3 3 3", {}, 1000000);
|
||||
// bench("~H_4", "5 3 3 3", {}, 1000000); // -H_4 == ~H_4 == H_5
|
||||
// bench("H_5", "5 3 3 3", {}, 1000000);
|
||||
bench("-BH_4", "4 3 3 5", {}, 1000000);
|
||||
bench("-K_4", "5 3 3 5", {}, 1000000);
|
||||
bench("-DH_4", "5 3 3 * [1 1]", {}, 1000000);
|
||||
bench("^AF_4", "{3 3 3 3 4}", {}, 1000000);
|
||||
bench("-BH_3", "4 3 5", {}, 10'000'000);
|
||||
bench("-K_3", "5 3 5", {}, 10'000'000);
|
||||
bench("-J_3", "3 5 3", {}, 10'000'000);
|
||||
// bench("~H_3", "3 5 3", {}, 10'000'000); // -J_3 == ~H_3
|
||||
bench("-DH_3", "5 3 * [1 1]", {}, 10'000'000);
|
||||
bench("^AB_3", "{3 3 3 4}", {}, 10'000'000);
|
||||
bench("^AH_3", "{3 3 3 5}", {}, 10'000'000);
|
||||
bench("^BB_3", "{3 4 3 4}", {}, 10'000'000);
|
||||
bench("^BH_3", "{3 4 3 5}", {}, 10'000'000);
|
||||
bench("^HH_3", "{3 5 3 5}", {}, 10'000'000);
|
||||
bench("-H_4", "5 3 3 3", {}, 10'000'000);
|
||||
// bench("~H_4", "5 3 3 3", {}, 10'000'000); // -H_4 == ~H_4 == H_5
|
||||
// bench("H_5", "5 3 3 3", {}, 10'000'000);
|
||||
bench("-BH_4", "4 3 3 5", {}, 10'000'000);
|
||||
bench("-K_4", "5 3 3 5", {}, 10'000'000);
|
||||
bench("-DH_4", "5 3 3 * [1 1]", {}, 10'000'000);
|
||||
bench("^AF_4", "{3 3 3 3 4}", {}, 10'000'000);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
55
tc/bench/named.cpp
Normal file
55
tc/bench/named.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
#include <tc/core.hpp>
|
||||
|
||||
template<typename G>
|
||||
void show(G const &g) { // todo add formatter for groups, cosets.
|
||||
fmt::print(" | ");
|
||||
for (const auto &v: g.gens()) {
|
||||
fmt::print("{} ", v);
|
||||
}
|
||||
fmt::print("\n");
|
||||
|
||||
for (const auto &u: g.gens()) {
|
||||
fmt::print("{} | ", u);
|
||||
for (const auto &v: g.gens()) {
|
||||
fmt::print("{} ", g.get(u, v));
|
||||
}
|
||||
fmt::print("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
tc::Group<char> group(4, {'r', 'g', 'b', 'y'});
|
||||
|
||||
group.set('r', 'g', 5);
|
||||
group.set('g', 'b', 4);
|
||||
group.set('b', 'y', 3);
|
||||
|
||||
show(group);
|
||||
|
||||
auto sub = group.sub({'r', 'g', 'y'});
|
||||
show(sub);
|
||||
|
||||
auto res = sub.solve({});
|
||||
fmt::print("res order: {}\n", res.order());
|
||||
|
||||
auto cos = sub.solve({'r', 'y'});
|
||||
|
||||
fmt::print("order: {}\n", cos.order());
|
||||
|
||||
// tc::Group<u_int8_t> group(4, {0, 1, 2, 3});
|
||||
//
|
||||
// group.set(0, 1, 5);
|
||||
// group.set(1, 2, 4);
|
||||
// group.set(2, 3, 3);
|
||||
//
|
||||
// show(group);
|
||||
//
|
||||
// auto sub = group.sub({3, 2, 0, 1});
|
||||
// show(sub);
|
||||
}
|
||||
@@ -1,30 +1,220 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <vector> //todo clean up includes. lots of duplicate cstdint, cassert.
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
||||
#include "util.hpp"
|
||||
#include "cosets.hpp"
|
||||
#include "group.hpp"
|
||||
#include <limits>
|
||||
|
||||
namespace tc {
|
||||
Cosets solve(const Group &group, const std::vector<Gen> &sub_gens, const Coset &bound = UNBOUNDED);
|
||||
|
||||
// todo
|
||||
///**
|
||||
// * Solve the cosets generated by sg_gens within the subgroup generated by g_gens of the group context
|
||||
// */
|
||||
// tc::Cosets solve(
|
||||
// const tc::Group &context,
|
||||
// const std::vector<tc::Gen> &g_gens,
|
||||
// const std::vector<tc::Gen> &sg_gens
|
||||
// ) {
|
||||
// // todo this should also be handled with 'global' generators.
|
||||
// const auto proper_sg_gens = recontext_gens(context, g_gens, sg_gens);
|
||||
//
|
||||
// return tc::solve(context.subgroup(g_gens), proper_sg_gens);
|
||||
// }
|
||||
using Mult = u_int16_t;
|
||||
constexpr Mult FREE = 0;
|
||||
|
||||
/**
|
||||
* @brief Mapping from "global" generator names or objects to indexes used for value lookup.
|
||||
* @tparam Gen_
|
||||
*/
|
||||
template<typename Gen_=void>
|
||||
struct Index;
|
||||
|
||||
/**
|
||||
* @brief Complete representation of a quotient group. Describes the action of each generator on each coset.
|
||||
* @tparam Gen_
|
||||
*/
|
||||
template<typename Gen_=void>
|
||||
struct Cosets;
|
||||
|
||||
/**
|
||||
* @brief Manage the presentation of a Coxeter group and enforce constraints
|
||||
* on the multiplicities of its relations.
|
||||
* <ul>
|
||||
* <li>
|
||||
* <code>m_ij = 1</code> iff <code>i != j</code>
|
||||
* </li>
|
||||
* <li>
|
||||
* <code>m_ij = m_ji</code>
|
||||
* </li>
|
||||
* <li>
|
||||
* If <code>m_ij == inf</code> (<code>tc::FREE</code>) then no relation is imposed.
|
||||
* </li>
|
||||
* </ul>
|
||||
* @see
|
||||
* <a href="https://en.wikipedia.org/wiki/Coxeter_group#Definition">Coxeter Group (Wikipedia)</a>
|
||||
*/
|
||||
template<typename Gen_=void>
|
||||
struct Group;
|
||||
|
||||
template<>
|
||||
struct Index<> {
|
||||
size_t operator()(size_t const &idx) const {
|
||||
return idx;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Gen_>
|
||||
struct Index {
|
||||
using Gen = Gen_;
|
||||
|
||||
std::vector<Gen> _gens{};
|
||||
|
||||
explicit Index(std::vector<Gen> gens) : _gens(gens) {}
|
||||
|
||||
size_t operator()(Gen const &gen) const {
|
||||
auto it = std::find(_gens.begin(), _gens.end(), gen);
|
||||
assert(it != _gens.end());
|
||||
return it - _gens.begin();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Cosets<> {
|
||||
static constexpr size_t UNSET = std::numeric_limits<size_t>::max();
|
||||
|
||||
private:
|
||||
size_t _rank;
|
||||
size_t _order;
|
||||
bool _complete;
|
||||
std::vector<size_t> _data;
|
||||
|
||||
public:
|
||||
Cosets(Cosets const &) = default;
|
||||
|
||||
Cosets(Cosets &&) noexcept = default;
|
||||
|
||||
~Cosets() = default;
|
||||
|
||||
void set(size_t coset, size_t gen, size_t target);
|
||||
|
||||
[[nodiscard]] size_t get(size_t coset, size_t gen) const;
|
||||
|
||||
[[nodiscard]] bool isset(size_t coset, size_t gen) const;
|
||||
|
||||
[[nodiscard]] size_t rank() const;
|
||||
|
||||
[[nodiscard]] size_t order() const;
|
||||
|
||||
[[nodiscard]] bool complete() const;
|
||||
|
||||
[[nodiscard]] size_t size() const;
|
||||
|
||||
friend Group<>; // only constructible via Group<>::solve
|
||||
|
||||
private:
|
||||
explicit Cosets(size_t rank);
|
||||
|
||||
void add_row();
|
||||
|
||||
void set(size_t idx, size_t target);
|
||||
|
||||
[[nodiscard]] size_t get(size_t idx) const;
|
||||
|
||||
[[nodiscard]] bool isset(size_t idx) const;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Group<> {
|
||||
using Rel = std::tuple<size_t, size_t, Mult>;
|
||||
|
||||
private:
|
||||
size_t _rank;
|
||||
std::vector<size_t> _mults;
|
||||
|
||||
public:
|
||||
Group(Group const &) = default;
|
||||
|
||||
Group(Group &&) noexcept = default;
|
||||
|
||||
~Group() = default;
|
||||
|
||||
explicit Group(size_t rank);
|
||||
|
||||
void set(size_t, size_t, Mult);
|
||||
|
||||
[[nodiscard]] Mult get(size_t, size_t) const;
|
||||
|
||||
[[nodiscard]] size_t rank() const;
|
||||
|
||||
[[nodiscard]] Group sub(std::vector<size_t> const &idxs) const;
|
||||
|
||||
[[nodiscard]] Cosets<> solve(std::vector<size_t> const &idxs, size_t bound = SIZE_MAX) const;
|
||||
};
|
||||
|
||||
template<typename Gen_>
|
||||
struct Cosets : public Cosets<> {
|
||||
using Gen = Gen_;
|
||||
|
||||
private:
|
||||
Index<Gen> _index;
|
||||
|
||||
public:
|
||||
Cosets(Cosets<> g, std::vector<Gen> gens)
|
||||
: Cosets<>(g), _index(gens) {}
|
||||
|
||||
void set(size_t coset, Gen const &gen, size_t target) {
|
||||
Cosets<>::set(coset, _index(gen), target);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t get(size_t coset, Gen const &gen) const {
|
||||
return Cosets<>::get(coset, _index(gen));
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isset(size_t coset, Gen const &gen) const {
|
||||
return Cosets<>::isset(coset, _index(gen));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<Gen> gens() const {
|
||||
return _index._gens;
|
||||
}
|
||||
|
||||
private:
|
||||
Cosets(size_t rank, std::vector<Gen> gens)
|
||||
: Cosets<>(rank), _index(gens) {}
|
||||
};
|
||||
|
||||
template<typename Gen_>
|
||||
struct Group : public Group<> {
|
||||
using Gen = Gen_;
|
||||
using Rel = std::tuple<Gen, Gen, Mult>;
|
||||
|
||||
private:
|
||||
Index<Gen> _index;
|
||||
|
||||
public:
|
||||
Group(Group const &) = default;
|
||||
|
||||
Group(Group &&) noexcept = default;
|
||||
|
||||
Group(Group<> g, std::vector<Gen> gens)
|
||||
: Group<>(g), _index(gens) {}
|
||||
|
||||
Group(size_t rank, std::vector<Gen> gens)
|
||||
: Group<>(rank), _index(gens) {}
|
||||
|
||||
~Group() = default;
|
||||
|
||||
void set(Gen const &u, Gen const &v, Mult m) {
|
||||
Group<>::set(_index(u), _index(v), m);
|
||||
}
|
||||
|
||||
[[nodiscard]] Mult get(Gen const &u, Gen const &v) const {
|
||||
return Group<>::get(_index(u), _index(v));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<Gen> gens() const {
|
||||
return _index._gens;
|
||||
}
|
||||
|
||||
[[nodiscard]] Group sub(std::vector<Gen> const &gens) const {
|
||||
std::vector<size_t> idxs(gens.size());
|
||||
std::transform(gens.begin(), gens.end(), idxs.begin(), _index);
|
||||
return Group(Group<>::sub(idxs), gens);
|
||||
}
|
||||
|
||||
[[nodiscard]] Cosets<Gen> solve(std::vector<Gen> const &gens, size_t bound = SIZE_MAX) const {
|
||||
std::vector<size_t> idxs(gens.size());
|
||||
std::transform(gens.begin(), gens.end(), idxs.begin(), _index);
|
||||
|
||||
return Cosets<Gen>(Group<>::solve(idxs, bound), gens);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "util.hpp"
|
||||
|
||||
namespace tc {
|
||||
struct Cosets {
|
||||
Gen ngens;
|
||||
std::vector<int> data;
|
||||
Path path;
|
||||
bool complete = false;
|
||||
|
||||
Cosets(const Cosets &) = default;
|
||||
|
||||
explicit Cosets(Gen ngens)
|
||||
: ngens(ngens) {
|
||||
}
|
||||
|
||||
void add_row() {
|
||||
data.resize(data.size() + ngens, UNSET);
|
||||
path.add_row();
|
||||
}
|
||||
|
||||
void put(Coset coset, Gen gen, Coset target) {
|
||||
data[coset * ngens + gen] = target;
|
||||
data[target * ngens + gen] = coset;
|
||||
|
||||
if (path.get(target).from_idx == UNSET) {
|
||||
path.put(coset, gen, target);
|
||||
}
|
||||
}
|
||||
|
||||
void put(size_t idx, Coset target) {
|
||||
Coset coset = idx / ngens;
|
||||
Gen gen = idx % ngens;
|
||||
|
||||
data[idx] = target;
|
||||
data[target * ngens + gen] = coset;
|
||||
|
||||
if (path.get(target).from_idx == UNSET) {
|
||||
path.put(coset, gen, target);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] Coset get(Coset coset, Gen gen) const {
|
||||
return data[coset * ngens + gen];
|
||||
}
|
||||
|
||||
[[nodiscard]] Coset get(size_t idx) const {
|
||||
return data[idx];
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isset(Coset coset, Gen gen) const {
|
||||
return get(coset, gen) != UNSET;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isset(size_t idx) const {
|
||||
return get(idx) != UNSET;
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]] tc::Coset size() const {
|
||||
return path.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
|
||||
#include <tc/util.hpp>
|
||||
#include <tc/pair_map.hpp>
|
||||
|
||||
namespace tc {
|
||||
struct Group;
|
||||
struct SubGroup;
|
||||
|
||||
struct Graph {
|
||||
size_t rank{};
|
||||
std::vector<tc::Rel> edges{};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Manage the presentation of a Coxeter group and enforce constraints
|
||||
* on the multiplicities of its relations.
|
||||
* <ul>
|
||||
* <li>
|
||||
* <code>m_ij = 1</code> iff <code>i != j</code>
|
||||
* </li>
|
||||
* <li>
|
||||
* <code>m_ij = m_ji</code>
|
||||
* </li>
|
||||
* <li>
|
||||
* If <code>m_ij == inf</code> (<code>tc::FREE</code>) then no relation is imposed.
|
||||
* </li>
|
||||
* </ul>
|
||||
* @see
|
||||
* <a href="https://en.wikipedia.org/wiki/Coxeter_group#Definition">Coxeter Group (Wikipedia)</a>
|
||||
*/
|
||||
struct Group {
|
||||
int rank;
|
||||
tc::pair_map<int> _orders;
|
||||
|
||||
Group(const Group &) = default;
|
||||
|
||||
explicit Group(int rank, const std::vector<Rel> &rels = {})
|
||||
: rank(rank), _orders(rank, 2) {
|
||||
|
||||
for (int i = 0; i < rank; ++i) {
|
||||
set(Rel{i, i, 1});
|
||||
}
|
||||
|
||||
for (const auto &rel: rels) {
|
||||
set(rel);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
auto &[i, j, m] = r;
|
||||
assert(i != j || m == 1);
|
||||
_orders(i, j) = m;
|
||||
}
|
||||
|
||||
[[nodiscard]] int get(int i, int j) const {
|
||||
return _orders(i, j);
|
||||
}
|
||||
|
||||
[[nodiscard]] SubGroup subgroup(const std::vector<tc::Gen> &gens) const;
|
||||
};
|
||||
|
||||
struct SubGroup : public Group {
|
||||
std::vector<tc::Gen> gen_map;
|
||||
const Group &parent;
|
||||
|
||||
SubGroup(const Group &parent, std::vector<tc::Gen> gen_map)
|
||||
: Group(gen_map.size()), parent(parent), gen_map() {
|
||||
|
||||
std::sort(gen_map.begin(), gen_map.end());
|
||||
this->gen_map = gen_map;
|
||||
|
||||
for (size_t i = 0; i < gen_map.size(); ++i) {
|
||||
for (size_t j = 0; j < gen_map.size(); ++j) {
|
||||
int mult = parent.get(gen_map[i], gen_map[j]);
|
||||
set(Rel(i, j, mult));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,22 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <tc/group.hpp>
|
||||
#include <tc/core.hpp>
|
||||
|
||||
namespace tc {
|
||||
/**
|
||||
* Construct a group from a (simplified) Schlafli Symbol of the form [a, b, ..., c]
|
||||
* @param mults: The sequence of multiplicites between adjacent generators.
|
||||
*/
|
||||
Group schlafli(const std::vector<unsigned int> &mults);
|
||||
Group<> schlafli(const std::vector<unsigned int> &mults);
|
||||
|
||||
Group coxeter(const std::string &symbol);
|
||||
Group<> coxeter(const std::string &symbol);
|
||||
|
||||
Group vcoxeter(const std::string &symbol, const std::vector<unsigned int> &values);
|
||||
Group<> vcoxeter(const std::string &symbol, const std::vector<unsigned int> &values);
|
||||
|
||||
template<typename ...Args>
|
||||
Group coxeter(const std::string &symbol, const Args &... args) {
|
||||
Group<> coxeter(const std::string &symbol, const Args &... args) {
|
||||
std::vector<unsigned int> values = {{args...}};
|
||||
return vcoxeter(symbol, values);
|
||||
}
|
||||
|
||||
@@ -1,453 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace tc {
|
||||
template<typename T>
|
||||
struct pair_map {
|
||||
struct iterator;
|
||||
struct const_iterator;
|
||||
struct view;
|
||||
struct const_view;
|
||||
|
||||
private:
|
||||
size_t _size;
|
||||
std::vector<T> _data{};
|
||||
|
||||
static size_t idx(size_t i, size_t j);
|
||||
|
||||
public:
|
||||
explicit pair_map(size_t size);
|
||||
|
||||
explicit pair_map(size_t size, const T &value);
|
||||
|
||||
[[nodiscard]] size_t size() const;
|
||||
|
||||
T &operator()(size_t i, size_t j);
|
||||
|
||||
T operator()(size_t i, size_t j) const;
|
||||
|
||||
view of(size_t f);
|
||||
|
||||
const_view of(size_t f) const;
|
||||
|
||||
const_view cof(size_t f);
|
||||
|
||||
iterator begin();
|
||||
|
||||
iterator end();
|
||||
|
||||
const_iterator begin() const;
|
||||
|
||||
const_iterator end() const;
|
||||
|
||||
const_iterator cbegin();
|
||||
|
||||
const_iterator cend();
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct pair_map<T>::iterator {
|
||||
using reference = std::tuple<size_t, size_t, T &>;
|
||||
|
||||
private:
|
||||
pair_map<T> &_map;
|
||||
size_t _i, _j;
|
||||
|
||||
public:
|
||||
iterator(pair_map<T> &map, size_t i, size_t j);
|
||||
|
||||
iterator operator++();
|
||||
|
||||
iterator operator++(int) &;
|
||||
|
||||
reference operator*();
|
||||
|
||||
bool operator!=(const iterator &other);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct pair_map<T>::const_iterator {
|
||||
using value_type = std::tuple<size_t, size_t, T>;
|
||||
|
||||
private:
|
||||
const pair_map<T> &_map;
|
||||
size_t _i, _j;
|
||||
|
||||
public:
|
||||
const_iterator(const pair_map<T> &map, size_t i, size_t j);
|
||||
|
||||
const_iterator operator++();
|
||||
|
||||
const_iterator operator++(int) &;
|
||||
|
||||
value_type operator*();
|
||||
|
||||
bool operator!=(const const_iterator &other);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct pair_map<T>::view {
|
||||
struct iterator;
|
||||
struct const_iterator;
|
||||
|
||||
private:
|
||||
pair_map<T> &_map;
|
||||
size_t _f;
|
||||
|
||||
public:
|
||||
view(pair_map<T> &map, size_t f);
|
||||
|
||||
iterator begin();
|
||||
|
||||
iterator end();
|
||||
|
||||
const_iterator begin() const;
|
||||
|
||||
const_iterator end() const;
|
||||
|
||||
const_iterator cbegin();
|
||||
|
||||
const_iterator cend();
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct pair_map<T>::view::iterator {
|
||||
using reference = std::tuple<size_t, size_t, T &>;
|
||||
|
||||
private:
|
||||
pair_map<T> &_map;
|
||||
size_t _f, _v;
|
||||
|
||||
public:
|
||||
iterator(pair_map<T> &map, size_t f, size_t v);
|
||||
|
||||
iterator operator++();
|
||||
|
||||
iterator operator++(int);
|
||||
|
||||
reference operator*();
|
||||
|
||||
bool operator!=(const iterator &other);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct pair_map<T>::view::const_iterator {
|
||||
using value_type = std::tuple<size_t, size_t, T>;
|
||||
|
||||
private:
|
||||
const pair_map<T> &_map;
|
||||
size_t _f, _v;
|
||||
|
||||
public:
|
||||
const_iterator(const pair_map<T> &map, size_t f, size_t v);
|
||||
|
||||
const_iterator operator++();
|
||||
|
||||
const_iterator operator++(int);
|
||||
|
||||
value_type operator*();
|
||||
|
||||
bool operator!=(const const_iterator &other);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct pair_map<T>::const_view {
|
||||
using const_iterator = typename pair_map<T>::view::const_iterator;
|
||||
|
||||
private:
|
||||
const pair_map<T> &_map;
|
||||
size_t _f;
|
||||
|
||||
public:
|
||||
const_view(const pair_map<T> &map, size_t f);
|
||||
|
||||
const_iterator begin() const;
|
||||
|
||||
const_iterator end() const;
|
||||
|
||||
const_iterator cbegin();
|
||||
|
||||
const_iterator cend();
|
||||
};
|
||||
|
||||
// region pair_map
|
||||
|
||||
template<typename T>
|
||||
size_t pair_map<T>::idx(size_t i, size_t j) {
|
||||
if (i > j) std::swap(i, j);
|
||||
return j * (j + 1) / 2 + i;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
pair_map<T>::pair_map(size_t size)
|
||||
: _size(size), _data(size * (size + 1) / 2) {}
|
||||
|
||||
template<typename T>
|
||||
pair_map<T>::pair_map(size_t size, const T &value)
|
||||
: _size(size), _data(size * (size + 1) / 2, value) {}
|
||||
|
||||
template<typename T>
|
||||
size_t pair_map<T>::size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T &pair_map<T>::operator()(size_t i, size_t j) {
|
||||
return _data[idx(i, j)];
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T pair_map<T>::operator()(size_t i, size_t j) const {
|
||||
return _data[idx(i, j)];
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::view pair_map<T>::of(size_t f) {
|
||||
return view(*this, f);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::const_view pair_map<T>::of(size_t f) const {
|
||||
return const_view(*this, f);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::const_view pair_map<T>::cof(size_t f) {
|
||||
return const_view(*this, f);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::iterator pair_map<T>::begin() {
|
||||
return iterator(*this, 0, 0);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::iterator pair_map<T>::end() {
|
||||
return iterator(*this, 0, _size);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::const_iterator pair_map<T>::begin() const {
|
||||
return const_iterator(*this, 0, 0);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::const_iterator pair_map<T>::end() const {
|
||||
return const_iterator(*this, 0, _size);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::const_iterator pair_map<T>::cbegin() {
|
||||
return const_iterator(*this, 0, 0);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::const_iterator pair_map<T>::cend() {
|
||||
return const_iterator(*this, 0, _size);
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region pair_map::iterator
|
||||
|
||||
template<typename T>
|
||||
pair_map<T>::iterator::iterator(pair_map<T> &map, size_t i, size_t j)
|
||||
:_map(map), _i(i), _j(j) {}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::iterator pair_map<T>::iterator::operator++() {
|
||||
if (++_i > _j) {
|
||||
_i = 0;
|
||||
++_j;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::iterator pair_map<T>::iterator::operator++(int) &{
|
||||
iterator it = *this;
|
||||
++this;
|
||||
return it;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::iterator::reference pair_map<T>::iterator::operator*() {
|
||||
return std::tie(_i, _j, _map(_i, _j));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool pair_map<T>::iterator::operator!=(const pair_map::iterator &other) {
|
||||
return &_map != &other._map || _i != other._i || _j != other._j;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region pair_map::const_iterator
|
||||
|
||||
template<typename T>
|
||||
pair_map<T>::const_iterator::const_iterator(const pair_map<T> &map, size_t i, size_t j)
|
||||
:_map(map), _i(i), _j(j) {}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::const_iterator pair_map<T>::const_iterator::operator++() {
|
||||
if (++_i > _j) {
|
||||
_i = 0;
|
||||
++_j;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::const_iterator pair_map<T>::const_iterator::operator++(int) &{
|
||||
const_iterator it = *this;
|
||||
++this;
|
||||
return it;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::const_iterator::value_type pair_map<T>::const_iterator::operator*() {
|
||||
return std::tuple(_i, _j, _map(_i, _j));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool pair_map<T>::const_iterator::operator!=(const pair_map::const_iterator &other) {
|
||||
return &_map != &other._map || _i != other._i || _j != other._j;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region pair_map::view
|
||||
|
||||
template<typename T>
|
||||
pair_map<T>::view::view(pair_map<T> &map, size_t f)
|
||||
: _map(map), _f(f) {}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::view::iterator pair_map<T>::view::begin() {
|
||||
return iterator(_map, _f, 0);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::view::iterator pair_map<T>::view::end() {
|
||||
return iterator(_map, _f, _map._size);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::view::const_iterator pair_map<T>::view::begin() const {
|
||||
return const_iterator(_map, _f, 0);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::view::const_iterator pair_map<T>::view::end() const {
|
||||
return const_iterator(_map, _f, _map._size);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::view::const_iterator pair_map<T>::view::cbegin() {
|
||||
return const_iterator(_map, _f, 0);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::view::const_iterator pair_map<T>::view::cend() {
|
||||
return const_iterator(_map, _f, _map._size);
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region pair_map::view::iterator
|
||||
|
||||
template<typename T>
|
||||
pair_map<T>::view::iterator::iterator(pair_map<T> &map, size_t f, size_t v)
|
||||
: _map(map), _f(f), _v(v) {}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::view::iterator pair_map<T>::view::iterator::operator++() {
|
||||
++_v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::view::iterator pair_map<T>::view::iterator::operator++(int) {
|
||||
iterator it = *this;
|
||||
++this;
|
||||
return it;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::view::iterator::reference pair_map<T>::view::iterator::operator*() {
|
||||
auto [i, j] = std::minmax(_f, _v);
|
||||
return std::tie(i, j, _map(i, j));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool pair_map<T>::view::iterator::operator!=(const pair_map::view::iterator &other) {
|
||||
return &_map != &other._map || _f != other._f || _v != other._v;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region pair_map::view::const_iterator
|
||||
|
||||
template<typename T>
|
||||
pair_map<T>::view::const_iterator::const_iterator(const pair_map<T> &map, size_t f, size_t v)
|
||||
: _map(map), _f(f), _v(v) {}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::view::const_iterator pair_map<T>::view::const_iterator::operator++() {
|
||||
++_v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::view::const_iterator pair_map<T>::view::const_iterator::operator++(int) {
|
||||
const_iterator it = *this;
|
||||
++this;
|
||||
return it;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::view::const_iterator::value_type pair_map<T>::view::const_iterator::operator*() {
|
||||
auto [i, j] = std::minmax(_f, _v);
|
||||
return std::tuple(i, j, _map(i, j));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool pair_map<T>::view::const_iterator::operator!=(const pair_map::view::const_iterator &other) {
|
||||
return &_map != &other._map || _f != other._f || _v != other._v;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region pair_map::const_view
|
||||
|
||||
template<typename T>
|
||||
pair_map<T>::const_view::const_view(const pair_map<T> &map, size_t f)
|
||||
: _map(map), _f(f) {}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::const_view::const_iterator pair_map<T>::const_view::begin() const {
|
||||
return const_iterator(_map, _f, 0);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::const_view::const_iterator pair_map<T>::const_view::end() const {
|
||||
return const_iterator(_map, _f, _map._size);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::const_view::const_iterator pair_map<T>::const_view::cbegin() {
|
||||
return const_iterator(_map, _f, 0);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename pair_map<T>::const_view::const_iterator pair_map<T>::const_view::cend() {
|
||||
return const_iterator(_map, _f, _map._size);
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
@@ -4,18 +4,11 @@
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
|
||||
namespace tc {
|
||||
using Gen = uint8_t;
|
||||
using Mult = uint16_t;
|
||||
|
||||
constexpr Mult FREE = Mult(-1);
|
||||
|
||||
using Coset = uint32_t;
|
||||
constexpr Coset UNSET = Coset(-1);
|
||||
constexpr Coset UNBOUNDED = (Coset) (-1);
|
||||
|
||||
using Rel = std::tuple<Gen, Gen, Mult>;
|
||||
// using Mult = uint16_t;
|
||||
// constexpr Mult FREE = 0;
|
||||
|
||||
struct Action {
|
||||
int from_idx = -1;
|
||||
|
||||
55
tc/src/cosets.cpp
Normal file
55
tc/src/cosets.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include <tc/core.hpp>
|
||||
|
||||
namespace tc {
|
||||
Cosets<>::Cosets(size_t rank)
|
||||
: _rank(rank), _order(0), _complete(false), _data() {}
|
||||
|
||||
void Cosets<>::set(size_t coset, size_t gen, size_t target) {
|
||||
set(coset * rank() + gen, target);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t Cosets<>::get(size_t coset, size_t gen) const {
|
||||
return get(coset * rank() + gen);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Cosets<>::isset(size_t coset, size_t gen) const {
|
||||
return isset(coset * rank() + gen);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t Cosets<>::rank() const {
|
||||
return _rank;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t Cosets<>::order() const {
|
||||
return _order;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Cosets<>::complete() const {
|
||||
return _complete;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t Cosets<>::size() const {
|
||||
return _data.size();
|
||||
}
|
||||
|
||||
void Cosets<>::add_row() {
|
||||
_data.resize(_data.size() + rank(), UNSET);
|
||||
_order++;
|
||||
}
|
||||
|
||||
void Cosets<>::set(size_t idx, size_t target) {
|
||||
size_t coset = idx / rank();
|
||||
size_t gen = idx % rank();
|
||||
_data[idx] = target;
|
||||
_data[target * rank() + gen] = coset;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t Cosets<>::get(size_t idx) const {
|
||||
return _data[idx];
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Cosets<>::isset(size_t idx) const {
|
||||
return get(idx) != UNSET;
|
||||
}
|
||||
|
||||
}
|
||||
42
tc/src/group.cpp
Normal file
42
tc/src/group.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include <tc/core.hpp>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace tc {
|
||||
Group<>::Group(size_t rank) : _rank(rank), _mults(_rank * _rank, 2) {
|
||||
for (int idx = 0; idx < rank; ++idx) {
|
||||
set(idx, idx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Group<>::set(size_t u, size_t v, Mult m) {
|
||||
assert(u < rank());
|
||||
assert(v < rank());
|
||||
|
||||
_mults[u * rank() + v] = m;
|
||||
_mults[v * rank() + u] = m;
|
||||
}
|
||||
|
||||
[[nodiscard]] Mult Group<>::get(size_t u, size_t v) const {
|
||||
assert(u < rank());
|
||||
assert(v < rank());
|
||||
|
||||
return _mults[u * rank() + v];
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t Group<>::rank() const {
|
||||
return _rank;
|
||||
}
|
||||
|
||||
[[nodiscard]] Group<> Group<>::sub(std::vector<size_t> const &idxs) const {
|
||||
Group<> res(idxs.size());
|
||||
|
||||
for (int i = 0; i < idxs.size(); ++i) {
|
||||
for (int j = i; j < idxs.size(); ++j) {
|
||||
res.set(i, j, get(idxs[i], idxs[j]));
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -2,17 +2,18 @@
|
||||
|
||||
#include <fmt/args.h>
|
||||
#include <fmt/core.h>
|
||||
#include <numeric>
|
||||
|
||||
namespace tc {
|
||||
Group schlafli(const std::vector<unsigned int> &mults) {
|
||||
Group res(mults.size() + 1);
|
||||
Group<> schlafli(const std::vector<unsigned int> &mults) {
|
||||
Group<> res(mults.size() + 1);
|
||||
for (size_t i = 0; i < mults.size(); ++i) {
|
||||
res.set(Rel{i, i + 1, mults[i]});
|
||||
res.set(i, i + 1, mults[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Group vcoxeter(const std::string &symbol, const 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;
|
||||
|
||||
for (const auto &value: values) {
|
||||
|
||||
@@ -4,13 +4,19 @@
|
||||
#include <cassert>
|
||||
|
||||
#include <tc/util.hpp>
|
||||
#include <tc/group.hpp>
|
||||
#include <tc/core.hpp>
|
||||
#include <tc/groups.hpp>
|
||||
|
||||
#include <peglib.h>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/ranges.h>
|
||||
#include <numeric>
|
||||
|
||||
struct Graph {
|
||||
size_t rank{};
|
||||
std::vector<tc::Group<>::Rel> edges{};
|
||||
};
|
||||
|
||||
struct Op {
|
||||
enum Code {
|
||||
@@ -191,6 +197,7 @@ peg::parser build_parser() {
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
peg::parser build_ast_parser() {
|
||||
peg::parser parser;
|
||||
parser.set_logger([](size_t line, size_t col, const std::string &msg, const std::string &rule) {
|
||||
@@ -203,6 +210,7 @@ peg::parser build_ast_parser() {
|
||||
|
||||
return parser;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
std::vector<Op> compile(const std::string &source) {
|
||||
@@ -221,10 +229,10 @@ std::vector<Op> compile(const std::string &source) {
|
||||
return cg.ops;
|
||||
}
|
||||
|
||||
tc::Graph eval(const std::vector<Op> &ops) {
|
||||
Graph eval(const std::vector<Op> &ops) {
|
||||
std::vector<std::stack<size_t>> stacks(1);
|
||||
|
||||
tc::Graph g;
|
||||
Graph g;
|
||||
stacks.back().emplace(g.rank++);
|
||||
|
||||
for (const auto &op: ops) {
|
||||
@@ -280,9 +288,13 @@ tc::Graph eval(const std::vector<Op> &ops) {
|
||||
}
|
||||
|
||||
namespace tc {
|
||||
Group coxeter(const std::string &symbol) {
|
||||
Group<> coxeter(const std::string &symbol) {
|
||||
auto ops = compile(symbol);
|
||||
auto diagram = eval(ops);
|
||||
return Group(diagram);
|
||||
Group<> res(diagram.rank);
|
||||
for (const auto &[i, j, m]: diagram.edges) {
|
||||
res.set(i, j, m);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
#include <tc/core.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace tc {
|
||||
SubGroup Group::subgroup(const std::vector<tc::Gen> &gens) const {
|
||||
return {*this, gens};
|
||||
}
|
||||
#include <tc/core.hpp>
|
||||
|
||||
namespace tc {
|
||||
/**
|
||||
* Each coset is associated a row in each table.
|
||||
* Rows document the "loops" formed by
|
||||
@@ -24,10 +20,10 @@ namespace tc {
|
||||
};
|
||||
|
||||
struct Tables {
|
||||
std::vector<Rel> rels;
|
||||
std::vector<Group<>::Rel> rels;
|
||||
std::vector<std::vector<Row>> rows;
|
||||
|
||||
explicit Tables(std::vector<Rel> rels)
|
||||
explicit Tables(std::vector<Group<>::Rel> rels)
|
||||
: rels(std::move(rels)), rows() {
|
||||
}
|
||||
|
||||
@@ -40,46 +36,44 @@ namespace tc {
|
||||
}
|
||||
};
|
||||
|
||||
Cosets solve(
|
||||
const Group &group,
|
||||
const std::vector<Gen> &sub_gens,
|
||||
const Coset &bound
|
||||
) {
|
||||
auto rank = group.rank;
|
||||
|
||||
[[nodiscard]] Cosets<> Group<>::solve(std::vector<size_t> const &idxs, size_t bound) const {
|
||||
// region Initialize Cosets Table
|
||||
Cosets cosets(rank);
|
||||
Cosets<> cosets(rank());
|
||||
cosets.add_row();
|
||||
|
||||
if (rank == 0) {
|
||||
cosets.complete = true;
|
||||
if (rank() == 0) {
|
||||
cosets._complete = true;
|
||||
return cosets;
|
||||
}
|
||||
|
||||
for (Coset g: sub_gens) {
|
||||
if (g < rank)
|
||||
cosets.put(0, g, 0);
|
||||
for (size_t g: idxs) {
|
||||
if (g < rank())
|
||||
cosets.set(0, g, 0);
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Initialize Relation Tables
|
||||
std::vector<std::tuple<Gen, Gen, Mult>> rels;
|
||||
for (const auto &[i, j, m]: group._orders) {
|
||||
// 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;
|
||||
std::vector<Group<>::Rel> rels;
|
||||
for (int i = 0; i < rank(); ++i) {
|
||||
for (int j = i + 1; j < rank(); ++j) {
|
||||
// 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;
|
||||
// Coxeter groups admit infinite multiplicities, represented by contexpr tc::FREE. Relation tables
|
||||
// for these should be skipped.
|
||||
auto m = get(i, j);
|
||||
|
||||
if (m == FREE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rels.emplace_back(i, j, m);
|
||||
}
|
||||
|
||||
rels.emplace_back(i, j, m);
|
||||
}
|
||||
|
||||
Tables rel_tables(rels);
|
||||
std::vector<std::vector<size_t>> tables_for(rank);
|
||||
std::vector<std::vector<size_t>> tables_for(rank());
|
||||
int rel_idx = 0;
|
||||
for (const auto &[i, j, m]: rels) {
|
||||
tables_for[i].push_back(rel_idx);
|
||||
@@ -87,7 +81,7 @@ namespace tc {
|
||||
rel_idx++;
|
||||
}
|
||||
|
||||
std::vector<Coset> lst_vals;
|
||||
std::vector<size_t> lst_vals;
|
||||
rel_tables.add_row();
|
||||
for (int table_idx = 0; table_idx < rel_tables.size(); ++table_idx) {
|
||||
const auto &[i, j, m] = rel_tables.rels[table_idx];
|
||||
@@ -108,26 +102,26 @@ namespace tc {
|
||||
|
||||
size_t idx = 0;
|
||||
size_t fact_idx;
|
||||
Coset coset, gen, target, lst;
|
||||
size_t coset, gen, target, lst;
|
||||
|
||||
while (true) {
|
||||
// find next unknown product
|
||||
while (idx < cosets.data.size() and cosets.isset(idx))
|
||||
while (idx < cosets.size() and cosets.isset(idx))
|
||||
idx++;
|
||||
|
||||
if (cosets.size() >= bound) {
|
||||
if (cosets.order() >= bound) {
|
||||
return cosets;
|
||||
}
|
||||
|
||||
// if there are none, then return
|
||||
if (idx == cosets.data.size()) {
|
||||
if (idx == cosets.size()) {
|
||||
// todo unrolled linked list interval
|
||||
// rel_tables.del_rows_to(idx / ngens);
|
||||
break;
|
||||
}
|
||||
|
||||
// the unknown product must be a new coset, so add it
|
||||
target = cosets.size();
|
||||
target = cosets.order();
|
||||
cosets.add_row();
|
||||
rel_tables.add_row();
|
||||
|
||||
@@ -136,8 +130,6 @@ namespace tc {
|
||||
facts.push(idx); // new product should be recorded and propagated
|
||||
|
||||
// todo unrolled linked list interval
|
||||
// coset = idx / ngens;
|
||||
// gen = idx % ngens;
|
||||
// rel_tables.del_rows_to(coset);
|
||||
|
||||
// find all products which also lead to target
|
||||
@@ -148,10 +140,10 @@ namespace tc {
|
||||
// skip if this product was already learned
|
||||
if (cosets.get(fact_idx) != -1) continue;
|
||||
|
||||
cosets.put(fact_idx, target);
|
||||
cosets.set(fact_idx, target);
|
||||
|
||||
coset = fact_idx / rank;
|
||||
gen = fact_idx % rank;
|
||||
coset = fact_idx / rank();
|
||||
gen = fact_idx % rank();
|
||||
|
||||
// If the product stays within the coset todo
|
||||
for (size_t table_idx: tables_for[gen]) {
|
||||
@@ -159,7 +151,7 @@ namespace tc {
|
||||
auto &trow = rel_tables.rows[target][table_idx];
|
||||
auto &crow = rel_tables.rows[coset][table_idx];
|
||||
|
||||
Coset other_gen = (i == gen) ? j : i;
|
||||
size_t other_gen = (i == gen) ? j : i;
|
||||
|
||||
// Test if loop is closed
|
||||
if (trow.free) {
|
||||
@@ -174,7 +166,7 @@ namespace tc {
|
||||
if (trow.gnr == m) {
|
||||
// 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.
|
||||
facts.push(target * rank + other_gen);
|
||||
facts.push(target * rank() + other_gen);
|
||||
}
|
||||
} else {
|
||||
if (trow.gnr == m - 1) {
|
||||
@@ -184,7 +176,7 @@ namespace tc {
|
||||
// loop is closed. We know the last element in the loop must link with this one.
|
||||
lst = lst_vals[trow.lst_idx];
|
||||
// delete trow.lst_ptr;
|
||||
facts.push(lst * rank + other_gen);
|
||||
facts.push(lst * rank() + other_gen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -213,7 +205,7 @@ namespace tc {
|
||||
}
|
||||
}
|
||||
|
||||
cosets.complete = true;
|
||||
cosets._complete = true;
|
||||
return cosets;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
include(GoogleTest)
|
||||
|
||||
add_executable(test_pair_map test_pair_map.cpp)
|
||||
target_link_libraries(test_pair_map tc GTest::gtest_main)
|
||||
|
||||
add_executable(test_solve test_solve.cpp)
|
||||
target_link_libraries(test_solve tc GTest::gtest_main)
|
||||
|
||||
@@ -17,6 +14,5 @@ target_compile_definitions(
|
||||
MIN_COS_PER_SEC=$<IF:$<CONFIG:Debug>,${MIN_DEBUG_CPS},${MIN_RELEASE_CPS}>
|
||||
)
|
||||
|
||||
gtest_discover_tests(test_pair_map)
|
||||
gtest_discover_tests(test_solve)
|
||||
gtest_discover_tests(test_lang)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <tc/group.hpp>
|
||||
#include <tc/core.hpp>
|
||||
#include <tc/groups.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
@@ -20,24 +20,33 @@
|
||||
TEST(coxeter, simple) {
|
||||
auto g = tc::coxeter("5 3 3");
|
||||
|
||||
ASSERT_EQ(g.rank, 4);
|
||||
ASSERT_EQ(g.rank(), 4);
|
||||
|
||||
EXPECT_EQ(g.get(0, 0), 1);
|
||||
EXPECT_EQ(g.get(0, 1), 5);
|
||||
EXPECT_EQ(g.get(1, 2), 3);
|
||||
EXPECT_EQ(g.get(3, 4), 3);
|
||||
|
||||
EXPECT_EQ(g.get(0, 2), 2);
|
||||
EXPECT_EQ(g.get(0, 3), 2);
|
||||
EXPECT_EQ(g.get(0, 4), 2);
|
||||
|
||||
EXPECT_EQ(g.get(1, 0), 5);
|
||||
EXPECT_EQ(g.get(1, 1), 1);
|
||||
EXPECT_EQ(g.get(1, 2), 3);
|
||||
EXPECT_EQ(g.get(1, 3), 2);
|
||||
EXPECT_EQ(g.get(1, 4), 2);
|
||||
EXPECT_EQ(g.get(2, 4), 2);
|
||||
|
||||
EXPECT_EQ(g.get(2, 0), 2);
|
||||
EXPECT_EQ(g.get(2, 1), 3);
|
||||
EXPECT_EQ(g.get(2, 2), 1);
|
||||
EXPECT_EQ(g.get(2, 3), 3);
|
||||
|
||||
EXPECT_EQ(g.get(3, 0), 2);
|
||||
EXPECT_EQ(g.get(3, 1), 2);
|
||||
EXPECT_EQ(g.get(3, 2), 3);
|
||||
EXPECT_EQ(g.get(3, 3), 1);
|
||||
}
|
||||
|
||||
TEST(coxeter, looping) {
|
||||
auto g = tc::coxeter("{5 3 4}");
|
||||
|
||||
ASSERT_EQ(g.rank, 3);
|
||||
ASSERT_EQ(g.rank(), 3);
|
||||
|
||||
EXPECT_EQ(g.get(0, 1), 5);
|
||||
EXPECT_EQ(g.get(1, 2), 3);
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <tc/pair_map.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
/// helper for comparing against two options
|
||||
template<typename T, typename U, typename V>
|
||||
testing::AssertionResult AssertEqEither(
|
||||
const char *val_expr,
|
||||
const char *o1_expr,
|
||||
const char *o2_expr,
|
||||
T val, U o1, V o2
|
||||
) {
|
||||
if ((val == o1) || (val == o2)) {
|
||||
return testing::AssertionSuccess();
|
||||
}
|
||||
|
||||
return testing::AssertionFailure()
|
||||
<< val_expr << " (" << val << ") " << "does not equal " << o1_expr
|
||||
<< " (" << o1 << ") " << "or " << o2_expr << " (" << o2 << ")";
|
||||
}
|
||||
|
||||
#define EXPECT_EQ_EITHER(val, o1, o2) EXPECT_PRED_FORMAT3(AssertEqEither, val, o1, o2)
|
||||
|
||||
|
||||
/// Naive symmetric pair hash
|
||||
size_t key(size_t i, size_t j) {
|
||||
return ((i + j) << 12) ^ i ^ j;
|
||||
}
|
||||
|
||||
/// factory to build a simple pair_map
|
||||
tc::pair_map<size_t> populate(size_t size) {
|
||||
tc::pair_map<size_t> pm(6);
|
||||
|
||||
for (int i = 0; i < pm.size(); ++i) {
|
||||
for (int j = i; j < pm.size(); ++j) {
|
||||
pm(i, j) = key(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
return pm;
|
||||
}
|
||||
|
||||
TEST(pair_map, fill) {
|
||||
tc::pair_map<size_t> pm(6, 42);
|
||||
|
||||
for (int i = 0; i < pm.size(); ++i) {
|
||||
for (int j = i; j < pm.size(); ++j) {
|
||||
EXPECT_EQ(pm(i, j), 42);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(pair_map, symmetry) {
|
||||
auto pm = populate(6);
|
||||
|
||||
for (int i = 0; i < pm.size(); ++i) {
|
||||
for (int j = i; j < pm.size(); ++j) {
|
||||
EXPECT_EQ(pm(i, j), key(i, j));
|
||||
EXPECT_EQ(pm(j, i), pm(i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(pair_map, copy) {
|
||||
auto pm = populate(6);
|
||||
auto pm_ = pm;
|
||||
|
||||
ASSERT_EQ(pm_.size(), 6);
|
||||
|
||||
for (int i = 0; i < pm_.size(); ++i) {
|
||||
for (int j = i; j < pm_.size(); ++j) {
|
||||
EXPECT_EQ(pm_(i, j), pm(i, j));
|
||||
EXPECT_EQ(pm_(i, j), key(i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(pair_map, move) {
|
||||
auto pm = populate(6);
|
||||
auto pm_ = std::move(pm);
|
||||
|
||||
ASSERT_EQ(pm_.size(), 6);
|
||||
|
||||
for (int i = 0; i < pm_.size(); ++i) {
|
||||
for (int j = i; j < pm_.size(); ++j) {
|
||||
EXPECT_EQ(pm_(i, j), key(i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(pair_map, iterate) {
|
||||
auto pm = populate(6);
|
||||
|
||||
size_t count = 0;
|
||||
for (const auto &[i, j, m]: pm) {
|
||||
EXPECT_EQ(m, key(i, j));
|
||||
count++;
|
||||
}
|
||||
EXPECT_EQ(count, 21);
|
||||
}
|
||||
|
||||
TEST(pair_map, iterate_ref) {
|
||||
auto pm = populate(6);
|
||||
|
||||
for (const auto &[i, j, m]: pm) {
|
||||
m = 42;
|
||||
}
|
||||
|
||||
for (const auto &[i, j, m]: pm) {
|
||||
EXPECT_EQ(m, 42);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(pair_map, view) {
|
||||
auto pm = populate(6);
|
||||
|
||||
size_t count = 0;
|
||||
for (const auto &[i, j, m]: pm.of(4)) {
|
||||
EXPECT_EQ_EITHER(4, i, j);
|
||||
count++;
|
||||
}
|
||||
EXPECT_EQ(count, pm.size());
|
||||
}
|
||||
@@ -11,15 +11,15 @@ testing::AssertionResult AssertSolveOrder(
|
||||
const char *group_expr,
|
||||
const char *sub_gens_expr,
|
||||
const char *expected_order_expr,
|
||||
const tc::Group &group,
|
||||
const std::vector<tc::Gen> &sub_gens,
|
||||
tc::Coset expected_order
|
||||
const tc::Group<> &group,
|
||||
const std::vector<size_t> &sub_gens,
|
||||
size_t expected_order
|
||||
) {
|
||||
auto start = std::clock();
|
||||
auto cosets = tc::solve(group, sub_gens);
|
||||
auto cosets = group.solve(sub_gens);
|
||||
auto end = std::clock();
|
||||
|
||||
tc::Coset actual_order = cosets.size();
|
||||
size_t actual_order = cosets.order();
|
||||
|
||||
auto total_sec = (double) (end - start) / CLOCKS_PER_SEC;
|
||||
auto cosets_per_sec = (double) actual_order / total_sec;
|
||||
@@ -48,45 +48,45 @@ testing::AssertionResult AssertSolveOrder(
|
||||
|
||||
#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<size_t>;
|
||||
|
||||
tc::Group A(unsigned int n) {
|
||||
tc::Group<> A(unsigned int n) {
|
||||
return tc::vcoxeter("3 * {}", {n - 1});
|
||||
}
|
||||
|
||||
tc::Group B(unsigned int n) {
|
||||
tc::Group<> B(unsigned int n) {
|
||||
return tc::vcoxeter("4 3 * {}", {n - 2});
|
||||
}
|
||||
|
||||
tc::Group D(unsigned int n) {
|
||||
tc::Group<> D(unsigned int n) {
|
||||
return tc::vcoxeter("3 * [1 1 {}]", {n - 3});
|
||||
}
|
||||
|
||||
tc::Group E(unsigned int n) {
|
||||
tc::Group<> E(unsigned int n) {
|
||||
return tc::vcoxeter("3 * [1 2 {}]", {n - 4});
|
||||
}
|
||||
|
||||
tc::Group F4() {
|
||||
tc::Group<> F4() {
|
||||
return tc::coxeter("3 4 3");
|
||||
}
|
||||
|
||||
tc::Group G2() {
|
||||
tc::Group<> G2() {
|
||||
return tc::coxeter("6");
|
||||
}
|
||||
|
||||
tc::Group H(unsigned int n) {
|
||||
tc::Group<> H(unsigned int n) {
|
||||
return tc::vcoxeter("5 3 * {}", {n - 2});
|
||||
}
|
||||
|
||||
tc::Group I2(unsigned int n) {
|
||||
tc::Group<> I2(unsigned int n) {
|
||||
return tc::vcoxeter("{}", {n});
|
||||
}
|
||||
|
||||
tc::Group T(unsigned int m, unsigned int n) {
|
||||
tc::Group<> T(unsigned int m, unsigned int n) {
|
||||
return tc::vcoxeter("{} 2 {}", {m, n});
|
||||
}
|
||||
|
||||
tc::Group T(unsigned int n) {
|
||||
tc::Group<> T(unsigned int n) {
|
||||
return T(n, n);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user