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:
David Allemang
2022-11-18 11:46:55 -05:00
parent 928f7d7b45
commit 47de177626
20 changed files with 524 additions and 913 deletions

View File

@@ -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);
}
};
}

View File

@@ -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();
}
};
}

View File

@@ -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));
}
}
}
};
}

View File

@@ -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);
}

View File

@@ -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
}

View File

@@ -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;