#pragma once
/*
Copyright (c) 2003-2021 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 .
*/
/**
* \namespace bliss
* The namespace bliss contains all the classes and functions of the bliss
* tool except for the C programming language API.
*/
namespace bliss {
class AbstractGraph;
}
#include
#include
#include
#include "bliss/stats.hh"
#include "bliss/kqueue.hh"
#include "bliss/heap.hh"
#include "bliss/orbit.hh"
#include "bliss/partition.hh"
#include "bliss/uintseqhash.hh"
namespace bliss {
/**
* \brief An abstract base class for different types of graphs.
*/
class AbstractGraph
{
friend class Partition;
public:
AbstractGraph();
virtual ~AbstractGraph();
/**
* Set the verbose output level for the algorithms.
* \param level the level of verbose output, 0 means no verbose output
*/
void set_verbose_level(const unsigned int 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.
*/
virtual unsigned int add_vertex(const unsigned int 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.
*/
virtual void add_edge(const unsigned int source, const unsigned int target) = 0;
/**
* Get the color of a vertex.
*/
virtual unsigned int get_color(const unsigned int vertex) const = 0;
/**
* Change the color of the vertex \a vertex to \a color.
*/
virtual void change_color(const unsigned int vertex, const unsigned int color) = 0;
/** 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(const bool active) {
assert(not 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(const bool active) {
assert(not in_search);
opt_use_comprec = active;
}
/**
* Return the number of vertices in the graph.
*/
virtual unsigned int 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(const unsigned int* const perm) 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(const std::vector& perm) const = 0;
/**
* Check whether \a perm is an automorphism of this graph.
* Unoptimized, mainly for debugging purposes.
*/
virtual bool is_automorphism(unsigned int* const perm) const = 0;
/**
* Check whether \a perm is an automorphism of this graph.
* Unoptimized, mainly for debugging purposes.
*/
virtual bool is_automorphism(const std::vector& perm) const = 0;
/**
* Find a set of generators for the automorphism group of the graph.
* The function \a report (if non-null) is called each time a new generator
* for the automorphism group is found.
* The first argument \a n for the function
* is the length of the automorphism (equal to get_nof_vertices()), and
* the second 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 \a report function;
* if you want to use the automorphism later, you have to take a copy of it.
* Do not call any member functions from the \a report function.
*
* The search statistics are copied in \a stats.
*
* If the \a terminate function argument is given,
* it is called in each search tree node: if the function returns true,
* then the search is terminated and thus not all the automorphisms
* may have been generated.
* The \a terminate function may be used to limit the time spent in bliss
* in case the graph is too difficult under the available time constraints.
* If used, keep the function simple to evaluate so that
* it does not consume too much time.
*/
void find_automorphisms(Stats& stats,
const std::function& report = nullptr,
const std::function& terminate = nullptr);
/**
* 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()).
*
* If the \a terminate function argument is given,
* it is called in each search tree node: if the function returns true,
* then the search is terminated and thus (i) not all the automorphisms
* may have been generated and (ii) the returned labeling may not
* be canonical.
* The \a terminate function may be used to limit the time spent in bliss
* in case the graph is too difficult under the available time constraints.
* If used, keep the function simple to evaluate so that
* it does not consume too much time.
*/
const unsigned int* canonical_form(Stats& stats,
const std::function& report = nullptr,
const std::function& terminate = nullptr);
/**
* Write the graph to a file in a variant of the DIMACS format.
* See the bliss website
* 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
*/
virtual void 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
*/
virtual void 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
*/
virtual void write_dot(const char * const file_name) = 0;
/**
* Get a hash value for the graph.
* \return the hash value
*/
virtual unsigned int 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(const bool active) {
assert(not in_search);
opt_use_long_prune = active;
}
protected:
/** \internal
* How much verbose output is produced (0 means none) */
unsigned int verbose_level;
/** \internal
* The output stream for verbose output. */
FILE *verbstr;
/** \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 */
unsigned int failure_recording_fp_deviation;
/** \internal
* Is component recursion in use?
*/
bool opt_use_comprec;
unsigned int refine_current_path_certificate_index;
bool refine_compare_certificate;
bool refine_equal_to_first;
unsigned int refine_first_path_subcertificate_end;
int refine_cmp_to_best;
unsigned int refine_best_path_subcertificate_end;
static const unsigned int CERT_SPLIT = 0; //UINT_MAX;
static const unsigned int 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(const unsigned int v1,
const unsigned int v2,
const unsigned int 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(const unsigned int x,
const unsigned int y,
const unsigned int 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
*/
static const unsigned int 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
*/
static const unsigned int long_prune_options_max_stored_auts = 100;
unsigned int long_prune_max_stored_autss;
std::vector *> long_prune_fixed;
std::vector *> long_prune_mcrs;
std::vector long_prune_temp;
unsigned int long_prune_begin;
unsigned int 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(const unsigned int *aut);
std::vector& long_prune_get_fixed(const unsigned int index);
std::vector& long_prune_allocget_fixed(const unsigned int index);
std::vector& long_prune_get_mcrs(const unsigned int index);
std::vector& long_prune_allocget_mcrs(const unsigned int 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(const unsigned int i, const unsigned int j);
/*
* Data structures and routines for refining the partition p into equitable
*/
Heap neighbour_heap;
virtual bool split_neighbourhood_of_unit_cell(Partition::Cell * const) = 0;
virtual bool 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();
unsigned int 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.
*/
virtual bool is_equitable() const = 0;
unsigned int *first_path_labeling;
unsigned int *first_path_labeling_inv;
Orbit first_path_orbits;
unsigned int *first_path_automorphism;
unsigned int *best_path_labeling;
unsigned int *best_path_labeling_inv;
Orbit best_path_orbits;
unsigned int *best_path_automorphism;
void update_labeling(unsigned int * const lab);
void update_labeling_and_its_inverse(unsigned int * const lab,
unsigned int * const lab_inv);
void update_orbit_information(Orbit &o, const unsigned int *perm);
void reset_permutation(unsigned int *perm);
std::vector certificate_current_path;
std::vector certificate_first_path;
std::vector certificate_best_path;
unsigned int certificate_index;
virtual void initialize_certificate() = 0;
/* Remove duplicates from seq.
* If m is the largest element in seq, them m < tmp.size() must hold.
* All entries in tmp must be false when called.
* Under that condition, all entries in tmp are false on exit as well.
*/
static void remove_duplicates(std::vector& seq, std::vector& tmp);
virtual void remove_duplicate_edges() = 0;
virtual void make_initial_equitable_partition() = 0;
virtual Partition::Cell* find_next_cell_to_be_splitted(Partition::Cell *cell) = 0;
/** \struct PathInfo
*
* A structure for holding first, current, and best path information.
*/
typedef struct {
unsigned int splitting_element;
unsigned int certificate_index;
unsigned int subcertificate_length;
UintSeqHash eqref_hash;
} PathInfo;
void search(const bool canonical, Stats &stats,
const std::function& report_function = nullptr,
const std::function& terminate = nullptr);
/*
*
* Nonuniform component recursion (NUCR)
*
*/
/* The currently traversed component */
unsigned int cr_level;
/** @internal @class CR_CEP
* The "Component End Point" data structure
*/
class CR_CEP {
public:
/** At which level in the search was this CEP created */
unsigned int creation_level;
/** The current component has been fully traversed when the partition has
* this many discrete cells left */
unsigned int discrete_cell_limit;
/** The component to be traversed after the current one */
unsigned int next_cr_level;
/** The next component end point */
unsigned int next_cep_index;
bool first_checked;
bool best_checked;
};
/** \internal
* A stack for storing Component End Points
*/
std::vector 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).
*/
virtual bool nucr_find_first_component(const unsigned int level) = 0;
virtual bool nucr_find_first_component(const unsigned int level,
std::vector& component,
unsigned int& component_elements,
Partition::Cell*& sh_return) = 0;
/** \internal
* The non-uniformity component found by nucr_find_first_component()
* is stored here.
*/
std::vector cr_component;
/** \internal
* The number of vertices in the component \a cr_component
*/
unsigned int cr_component_elements;
};
} // namespace bliss