8#define DEJAVU_VERSION_MAJOR 2
9#define DEJAVU_VERSION_MINOR 0
10#define DEJAVU_VERSION_IS_PREVIEW true
22#if ((defined(_MSVC_LANG) && _MSVC_LANG < 201402L) || __cplusplus < 201402L)
23# error "dejavu requires to be compiled with C++ 2014 or newer"
27#if defined(DEJDEBUG) && !defined(NDEBUG)
33 #if defined(DEJDEBUG) && !defined(NDEBUG)
34 static inline void test_hook(
int,
const int *p,
int nsupp,
const int *supp) {
57 std::vector<dejavu_hook*> hooks;
59 void hook_func(
int n,
const int *p,
int nsupp,
const int *supp) {
61 (*hook)(n, p, nsupp, supp);
66 hooks.push_back(hook);
79 my_hook = [
this](
auto && PH1,
auto && PH2,
auto && PH3,
auto && PH4)
80 {
return this->hook_func(std::forward<
decltype(PH1)>(PH1), std::forward<
decltype(PH2)>(PH2),
81 std::forward<
decltype(PH3)>(PH3), std::forward<
decltype(PH4)>(PH4));
84 my_hook = std::bind(&multi_hook::hook_func,
this, std::placeholders::_1, std::placeholders::_2,
85 std::placeholders::_3, std::placeholders::_4);
99 std::ostream& my_ostream;
102 void hook_func(
int n,
const int *p,
int nsupp,
const int *supp) {
105 for(
int i = 0; i < nsupp; ++i) {
106 const int v_from = supp[i];
107 if(test_set.
get(v_from))
continue;
108 int v_next = p[v_from];
109 if(v_from == v_next)
continue;
110 test_set.
set(v_from);
111 my_ostream <<
"(" << v_from;
112 while(!test_set.
get(v_next)) {
113 test_set.
set(v_next);
114 my_ostream <<
" " << v_next;
119 my_ostream << std::endl;
126 my_hook = [
this](
auto && PH1,
auto && PH2,
auto && PH3,
auto && PH4)
127 {
return this->hook_func(std::forward<
decltype(PH1)>(PH1), std::forward<
decltype(PH2)>(PH2),
128 std::forward<
decltype(PH3)>(PH3), std::forward<
decltype(PH4)>(PH4));
131 my_hook = std::bind(&ostream_hook::hook_func,
this, std::placeholders::_1, std::placeholders::_2,
132 std::placeholders::_3, std::placeholders::_4);
156 void hook_func(
int n,
const int *p,
int nsupp,
const int *supp) {
159 (*my_call_hook)(n, p, nsupp, supp);
174 my_call_hook = call_hook;
180 my_hook = [
this](
auto && PH1,
auto && PH2,
auto && PH3,
auto && PH4)
181 {
return this->hook_func(std::forward<
decltype(PH1)>(PH1), std::forward<
decltype(PH2)>(PH2),
182 std::forward<
decltype(PH3)>(PH3), std::forward<
decltype(PH4)>(PH4));
185 my_hook = std::bind(&strong_certification_hook::hook_func,
this, std::placeholders::_1,
186 std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
201 void hook_func(
int n,
const int *p,
int nsupp,
const int *supp) {
202 my_schreier.
sift(n, p, nsupp, supp);
209 my_hook = [
this](
auto && PH1,
auto && PH2,
auto && PH3,
auto && PH4)
210 {
return this->hook_func(std::forward<
decltype(PH1)>(PH1), std::forward<
decltype(PH2)>(PH2),
211 std::forward<
decltype(PH3)>(PH3), std::forward<
decltype(PH4)>(PH4));
214 my_hook = std::bind(&schreier_hook::hook_func,
this, std::placeholders::_1, std::placeholders::_2,
215 std::placeholders::_3, std::placeholders::_4);
230 void hook_func(
int n,
const int *p,
int nsupp,
const int *supp) {
238 my_hook = [
this](
auto && PH1,
auto && PH2,
auto && PH3,
auto && PH4)
239 {
return this->hook_func(std::forward<
decltype(PH1)>(PH1), std::forward<
decltype(PH2)>(PH2),
240 std::forward<
decltype(PH3)>(PH3), std::forward<
decltype(PH4)>(PH4));
243 my_hook = std::bind(&orbit_hook::hook_func,
this, std::placeholders::_1, std::placeholders::_2,
244 std::placeholders::_3, std::placeholders::_4);
259 int h_error_bound = 10;
262 bool h_random_use_true_random =
false;
263 int h_random_seed = 0;
264 bool h_silent =
false;
265 int h_bfs_memory_limit = 0x20000000;
266 bool h_decompose =
true;
267 int h_base_max_diff = 5;
269 bool h_prefer_dfs =
false;
270 bool h_destructive =
true;
271 bool h_strong_certification =
false;
279 bool s_deterministic_termination =
true;
289 h_error_bound = error_bound;
299 return h_error_bound;
309 h_random_use_true_random = use_true_random;
319 h_prefer_dfs = prefer_dfs;
329 h_random_use_true_random = !use_pseudo_random;
337 h_random_seed = seed;
346 h_strong_certification = use_strong_certification;
355 h_destructive = may_alter_graph;
364 h_decompose = use_decompose;
372 std::random_device rd;
373 h_random_seed =
static_cast<int>(rd());
398 return s_deterministic_termination;
433 enum termination_strategy { t_prep, t_inproc, t_dfs, t_bfs, t_det_schreier, t_rand_schreier };
434 termination_strategy s_term = t_prep;
435 s_grp_sz.
set(1.0, 0);
443 if(h_strong_certification) {
445 hook = certification_hook.
get_hook();
446 h_destructive =
false;
451 if (colmap ==
nullptr) {
454 for (
int i = 0; i < g->
v_size; ++i) colmap[i] = 0;
455 }
else if(!h_destructive) {
456 int* orig_colmap = colmap;
459 for (
int i = 0; i < g->
v_size; ++i) colmap[i] = orig_colmap[i];
466 g = &graph_substitute;
473 m_printer.
print(
"preprocessing");
474 m_prep.
reduce(g, colmap, hook);
478 if(g->
v_size <= 1)
return;
486 int s_num_components = 1;
491 s_num_components = ir::quotient_components(g, colmap, &vertex_to_component);
493 m_decompose.
decompose(g, colmap, vertex_to_component, s_num_components);
497 for(
int i = 0; i < s_num_components; ++i) {
500 if(s_num_components > 1) {
509 PRINT(
"\r\nsolving_component " << i+1 <<
"/" << s_num_components <<
" (n=" << g->
v_size <<
")")
518 int h_budget_inc_fact = 5;
522 int s_inproc_success = 0;
524 bool s_long_base =
false;
525 bool s_short_base =
false;
526 bool s_prunable =
false;
527 double s_random_budget_bias = 1.0;
531 bool h_used_shallow_inprocess =
false;
532 bool h_used_shallow_inprocess_quadratic =
false;
533 bool s_inprocessed =
false;
534 int s_consecutive_discard = 0;
537 bool s_last_bfs_pruned =
false;
538 bool s_any_bfs_pruned =
false;
542 std::vector<int> base_vertex;
543 std::vector<int> base_sizes;
570 const bool s_regular = local_coloring.
cells == 1;
574 ir::controller local_state_left(&m_refinement, &local_coloring_left);
583 int s_last_base_size = g->
v_size + 1;
590 const bool s_hard = h_budget > 256;
591 const bool s_easy = s_restarts == -1;
594 if (s_restarts > 0) {
598 const int s_inc_fac = (s_restarts >= 3)? h_budget_inc_fact : 2;
599 if (s_inproc_success >= 3) {
600 s_random_budget_bias = 0.1;
602 h_budget_inc_fact = 2;
604 if (s_inproc_success >= 6) s_random_budget_bias = 0.01;
605 if (s_inprocessed) h_budget = 1;
606 h_budget *= s_inc_fac;
616 m_selectors.
make_cell_selector(g, &local_state, &local_state_left, style, s_restarts, h_budget);
624 s_long_base = base_size > sqrt(g->
v_size);
625 s_short_base = base_size <= 2;
630 const bool s_too_long = base_size > h_base_max_diff * s_last_base_size && s_inproc_success <= 1;
631 const bool s_too_big = (s_inproc_success <= (s_regular + !s_prunable)) &&
632 (s_last_tree_sz < s_tree_estimate) &&
637 if ((s_too_big || s_too_long) && s_consecutive_discard < 3) {
639 ++s_consecutive_discard;
642 s_consecutive_discard = 0;
643 s_last_base_size = base_size;
645 const bool s_last_base_eq = (base_vertex == local_state.
base_vertex);
652 base_sizes.reserve(local_state.
base.size());
653 for(
auto& bi : local_state.
base) base_sizes.push_back(bi.color_sz);
661 dfs_level = s_last_base_eq ? dfs_level : m_dfs.
do_paired_dfs(hook, g, local_state_left, local_state,
663 base_size > 1 || s_restarts > 0);
665 m_printer.
timer_print(
"dfs", std::to_string(base_size) +
"-" + std::to_string(dfs_level),
668 s_prunable = s_prunable || (dfs_level < base_size - 5);
669 if (dfs_level == 0) {
680 const bool can_keep_previous = (s_restarts >= 3) && !s_inprocessed;
688 const bool reset_prob = sh_schreier.
reset(&m_compress, g->
v_size, schreierw, base_vertex,
689 base_sizes, dfs_level, can_keep_previous,
699 sh_tree.
reset(base_vertex, &root_save, can_keep_previous);
705 s_last_bfs_pruned =
false;
706 s_any_bfs_pruned =
false;
707 int h_rand_fail_lim_total = 0;
708 int h_rand_fail_lim_now = 0;
709 double s_path_fail1_avg = 0;
713 enum decision {random_ir, bfs_ir, restart};
714 decision next_routine = random_ir;
715 decision last_routine = random_ir;
718 bool do_a_restart =
false;
719 bool finished_symmetries =
false;
721 h_rand_fail_lim_now = 4;
722 last_routine = restart;
724 while (!do_a_restart && !finished_symmetries) {
729 const int s_bfs_next_level_nodes =
732 const bool s_have_rand_estimate = (m_rand.
s_paths >= 4);
734 double s_trace_cost1_avg = s_trace_full_cost;
737 if (s_have_rand_estimate) {
743 next_routine = random_ir;
744 double score_rand, score_bfs;
746 if (s_have_rand_estimate) {
749 const double reset_cost_rand = g->
v_size;
750 const double reset_cost_bfs = std::min(s_trace_cost1_avg, (
double) g->
v_size);
751 double s_bfs_estimate = (s_trace_cost1_avg + reset_cost_bfs) * (s_bfs_next_level_nodes);
752 double s_rand_estimate = (s_random_path_trace_cost + reset_cost_rand) * h_rand_fail_lim_now;
756 score_bfs = s_bfs_estimate * (0.1 + 1 - s_path_fail1_avg);
761 if (s_path_fail1_avg < 0.01) score_bfs *= 2;
768 next_routine = (score_rand < score_bfs) ? random_ir : bfs_ir;
771 h_rand_fail_lim_now = next_routine==random_ir? 2*h_rand_fail_lim_now : h_rand_fail_lim_now;
776 if (next_routine == bfs_ir && s_bfs_next_level_nodes * (1 - s_path_fail1_avg) > 2 * h_budget)
777 next_routine = restart;
781 const long s_bfs_est_mem = (long) round(s_bfs_next_level_nodes * (1 - s_path_fail1_avg) *
783 if (next_routine == bfs_ir && (s_bfs_est_mem > h_bfs_memory_limit)) next_routine = random_ir;
786 if (s_cost > h_budget) next_routine = restart;
791 if (next_routine == restart && 2 * base_size > s_bfs_next_level_nodes
792 && s_trace_cost1_avg < base_size && s_path_fail1_avg > 0.01) next_routine = bfs_ir;
797 next_routine = random_ir;
798 if (m_rand.
s_rolling_success > 0.1 && s_cost <= h_budget * 4) next_routine = random_ir;
799 if (m_rand.
s_rolling_success > 0.2 && s_cost <= h_budget * 6) next_routine = random_ir;
801 next_routine == restart)
802 next_routine = random_ir;
803 if (dfs_level == sh_tree.
get_finished_up_to() && s_any_bfs_pruned && s_cost <= h_budget * 20)
804 next_routine = random_ir;
809 next_routine = restart;
811 next_routine = restart;
816 && s_dfs_potential_ind > 8 && s_dfs_potential_ind > base_size - dfs_level - 2)
817 next_routine = restart;
822 if(last_routine == random_ir && next_routine != random_ir) {
826 "/d" + std::to_string(sh_schreier.
s_densegen()),
"_");
831 switch (next_routine) {
836 !s_short_base) || (s_have_rand_estimate &&
841 h_rand_fail_lim_total +=
842 static_cast<int>(std::max(5.0, h_rand_fail_lim_now * s_random_budget_bias));
848 m_rand.
random_walks(g, hook, selector, sh_tree, sh_schreier, local_state,
849 local_state_left, h_rand_fail_lim_total);
853 local_state, local_state_left,
854 h_rand_fail_lim_total);
858 s_cost += h_rand_fail_lim_now;
861 if(finished_symmetries) {
866 "/d" + std::to_string(sh_schreier.
s_densegen()),
"_");
874 m_bfs.
do_a_level(g, hook, sh_tree, local_state, selector);
877 std::to_string(s_bfs_next_level_nodes) +
")",
885 finished_symmetries =
true;
887 if (base_size == 2) {
892 }
else if (base_size == 1) {
904 s_any_bfs_pruned = s_any_bfs_pruned || s_last_bfs_pruned;
914 last_routine = next_routine;
918 if (finished_symmetries) {
927 const bool h_use_shallow_inprocess = !h_used_shallow_inprocess && s_inproc_success == 0 &&
928 s_path_fail1_avg > 0.1;
929 const bool h_use_shallow_inprocess_quadratic =
930 !h_used_shallow_inprocess_quadratic && s_inproc_success >= 1 + s_regular &&
931 s_prunable && 2*local_state.
c->
cells > sqrt(g->
v_size) && s_hard;
932 h_used_shallow_inprocess = h_used_shallow_inprocess || h_use_shallow_inprocess;
933 h_used_shallow_inprocess_quadratic = h_used_shallow_inprocess_quadratic ||
934 h_use_shallow_inprocess_quadratic;
937 s_inprocessed = m_inprocess.
inprocess(g, sh_tree, sh_schreier, local_state, root_save, h_budget,
939 h_use_shallow_inprocess || h_use_shallow_inprocess_quadratic,
940 h_use_shallow_inprocess_quadratic, m_dfs.
orbs);
943 s_inproc_success += s_inprocessed;
944 if(s_inprocessed) m_printer.
timer_print(
"inprocess", local_state.
c->
cells, s_inproc_success);
955 s_deterministic_termination = (s_term != t_rand_schreier) && s_deterministic_termination;
965 m_printer.
timer_print(
"done", s_deterministic_termination, s_term);
void set(long double set_mantissa, int set_exponent)
void multiply(int number)
Vertex coloring for a graph.
Set specialized for quick resets.
void initialize(int size)
void resize(const int size)
Workspace for sparse automorphisms.
Compressed Schreier structure.
bool reset(domain_compressor *new_compressor, int new_domain_size, schreier_workspace &w, std::vector< int > &new_base, std::vector< int > &new_base_sizes, const int stop, bool keep_old, std::vector< int > &global_fixed_points)
dej_nodiscard bool deterministic_abort_criterion() const
void compute_group_size()
dej_nodiscard big_number get_group_size() const
double h_min_compression_ratio
dej_nodiscard bool any_abort_criterion() const
dej_nodiscard int s_densegen() const
void reset_probabilistic_criterion()
void set_error_bound(int error_bound)
dej_nodiscard int s_sparsegen() const
Compresses vertex set to smaller window.
void determine_compression_map(coloring &c, std::vector< int > &base, int stop)
int orbit_size(const int v)
void add_automorphism_to_orbit(const int *p, const int nsupp, const int *supp)
API for the dejavu Schreier structure.
bool sift(automorphism_workspace &automorphism, bool known_in_group=false)
Auxiliary workspace used for Schreier computations.
virtual dejavu_hook * get_hook()=0
virtual ~hook_interface()=default
Calls several other hooks.
void add_hook(dejavu_hook *hook)
dej_nodiscard size_t size() const
dejavu_hook * get_hook() override
Hook to feed an orbit structure.
orbit_hook(groups::orbit &save_orbit)
dejavu_hook * get_hook() override
dejavu_hook * get_hook() override
ostream_hook(std::ostream &ostream)
Hook to feed a Schreier structure.
schreier_hook(groups::random_schreier &schreier)
dejavu_hook * get_hook() override
Certification on the original graph.
dejavu_hook * get_hook() override
strong_certification_hook(static_graph &g, dejavu_hook *call_hook)
strong_certification_hook()=default
strong_certification_hook(sgraph &g, dejavu_hook *call_hook)
void initialize(sgraph &g, dejavu_hook *call_hook)
big_number get_ir_size_estimate()
void make_cell_selector(sgraph *g, controller *state, controller *state_probe, const selector_style style, const int h_seed, const int h_budget)
std::function< type_selector_hook > * get_selector_hook()
static bool certify_automorphism_sparse(markset &scratch_set, const sgraph *g, const int *p, int supp, const int *supp_arr)
Decomposes graphs and manages decomposition information.
sgraph * get_component(int i)
void decompose(sgraph *g, int *colmap, ds::worklist &vertex_to_component, int new_num_components)
coloring * get_coloring()
Color refinement and related algorithms.
bool certify_automorphism_sparse(const sgraph *g, const int *p, int supp, const int *supp_arr)
int get_current_level_tracepos()
int get_current_level_size()
int get_level_size(int level)
int h_bfs_automorphism_pw
groups::orbit h_bfs_top_level_orbit
bool reset(std::vector< int > &new_base, ir::limited_save *root, bool keep_old)
dej_nodiscard int get_finished_up_to() const
dej_nodiscard int get_position() const
preprocessor for symmetry detection
void inject_decomposer(dejavu::ir::graph_decomposer *new_decomposer, int component)
void reduce(dejavu::static_graph *g, dejavu_hook *hook, std::vector< preop > *schedule=nullptr)
dejavu::big_number grp_sz
static void _dejavu_hook(int n, const int *aut, int nsupp, const int *supp)
Random number generation.
bool h_use_deviation_pruning
void do_a_level(sgraph *g, dejavu_hook *hook, ir::shared_tree &ir_tree, ir::controller &local_state, std::function< ir::type_selector_hook > *selector)
static int next_level_estimate(ir::shared_tree &ir_tree, std::function< ir::type_selector_hook > *selector)
Depth-first search without backtracking.
double h_recent_cost_snapshot_limit
int do_paired_dfs(dejavu_hook *hook, sgraph *g, ir::controller &state_left, ir::controller &state_right, std::vector< std::pair< int, int > > &computed_orbits, bool prune=true)
termination_reason s_termination
Inprocessing for symmetry detection.
std::vector< std::pair< int, int > > inproc_maybe_individualize
std::vector< std::pair< int, int > > inproc_can_individualize
void set_splits_hint(int splits_hint)
std::vector< int > inproc_fixed_points
bool inprocess(sgraph *g, ir::shared_tree &tree, groups::compressed_schreier &group, ir::controller &local_state, ir::limited_save &root_save, int, bool use_bfs_inprocess, bool use_shallow_inprocess, bool use_shallow_quadratic_inprocess, groups::orbit &orbit_partition)
int check_individualizations(ir::limited_save &root_save)
IR search using random walks.
double s_rolling_first_level_success
static void specific_walk(sgraph *g, ir::shared_tree &ir_tree, ir::controller &local_state, std::vector< int > &base_vertex)
static bool h_almost_done(groups::compressed_schreier &group)
void random_walks_from_tree(sgraph *g, dejavu_hook *hook, std::function< ir::type_selector_hook > *selector, ir::shared_tree &ir_tree, groups::compressed_schreier &group, ir::controller &local_state, ir::controller &other_state, int fail_limit)
void random_walks(sgraph *g, dejavu_hook *hook, std::function< ir::type_selector_hook > *selector, ir::shared_tree &ir_tree, groups::compressed_schreier &group, ir::controller &local_state, ir::controller &other_state, int fail_limit)
void use_look_close(bool look_close=false)
Internal graph data structure.
void copy_graph(sgraph *g)
void initialize_coloring(ds::coloring *c, int *vertex_to_col)
dej_nodiscard big_number get_automorphism_group_size() const
void set_pseudo_random(bool use_pseudo_random=true)
dej_nodiscard bool get_deterministic_termination() const
int get_error_bound() const
void set_decompose(bool use_decompose=true)
void automorphisms(static_graph *g, hooks::hook_interface &hook)
void set_true_random(bool use_true_random=true)
void automorphisms(sgraph *g, int *colmap=nullptr, dejavu_hook *hook=nullptr)
void set_prefer_dfs(bool prefer_dfs=true)
void automorphisms(static_graph *g, dejavu_hook hook)
void set_seed(int seed=0)
void set_strong_certification(bool use_strong_certification=true)
void set_print(bool print=true)
void set_disallow_alteration(bool may_alter_graph=false)
void automorphisms(static_graph *g, dejavu_hook *hook=nullptr)
void set_error_bound(int error_bound=10)
Graph with static number of vertices and edges.
dejavu::sgraph * get_sgraph()
Prints information to the console.
void print(const std::string &str) const
void timer_print(const std::string &proc, const std::string &p1, const std::string &p2)
void print_header() const
dejavu::ir::refinement test_r
dejavu::sgraph dej_test_graph
Controls movement in IR tree.
std::vector< base_info > base
void save_reduced_state(limited_save &state)
void set_increase_deviation(int deviation_inc=48)
void load_reduced_state(limited_save &state)
std::vector< int > base_vertex
std::function< void(int, const int *, int, const int *)> dejavu_hook