/* 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/>.
*/
/** * \namespace bliss_digraphs * The namespace bliss_digraphs contains all the classes and functions of the bliss * tool except for the C programming language API.
*/ namespace bliss_digraphs { class AbstractGraph;
}
/** * \brief Statistics returned by the bliss search algorithm.
*/ class Stats
{ friendclass AbstractGraph; /** \internal The size of the automorphism group. */
BigNum group_size; /** \internal An approximation (due to possible overflows) of
* the size of the automorphism group. */ longdouble group_size_approx; /** \internal The number of nodes in the search tree. */ longunsignedint nof_nodes; /** \internal The number of leaf nodes in the search tree. */ longunsignedint nof_leaf_nodes; /** \internal The number of bad nodes in the search tree. */ longunsignedint nof_bad_nodes; /** \internal The number of canonical representative updates. */ longunsignedint nof_canupdates; /** \internal The number of generator permutations. */ longunsignedint nof_generators; /** \internal The maximal depth of the search tree. */ unsignedlongint max_level; /** */ void reset()
{
group_size.assign(1);
group_size_approx = 1.0;
nof_nodes = 0;
nof_leaf_nodes = 0;
nof_bad_nodes = 0;
nof_canupdates = 0;
nof_generators = 0;
max_level = 0;
} public:
Stats() { reset(); } /** Print the statistics. */
size_t print(FILE* const fp) const
{
size_t r = 0;
r += fprintf(fp, "Nodes: %lu\n", nof_nodes);
r += fprintf(fp, "Leaf nodes: %lu\n", nof_leaf_nodes);
r += fprintf(fp, "Bad nodes: %lu\n", nof_bad_nodes);
r += fprintf(fp, "Canrep updates: %lu\n", nof_canupdates);
r += fprintf(fp, "Generators: %lu\n", nof_generators);
r += fprintf(fp, "Max level: %lu\n", max_level);
r += fprintf(fp, "|Aut|: ")+group_size.print(fp)+fprintf(fp, "\n");
fflush(fp); return r;
}
BigNum get_group_size() const { return group_size; }
/** An approximation (due to possible overflows/rounding errors) of
* the size of the automorphism group. */ longdouble get_group_size_approx() const {return group_size_approx;} /** The number of nodes in the search tree. */ longunsignedint get_nof_nodes() const {return nof_nodes;} /** The number of leaf nodes in the search tree. */ longunsignedint get_nof_leaf_nodes() const {return nof_leaf_nodes;} /** The number of bad nodes in the search tree. */ longunsignedint get_nof_bad_nodes() const {return nof_bad_nodes;} /** The number of canonical representative updates. */ longunsignedint get_nof_canupdates() const {return nof_canupdates;} /** The number of generator permutations. */ longunsignedint get_nof_generators() const {return nof_generators;} /** The maximal depth of the search tree. */ unsignedlongint get_max_level() const {return max_level;}
};
/** * \brief An abstract base class for different types of graphs.
*/ class AbstractGraph
{ friendclass Partition;
/** * Set the verbose output level for the algorithms. * \param level the level of verbose output, 0 means no verbose output
*/ void set_verbose_level(constunsignedint level);
/** * Set the file stream for the verbose output. * \param fp the file stream; if null, no verbose output is written
*/ void set_verbose_file(FILE * const fp);
/** * Add a new vertex with color \a color in the graph and return its index.
*/ virtualunsignedint add_vertex(constunsignedint color = 0) = 0;
/** * Add an edge between vertices \a source and \a target. * Duplicate edges between vertices are ignored but try to avoid introducing * them in the first place as they are not ignored immediately but will * consume memory and computation resources for a while.
*/ virtualvoid add_edge(constunsignedint source, constunsignedint target) = 0;
/** * Change the color of the vertex \a vertex to \a color.
*/ virtualvoid change_color(constunsignedint vertex, constunsignedint color) = 0;
/** * Check whether \a perm is an automorphism of this graph. * Unoptimized, mainly for debugging purposes.
*/ virtualbool is_automorphism(const std::vector<unsignedint>& perm) const;
/** Activate/deactivate failure recording. * May not be called during the search, i.e. from an automorphism reporting * hook function. * \param active if true, activate failure recording, deactivate otherwise
*/ void set_failure_recording(constbool active) {assert(!in_search); opt_use_failure_recording = active;}
/** Activate/deactivate component recursion. * The choice affects the computed canonical labelings; * therefore, if you want to compare whether two graphs are isomorphic by * computing and comparing (for equality) their canonical versions, * be sure to use the same choice for both graphs. * May not be called during the search, i.e. from an automorphism reporting * hook function. * \param active if true, activate component recursion, deactivate otherwise
*/ void set_component_recursion(constbool active) {assert(!in_search); opt_use_comprec = active;}
/** * Return the number of vertices in the graph.
*/ virtualunsignedint get_nof_vertices() const = 0;
/** * Return a new graph that is the result of applying the permutation \a perm * to this graph. This graph is not modified. * \a perm must contain N=this.get_nof_vertices() elements and be a bijection * on {0,1,...,N-1}, otherwise the result is undefined or a segfault.
*/ virtual AbstractGraph* permute(constunsignedint* const perm) const = 0; virtual AbstractGraph* permute(const std::vector<unsignedint>& perm) const = 0;
/** * Find a set of generators for the automorphism group of the graph. * The function \a hook (if non-null) is called each time a new generator * for the automorphism group is found. * The first argument \a user_param for the hook is the * \a hook_user_param given below, * the second argument \a n is the length of the automorphism (equal to * get_nof_vertices()) and * the third argument \a aut is the automorphism * (a bijection on {0,...,get_nof_vertices()-1}). * The memory for the automorphism \a aut will be invalidated immediately * after the return from the hook function; * if you want to use the automorphism later, you have to take a copy of it. * Do not call any member functions in the hook. * The search statistics are copied in \a stats.
*/ void find_automorphisms(Stats& stats, void (*hook)(void* user_param, unsignedint n, constunsignedint* aut), void* hook_user_param);
/** * Otherwise the same as find_automorphisms() except that * a canonical labeling of the graph (a bijection on * {0,...,get_nof_vertices()-1}) is returned. * The memory allocated for the returned canonical labeling will remain * valid only until the next call to a member function with the exception * that constant member functions (for example, bliss::Graph::permute()) can * be called without invalidating the labeling. * To compute the canonical version of an undirected graph, call this * function and then bliss::Graph::permute() with the returned canonical * labeling. * Note that the computed canonical version may depend on the applied version * of bliss as well as on some other options (for instance, the splitting * heuristic selected with bliss::Graph::set_splitting_heuristic()).
*/
uint_pointer_to_const_substitute canonical_form(Stats& stats, void (*hook)(void* user_param, unsignedint n, constunsignedint* aut), void* hook_user_param);
/** * Write the graph to a file in a variant of the DIMACS format. * See the <A href="http://www.tcs.hut.fi/Software/bliss/">bliss website</A> * for the definition of the file format. * Note that in the DIMACS file the vertices are numbered from 1 to N while * in this C++ API they are from 0 to N-1. * Thus the vertex n in the file corresponds to the vertex n-1 in the API. * \param fp the file stream where the graph is written
*/ virtualvoid write_dimacs(FILE * const fp) = 0;
/** * Write the graph to a file in the graphviz dotty format. * \param fp the file stream where the graph is written
*/ virtualvoid write_dot(FILE * const fp) = 0;
/** * Write the graph in a file in the graphviz dotty format. * Do nothing if the file cannot be written. * \param file_name the name of the file to which the graph is written
*/ virtualvoid write_dot(constchar * const file_name) = 0;
/** * Get a hash value for the graph. * \return the hash value
*/ virtualunsignedint get_hash() = 0;
/** * Disable/enable the "long prune" method. * The choice affects the computed canonical labelings; * therefore, if you want to compare whether two graphs are isomorphic by * computing and comparing (for equality) their canonical versions, * be sure to use the same choice for both graphs. * May not be called during the search, i.e. from an automorphism reporting * hook function. * \param active if true, activate "long prune", deactivate otherwise
*/ void set_long_prune_activity(constbool active) {
assert(!in_search);
opt_use_long_prune = active;
}
protected: /** \internal
* How much verbose output is produced (0 means none) */ unsignedint verbose_level; /** \internal
* The output stream for verbose output. */
FILE *verbstr; protected:
/** \internal
* The ordered partition used in the search algorithm. */
Partition p;
/** \internal * Whether the search for automorphisms and a canonical labeling is * in progress.
*/ bool in_search;
/** \internal * Is failure recording in use?
*/ bool opt_use_failure_recording; /* The "tree-specific" invariant value for the point when current path
* got different from the first path */ unsignedint failure_recording_fp_deviation;
/** \internal * Is component recursion in use?
*/ bool opt_use_comprec;
staticconstunsignedint CERT_SPLIT = 0; //UINT_MAX; staticconstunsignedint CERT_EDGE = 1; //UINT_MAX-1; /** \internal * Add a triple (v1,v2,v3) in the certificate. * May modify refine_equal_to_first and refine_cmp_to_best.
* May also update eqref_hash and failure_recording_fp_deviation. */ void cert_add(constunsignedint v1, constunsignedint v2, constunsignedint v3);
/** \internal * Add a redundant triple (v1,v2,v3) in the certificate. * Can also just dicard the triple. * May modify refine_equal_to_first and refine_cmp_to_best.
* May also update eqref_hash and failure_recording_fp_deviation. */ void cert_add_redundant(constunsignedint x, constunsignedint y, constunsignedint z);
/**\internal * Is the long prune method in use?
*/ bool opt_use_long_prune; /**\internal * Maximum amount of memory (in megabytes) available for * the long prune method
*/ staticconstunsignedint long_prune_options_max_mem = 50; /**\internal * Maximum amount of automorphisms stored for the long prune method; * less than this is stored if the memory limit above is reached first
*/ staticconstunsignedint long_prune_options_max_stored_auts = 100;
unsignedint long_prune_max_stored_autss;
std::vector<std::vector<bool> > long_prune_fixed;
std::vector<std::vector<bool> > long_prune_mcrs;
std::vector<bool> long_prune_temp; unsignedint long_prune_begin; unsignedint long_prune_end; /** \internal * Initialize the "long prune" data structures.
*/ void long_prune_init(); /** \internal * Release the memory allocated for "long prune" data structures.
*/ void long_prune_deallocate(); void long_prune_add_automorphism(uint_pointer_to_const_substitute aut);
std::vector<bool>& long_prune_get_fixed(constunsignedint index);
std::vector<bool>& long_prune_allocget_fixed(constunsignedint index);
std::vector<bool>& long_prune_get_mcrs(constunsignedint index);
std::vector<bool>& long_prune_allocget_mcrs(constunsignedint index); /** \internal * Swap the i:th and j:th stored automorphism information; * i and j must be "in window, i.e. in [long_prune_begin,long_prune_end[
*/ void long_prune_swap(constunsignedint i, constunsignedint j);
/* * Data structures and routines for refining the partition p into equitable
*/
Heap neighbour_heap; virtualbool split_neighbourhood_of_unit_cell(Partition::Cell *) = 0; virtualbool split_neighbourhood_of_cell(Partition::Cell * const) = 0; void refine_to_equitable(); void refine_to_equitable(Partition::Cell * const unit_cell); void refine_to_equitable(Partition::Cell * const unit_cell1, Partition::Cell * const unit_cell2);
/** \internal * \return false if it was detected that the current certificate * is different from the first and/or best (whether this is checked * depends on in_search and refine_compare_certificate flags.
*/ bool do_refine_to_equitable();
unsignedint eqref_max_certificate_index; /** \internal * Whether eqref_hash is updated during equitable refinement process.
*/ bool compute_eqref_hash;
UintSeqHash eqref_hash;
/** \internal * Check whether the current partition p is equitable. * Performance: very slow, use only for debugging purposes.
*/ virtualbool is_equitable() const = 0;
void (*report_hook)(void *user_param, unsignedint n, constunsignedint *aut); void *report_user_param;
/* * * Nonuniform component recursion (NUCR) *
*/
/** The currently traversed component */ unsignedint cr_level;
/** \internal * The "Component End Point" data structure
*/ class CR_CEP { public: /** At which level in the search was this CEP created */ unsignedint creation_level; /** The current component has been fully traversed when the partition has
* this many discrete cells left */ unsignedint discrete_cell_limit; /** The component to be traversed after the current one */ unsignedint next_cr_level; /** The next component end point */ unsignedint next_cep_index; bool first_checked; bool best_checked;
}; /** \internal * A stack for storing Component End Points
*/
std::vector<CR_CEP> cr_cep_stack;
/** \internal * Find the first non-uniformity component at the component recursion * level \a level. * The component is stored in \a cr_component. * If no component is found, \a cr_component is empty. * Returns false if all the cells in the component recursion level \a level * were discrete. * Modifies the max_ival and max_ival_count fields of Partition:Cell * (assumes that they are 0 when called and * quarantees that they are 0 when returned).
*/ virtualbool nucr_find_first_component(constunsignedint level) = 0; virtualbool nucr_find_first_component(constunsignedint level,
std::vector<unsignedint>& component, unsignedint& component_elements,
Partition::Cell*& sh_return) = 0; /** \internal * The non-uniformity component found by nucr_find_first_component() * is stored here.
*/
std::vector<unsignedint> cr_component; /** \internal * The number of vertices in the component \a cr_component
*/ unsignedint cr_component_elements;
};
/** * \brief The class for undirected, vertex colored graphs. * * Multiple edges between vertices are not allowed (i.e., are ignored).
*/ class Graph : public AbstractGraph
{ public: /** * The possible splitting heuristics. * The selected splitting heuristics affects the computed canonical * labelings; therefore, if you want to compare whether two graphs * are isomorphic by computing and comparing (for equality) their * canonical versions, be sure to use the same splitting heuristics * for both graphs.
*/ typedefenum { /** First non-unit cell. * Very fast but may result in large search spaces on difficult graphs.
* Use for large but easy graphs. */
shs_f = 0, /** First smallest non-unit cell.
* Fast, should usually produce smaller search spaces than shs_f. */
shs_fs, /** First largest non-unit cell.
* Fast, should usually produce smaller search spaces than shs_f. */
shs_fl, /** First maximally non-trivially connected non-unit cell. * Not so fast, should usually produce smaller search spaces than shs_f,
* shs_fs, and shs_fl. */
shs_fm, /** First smallest maximally non-trivially connected non-unit cell. * Not so fast, should usually produce smaller search spaces than shs_f,
* shs_fs, and shs_fl. */
shs_fsm, /** First largest maximally non-trivially connected non-unit cell. * Not so fast, should usually produce smaller search spaces than shs_f,
* shs_fs, and shs_fl. */
shs_flm
} SplittingHeuristic;
unsignedint color;
std::vector<unsignedint> edges; void clear() {
edges.clear();
} unsignedint nof_edges() const {return edges.size(); }
};
std::vector<Vertex> vertices; void sort_edges(); void remove_duplicate_edges(); public: void clear() { for (std::vector<Vertex>::iterator it = vertices.begin();
it < vertices.end();
++it) {
it->clear();
}
} protected: /** \internal * Partition independent invariant. * Returns the color of the vertex. * Time complexity: O(1).
*/ staticunsignedint vertex_color_invariant(const Graph* const g, constunsignedint v); /** \internal * Partition independent invariant. * Returns the degree of the vertex. * DUPLICATE EDGES MUST HAVE BEEN REMOVED BEFORE. * Time complexity: O(1).
*/ staticunsignedint degree_invariant(const Graph* const g, constunsignedint v); /** \internal * Partition independent invariant. * Returns 1 if there is an edge from the vertex to itself, 0 if not. * Time complexity: O(k), where k is the number of edges leaving the vertex.
*/ staticunsignedint selfloop_invariant(const Graph* const g, constunsignedint v);
/* * Routines needed when refining the partition p into equitable
*/ bool split_neighbourhood_of_unit_cell(Partition::Cell *); bool split_neighbourhood_of_cell(Partition::Cell * const);
public: /** * Create a new graph with \a N vertices and no edges.
*/
Graph(constunsignedint N = 0);
/** * Destroy the graph.
*/
~Graph();
/** * Read the graph from the file \a fp in a variant of the DIMACS format. * See the <A href="http://www.tcs.hut.fi/Software/bliss/">bliss website</A> * for the definition of the file format. * Note that in the DIMACS file the vertices are numbered from 1 to N while * in this C++ API they are from 0 to N-1. * Thus the vertex n in the file corresponds to the vertex n-1 in the API. * * \param fp the file stream for the graph file * \param errstr if non-null, the possible error messages are printed * in this file stream * \return a new Graph object or 0 if reading failed for some * reason
*/ static Graph* read_dimacs(FILE* const fp, FILE* const errstr = stderr);
/** * Write the graph to a file in a variant of the DIMACS format. * See the <A href="http://www.tcs.hut.fi/Software/bliss/">bliss website</A> * for the definition of the file format.
*/ void write_dimacs(FILE* const fp);
/** * Add a new vertex with color \a color in the graph and return its index.
*/ unsignedint add_vertex(constunsignedint color = 0);
/** * Add an edge between vertices \a v1 and \a v2. * Duplicate edges between vertices are ignored but try to avoid introducing * them in the first place as they are not ignored immediately but will * consume memory and computation resources for a while.
*/ void add_edge(constunsignedint v1, constunsignedint v2);
/** * Change the color of the vertex \a vertex to \a color.
*/ void change_color(constunsignedint vertex, constunsignedint color);
/** * Compare this graph with the graph \a other. * Returns 0 if the graphs are equal, and a negative (positive) integer * if this graph is "smaller than" ("greater than", resp.) than \a other.
*/ int cmp(Graph& other);
/** * Set the splitting heuristic used by the automorphism and canonical * labeling algorithm. * The selected splitting heuristics affects the computed canonical * labelings; therefore, if you want to compare whether two graphs * are isomorphic by computing and comparing (for equality) their * canonical versions, be sure to use the same splitting heuristics * for both graphs.
*/ void set_splitting_heuristic(const SplittingHeuristic shs) {sh = shs; }
};
/** * \brief The class for directed, vertex colored graphs. * * Multiple edges between vertices are not allowed (i.e., are ignored).
*/ class Digraph : public AbstractGraph
{ public: /** * The possible splitting heuristics. * The selected splitting heuristics affects the computed canonical * labelings; therefore, if you want to compare whether two graphs * are isomorphic by computing and comparing (for equality) their * canonical versions, be sure to use the same splitting heuristics * for both graphs.
*/ typedefenum { /** First non-unit cell. * Very fast but may result in large search spaces on difficult graphs.
* Use for large but easy graphs. */
shs_f = 0, /** First smallest non-unit cell.
* Fast, should usually produce smaller search spaces than shs_f. */
shs_fs, /** First largest non-unit cell.
* Fast, should usually produce smaller search spaces than shs_f. */
shs_fl, /** First maximally non-trivially connected non-unit cell. * Not so fast, should usually produce smaller search spaces than shs_f,
* shs_fs, and shs_fl. */
shs_fm, /** First smallest maximally non-trivially connected non-unit cell. * Not so fast, should usually produce smaller search spaces than shs_f,
* shs_fs, and shs_fl. */
shs_fsm, /** First largest maximally non-trivially connected non-unit cell. * Not so fast, should usually produce smaller search spaces than shs_f,
* shs_fs, and shs_fl. */
shs_flm
} SplittingHeuristic;
public: void clear() { for (std::vector<Vertex>::iterator it = vertices.begin();
it < vertices.end();
++it) {
it->clear();
}
} protected: void remove_duplicate_edges();
/** \internal * Partition independent invariant. * Returns the color of the vertex. * Time complexity: O(1).
*/ staticunsignedint vertex_color_invariant(const Digraph* const g, constunsignedint v); /** \internal * Partition independent invariant. * Returns the indegree of the vertex. * DUPLICATE EDGES MUST HAVE BEEN REMOVED BEFORE. * Time complexity: O(1).
*/ staticunsignedint indegree_invariant(const Digraph* const g, constunsignedint v); /** \internal * Partition independent invariant. * Returns the outdegree of the vertex. * DUPLICATE EDGES MUST HAVE BEEN REMOVED BEFORE. * Time complexity: O(1).
*/ staticunsignedint outdegree_invariant(const Digraph* const g, constunsignedint v); /** \internal * Partition independent invariant. * Returns 1 if there is an edge from the vertex to itself, 0 if not. * Time complexity: O(k), where k is the number of edges leaving the vertex.
*/ staticunsignedint selfloop_invariant(const Digraph* const g, constunsignedint v);
/** \internal * Refine the partition \a p according to * the partition independent invariant \a inv.
*/ bool refine_according_to_invariant(unsignedint (*inv)(const Digraph* const g, constunsignedint v));
/* * Routines needed when refining the partition p into equitable
*/ bool split_neighbourhood_of_unit_cell(Partition::Cell* const); bool split_neighbourhood_of_cell(Partition::Cell* const);
public: /** * Create a new directed graph with \a N vertices and no edges.
*/
Digraph(constunsignedint N = 0);
/** * Destroy the graph.
*/
~Digraph();
/** * Read the graph from the file \a fp in a variant of the DIMACS format. * See the <A href="http://www.tcs.hut.fi/Software/bliss/">bliss website</A> * for the definition of the file format. * Note that in the DIMACS file the vertices are numbered from 1 to N while * in this C++ API they are from 0 to N-1. * Thus the vertex n in the file corresponds to the vertex n-1 in the API. * \param fp the file stream for the graph file * \param errstr if non-null, the possible error messages are printed * in this file stream * \return a new Digraph object or 0 if reading failed for some * reason
*/ static Digraph* read_dimacs(FILE* const fp, FILE* const errstr = stderr);
/** * Return the number of vertices in the graph.
*/ unsignedint get_nof_vertices() const {return vertices.size(); }
/** * Add a new vertex with color 'color' in the graph and return its index.
*/ unsignedint add_vertex(constunsignedint color = 0);
/** * Add an edge from the vertex \a source to the vertex \a target. * Duplicate edges are ignored but try to avoid introducing * them in the first place as they are not ignored immediately but will * consume memory and computation resources for a while.
*/ void add_edge(constunsignedint source, constunsignedint target);
/** * Change the color of the vertex 'vertex' to 'color'.
*/ void change_color(constunsignedint vertex, constunsignedint color);
/** * Compare this graph with the graph \a other. * Returns 0 if the graphs are equal, and a negative (positive) integer * if this graph is "smaller than" ("greater than", resp.) than \a other.
*/ int cmp(Digraph& other);
/** * Set the splitting heuristic used by the automorphism and canonical * labeling algorithm. * The selected splitting heuristics affects the computed canonical * labelings; therefore, if you want to compare whether two graphs * are isomorphic by computing and comparing (for equality) their * canonical versions, be sure to use the same splitting heuristics * for both graphs.
*/ void set_splitting_heuristic(SplittingHeuristic shs) {sh = shs; }
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 und die Messung sind noch experimentell.