<tb@panthema.net>
<http://www.gnu.org/licenses/>
#ifndef BISPANNING_ENUMERATE_HEADER
#define BISPANNING_ENUMERATE_HEADER
#include "alg_bispanning.h"
#include "bispanning.h"
#include "thread_pool.hpp"
#include <boost/regex.hpp>
#include <string>
BaseGraph load_graph(const std::string& graph)
{
if (graph == "k4")
{
BaseGraph g = BaseGraph(4);
g.add_edge(0, 1);
g.add_edge(0, 2);
g.add_edge(0, 3);
g.add_edge(1, 2);
g.add_edge(1, 3);
g.add_edge(2, 3);
return g;
}
else if (graph == "g4a")
{
BaseGraph g = BaseGraph(4);
g.add_edge(0, 1);
g.add_edge(0, 1);
g.add_edge(0, 2);
g.add_edge(0, 3);
g.add_edge(1, 2);
g.add_edge(2, 3);
return g;
}
else if (graph == "w5")
{
BaseGraph g = BaseGraph(5);
g.add_edge(0, 1);
g.add_edge(1, 2);
g.add_edge(0, 2);
g.add_edge(2, 3);
g.add_edge(0, 3);
g.add_edge(3, 4);
g.add_edge(0, 4);
g.add_edge(4, 1);
return g;
}
else if (graph == "w6")
{
BaseGraph g = BaseGraph(6);
g.add_edge(0, 1);
g.add_edge(1, 2);
g.add_edge(0, 2);
g.add_edge(2, 3);
g.add_edge(0, 3);
g.add_edge(3, 4);
g.add_edge(0, 4);
g.add_edge(4, 5);
g.add_edge(0, 5);
g.add_edge(5, 1);
return g;
}
else if (graph == "k4,k4")
{
BaseGraph g = BaseGraph(6);
g.add_edge(0, 1);
g.add_edge(0, 3);
g.add_edge(0, 2);
g.add_edge(1, 3);
g.add_edge(1, 2);
g.add_edge(2, 3);
g.add_edge(1, 4);
g.add_edge(1, 5);
g.add_edge(1, 3);
g.add_edge(4, 5);
g.add_edge(4, 3);
g.add_edge(3, 5);
g.delete_edge(g.edge(3));
g.delete_edge(g.edge(8));
return g;
}
else if (graph == "k3,4")
{
BaseGraph g = BaseGraph(7);
g.add_edge(0, 3);
g.add_edge(0, 4);
g.add_edge(0, 5);
g.add_edge(0, 6);
g.add_edge(1, 3);
g.add_edge(1, 4);
g.add_edge(1, 5);
g.add_edge(1, 6);
g.add_edge(2, 3);
g.add_edge(2, 4);
g.add_edge(2, 5);
g.add_edge(2, 6);
return g;
}
else {
return read_graph6<BaseGraph>(graph);
}
}
class BispanningFilter
{
public:
bool add_keyword(const std::string& word)
{
if (word == "parallel") run_parallel_ = true;
else if (word == "general") enumerate_general_ = true;
else if (word == "atomic") only_atomic_ = true;
else if (word == "composite") only_composite_ = true;
else if (word == "triangle-free") only_triangle_free_ = true;
else if (word == "square-free") only_square_free_ = true;
else if (word == "vconn=1") only_vconn_ = 1;
else if (word == "vconn=2") only_vconn_ = 2;
else if (word == "vconn=3") only_vconn_ = 3;
else if (word == "econn=2") only_econn_ = 2;
else if (word == "econn=3") only_econn_ = 3;
else if (word == "maxdeg=4") max_degree_ = 4;
else return false;
return true;
}
template <typename Functor>
void pre_process(BaseGraph& g, Functor&& process, size_t* process_count)
{
if (enumerate_general_) {
if (only_atomic_ && g.get_min_degree() != 3) return;
if (max_degree_ && g.get_max_degree() > max_degree_) return;
}
AlgBispanning<BaseGraph> ab(g);
if (!ab.good()) return;
ab.write(g);
if (only_vconn_ || only_econn_) {
iGraph<BaseGraph> ig(g);
DBGG0("vconn = " << ig.get_vertex_connectivity() << " - " <<
"econn = " << ig.get_edge_connectivity());
if (only_vconn_ && ig.get_vertex_connectivity() != only_vconn_) return;
if (only_econn_ && ig.get_edge_connectivity() != only_econn_) return;
}
if (only_atomic_ || only_composite_) {
bool atomic = g.is_atomic_bispanner();
if (only_atomic_ && !atomic) return;
if (only_composite_ && atomic) return;
}
if (process_count)
++(*process_count);
if (!run_parallel_) {
std::forward<Functor>(process)(g);
}
else {
std::function<void(BaseGraph&)> p = process;
thread_pool_.Enqueue([=]() mutable { p(g); });
}
}
template <typename Functor>
void enumerate(size_t num_vertex, Functor&& process)
{
if (enumerate_general_ && !only_atomic_)
return enumerate_general(num_vertex, std::forward<Functor>(process));
std::vector<std::string> options;
if (only_atomic_) options.push_back("-d3");
if (max_degree_) options.push_back("-D" + std::to_string(max_degree_));
if (only_triangle_free_) options.push_back("-t");
if (only_square_free_) options.push_back("-f");
GengEnumerate geng(num_vertex, 2 * num_vertex - 2, join(" ", options));
BaseGraph g;
size_t count = 0;
size_t offset = offset_;
size_t process_count = 0;
while (geng(g) && count < limit_)
{
if (offset > 0) {
--offset;
continue;
}
++count;
pre_process(g, std::forward<Functor>(process), &process_count);
}
std::cerr << "// processed " << process_count << " bispanners."
<< std::endl;
}
template <typename Functor>
void enumerate_general(size_t num_vertex, Functor&& process)
{
size_t num_edge = 2 * num_vertex - 2;
GenbgEnumerate genbg(num_vertex, num_edge);
using id_type = BaseGraph::id_type;
using vertex = BaseGraph::vertex_iterator;
using vertex_edge = BaseGraph::vertex_edge_iterator;
BaseGraph bipartite;
unsigned int count = 0;
size_t offset = offset_;
size_t process_count = 0;
while (genbg(bipartite) && count < limit_)
{
if (offset > 0) {
--offset;
continue;
}
++count;
BaseGraph g(num_vertex);
for (size_t i = 0; i < num_edge; ++i)
{
vertex v = bipartite.vertex(i);
assert(v.degree() == 2);
vertex_edge ve0 = bipartite.vertex_edge(i, 0);
vertex_edge ve1 = bipartite.vertex_edge(i, 1);
id_type v0 = ve0.other(v).vertex_id();
id_type v1 = ve1.other(v).vertex_id();
g.add_edge(v0 - num_edge, v1 - num_edge);
}
pre_process(g, std::forward<Functor>(process), &process_count);
}
std::cerr << "// processed " << process_count << " bispanners."
<< std::endl;
}
private:
size_t offset_ = 0;
size_t limit_ = std::numeric_limits<size_t>::max();
bool run_parallel_ = false;
bool enumerate_general_ = false;
bool only_atomic_ = false;
bool only_composite_ = false;
bool only_triangle_free_ = false;
bool only_square_free_ = false;
unsigned only_vconn_ = 0;
unsigned only_econn_ = 0;
unsigned max_degree_ = 0;
ThreadPool thread_pool_;
};
template <typename Functor>
class GenerateSpecific
{
public:
void pre_process(const BaseGraph& g)
{
if (!isocheck(g)) {
std::cout << "// dup" << std::endl;
return;
}
++count;
BaseGraph g_copy = g;
filter_.pre_process(g_copy, std::forward<Functor>(process_), &process_count);
}
void recurse_specific(const BaseGraph& g, const std::string& desc)
{
if (desc.size() == 0)
return;
boost::regex re1("(da|es)\\(([0-9]+|\\?),([0-9]+|\\?)\\),?(.*)");
boost::smatch m;
if (!boost::regex_match(desc, m, re1)) {
DBGG1("Invalid specific da/es sequence: " << desc);
abort();
}
if (m[1] == "da")
{
int v1b, v1e, v2b, v2e;
if (m[2] == "?") {
v1b = 0, v1e = g.total_vertex() - 1;
}
else {
v1b = v1e = atoi(m[2].str().c_str());
}
if (m[3] == "?") {
v2b = 0, v2e = g.total_vertex() - 1;
}
else {
v2b = v2e = atoi(m[3].str().c_str());
}
for (int v1 = v1b; v1 <= v1e; ++v1)
{
if (g.vertex_deleted(v1)) continue;
for (int v2 = v2b; v2 <= v2e; ++v2)
{
if (g.vertex_deleted(v2)) continue;
BaseGraph g2 = g;
g2.add_vertex();
genbig_double_attach(g2, g2.total_vertex() - 1, v1, v2);
pre_process(g2);
recurse_specific(g2, m[4]);
}
}
}
else if (m[1] == "es")
{
int e1b, e1e, v2b, v2e;
if (m[2] == "?") {
e1b = 0, e1e = g.total_edge() - 1;
}
else {
e1b = e1e = atoi(m[2].str().c_str());
}
if (m[3] == "?") {
v2b = 0, v2e = g.total_vertex() - 1;
}
else {
v2b = v2e = atoi(m[3].str().c_str());
}
for (int e1 = e1b; e1 <= e1e; ++e1)
{
if (g.edge_deleted(e1)) continue;
for (int v2 = v2b; v2 <= v2e; ++v2)
{
if (g.vertex_deleted(v2)) continue;
BaseGraph g2 = g;
g2.add_vertex();
genbig_split_edge(g2, g2.total_vertex() - 1,
g2.edge(e1), g2.vertex(v2));
pre_process(g2);
recurse_specific(g2, m[4]);
}
}
}
else {
abort();
}
}
GenerateSpecific(BispanningFilter& filter, Functor&& process)
: filter_(filter), process_(std::forward<Functor>(process))
{
filter_.add_keyword("general");
}
void Run(const std::string& desc)
{
BaseGraph g(1);
recurse_specific(g, desc);
DBGG1("// processed " << process_count << " bispanners "
<< "from " << count << " non-isomorphic graphs.");
}
private:
BaseGraph::IsomorphismCheck isocheck;
size_t count = 0;
size_t process_count = 0;
BispanningFilter& filter_;
Functor && process_;
};
template <typename Functor>
void enumerate_bispanning_graphs(
const std::vector<std::string>& wordlist, Functor&& process)
{
BispanningFilter filter;
for (const std::string& word : wordlist)
{
if (filter.add_keyword(word)) {
continue;
}
else if (str_is_numeric(word)) {
unsigned num = from_str<unsigned>(word);
filter.enumerate(num, std::forward<Functor>(process));
}
else if (word.size() > 2 &&
(word.substr(0, 2) == "es" || word.substr(0, 2) == "da")) {
GenerateSpecific<Functor>(
filter, std::forward<Functor>(process)).Run(word);
}
else {
BaseGraph g = load_graph(word);
filter.pre_process(g, std::forward<Functor>(process), nullptr);
}
}
}
template <typename Functor>
void enumerate_bispanning_graphs(
const std::string& filter, Functor&& process)
{
return enumerate_bispanning_graphs(
split(filter, ' '), std::forward<Functor>(process));
}
#endif