1
0

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.
This commit is contained in:
David Allemang
2021-11-01 12:34:02 -04:00
parent d36b03b4f9
commit aa3d3b5149
6 changed files with 105 additions and 118 deletions

View File

@@ -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() << ", "

View File

@@ -4,12 +4,12 @@
#include <iostream>
int main() {
auto cube = tc::group::B(3);
auto cube = tc::group::B<3>();
auto vars = tc::solve(cube);
std::string start;
std::vector<std::string> 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;

View File

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

View File

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

View File

@@ -1,54 +0,0 @@
#pragma once
#include <vector>
#include <functional>
#include "action.hpp"
struct Path {
std::vector<Action> 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<class T, class F>
std::vector<T> walk(const T &start, const F &op) {
std::vector<T> 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<class T, class E, class F>
std::vector<T> walk(const T &start, const E &gens, const F &op) {
return walk(start, [&](const T &from, const int gen) {
return op(from, gens[gen]);
});
}
};

View File

@@ -4,39 +4,96 @@
#include <vector>
namespace tc {
struct Cosets {
int ngens;
template<unsigned int Rank>
class Path;
template<unsigned int Rank>
class Cosets {
private:
std::vector<int> data;
Path path;
Cosets(const Cosets &) = default;
public:
Cosets(const Cosets<Rank> &) = 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<Rank> path() const;
};
template<unsigned int Rank>
class Path {
private:
friend class Cosets<Rank>;
std::vector<unsigned int> source;
std::vector<unsigned int> gen;
size_t _order;
explicit Path(size_t order) : _order(order), source(order), gen(order) {}
public:
size_t order() const {
return _order;
}
template<class T, class F>
std::vector<T> walk(const T &start, const F &op) {
std::vector<T> 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<class T, class E, class F>
std::vector<T> 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<unsigned int Rank>
Path<Rank> Cosets<Rank>::path() const {
Path<Rank> res(order());
std::vector<bool> 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<Rank> &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<int> &facts) {
void learn(int coset, int gen, int target, const tc::Cosets<Rank> &cosets, std::priority_queue<int> &facts) {
if (target == coset) {
for (auto &table: deps[gen]) {
Row &target_row = table->rows[target];
@@ -186,8 +243,8 @@ namespace {
namespace tc {
template<unsigned int Rank>
tc::Cosets solve(const Group <Rank> &g, const std::vector<int> &sub_gens = {}) {
tc::Cosets cosets(Rank);
tc::Cosets<Rank> solve(const Group <Rank> &g, const std::vector<int> &sub_gens = {}) {
tc::Cosets<Rank> cosets;
cosets.add_row();
if (Rank == 0) {
@@ -205,38 +262,38 @@ namespace tc {
std::priority_queue<int> 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<bool> 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;