/* Copyright (c) 2003-2015 Tommi Junttila Released under the GNU Lesser General Public License version 3.
This file is part of bliss.
bliss is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, version 3 of the License.
bliss is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with bliss. If not, see <http://www.gnu.org/licenses/>.
*/
#define _INTERNAL_ERROR() \
fatal_error("%s:%d: internal error", __FILE__, __LINE__) #define _OUT_OF_MEMORY() fatal_error("%s:%d: out of memory", __FILE__, __LINE__)
/*------------------------------------------------------------------------- * * Constructor and destructor routines for the abstract graph class *
*-------------------------------------------------------------------------*/
/* Default value for using "long prune" */
opt_use_long_prune = true; /* Default value for using failure recording */
opt_use_failure_recording = true; /* Default value for using component recursion */
opt_use_comprec = true;
/*------------------------------------------------------------------------- * * Routines for refinement to equitable partition *
*-------------------------------------------------------------------------*/
void AbstractGraph::refine_to_equitable() { /* Start refinement from all cells -> push 'em all in the splitting queue */ for (Partition::Cell* cell = p.first_cell; cell; cell = cell->next)
p.splitting_queue_add(cell);
/*------------------------------------------------------------------------- * * Routines for handling the canonical labeling *
*-------------------------------------------------------------------------*/
/** \internal * Assign the labeling induced by the current partition 'this.p' to * \a labeling. * That is, if the partition is [[2,0],[1]], * then \a labeling will map 0 to 1, 1 to 2, and 2 to 0.
*/ void AbstractGraph::update_labeling(uint_pointer_substitute const labeling) { constunsignedint N = get_nof_vertices();
uint_pointer_substitute ep = p.elements; for (unsignedint i = 0; i < N; i++, ep++)
labeling[*ep] = i;
}
/** \internal * The same as update_labeling() except that the inverse of the labeling * is also produced and assigned to \a labeling_inv.
*/ void AbstractGraph::update_labeling_and_its_inverse(
uint_pointer_substitute const labeling,
uint_pointer_substitute const labeling_inv) { constunsignedint N = get_nof_vertices();
uint_pointer_substitute ep = p.elements;
uint_pointer_substitute clip = labeling_inv;
for (unsignedint i = 0; i < N;) {
labeling[*ep] = i;
i++;
*clip = *ep;
ep++;
clip++;
}
}
/*------------------------------------------------------------------------- * * Routines for handling automorphisms *
*-------------------------------------------------------------------------*/
/** \internal * Reset the permutation \a perm to the identity permutation.
*/ void AbstractGraph::reset_permutation(uint_pointer_substitute perm) { constunsignedint N = get_nof_vertices(); for (unsignedint i = 0; i < N; i++, perm++)
*perm = i;
}
/*------------------------------------------------------------------------- * * Long prune code *
*-------------------------------------------------------------------------*/
void AbstractGraph::long_prune_init() { constunsignedint N = get_nof_vertices();
long_prune_temp.clear();
long_prune_temp.resize(N); /* Of how many automorphisms we can store information in
the predefined, fixed amount of memory? */ constunsignedint nof_fitting_in_max_mem
= (long_prune_options_max_mem * 1024 * 1024) / (((N * 2) / 8) + 1);
long_prune_max_stored_autss = long_prune_options_max_stored_auts; /* Had some problems with g++ in using (a<b)?a:b when constants involved,
so had to make this in a stupid way... */ if (nof_fitting_in_max_mem < long_prune_options_max_stored_auts)
long_prune_max_stored_autss = nof_fitting_in_max_mem;
void AbstractGraph::long_prune_deallocate() { for (std::vector<std::vector<bool> >::iterator it
= long_prune_fixed.begin();
it < long_prune_fixed.end();
++it) {
it->clear();
} for (std::vector<std::vector<bool> >::iterator it = long_prune_mcrs.begin();
it < long_prune_mcrs.end();
++it) {
it->clear();
}
}
void AbstractGraph::long_prune_add_automorphism(
uint_pointer_to_const_substitute aut) { if (long_prune_max_stored_autss == 0) return;
constunsignedint N = get_nof_vertices();
/* If the buffer of stored auts is full, remove the oldest aut */ if (long_prune_end - long_prune_begin == long_prune_max_stored_autss) {
long_prune_begin++;
}
long_prune_end++;
std::vector<bool>& fixed = long_prune_allocget_fixed(long_prune_end - 1);
std::vector<bool>& mcrs = long_prune_allocget_mcrs(long_prune_end - 1); /* Mark nodes that are (i) fixed or (ii) minimal orbit representatives
* under the automorphism 'aut' */ for (unsignedint i = 0; i < N; i++) {
fixed[i] = (aut[i] == i); if (long_prune_temp[i] == false) {
mcrs[i] = true; unsignedint j = aut[i]; while (j != i) {
long_prune_temp[j] = true;
j = aut[j];
}
} else {
mcrs[i] = false;
} /* Clear the temp array on-the-fly... */
long_prune_temp[i] = false;
}
}
/*------------------------------------------------------------------------- * * Routines for handling orbit information *
*-------------------------------------------------------------------------*/
void AbstractGraph::update_orbit_information(Orbit& o,
uint_pointer_substitute perm) { constunsignedint N = get_nof_vertices(); for (unsignedint i = 0; i < N; i++) if (perm[i] != i)
o.merge_orbits(i, perm[i]);
}
/*------------------------------------------------------------------------- * * The actual backtracking search *
*-------------------------------------------------------------------------*/
class TreeNode { // friend class AbstractGraph; public: unsignedint split_cell_first;
int split_element; staticconstint SPLIT_START = -1; staticconstint SPLIT_END = -2;
/* Free old first path data structures */
first_path_labeling_vec.clear();
first_path_labeling_inv_vec.clear();
first_path_automorphism_vec.clear();
/* Free old best path data structures */
best_path_labeling_vec.clear();
best_path_labeling_inv_vec.clear();
best_path_automorphism_vec.clear();
if (N == 0) { /* Nothing to do, return... */ return;
}
/* Initialize the partition ... */
p.init(N); /* ... and the component recursion data structures in the partition */ if (opt_use_comprec)
p.cr_init();
neighbour_heap.init(N);
in_search = false; /* Do not compute certificate when building the initial partition */
refine_compare_certificate = false; /* The 'eqref_hash' hash value is not computed when building * the initial partition as it is not used for anything at the moment.
* This saves some cycles. */
compute_eqref_hash = false;
Timer timer1;
make_initial_equitable_partition();
if (verbstr and verbose_level >= 2) {
fprintf(verbstr, "Initial partition computed in %.2f seconds\n",
timer1.get_duration());
fflush(verbstr);
}
/* * Allocate space for the "first path" and "best path" labelings
*/
first_path_labeling_vec.clear();
first_path_labeling_vec.resize(N, 0);
first_path_labeling = first_path_labeling_vec.begin();
/* * Is the initial partition discrete?
*/ if (p.is_discrete()) { /* Make the best path labeling i.e. the canonical labeling */
update_labeling(best_path_labeling); /* Update statistics */
stats.nof_leaf_nodes = 1; return;
}
/* * Allocate the inverses of the "first path" and "best path" labelings
*/
first_path_labeling_inv_vec.clear();
first_path_labeling_inv_vec.resize(N, 0);
first_path_labeling_inv = first_path_labeling_inv_vec.begin();
/* * Allocate space for the automorphisms
*/
first_path_automorphism_vec.clear();
first_path_automorphism_vec.resize(N);
first_path_automorphism = first_path_automorphism_vec.begin();
/* Save component recursion info for backtracking */
root.cr_level = cr_level;
root.cr_cep_stack_size = cr_cep_stack.size();
root.cr_cep_index = cr_cep_index;
search_stack.push_back(root);
}
/* * Set status and global flags for search related procedures
*/
in_search = true; /* Do not compare certificates during refinement until the first path has
* been traversed to the leaf */
refine_compare_certificate = false;
/* * The actual backtracking search
*/ while (!search_stack.empty()) {
TreeNode& current_node = search_stack.back(); constunsignedint current_level = (unsignedint) search_stack.size() - 1;
if (opt_use_comprec) {
CR_CEP& cep = cr_cep_stack[current_node.cr_cep_index]; if (cep.first_checked == true and current_node.fp_extendable == TreeNode::MAYBE and !search_stack[cep.creation_level].fp_on) {
current_node.fp_extendable = TreeNode::NO;
}
}
if (current_node.fp_on) { if (current_node.split_element == TreeNode::SPLIT_END) {
search_stack.pop_back(); continue;
}
} else { if (current_node.fp_extendable == TreeNode::YES) {
search_stack.pop_back(); continue;
} if (current_node.split_element == TreeNode::SPLIT_END) { if (opt_use_failure_recording) {
TreeNode& parent_node = search_stack[current_level - 1]; if (parent_node.fp_on)
failure_recording_hashes[current_level - 1].insert(
current_node.failure_recording_ival);
}
search_stack.pop_back(); continue;
} if (current_node.fp_extendable == TreeNode::NO and (!canonical or current_node.cmp_to_best_path < 0)) { if (opt_use_failure_recording) {
TreeNode& parent_node = search_stack[current_level - 1]; if (parent_node.fp_on)
failure_recording_hashes[current_level - 1].insert(
current_node.failure_recording_ival);
}
search_stack.pop_back(); continue;
}
}
/* Restore partition ... */
p.goto_backtrack_point(current_node.partition_bt_point); /* ... and re-remember backtracking point */
current_node.partition_bt_point = p.set_backtrack_point();
/* * Update long prune redundancy sets
*/ if (opt_use_long_prune and current_level >= 1 and !current_node.fp_on) { unsignedint begin = (current_node.long_prune_begin > long_prune_begin)
? current_node.long_prune_begin
: long_prune_begin; for (unsignedint i = begin; i < long_prune_end; i++) { const std::vector<bool>& fixed = long_prune_get_fixed(i); #ifdefined(BLISS_CONSISTENCY_CHECKS) for (unsignedint l = 0; l < search_stack.size() - 2; l++)
assert(fixed[search_stack[l].split_element]); #endif if (fixed[search_stack[search_stack.size() - 1 - 1].split_element]
== false) {
long_prune_swap(begin, i);
begin++;
current_node.long_prune_begin = begin; continue;
}
}
if (current_node.split_element == TreeNode::SPLIT_START) {
current_node.needs_long_prune = true;
} elseif (current_node.needs_long_prune) {
current_node.needs_long_prune = false;
begin = (current_node.long_prune_begin > long_prune_begin)
? current_node.long_prune_begin
: long_prune_begin; for (unsignedint i = begin; i < long_prune_end; i++) { const std::vector<bool>& fixed = long_prune_get_fixed(i); #ifdefined(BLISS_CONSISTENCY_CHECKS) for (unsignedint l = 0; l < search_stack.size() - 2; l++)
assert(fixed[search_stack[l].split_element]); #endif
assert(fixed[search_stack[current_level - 1].split_element]
== true); if (fixed[search_stack[current_level - 1].split_element] == false) {
long_prune_swap(begin, i);
begin++;
current_node.long_prune_begin = begin; continue;
} const std::vector<bool>& mcrs = long_prune_get_mcrs(i);
uint_pointer_substitute ep = p.elements + cell->first; for (unsignedint j = cell->length; j > 0; j--, ep++) { if (mcrs[*ep] == false)
current_node.long_prune_redundant.insert(*ep);
}
}
}
}
/* * Find the next smallest, non-isomorphic element in the cell and * store it in current_node.split_element
*/
{ unsignedint next_split_element = UINT_MAX;
uint_pointer_substitute next_split_element_pos;
uint_pointer_substitute ep = p.elements + cell->first; if (current_node.fp_on) { /* Find the next larger splitting element that is
* a minimal orbit representative w.r.t. first_path_orbits */ for (unsignedint i = cell->length; i > 0; i--, ep++) { if ((int) (*ep) > current_node.split_element and *ep < next_split_element and first_path_orbits.is_minimal_representative(*ep)) {
next_split_element = *ep;
next_split_element_pos = ep;
}
}
} elseif (current_node.in_best_path) { /* Find the next larger splitting element that is
* a minimal orbit representative w.r.t. best_path_orbits */ for (unsignedint i = cell->length; i > 0; i--, ep++) { if ((int) (*ep) > current_node.split_element and *ep < next_split_element and best_path_orbits.is_minimal_representative(*ep) and (!opt_use_long_prune or current_node.long_prune_redundant.find(*ep)
== current_node.long_prune_redundant.end())) {
next_split_element = *ep;
next_split_element_pos = ep;
}
}
} else { /* Find the next larger splitting element */ for (unsignedint i = cell->length; i > 0; i--, ep++) { if ((int) (*ep) > current_node.split_element and *ep < next_split_element and (!opt_use_long_prune or current_node.long_prune_redundant.find(*ep)
== current_node.long_prune_redundant.end())) {
next_split_element = *ep;
next_split_element_pos = ep;
}
}
} if (next_split_element == UINT_MAX) { /* No more (unexplored children) in the cell */
current_node.split_element = TreeNode::SPLIT_END; if (current_node.fp_on) { /* Update group size */ constunsignedint index = first_path_orbits.orbit_size(
first_path_info[search_stack.size() - 1].splitting_element);
stats.group_size.multiply(index);
stats.group_size_approx *= (longdouble) index; /* * Update all_same_level
*/ if (index == cell->length and all_same_level == current_level + 1)
all_same_level = current_level; if (verbstr and verbose_level >= 2) {
fprintf(verbstr, "Level %u: orbits=%u, index=%u/%u, all_same_level=%u\n",
current_level,
first_path_orbits.nof_orbits(),
index,
cell->length,
all_same_level);
fflush(verbstr);
}
} continue;
}
/* Split on smallest */
current_node.split_element = next_split_element;
}
constunsignedint child_level = current_level + 1; /* Update some statistics */
stats.nof_nodes++; if (search_stack.size() > stats.max_level)
stats.max_level = search_stack.size();
/* Set flags and indices for the refiner certificate builder */
refine_equal_to_first = current_node.fp_cert_equal;
refine_cmp_to_best = current_node.cmp_to_best_path; if (!first_path_info.empty()) { if (refine_equal_to_first)
refine_first_path_subcertificate_end
= first_path_info[search_stack.size() - 1].certificate_index
+ first_path_info[search_stack.size() - 1]
.subcertificate_length; if (canonical) { if (refine_cmp_to_best == 0)
refine_best_path_subcertificate_end
= best_path_info[search_stack.size() - 1].certificate_index
+ best_path_info[search_stack.size() - 1]
.subcertificate_length;
} else
refine_cmp_to_best = -1;
}
/* Individualize, i.e. split the cell in two, the latter new cell
* will be a unit one containing info.split_element */
Partition::Cell* const new_cell
= p.individualize(cell, current_node.split_element);
/* * Refine the new partition to equitable
*/ if (cell->is_unit())
refine_to_equitable(cell, new_cell); else
refine_to_equitable(new_cell);
/* Update statistics */ if (p.is_discrete())
stats.nof_leaf_nodes++;
if (!first_path_info.empty()) { /* We are no longer on the first path */ constunsignedint subcertificate_length
= certificate_current_path.size() - certificate_index; if (refine_equal_to_first) { /* Was equal to the first path so far */
PathInfo& first_pinfo = first_path_info[current_level];
assert(first_pinfo.certificate_index == certificate_index); if (subcertificate_length != first_pinfo.subcertificate_length) {
refine_equal_to_first = false; if (opt_use_failure_recording)
failure_recording_fp_deviation = subcertificate_length;
} elseif (first_pinfo.eqref_hash.cmp(eqref_hash) != 0) {
refine_equal_to_first = false; if (opt_use_failure_recording)
failure_recording_fp_deviation = eqref_hash.get_value();
}
} if (canonical and (refine_cmp_to_best == 0)) { /* Was equal to the best path so far */
PathInfo& bestp_info = best_path_info[current_level];
assert(bestp_info.certificate_index == certificate_index); if (subcertificate_length < bestp_info.subcertificate_length) {
refine_cmp_to_best = -1;
} elseif (subcertificate_length > bestp_info.subcertificate_length) {
refine_cmp_to_best = 1;
} elseif (bestp_info.eqref_hash.cmp(eqref_hash) > 0) {
refine_cmp_to_best = -1;
} elseif (bestp_info.eqref_hash.cmp(eqref_hash) < 0) {
refine_cmp_to_best = 1;
}
}
if (opt_use_failure_recording and was_fp_cert_equal and !refine_equal_to_first) {
UintSeqHash k;
k.update(failure_recording_fp_deviation);
k.update(eqref_hash.get_value());
failure_recording_fp_deviation = k.get_value();
if (current_node.fp_on)
failure_recording_hashes[current_level].insert(
failure_recording_fp_deviation); else { for (unsignedint i = current_level; i > 0; i--) { if (search_stack[i].fp_on) break; const FailureRecordingSet& s = failure_recording_hashes[i]; if (i == current_level and s.find(failure_recording_fp_deviation) != s.end()) break; if (s.find(0) != s.end()) break;
search_stack[i].fp_extendable = TreeNode::NO;
}
}
}
/* Check if no longer equal to the first path and, * if canonical labeling is desired, also worse than the
* current best path */ if (refine_equal_to_first == false and (!canonical or (refine_cmp_to_best < 0))) { /* Yes, backtrack */
stats.nof_bad_nodes++; if (current_node.fp_cert_equal == true and current_level + 1 > all_same_level) {
assert(all_same_level >= 1); for (unsignedint i = all_same_level; i < search_stack.size();
i++) {
search_stack[i].fp_extendable = TreeNode::NO;
}
}
continue;
}
}
#ifdefined(BLISS_VERIFY_EQUITABLEDNESS) /* The new partition should be equitable */ if (!is_equitable())
fatal_error("consistency check failed - partition after refinement is " "not equitable"); #endif
/* * Next level search tree node info
*/
TreeNode child_node;
/* No more in the first path */
child_node.fp_on = false; /* No more in the best path */
child_node.in_best_path = false;
child_node.fp_cert_equal = refine_equal_to_first; if (current_node.fp_extendable == TreeNode::NO or (current_node.fp_extendable == TreeNode::MAYBE and child_node.fp_cert_equal == false))
child_node.fp_extendable = TreeNode::NO; else
child_node.fp_extendable = TreeNode::MAYBE;
child_node.cmp_to_best_path = refine_cmp_to_best;
/* * Backtrack to the previous level
*/ continue;
}
if (p.is_discrete() and child_node.fp_cert_equal) { /* * A leaf node that is equal to the first one. * An automorphism found: aut[i] = elements[first_path_labeling[i]]
*/ goto handle_first_path_automorphism;
}
if (!p.is_discrete()) {
Partition::Cell* next_split_cell = 0; /* * An internal, non-leaf node
*/ if (opt_use_comprec) {
assert(p.nof_discrete_cells()
<= cr_cep_stack[cr_cep_index].discrete_cell_limit);
assert(cr_level == child_node.cr_level);
if (p.nof_discrete_cells()
== cr_cep_stack[cr_cep_index].discrete_cell_limit) { /* We have reached the end of a component */
assert(cr_cep_index != 0);
CR_CEP& cep = cr_cep_stack[cr_cep_index];
/* First, compare with respect to the first path */ if (first_path_info.empty() or child_node.fp_cert_equal) { if (cep.first_checked == false) { /* First time, go to the next component */
cep.first_checked = true;
} else {
assert(!first_path_info.empty());
assert(cep.creation_level < search_stack.size());
TreeNode& old_info = search_stack[cep.creation_level]; /* If the component was found when on the first path, * handle the found automorphism as the other
* first path automorphisms */ if (old_info.fp_on) goto handle_first_path_automorphism; /* Should never get here because of CR:FP */ //_INTERNAL_ERROR();
}
}
if (canonical and !first_path_info.empty() and child_node.cmp_to_best_path >= 0) { if (cep.best_checked == false) { /* First time, go to the next component */
cep.best_checked = true;
} else {
assert(cep.creation_level < search_stack.size());
TreeNode& old_info = search_stack[cep.creation_level]; if (child_node.cmp_to_best_path == 0) { /* If the component was found when on the best path, * handle the found automorphism as the other
* best path automorphisms */ if (old_info.in_best_path) goto handle_best_path_automorphism; /* Otherwise, we do not remember the automorhism as * we didn't memorize the path that was invariant * equal to the best one and passed through the * component.
* Thus we can only backtrack to the previous level */
child_node.cmp_to_best_path = -1; if (!child_node.fp_cert_equal) { continue;
}
} else {
assert(child_node.cmp_to_best_path > 0); if (old_info.in_best_path) {
stats.nof_canupdates++; /* * Update canonical labeling and its inverse
*/ for (unsignedint i = 0; i < N; i++) { if (p.get_cell(p.elements[i])->is_unit()) {
best_path_labeling[p.elements[i]] = i;
best_path_labeling_inv[i] = p.elements[i];
}
} // update_labeling_and_its_inverse(best_path_labeling, // best_path_labeling_inv); /* Reset best path automorphism */
reset_permutation(best_path_automorphism); /* Reset best path orbit structure */
best_path_orbits.reset(); /* Mark to be the best one and save prefix */ unsignedint postfix_start = cep.creation_level;
assert(postfix_start < best_path_info.size()); while (p.get_cell(
best_path_info[postfix_start].splitting_element)
->is_unit()) {
postfix_start++;
assert(postfix_start < best_path_info.size());
} unsignedint postfix_start_cert
= best_path_info[postfix_start].certificate_index;
std::vector<PathInfo> best_path_temp = best_path_info;
best_path_info.clear(); for (unsignedint i = 0; i < search_stack.size(); i++) {
TreeNode& ss_info = search_stack[i];
PathInfo bp_info;
ss_info.cmp_to_best_path = 0;
ss_info.in_best_path = true;
bp_info.splitting_element = ss_info.split_element;
bp_info.certificate_index = ss_info.certificate_index;
bp_info.subcertificate_length
= ss_info.subcertificate_length;
bp_info.eqref_hash = ss_info.eqref_hash;
best_path_info.push_back(bp_info);
} /* Copy the postfix of the previous best path */ for (unsignedint i = postfix_start;
i < best_path_temp.size();
i++) {
best_path_info.push_back(best_path_temp[i]);
best_path_info[best_path_info.size() - 1]
.certificate_index
= best_path_info[best_path_info.size() - 2]
.certificate_index
+ best_path_info[best_path_info.size() - 2]
.subcertificate_length;
}
std::vector<unsignedint> certificate_best_path_old
= certificate_best_path;
certificate_best_path = certificate_current_path; for (unsignedint i = postfix_start_cert;
i < certificate_best_path_old.size();
i++)
certificate_best_path.push_back(
certificate_best_path_old[i]);
assert(
certificate_best_path.size()
== best_path_info.back().certificate_index
+ best_path_info.back().subcertificate_length); /* Backtrack to the previous level */ continue;
}
}
}
}
/* No backtracking performed, go to next componenet */
cr_level = cep.next_cr_level;
cr_cep_index = cep.next_cep_index;
}
/* Check if the current component has been split into
* new non-uniformity subcomponents */ // if(nucr_find_first_component(cr_level) == true and // p.nof_discrete_cells() + cr_component_elements < // cr_cep_stack[cr_cep_index].discrete_cell_limit) if (nucr_find_first_component(cr_level,
cr_component,
cr_component_elements,
next_split_cell)
== true and p.nof_discrete_cells() + cr_component_elements
< cr_cep_stack[cr_cep_index].discrete_cell_limit) { constunsignedint next_cr_level
= p.cr_split_level(cr_level, cr_component);
CR_CEP cep;
cep.creation_level = search_stack.size();
cep.discrete_cell_limit
= p.nof_discrete_cells() + cr_component_elements;
cep.next_cr_level = cr_level;
cep.next_cep_index = cr_cep_index;
cep.first_checked = false;
cep.best_checked = false;
cr_cep_index = cr_cep_stack.size();
cr_cep_stack.push_back(cep);
cr_level = next_cr_level;
}
}
/* * Build the next node info
*/ /* Find the next cell to be splitted */ if (!next_split_cell)
next_split_cell = find_next_cell_to_be_splitted(
p.get_cell(p.elements[current_node.split_cell_first])); // Partition::Cell * const next_split_cell = // find_next_cell_to_be_splitted(p.get_cell(p.elements[current_node.split_cell_first]));
child_node.split_cell_first = next_split_cell->first;
child_node.split_element = TreeNode::SPLIT_START;
child_node.certificate_index = certificate_index;
child_node.partition_bt_point = p.set_backtrack_point();
child_node.long_prune_redundant.clear();
child_node.long_prune_begin = current_node.long_prune_begin;
/* Save component recursion info for backtracking */
child_node.cr_level = cr_level;
child_node.cr_cep_stack_size = cr_cep_stack.size();
child_node.cr_cep_index = cr_cep_index;
search_stack.push_back(child_node); continue;
}
/* * A leaf node not in the first path or equivalent to the first path
*/
if (child_node.cmp_to_best_path > 0) { /* * A new, better representative found
*/ // fprintf(stdout, "Level %u: NEW BEST\n", child_level); fflush(stdout);
stats.nof_canupdates++; /* * Update canonical labeling and its inverse
*/
update_labeling_and_its_inverse(best_path_labeling,
best_path_labeling_inv); /* Reset best path automorphism */
reset_permutation(best_path_automorphism); /* Reset best path orbit structure */
best_path_orbits.reset(); /* * Mark the current path to be the best one and save it
*/ constunsignedint base_size = search_stack.size();
assert(current_level + 1 == base_size);
best_path_info.clear(); for (unsignedint i = 0; i < base_size; i++) {
search_stack[i].cmp_to_best_path = 0;
search_stack[i].in_best_path = true;
PathInfo path_info;
path_info.splitting_element = search_stack[i].split_element;
path_info.certificate_index = search_stack[i].certificate_index;
path_info.subcertificate_length
= search_stack[i].subcertificate_length;
path_info.eqref_hash = search_stack[i].eqref_hash;
best_path_info.push_back(path_info);
}
certificate_best_path = certificate_current_path; /* * Backtrack to the previous level
*/ continue;
}
handle_best_path_automorphism: /* * * Best path automorphism handling *
*/
{ /* * Equal to the previous best path
*/ if (p.is_discrete()) { #ifdefined(BLISS_CONSISTENCY_CHECKS) /* Verify that the automorphism is correctly built */ for (unsignedint i = 0; i < N; i++)
assert(best_path_automorphism[i]
== p.elements[best_path_labeling[i]]); #endif
} else { /* An automorphism that was found before the partition was discrete.
* Set the image of all elements in non-disrete cells accordingly */ for (Partition::Cell* c = p.first_nonsingleton_cell; c;
c = c->next_nonsingleton) { for (unsignedint i = c->first; i < c->first + c->length; i++) if (p.get_cell(p.elements[best_path_labeling[p.elements[i]]])
->is_unit())
best_path_automorphism
[p.elements[best_path_labeling[p.elements[i]]]]
= p.elements[i]; else
best_path_automorphism[p.elements[i]] = p.elements[i];
}
}
#ifdefined(BLISS_VERIFY_AUTOMORPHISMS) /* Verify that it really is an automorphism */ if (!is_automorphism(best_path_automorphism))
fatal_error("Best path automorhism validation check failed"); #endif
unsignedint gca_level_with_first = 0; for (unsignedint i = search_stack.size(); i > 0; i--) { if ((int) first_path_info[gca_level_with_first].splitting_element
!= search_stack[gca_level_with_first].split_element) break;
gca_level_with_first++;
}
unsignedint gca_level_with_best = 0; for (unsignedint i = search_stack.size(); i > 0; i--) { if ((int) best_path_info[gca_level_with_best].splitting_element
!= search_stack[gca_level_with_best].split_element) break;
gca_level_with_best++;
}
if (opt_use_long_prune) { /* Record automorphism */
long_prune_add_automorphism(best_path_automorphism);
}
/* * Update orbit information
*/
update_orbit_information(best_path_orbits, best_path_automorphism);
/* * Update orbit information
*/ constunsignedint nof_old_orbits = first_path_orbits.nof_orbits();
update_orbit_information(first_path_orbits, best_path_automorphism); if (nof_old_orbits != first_path_orbits.nof_orbits()) { /* Some orbits were merged */ /* Report automorphism */ if (report_hook)
(*report_hook)(report_user_param,
get_nof_vertices(),
&(*best_path_automorphism)); /* Update statistics */
stats.nof_generators++;
}
if (p.is_discrete()) { #ifdefined(BLISS_CONSISTENCY_CHECKS) /* Verify that the complete automorphism is correctly built */ for (unsignedint i = 0; i < N; i++)
assert(first_path_automorphism[i]
== p.elements[first_path_labeling[i]]); #endif
} else { /* An automorphism that was found before the partition was discrete.
* Set the image of all elements in non-disrete cells accordingly */ for (Partition::Cell* c = p.first_nonsingleton_cell; c;
c = c->next_nonsingleton) { for (unsignedint i = c->first; i < c->first + c->length; i++) if (p.get_cell(p.elements[first_path_labeling[p.elements[i]]])
->is_unit())
first_path_automorphism
[p.elements[first_path_labeling[p.elements[i]]]]
= p.elements[i]; else
first_path_automorphism[p.elements[i]] = p.elements[i];
}
}
#ifdefined(BLISS_VERIFY_AUTOMORPHISMS) /* Verify that it really is an automorphism */ if (!is_automorphism(first_path_automorphism))
fatal_error("First path automorphism validation check failed"); #endif
if (opt_use_long_prune) {
long_prune_add_automorphism(first_path_automorphism);
}
/* * Update orbit information
*/
update_orbit_information(first_path_orbits, first_path_automorphism);
/* * Compute backjumping level
*/ for (unsignedint i = 0; i < search_stack.size(); i++) {
TreeNode& n = search_stack[i]; if (n.fp_on) {
;
} else {
n.fp_extendable = TreeNode::YES;
}
}
/* Report automorphism by calling the user defined hook function */ if (report_hook)
(*report_hook)(
report_user_param, get_nof_vertices(), &(*first_path_automorphism));
void Digraph::Vertex::remove_duplicate_edges(std::vector<bool>& tmp) { #ifdefined(BLISS_CONSISTENCY_CHECKS) /* Pre-conditions */ for (unsignedint i = 0; i < tmp.size(); i++)
assert(tmp[i] == false); #endif for (std::vector<unsignedint>::iterator iter = edges_out.begin();
iter != edges_out.end();) { constunsignedint dest_vertex = *iter; if (tmp[dest_vertex] == true) { /* A duplicate edge found! */
iter = edges_out.erase(iter);
} else { /* Not seen earlier, mark as seen */
tmp[dest_vertex] = true;
iter++;
}
}
/* Clear tmp */ for (std::vector<unsignedint>::iterator iter = edges_out.begin();
iter != edges_out.end();
iter++) {
tmp[*iter] = false;
}
for (std::vector<unsignedint>::iterator iter = edges_in.begin();
iter != edges_in.end();) { constunsignedint dest_vertex = *iter; if (tmp[dest_vertex] == true) { /* A duplicate edge found! */
iter = edges_in.erase(iter);
} else { /* Not seen earlier, mark as seen */
tmp[dest_vertex] = true;
iter++;
}
}
/* Clear tmp */ for (std::vector<unsignedint>::iterator iter = edges_in.begin();
iter != edges_in.end();
iter++) {
tmp[*iter] = false;
} #ifdefined(BLISS_CONSISTENCY_CHECKS) /* Post-conditions */ for (unsignedint i = 0; i < tmp.size(); i++)
assert(tmp[i] == false); #endif
}
/** * Sort the edges entering and leaving the vertex according to * the vertex number of the other edge end. * Time complexity: O(e log(e)), where e is the number of edges * entering/leaving the vertex.
*/ void Digraph::Vertex::sort_edges() {
std::sort(edges_in.begin(), edges_in.end());
std::sort(edges_out.begin(), edges_out.end());
}
/*------------------------------------------------------------------------- * * Constructor and destructor for directed graphs *
*-------------------------------------------------------------------------*/
Digraph::Digraph(constunsignedint nof_vertices) {
vertices.resize(nof_vertices);
sh = shs_flm;
}
void Digraph::sort_edges() { for (unsignedint i = 0; i < get_nof_vertices(); i++)
vertices[i].sort_edges();
}
int Digraph::cmp(Digraph& other) { /* Compare the numbers of vertices */ if (get_nof_vertices() < other.get_nof_vertices()) return -1; if (get_nof_vertices() > other.get_nof_vertices()) return 1; /* Compare vertex colors */ for (unsignedint i = 0; i < get_nof_vertices(); i++) { if (vertices[i].color < other.vertices[i].color) return -1; if (vertices[i].color > other.vertices[i].color) return 1;
} /* Compare vertex degrees */
remove_duplicate_edges();
other.remove_duplicate_edges(); for (unsignedint i = 0; i < get_nof_vertices(); i++) { if (vertices[i].nof_edges_in() < other.vertices[i].nof_edges_in()) return -1; if (vertices[i].nof_edges_in() > other.vertices[i].nof_edges_in()) return 1; if (vertices[i].nof_edges_out() < other.vertices[i].nof_edges_out()) return -1; if (vertices[i].nof_edges_out() > other.vertices[i].nof_edges_out()) return 1;
} /* Compare edges */ for (unsignedint i = 0; i < get_nof_vertices(); i++) {
Vertex& v1 = vertices[i];
Vertex& v2 = other.vertices[i];
v1.sort_edges();
v2.sort_edges();
std::vector<unsignedint>::const_iterator ei1 = v1.edges_in.begin();
std::vector<unsignedint>::const_iterator ei2 = v2.edges_in.begin(); while (ei1 != v1.edges_in.end()) { if (*ei1 < *ei2) return -1; if (*ei1 > *ei2) return 1;
ei1++;
ei2++;
}
ei1 = v1.edges_out.begin();
ei2 = v2.edges_out.begin(); while (ei1 != v1.edges_out.end()) { if (*ei1 < *ei2) return -1; if (*ei1 > *ei2) return 1;
ei1++;
ei2++;
}
} return 0;
}
Digraph* Digraph::permute(const std::vector<unsignedint>& perm) const {
Digraph* const g = new Digraph(get_nof_vertices()); for (unsignedint i = 0; i < get_nof_vertices(); i++) { const Vertex& v = vertices[i];
g->change_color(perm[i], v.color); for (std::vector<unsignedint>::const_iterator ei = v.edges_out.begin();
ei != v.edges_out.end();
ei++) {
g->add_edge(perm[i], perm[*ei]);
}
}
g->sort_edges(); return g;
}
Digraph* Digraph::permute(constunsignedint* const perm) const {
Digraph* const g = new Digraph(get_nof_vertices()); for (unsignedint i = 0; i < get_nof_vertices(); i++) { const Vertex& v = vertices[i];
g->change_color(perm[i], v.color); for (std::vector<unsignedint>::const_iterator ei = v.edges_out.begin();
ei != v.edges_out.end();
ei++) {
g->add_edge(perm[i], perm[*ei]);
}
}
g->sort_edges(); return g;
}
/*------------------------------------------------------------------------- * * Print graph in graphviz format *
*-------------------------------------------------------------------------*/
for (std::vector<Vertex>::iterator vi = vertices.begin();
vi != vertices.end();
vi++) { #ifdefined(BLISS_EXPENSIVE_CONSISTENCY_CHECKS) for (unsignedint i = 0; i < tmp.size(); i++)
assert(tmp[i] == false); #endif
(*vi).remove_duplicate_edges(tmp);
}
}
/*------------------------------------------------------------------------- * * Get a hash value for the graph. *
*-------------------------------------------------------------------------*/
/* Hash the color of each vertex */ for (unsignedint i = 0; i < get_nof_vertices(); i++) {
h.update(vertices[i].color);
}
/* Hash the edges */ for (unsignedint i = 0; i < get_nof_vertices(); i++) {
Vertex& v = vertices[i]; for (std::vector<unsignedint>::const_iterator ei = v.edges_out.begin();
ei != v.edges_out.end();
ei++) {
h.update(i);
h.update(*ei);
}
}
return h.get_value();
}
/*------------------------------------------------------------------------- * * Read directed graph in the DIMACS format. * Returns 0 if an error occurred. *
*-------------------------------------------------------------------------*/
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.