From aa3d3b51491567f4c23a06c338d52594a37c8e4c Mon Sep 17 00:00:00 2001 From: David Allemang Date: Mon, 1 Nov 2021 12:34:02 -0400 Subject: [PATCH] Build path on-demand, remove Action Removing the path checks on Cosets::set drastically improves performance. E7 is now <1s and B8 is now <5s. --- example/bench.cpp | 2 +- example/path.cpp | 4 +- include/tc/core.hpp | 2 - include/tc/core/action.hpp | 14 ---- include/tc/core/path.hpp | 54 -------------- include/tc/core/solver.hpp | 147 +++++++++++++++++++++++++------------ 6 files changed, 105 insertions(+), 118 deletions(-) delete mode 100644 include/tc/core/action.hpp delete mode 100644 include/tc/core/path.hpp diff --git a/example/bench.cpp b/example/bench.cpp index 5f993ef..250d34b 100644 --- a/example/bench.cpp +++ b/example/bench.cpp @@ -12,7 +12,7 @@ void test(const G &group) { auto e = std::clock(); double diff = (double) (e - s) / CLOCKS_PER_SEC; - int order = cosets.size(); + int order = cosets.order(); std::cout << std::setw(7) << group.name() << ", " diff --git a/example/path.cpp b/example/path.cpp index 2fc68f4..cbdd16f 100644 --- a/example/path.cpp +++ b/example/path.cpp @@ -4,12 +4,12 @@ #include int main() { - auto cube = tc::group::B(3); + auto cube = tc::group::B<3>(); auto vars = tc::solve(cube); std::string start; std::vector names = {"a", "b", "c"}; - auto words = vars.path.walk(start, names, std::plus<>()); + auto words = vars.path().walk(start, names, std::plus<>()); for (const auto &word: words) { std::cout << (word.empty() ? "-" : word) << std::endl; diff --git a/include/tc/core.hpp b/include/tc/core.hpp index 25f2fb9..4b6ed0d 100644 --- a/include/tc/core.hpp +++ b/include/tc/core.hpp @@ -1,7 +1,5 @@ #pragma once -#include "tc/core/action.hpp" -#include "tc/core/path.hpp" #include "tc/core/rel.hpp" #include "tc/core/group.hpp" #include "tc/core/solver.hpp" diff --git a/include/tc/core/action.hpp b/include/tc/core/action.hpp deleted file mode 100644 index 920cec2..0000000 --- a/include/tc/core/action.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -struct Action { - int from_idx = -1; - int gen = -1; - - Action() = default; - - Action(const Action &) = default; - - Action(int from_idx, int gen) - : from_idx(from_idx), gen(gen) { - } -}; diff --git a/include/tc/core/path.hpp b/include/tc/core/path.hpp deleted file mode 100644 index 2e8b9a1..0000000 --- a/include/tc/core/path.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include -#include - -#include "action.hpp" - -struct Path { - std::vector path; - - Path() = default; - - Path(const Path &) = default; - - void add_row() { - path.resize(path.size() + 1); - } - - [[nodiscard]] Action get(int to_idx) const { - return path[to_idx]; - } - - void put(int from_idx, int gen, int to_idx) { - path[to_idx] = Action(from_idx, gen); - } - - [[nodiscard]] size_t size() const { - return path.size(); - } - - template - std::vector walk(const T &start, const F &op) { - std::vector res; - res.reserve(path.size()); - res.push_back(start); - - for (size_t i = 1; i < path.size(); ++i) { - auto &action = path[i]; - auto &from = res[action.from_idx]; - auto &gen = action.gen; - - res.push_back(op(from, gen)); - } - - return res; - } - - template - std::vector walk(const T &start, const E &gens, const F &op) { - return walk(start, [&](const T &from, const int gen) { - return op(from, gens[gen]); - }); - } -}; diff --git a/include/tc/core/solver.hpp b/include/tc/core/solver.hpp index a04791f..356857b 100644 --- a/include/tc/core/solver.hpp +++ b/include/tc/core/solver.hpp @@ -4,39 +4,96 @@ #include namespace tc { - struct Cosets { - int ngens; + template + class Path; + + template + class Cosets { + private: std::vector data; - Path path; - Cosets(const Cosets &) = default; + public: + Cosets(const Cosets &) = default; - explicit Cosets(int ngens) - : ngens(ngens) { - } + Cosets() = default; void add_row() { - data.resize(data.size() + ngens, -1); - path.add_row(); + data.resize(data.size() + Rank, -1); } void put(int coset, int gen, int target) { - data[coset * ngens + gen] = target; - data[target * ngens + gen] = coset; - - if (path.get(target).from_idx == -1) { - path.put(coset, gen, target); - } + data[coset * Rank + gen] = target; + data[target * Rank + gen] = coset; } [[nodiscard]] int get(int coset, int gen) const { - return data[coset * ngens + gen]; + return data[coset * Rank + gen]; } - [[nodiscard]] size_t size() const { - return path.size(); + [[nodiscard]] size_t order() const { + return data.size() / Rank; + } + + Path path() const; + }; + + template + class Path { + private: + friend class Cosets; + + std::vector source; + std::vector gen; + size_t _order; + + explicit Path(size_t order) : _order(order), source(order), gen(order) {} + + public: + size_t order() const { + return _order; + } + + template + std::vector walk(const T &start, const F &op) { + std::vector res; + res.reserve(order()); + res.push_back(start); + + for (size_t i = 1; i < order(); ++i) { + auto val = op(res[source[i]], gen[i]); + res.push_back(val); + } + + return res; + } + + template + std::vector walk(const T &start, const E &gens, const F &op) { + return walk(start, [&](const T &s, const int g) { + return op(s, gens[g]); + }); } }; + + template + Path Cosets::path() const { + Path res(order()); + std::vector set(order()); + + for (int coset = 0; coset < order(); ++coset) { + for (int gen = 0; gen < Rank; ++gen) { + int target = get(coset, gen); + + if (!set[target]) { + res.source[target] = coset; + res.gen[target] = gen; + set[target] = true; + } + } + } + + return res; + } } namespace { @@ -119,7 +176,7 @@ namespace { } } - void initialize(int target, const tc::Cosets &cosets) { + void initialize(int target, const tc::Cosets &cosets) { for (auto &table: tables) { const Rel &rel = table->rel; Row &row = table->rows[target]; @@ -141,7 +198,7 @@ namespace { delete null_lst_ptr; } - void learn(int coset, int gen, int target, const tc::Cosets &cosets, std::priority_queue &facts) { + void learn(int coset, int gen, int target, const tc::Cosets &cosets, std::priority_queue &facts) { if (target == coset) { for (auto &table: deps[gen]) { Row &target_row = table->rows[target]; @@ -186,8 +243,8 @@ namespace { namespace tc { template - tc::Cosets solve(const Group &g, const std::vector &sub_gens = {}) { - tc::Cosets cosets(Rank); + tc::Cosets solve(const Group &g, const std::vector &sub_gens = {}) { + tc::Cosets cosets; cosets.add_row(); if (Rank == 0) { @@ -205,38 +262,38 @@ namespace tc { std::priority_queue facts; - for (int idx = 0; idx < cosets.data.size(); idx++) { - int coset = idx / Rank; - int gen = idx % Rank; - if (cosets.get(coset, gen) >= 0) continue; + for (int coset = 0; coset < cosets.order(); coset++) { + for (int gen = 0; gen < Rank; ++gen) { + if (cosets.get(coset, gen) >= 0) continue; // todo vector set - int target = cosets.size(); - cosets.add_row(); - tables.add_row(); + int target = cosets.order(); + cosets.add_row(); + tables.add_row(); - facts.push(idx); + facts.push(coset * Rank + gen); - // todo nothing before the current coset will be used. - // delete all table rows using old cosets to free memory early. - // probably some unrolled linked list would be good; just drop - // old blocks. + // todo nothing before the current coset will be used. + // delete all table rows using old cosets to free memory early. + // probably some unrolled linked list would be good; just drop + // old blocks. - while (!facts.empty()) { - int fact_idx = facts.top(); - facts.pop(); + while (!facts.empty()) { + int fact_idx = facts.top(); + facts.pop(); - int coset_ = fact_idx / Rank; - int gen_ = fact_idx % Rank; + int coset_ = fact_idx / Rank; + int gen_ = fact_idx % Rank; - if (cosets.get(coset_, gen_) != -1) - continue; + if (cosets.get(coset_, gen_) != -1) + continue; - cosets.put(coset_, gen_, target); + cosets.put(coset_, gen_, target); - tables.learn(coset_, gen_, target, cosets, facts); + tables.learn(coset_, gen_, target, cosets, facts); + } + + tables.initialize(target, cosets); } - - tables.initialize(target, cosets); } return cosets;