Add graph references

This commit is contained in:
Abdelrahman Said
2026-06-28 13:49:01 +01:00
parent 0a9807e448
commit a11edf0c53
2578 changed files with 868045 additions and 0 deletions
@@ -0,0 +1,764 @@
/*
Copyright (C) 2003-2006 Tommi Junttila
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */
#include "bliss/graph.hh"
#include "igraph_isomorphism.h"
#include "igraph_conversion.h"
#include "igraph_interface.h"
#include "igraph_interrupt.h"
#include "igraph_memory.h"
#include "igraph_vector.h"
#include "core/exceptions.h"
#include <climits>
#include <cmath>
#include <stdexcept>
using namespace bliss;
using namespace std;
/**
* \section about_bliss
*
* <para>
* Bliss is a successor of the famous NAUTY algorithm and
* implementation. While using the same ideas in general, with better
* heuristics and data structures Bliss outperforms NAUTY on most
* graphs.
* </para>
*
* <para>
* Bliss was developed and implemented by Tommi Junttila and Petteri Kaski at
* Helsinki University of Technology, Finland. For more information,
* see the Bliss homepage at https://users.aalto.fi/~tjunttil/bliss/ and the following
* publication:
* </para>
*
* <para>
* Tommi Junttila and Petteri Kaski: "Engineering an Efficient Canonical Labeling
* Tool for Large and Sparse Graphs" In ALENEX 2007, pages 135149, 2007
* https://doi.org/10.1137/1.9781611972870.13
* </para>
*
* <para>
* Tommi Junttila and Petteri Kaski: "Conflict Propagation and Component Recursion
* for Canonical Labeling" in TAPAS 2011, pages 151162, 2011.
* https://doi.org/10.1007/978-3-642-19754-3_16
* </para>
*
* <para>
* Bliss works with both directed graphs and undirected graphs. It supports graphs with
* self-loops, but not graphs with multi-edges.
* </para>
*
* <para>
* Bliss version 0.75 is included in igraph.
* </para>
*/
namespace { // unnamed namespace
inline AbstractGraph *bliss_from_igraph(const igraph_t *graph) {
igraph_int_t nof_vertices = igraph_vcount(graph);
igraph_int_t nof_edges = igraph_ecount(graph);
if (nof_vertices > UINT_MAX || nof_edges > UINT_MAX) {
throw std::runtime_error("Graph too large for BLISS");
}
AbstractGraph *g;
if (igraph_is_directed(graph)) {
g = new Digraph(static_cast<int>(nof_vertices));
} else {
g = new Graph(static_cast<int>(nof_vertices));
}
/* g->set_verbose_level(0); */
for (unsigned int i = 0; i < static_cast<unsigned int>(nof_edges); i++) {
g->add_edge(
static_cast<unsigned int>(IGRAPH_FROM(graph, i)),
static_cast<unsigned int>(IGRAPH_TO(graph, i))
);
}
return g;
}
void bliss_free_graph(AbstractGraph *g) {
delete g;
}
inline igraph_error_t bliss_set_sh(AbstractGraph *g, igraph_bliss_sh_t sh, bool directed) {
if (directed) {
Digraph::SplittingHeuristic gsh = Digraph::shs_fsm;
switch (sh) {
case IGRAPH_BLISS_F: gsh = Digraph::shs_f; break;
case IGRAPH_BLISS_FL: gsh = Digraph::shs_fl; break;
case IGRAPH_BLISS_FS: gsh = Digraph::shs_fs; break;
case IGRAPH_BLISS_FM: gsh = Digraph::shs_fm; break;
case IGRAPH_BLISS_FLM: gsh = Digraph::shs_flm; break;
case IGRAPH_BLISS_FSM: gsh = Digraph::shs_fsm; break;
default: IGRAPH_ERROR("Invalid splitting heuristic.", IGRAPH_EINVAL);
}
static_cast<Digraph *>(g)->set_splitting_heuristic(gsh);
} else {
Graph::SplittingHeuristic gsh = Graph::shs_fsm;
switch (sh) {
case IGRAPH_BLISS_F: gsh = Graph::shs_f; break;
case IGRAPH_BLISS_FL: gsh = Graph::shs_fl; break;
case IGRAPH_BLISS_FS: gsh = Graph::shs_fs; break;
case IGRAPH_BLISS_FM: gsh = Graph::shs_fm; break;
case IGRAPH_BLISS_FLM: gsh = Graph::shs_flm; break;
case IGRAPH_BLISS_FSM: gsh = Graph::shs_fsm; break;
default: IGRAPH_ERROR("Invalid splitting heuristic.", IGRAPH_EINVAL);
}
static_cast<Graph *>(g)->set_splitting_heuristic(gsh);
}
return IGRAPH_SUCCESS;
}
inline igraph_error_t bliss_set_colors(AbstractGraph *g, const igraph_vector_int_t *colors) {
if (colors == NULL) {
return IGRAPH_SUCCESS;
}
const int n = g->get_nof_vertices();
if (n != igraph_vector_int_size(colors)) {
IGRAPH_ERROR("Invalid vertex color vector length.", IGRAPH_EINVAL);
}
for (int i = 0; i < n; ++i) {
igraph_int_t color = VECTOR(*colors)[i];
if (color < INT_MIN || color > INT_MAX) {
IGRAPH_ERRORF("Invalid vertex color index %" IGRAPH_PRId " for vertex %d.", IGRAPH_EOVERFLOW, color, i);
}
g->change_color(i, static_cast<int>(color));
}
return IGRAPH_SUCCESS;
}
inline igraph_error_t bliss_info_to_igraph(igraph_bliss_info_t *info, const Stats &stats) {
if (info) {
size_t group_size_strlen;
info->max_level = stats.get_max_level();
info->nof_nodes = stats.get_nof_nodes();
info->nof_leaf_nodes = stats.get_nof_leaf_nodes();
info->nof_bad_nodes = stats.get_nof_bad_nodes();
info->nof_canupdates = stats.get_nof_canupdates();
info->nof_generators = stats.get_nof_generators();
mpz_t group_size;
mpz_init(group_size);
stats.get_group_size().get(group_size);
group_size_strlen = mpz_sizeinbase(group_size, /* base */ 10) + 2;
info->group_size = IGRAPH_CALLOC(group_size_strlen, char);
if (! info->group_size) {
IGRAPH_ERROR("Insufficient memory to retrieve automotphism group size.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */
}
mpz_get_str(info->group_size, /* base */ 10, group_size);
mpz_clear(group_size);
}
return IGRAPH_SUCCESS;
}
// This is the callback function that can tell Bliss to terminate early.
struct AbortChecker {
bool aborted;
AbortChecker() : aborted(false) { }
bool operator()() {
if (igraph_allow_interruption() != IGRAPH_SUCCESS) {
aborted = true;
return true;
}
return false;
}
};
// This is the callback function used with AbstractGraph::find_automorphisms().
// It collects the automorphism group generators into a pointer vector.
class AutCollector {
igraph_vector_int_list_t *generators;
public:
AutCollector(igraph_vector_int_list_t *generators_) : generators(generators_) { }
void operator ()(unsigned int n, const unsigned int *aut) {
igraph_vector_int_t newvector;
igraph_error_t err;
err = igraph_vector_int_init(&newvector, n);
if (err != IGRAPH_SUCCESS) {
throw bad_alloc();
}
copy(aut, aut + n, VECTOR(newvector)); // takes care of unsigned int -> igraph_int_t conversion
err = igraph_vector_int_list_push_back(generators, &newvector);
if (err != IGRAPH_SUCCESS) {
throw bad_alloc();
}
}
};
} // end unnamed namespace
/**
* \function igraph_canonical_permutation
* \brief Canonical permutation of a graph.
*
* This function computes the vertex permutation which transforms
* the graph into a canonical form. Two graphs have the same canonical form if
* and only if they are isomorphic. Use \ref igraph_is_same_graph() to compare
* two canonical forms.
*
* </para><para>
* The current implementation uses the BLISS isomorphism algorithms with
* sensible defaults. Use \ref igraph_canonical_permutation_bliss() to fine-tune
* the parameters.
*
* \param graph The input graph. Multiple edges between the same nodes
* are not supported and will cause an incorrect result to be returned.
* \param colors An optional vertex color vector for the graph. Supply a
* null pointer is the graph is not colored.
* \param labeling Pointer to a vector, the result is stored here. The
* permutation takes vertex 0 to the first element of the vector,
* vertex 1 to the second, etc. The vector will be resized as
* needed.
* \return Error code.
*
* \sa \ref igraph_is_same_graph()
*
* Time complexity: exponential, in practice it is fast for many graphs.
*/
igraph_error_t igraph_canonical_permutation(
const igraph_t *graph, const igraph_vector_int_t *colors,
igraph_vector_int_t *labeling
) {
return igraph_canonical_permutation_bliss(
graph, colors, labeling, IGRAPH_BLISS_FL, NULL
);
}
static igraph_error_t igraph_i_canonical_permutation_bliss(
const igraph_t *graph, const igraph_vector_int_t *colors,
igraph_vector_int_t *labeling, igraph_bliss_sh_t sh,
igraph_bliss_info_t *info
);
/**
* \function igraph_canonical_permutation_bliss
* \brief Canonical permutation using Bliss.
*
* This function computes the vertex permutation which transforms
* the graph into a canonical form, using the Bliss algorithm.
* Two graphs have the same canonical form if and only if they
* are isomorphic. Use \ref igraph_is_same_graph() to compare
* two canonical forms.
*
* \param graph The input graph. Multiple edges between the same nodes
* are not supported and will cause an incorrect result to be returned.
* \param colors An optional vertex color vector for the graph. Supply a
* null pointer is the graph is not colored.
* \param labeling Pointer to a vector, the result is stored here. The
* permutation takes vertex 0 to the first element of the vector,
* vertex 1 to the second, etc. The vector will be resized as
* needed.
* \param sh The splitting heuristics to be used in Bliss. See \ref
* igraph_bliss_sh_t.
* \param info If not \c NULL then information on Bliss internals is
* stored here. The memory used by this structure must to be freed
* when no longer needed, see \ref igraph_bliss_info_t.
* \return Error code.
*
* \sa \ref igraph_is_same_graph()
*
* Time complexity: exponential, in practice it is fast for many graphs.
*/
igraph_error_t igraph_canonical_permutation_bliss(
const igraph_t *graph, const igraph_vector_int_t *colors,
igraph_vector_int_t *labeling, igraph_bliss_sh_t sh,
igraph_bliss_info_t *info
) {
igraph_vector_int_t inv_permutation;
IGRAPH_VECTOR_INT_INIT_FINALLY(&inv_permutation, igraph_vcount(graph));
IGRAPH_CHECK(igraph_i_canonical_permutation_bliss(graph, colors, &inv_permutation, sh, info));
IGRAPH_CHECK(igraph_invert_permutation(&inv_permutation, labeling));
igraph_vector_int_destroy(&inv_permutation);
IGRAPH_FINALLY_CLEAN(1);
return IGRAPH_SUCCESS;
}
static igraph_error_t igraph_i_canonical_permutation_bliss(
const igraph_t *graph, const igraph_vector_int_t *colors,
igraph_vector_int_t *labeling, igraph_bliss_sh_t sh,
igraph_bliss_info_t *info
) {
IGRAPH_HANDLE_EXCEPTIONS(
AbstractGraph *g = bliss_from_igraph(graph);
IGRAPH_FINALLY(bliss_free_graph, g);
const unsigned int N = g->get_nof_vertices();
IGRAPH_CHECK(bliss_set_sh(g, sh, igraph_is_directed(graph)));
IGRAPH_CHECK(bliss_set_colors(g, colors));
Stats stats;
AbortChecker checker;
const unsigned int *cl = g->canonical_form(stats, /* report */ nullptr, /* terminate */ checker);
if (checker.aborted) {
return IGRAPH_INTERRUPTED;
}
IGRAPH_CHECK(igraph_vector_int_resize(labeling, N));
for (unsigned int i = 0; i < N; i++) {
VECTOR(*labeling)[i] = cl[i];
}
IGRAPH_CHECK(bliss_info_to_igraph(info, stats));
delete g;
IGRAPH_FINALLY_CLEAN(1);
);
return IGRAPH_SUCCESS;
}
/**
* \function igraph_count_automorphisms
* \brief Number of automorphisms of a graph.
*
* This function computes the number of automorphisms of a graph. Since the
* number of automorphisms may be very large, the result is returned as an
* \c igraph_real_t instead of an integer. If the number of automorphisms
* is larger than what can be represented in an \c igraph_real_t and you need
* the exact number, use \ref igraph_count_automorphisms_bliss(), which can
* return the number as a string.
*
* \param graph The input graph. Multiple edges between the same nodes
* are not supported and will cause an incorrect result to be returned.
* \param colors An optional vertex color vector for the graph. Supply a
* null pointer is the graph is not colored.
* \param result Pointer to an \c igraph_real_t, the number of automorphisms
* will be returned here.
* \return Error code. \c IGRAPH_EOVERFLOW if the number of automorphisms is
* too large to be represented in an \c igraph_real_t .
*
* Time complexity: exponential, in practice it is fast for many graphs.
*/
igraph_error_t igraph_count_automorphisms(
const igraph_t *graph, const igraph_vector_int_t *colors,
igraph_real_t *result
) {
igraph_bliss_info_t info;
double x;
IGRAPH_CHECK(igraph_count_automorphisms_bliss(graph, colors, IGRAPH_BLISS_FL, &info));
x = strtod(info.group_size, NULL);
igraph_free(info.group_size);
if (x == 0) {
return IGRAPH_FAILURE;
} else if (x == HUGE_VAL) {
return IGRAPH_EOVERFLOW;
} else {
if (result) {
*result = x;
}
return IGRAPH_SUCCESS;
}
}
/**
* \function igraph_count_automorphisms_bliss
* \brief Number of automorphisms using Bliss.
*
* The number of automorphisms of a graph is computed using Bliss. The
* result is returned as part of the \p info structure, in tag \c
* group_size. It is returned as a string, as it can be very high even
* for relatively small graphs. See also \ref igraph_bliss_info_t.
*
* \param graph The input graph. Multiple edges between the same nodes
* are not supported and will cause an incorrect result to be returned.
* \param colors An optional vertex color vector for the graph. Supply a
* null pointer is the graph is not colored.
* \param sh The splitting heuristics to be used in Bliss. See \ref
* igraph_bliss_sh_t.
* \param info The result is stored here, in particular in the \c
* group_size tag of \p info. The memory used by this structure must be
* released when no longer needed, see \ref igraph_bliss_info_t.
* \return Error code.
*
* Time complexity: exponential, in practice it is fast for many graphs.
*/
igraph_error_t igraph_count_automorphisms_bliss(
const igraph_t *graph, const igraph_vector_int_t *colors,
igraph_bliss_sh_t sh, igraph_bliss_info_t *info
) {
IGRAPH_HANDLE_EXCEPTIONS(
AbstractGraph *g = bliss_from_igraph(graph);
IGRAPH_FINALLY(bliss_free_graph, g);
IGRAPH_CHECK(bliss_set_sh(g, sh, igraph_is_directed(graph)));
IGRAPH_CHECK(bliss_set_colors(g, colors));
Stats stats;
AbortChecker checker;
g->find_automorphisms(stats, /* report */ nullptr, /* terminate */ checker);
if (checker.aborted) {
return IGRAPH_INTERRUPTED;
}
IGRAPH_CHECK(bliss_info_to_igraph(info, stats));
delete g;
IGRAPH_FINALLY_CLEAN(1);
);
return IGRAPH_SUCCESS;
}
/**
* \function igraph_automorphism_group
* \brief Automorphism group generators of a graph.
*
* This function computes the generators of the automorphism group of a graph.
* The generator set may not be minimal and may depend on the specific parameters
* of the algorithm under the hood. The generators are permutations represented
* using zero-based indexing.
*
* </para><para>
* The current implementation uses BLISS behind the scenes and the result may
* be dependent on the splitting heuristics. Use \ref igraph_automorphism_group_bliss()
* if you want to fine-tune the splitting heuristics.
*
* \param graph The input graph. Multiple edges between the same nodes
* are not supported and will cause an incorrect result to be returned.
* \param colors An optional vertex color vector for the graph. Supply a
* null pointer is the graph is not colored.
* \param generators Must be an initialized interger vector list.
* The generators of the automorphism group will be stored here.
* \return Error code.
*
* Time complexity: exponential, in practice it is fast for many graphs.
*/
igraph_error_t igraph_automorphism_group(
const igraph_t *graph, const igraph_vector_int_t *colors,
igraph_vector_int_list_t *generators
) {
return igraph_automorphism_group_bliss(
graph, colors, generators, IGRAPH_BLISS_FL, NULL
);
}
/**
* \function igraph_automorphism_group_bliss
* \brief Automorphism group generators using Bliss.
*
* The generators of the automorphism group of a graph are computed
* using Bliss. The generator set may not be minimal and may depend on
* the splitting heuristics. The generators are permutations represented
* using zero-based indexing.
*
* \param graph The input graph. Multiple edges between the same nodes
* are not supported and will cause an incorrect result to be returned.
* \param colors An optional vertex color vector for the graph. Supply a
* null pointer is the graph is not colored.
* \param generators Must be an initialized interger vector list.
* The generators of the automorphism group will be stored here.
* \param sh The splitting heuristics to be used in Bliss. See \ref
* igraph_bliss_sh_t.
* \param info If not \c NULL then information on Bliss internals is
* stored here. The memory used by this structure must to be freed
* when no longer needed, see \ref igraph_bliss_info_t.
* \return Error code.
*
* Time complexity: exponential, in practice it is fast for many graphs.
*/
igraph_error_t igraph_automorphism_group_bliss(
const igraph_t *graph, const igraph_vector_int_t *colors,
igraph_vector_int_list_t *generators, igraph_bliss_sh_t sh,
igraph_bliss_info_t *info
) {
IGRAPH_HANDLE_EXCEPTIONS(
AbstractGraph *g = bliss_from_igraph(graph);
IGRAPH_FINALLY(bliss_free_graph, g);
IGRAPH_CHECK(bliss_set_sh(g, sh, igraph_is_directed(graph)));
IGRAPH_CHECK(bliss_set_colors(g, colors));
Stats stats;
igraph_vector_int_list_clear(generators);
AutCollector collector(generators);
AbortChecker checker;
g->find_automorphisms(stats, collector, checker);
if (checker.aborted) {
return IGRAPH_INTERRUPTED;
}
IGRAPH_CHECK(bliss_info_to_igraph(info, stats));
delete g;
IGRAPH_FINALLY_CLEAN(1);
);
return IGRAPH_SUCCESS;
}
/* The following license notice applies to the rest of this file */
/*
igraph library.
Copyright (C) 2006-2021 The igraph development team <igraph@igraph.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* \function igraph_isomorphic_bliss
* \brief Graph isomorphism via Bliss.
*
* This function uses the Bliss graph isomorphism algorithm, a
* successor of the famous NAUTY algorithm and implementation. Bliss
* is open source and licensed according to the GNU LGPL. See
* https://users.aalto.fi/~tjunttil/bliss/ for
* details. Currently the 0.75 version of Bliss is included in igraph.
*
* </para><para>
* Isomorphism testing is implemented by producing the canonical form
* of both graphs using \ref igraph_canonical_permutation_bliss() and
* comparing them.
*
* \param graph1 The first input graph. Multiple edges between the same nodes
* are not supported and will cause an incorrect result to be returned.
* \param graph2 The second input graph. Multiple edges between the same nodes
* are not supported and will cause an incorrect result to be returned.
* \param colors1 An optional vertex color vector for the first graph. Supply a
* null pointer if your graph is not colored.
* \param colors2 An optional vertex color vector for the second graph. Supply a
* null pointer if your graph is not colored.
* \param iso Pointer to a boolean, the result is stored here.
* \param map12 A vector or \c NULL pointer. If not \c NULL then an
* isomorphic mapping from \p graph1 to \p graph2 is stored here.
* If the input graphs are not isomorphic then this vector is
* cleared, i.e. it will have length zero.
* \param map21 Similar to \p map12, but for the mapping from \p
* graph2 to \p graph1.
* \param sh Splitting heuristics to be used for the graphs. See
* \ref igraph_bliss_sh_t.
* \param info1 If not \c NULL, information about the canonization of
* the first input graph is stored here. Note that if the two graphs
* have different number of vertices or edges, then this is only
* partially filled. The memory used by this structure should be
* released when no longer needed, see \ref igraph_bliss_info_t
* for details.
* \param info2 Same as \p info1, but for the second graph.
* \return Error code.
*
* Time complexity: exponential, but in practice it is quite fast.
*/
igraph_error_t igraph_isomorphic_bliss(const igraph_t *graph1, const igraph_t *graph2,
const igraph_vector_int_t *colors1, const igraph_vector_int_t *colors2,
igraph_bool_t *iso, igraph_vector_int_t *map12,
igraph_vector_int_t *map21, igraph_bliss_sh_t sh,
igraph_bliss_info_t *info1, igraph_bliss_info_t *info2) {
igraph_int_t no_of_nodes = igraph_vcount(graph1);
igraph_int_t no_of_edges = igraph_ecount(graph1);
igraph_vector_int_t perm1, perm2;
igraph_vector_int_t vmap12, *mymap12 = &vmap12;
igraph_vector_int_t from, to, index;
igraph_vector_int_t from2, to2, index2;
igraph_bool_t directed;
igraph_int_t i, j;
*iso = 0;
if (info1) {
info1->nof_nodes = info1->nof_leaf_nodes = info1->nof_bad_nodes =
info1->nof_canupdates = info1->max_level = info1->nof_generators = 0;
info1->group_size = 0;
}
if (info2) {
info2->nof_nodes = info2->nof_leaf_nodes = info2->nof_bad_nodes =
info2->nof_canupdates = info2->max_level = info2->nof_generators = 0;
info2->group_size = 0;
}
directed = igraph_is_directed(graph1);
if (igraph_is_directed(graph2) != directed) {
IGRAPH_ERROR("Cannot compare directed and undirected graphs.",
IGRAPH_EINVAL);
}
if ((colors1 == NULL || colors2 == NULL) && colors1 != colors2) {
IGRAPH_WARNING("Only one of the graphs is vertex colored, colors will be ignored.");
colors1 = NULL; colors2 = NULL;
}
if (no_of_nodes != igraph_vcount(graph2) ||
no_of_edges != igraph_ecount(graph2)) {
if (map12) {
igraph_vector_int_clear(map12);
}
if (map21) {
igraph_vector_int_clear(map21);
}
return IGRAPH_SUCCESS;
}
if (map12) {
mymap12 = map12;
} else {
IGRAPH_VECTOR_INT_INIT_FINALLY(mymap12, 0);
}
IGRAPH_VECTOR_INT_INIT_FINALLY(&perm1, no_of_nodes);
IGRAPH_VECTOR_INT_INIT_FINALLY(&perm2, no_of_nodes);
IGRAPH_CHECK(igraph_i_canonical_permutation_bliss(graph1, colors1, &perm1, sh, info1));
IGRAPH_CHECK(igraph_i_canonical_permutation_bliss(graph2, colors2, &perm2, sh, info2));
IGRAPH_CHECK(igraph_vector_int_resize(mymap12, no_of_nodes));
/* The inverse of perm2 is produced in mymap12 */
for (i = 0; i < no_of_nodes; i++) {
VECTOR(*mymap12)[ VECTOR(perm2)[i] ] = i;
}
/* Now we produce perm2^{-1} o perm1 in perm2 */
for (i = 0; i < no_of_nodes; i++) {
VECTOR(perm2)[i] = VECTOR(*mymap12)[ VECTOR(perm1)[i] ];
}
/* Copy it to mymap12 */
IGRAPH_CHECK(igraph_vector_int_update(mymap12, &perm2));
igraph_vector_int_destroy(&perm1);
igraph_vector_int_destroy(&perm2);
IGRAPH_FINALLY_CLEAN(2);
/* Check isomorphism, we apply the permutation in mymap12 to graph1
and should get graph2 */
IGRAPH_VECTOR_INT_INIT_FINALLY(&from, no_of_edges);
IGRAPH_VECTOR_INT_INIT_FINALLY(&to, no_of_edges);
IGRAPH_VECTOR_INT_INIT_FINALLY(&index, no_of_edges);
IGRAPH_VECTOR_INT_INIT_FINALLY(&from2, no_of_edges * 2);
IGRAPH_VECTOR_INT_INIT_FINALLY(&to2, no_of_edges);
IGRAPH_VECTOR_INT_INIT_FINALLY(&index2, no_of_edges);
for (i = 0; i < no_of_edges; i++) {
VECTOR(from)[i] = VECTOR(*mymap12)[ IGRAPH_FROM(graph1, i) ];
VECTOR(to)[i] = VECTOR(*mymap12)[ IGRAPH_TO (graph1, i) ];
if (! directed && VECTOR(from)[i] < VECTOR(to)[i]) {
igraph_int_t tmp = VECTOR(from)[i];
VECTOR(from)[i] = VECTOR(to)[i];
VECTOR(to)[i] = tmp;
}
}
igraph_vector_int_pair_order(&from, &to, &index, no_of_nodes);
igraph_get_edgelist(graph2, &from2, /*bycol=*/ 1);
for (i = 0, j = no_of_edges; i < no_of_edges; i++, j++) {
VECTOR(to2)[i] = VECTOR(from2)[j];
if (! directed && VECTOR(from2)[i] < VECTOR(to2)[i]) {
igraph_int_t tmp = VECTOR(from2)[i];
VECTOR(from2)[i] = VECTOR(to2)[i];
VECTOR(to2)[i] = tmp;
}
}
igraph_vector_int_resize(&from2, no_of_edges);
igraph_vector_int_pair_order(&from2, &to2, &index2, no_of_nodes);
*iso = 1;
for (i = 0; i < no_of_edges; i++) {
igraph_int_t i1 = VECTOR(index)[i];
igraph_int_t i2 = VECTOR(index2)[i];
if (VECTOR(from)[i1] != VECTOR(from2)[i2] ||
VECTOR(to)[i1] != VECTOR(to2)[i2]) {
*iso = 0;
break;
}
}
/* If the graphs are coloured, we also need to check that applying the
permutation mymap12 to colors1 gives colors2. */
if (*iso && colors1 != NULL) {
for (i = 0; i < no_of_nodes; i++) {
if (VECTOR(*colors1)[i] != VECTOR(*colors2)[ VECTOR(*mymap12)[i] ]) {
*iso = 0;
break;
}
}
}
igraph_vector_int_destroy(&index2);
igraph_vector_int_destroy(&to2);
igraph_vector_int_destroy(&from2);
igraph_vector_int_destroy(&index);
igraph_vector_int_destroy(&to);
igraph_vector_int_destroy(&from);
IGRAPH_FINALLY_CLEAN(6);
if (*iso) {
/* The inverse of mymap12 */
if (map21) {
IGRAPH_CHECK(igraph_vector_int_resize(map21, no_of_nodes));
for (i = 0; i < no_of_nodes; i++) {
VECTOR(*map21)[ VECTOR(*mymap12)[i] ] = i;
}
}
} else {
if (map12) {
igraph_vector_int_clear(map12);
}
if (map21) {
igraph_vector_int_clear(map21);
}
}
if (!map12) {
igraph_vector_int_destroy(mymap12);
IGRAPH_FINALLY_CLEAN(1);
}
return IGRAPH_SUCCESS;
}
@@ -0,0 +1,43 @@
# Declare the files needed to compile bliss
add_library(
bliss
OBJECT
EXCLUDE_FROM_ALL
defs.cc
graph.cc
heap.cc
orbit.cc
partition.cc
uintseqhash.cc
utils.cc
)
target_include_directories(
bliss
PRIVATE
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/src
${PROJECT_SOURCE_DIR}/vendor
${PROJECT_BINARY_DIR}/include
${PROJECT_BINARY_DIR}/src
$<$<BOOL:${GMP_INCLUDE_DIR}>:${GMP_INCLUDE_DIR}>
)
if (BUILD_SHARED_LIBS)
set_property(TARGET bliss PROPERTY POSITION_INDEPENDENT_CODE ON)
endif()
# Since these are included as object files, they should call the
# function as is (without visibility specification)
target_compile_definitions(bliss PRIVATE IGRAPH_STATIC)
use_all_warnings(bliss)
if (MSVC)
target_compile_options(bliss PRIVATE /wd4100) # disable unreferenced parameter warning
else()
target_compile_options(
bliss PRIVATE
$<$<C_COMPILER_ID:GCC,Clang,AppleClang>:-Wno-unused-variable>
)
endif()
@@ -0,0 +1,103 @@
#ifndef BLISS_BIGNUM_HH
#define BLISS_BIGNUM_HH
/*
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 <http://www.gnu.org/licenses/>.
*/
#define BLISS_USE_GMP
#if defined(BLISS_USE_GMP)
#include "internal/gmp_internal.h"
#endif
#include <cstdlib>
#include "defs.hh"
namespace bliss {
/**
* \brief A simple wrapper class for big integers (or approximation of them).
*
* If the compile time flag BLISS_USE_GMP is set,
* then the GNU Multiple Precision Arithmetic library (GMP) is used to
* obtain arbitrary precision, otherwise "long double" is used to
* approximate big integers.
*/
#if defined(BLISS_USE_GMP)
class BigNum
{
mpz_t v;
public:
/**
* \brief Create a new big number and set it to zero.
*/
BigNum() {mpz_init(v); }
/**
* \brief Destroy the number.
*/
~BigNum() {mpz_clear(v); }
/**
* \brief Set the number to \a n.
*/
void assign(const int n) {mpz_set_si(v, n); }
/**
* \brief Multiply the number with \a n.
*/
void multiply(const int n) {mpz_mul_si(v, v, n); }
/**
* Get a copy of the internal GNU GMP integer.
* The caller is responsible for calling mpz_init before,
* and mpz_clear afterwards on the \a result variable.
*/
void get(mpz_t& result) const {mpz_set(result, v); }
};
#else
class BigNum
{
long double v;
public:
/**
* \brief Create a new big number and set it to zero.
*/
BigNum(): v(0.0) {}
/**
* \brief Set the number to \a n.
*/
void assign(const int n) {v = (long double)n; }
/**
* \brief Multiply the number with \a n.
*/
void multiply(const int n) {v *= (long double)n; }
};
#endif
} //namespace bliss
#endif // BLISS_BIGNUM_HH
@@ -0,0 +1,32 @@
#include "igraph_error.h"
#include "defs.hh"
/*
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 <http://www.gnu.org/licenses/>.
*/
namespace bliss {
void
fatal_error(const char* reason)
{
IGRAPH_FATAL(reason);
}
}
@@ -0,0 +1,90 @@
#ifndef BLISS_DEFS_HH
#define BLISS_DEFS_HH
#include <cassert>
#include <cstdarg>
/*
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 <http://www.gnu.org/licenses/>.
*/
/** \file
* \brief Some common definitions.
*/
namespace bliss {
/** \brief The version number of bliss. */
static const char * const version = "0.75";
/**
* If a fatal internal error is encountered,
* this function is called.
* There should no return from this function, but an exit or
* a jump/throw to code that deallocates the AbstractGraph instance calling this.
*/
void fatal_error(const char* fmt);
#if defined(BLISS_DEBUG)
#define BLISS_CONSISTENCY_CHECKS
#define BLISS_EXPENSIVE_CONSISTENCY_CHECKS
#endif
#if defined(BLISS_CONSISTENCY_CHECKS)
/* Force a check that the found automorphisms are valid */
#define BLISS_VERIFY_AUTOMORPHISMS
#endif
#if defined(BLISS_CONSISTENCY_CHECKS)
/* Force a check that the generated partitions are equitable */
#define BLISS_VERIFY_EQUITABLEDNESS
#endif
} // namespace bliss
/*! \mainpage Outline
*
* This is the C++ API documentation of bliss,
* produced by running <a href="http://www.doxygen.org">doxygen</a> in
* the source directory.
*
* The algorithms and data structures used in bliss,
* the graph file format, as well as the compilation process
* can be found at the
* <a href="https://users.aalto.fi/tjunttil/bliss">bliss web site</a>.
*
* The C++ language API is the main API to bliss.
* It basically consists of the public methods in the classes
* * bliss::Graph and
* * bliss::Digraph.
*
* For an example of its use,
* see the \ref executable "source of the bliss executable".
*
* \section capi_sec The C language API
*
* The C language API is given in the file bliss_C.h.
* It is currently only a subset of the C++ API,
* so consider using the C++ API whenever possible.
*/
#endif // BLISS_DEFS_HH
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,876 @@
#ifndef BLISS_GRAPH_HH
#define BLISS_GRAPH_HH
/*
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 <http://www.gnu.org/licenses/>.
*/
/**
* \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 <cstdio>
#include <functional>
#include <vector>
#include "stats.hh"
#include "kstack.hh"
#include "kqueue.hh"
#include "heap.hh"
#include "orbit.hh"
#include "partition.hh"
#include "uintseqhash.hh"
namespace bliss {
/**
* \brief An abstract base class for different types of graphs.
*/
class AbstractGraph
{
friend class Partition;
public:
AbstractGraph();
virtual ~AbstractGraph();
#if 0
/**
* 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);
#endif
/**
* 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;
/**
* Change the color of the vertex \a vertex to \a color.
*/
virtual void change_color(const unsigned int vertex, const unsigned int color) = 0;
/**
* Check whether \a perm is an automorphism of this graph.
* Unoptimized, mainly for debugging purposes.
*/
virtual bool is_automorphism(const std::vector<unsigned int>& perm) const = 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(!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(!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;
virtual AbstractGraph* permute(const std::vector<unsigned int>& 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<void(unsigned int n, const unsigned int* aut)>& report = nullptr,
const std::function<bool()>& 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<void(unsigned int n, const unsigned int* aut)>& report = nullptr,
const std::function<bool()>& terminate = nullptr);
/**
* 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(!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; */
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 */
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 = false;
bool refine_equal_to_first = false;
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<std::vector<bool> *> long_prune_fixed;
std::vector<std::vector<bool> *> long_prune_mcrs;
std::vector<bool> 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<bool>& long_prune_get_fixed(const unsigned int index);
std::vector<bool>& long_prune_allocget_fixed(const unsigned int index);
std::vector<bool>& long_prune_get_mcrs(const unsigned int index);
std::vector<bool>& 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);
/* Mainly for debugging purposes */
virtual bool is_automorphism(unsigned int* const perm) const = 0;
std::vector<unsigned int> certificate_current_path;
std::vector<unsigned int> certificate_first_path;
std::vector<unsigned int> certificate_best_path;
unsigned int certificate_index;
virtual void initialize_certificate() = 0;
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<void(unsigned int n, const unsigned int* aut)>& report_function = nullptr,
const std::function<bool()>& terminate = nullptr);
void (*report_hook)(void *user_param,
unsigned int n,
const unsigned int *aut);
void *report_user_param;
/*
*
* 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> 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<unsigned int>& 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<unsigned int> cr_component;
/** \internal
* The number of vertices in the component \a cr_component
*/
unsigned int 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.
*/
typedef enum {
/** 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;
protected:
class Vertex {
public:
Vertex();
~Vertex();
void add_edge(const unsigned int other_vertex);
void remove_duplicate_edges(std::vector<bool>& tmp);
void sort_edges();
unsigned int color;
std::vector<unsigned int> edges;
unsigned int nof_edges() const {
return static_cast<unsigned int>(edges.size());
}
};
std::vector<Vertex> vertices;
void sort_edges();
void remove_duplicate_edges();
/** \internal
* Partition independent invariant.
* Returns the color of the vertex.
* Time complexity: O(1).
*/
static unsigned int vertex_color_invariant(const Graph* const g,
const unsigned int v);
/** \internal
* Partition independent invariant.
* Returns the degree of the vertex.
* DUPLICATE EDGES MUST HAVE BEEN REMOVED BEFORE.
* Time complexity: O(1).
*/
static unsigned int degree_invariant(const Graph* const g,
const unsigned int 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.
*/
static unsigned int selfloop_invariant(const Graph* const g,
const unsigned int v);
bool refine_according_to_invariant(unsigned int (*inv)(const Graph* const g,
const unsigned int 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);
/** \internal
* \copydoc AbstractGraph::is_equitable() const
*/
bool is_equitable() const;
/* Splitting heuristics, documented in more detail in graph.cc */
SplittingHeuristic sh;
Partition::Cell* find_next_cell_to_be_splitted(Partition::Cell *cell);
Partition::Cell* sh_first();
Partition::Cell* sh_first_smallest();
Partition::Cell* sh_first_largest();
Partition::Cell* sh_first_max_neighbours();
Partition::Cell* sh_first_smallest_max_neighbours();
Partition::Cell* sh_first_largest_max_neighbours();
void make_initial_equitable_partition();
void initialize_certificate();
bool is_automorphism(unsigned int* const perm) const;
bool nucr_find_first_component(const unsigned int level);
bool nucr_find_first_component(const unsigned int level,
std::vector<unsigned int>& component,
unsigned int& component_elements,
Partition::Cell*& sh_return);
public:
/**
* Create a new graph with \a N vertices and no edges.
*/
Graph(const unsigned int N = 0);
/**
* Destroy the graph.
*/
~Graph();
/**
* \copydoc AbstractGraph::is_automorphism(const std::vector<unsigned int>& perm) const
*/
bool is_automorphism(const std::vector<unsigned int>& perm) const;
/**
* \copydoc AbstractGraph::get_hash()
*/
virtual unsigned int get_hash();
/**
* Return the number of vertices in the graph.
*/
unsigned int get_nof_vertices() const {
return static_cast<unsigned int>(vertices.size());
}
/**
* \copydoc AbstractGraph::permute(const unsigned int* const perm) const
*/
Graph* permute(const unsigned int* const perm) const;
Graph* permute(const std::vector<unsigned int>& perm) const;
/**
* Add a new vertex with color \a color in the graph and return its index.
*/
unsigned int add_vertex(const unsigned int 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(const unsigned int v1, const unsigned int v2);
/**
* Change the color of the vertex \a vertex to \a color.
*/
void change_color(const unsigned int vertex, const unsigned int 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.
*/
typedef enum {
/** 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;
protected:
class Vertex {
public:
Vertex();
~Vertex();
void add_edge_to(const unsigned int dest_vertex);
void add_edge_from(const unsigned int source_vertex);
void remove_duplicate_edges(std::vector<bool>& tmp);
void sort_edges();
unsigned int color;
std::vector<unsigned int> edges_out;
std::vector<unsigned int> edges_in;
unsigned int nof_edges_in() const { return static_cast<unsigned int>(edges_in.size()); }
unsigned int nof_edges_out() const { return static_cast<unsigned int>(edges_out.size()); }
};
std::vector<Vertex> vertices;
void remove_duplicate_edges();
/** \internal
* Partition independent invariant.
* Returns the color of the vertex.
* Time complexity: O(1).
*/
static unsigned int vertex_color_invariant(const Digraph* const g,
const unsigned int v);
/** \internal
* Partition independent invariant.
* Returns the indegree of the vertex.
* DUPLICATE EDGES MUST HAVE BEEN REMOVED BEFORE.
* Time complexity: O(1).
*/
static unsigned int indegree_invariant(const Digraph* const g,
const unsigned int v);
/** \internal
* Partition independent invariant.
* Returns the outdegree of the vertex.
* DUPLICATE EDGES MUST HAVE BEEN REMOVED BEFORE.
* Time complexity: O(1).
*/
static unsigned int outdegree_invariant(const Digraph* const g,
const unsigned int 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.
*/
static unsigned int selfloop_invariant(const Digraph* const g,
const unsigned int v);
/** \internal
* Refine the partition \a p according to
* the partition independent invariant \a inv.
*/
bool refine_according_to_invariant(unsigned int (*inv)(const Digraph* const g,
const unsigned int 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);
/** \internal
* \copydoc AbstractGraph::is_equitable() const
*/
bool is_equitable() const;
/* Splitting heuristics, documented in more detail in the cc-file. */
SplittingHeuristic sh;
Partition::Cell* find_next_cell_to_be_splitted(Partition::Cell *cell);
Partition::Cell* sh_first();
Partition::Cell* sh_first_smallest();
Partition::Cell* sh_first_largest();
Partition::Cell* sh_first_max_neighbours();
Partition::Cell* sh_first_smallest_max_neighbours();
Partition::Cell* sh_first_largest_max_neighbours();
void make_initial_equitable_partition();
void initialize_certificate();
bool is_automorphism(unsigned int* const perm) const;
void sort_edges();
bool nucr_find_first_component(const unsigned int level);
bool nucr_find_first_component(const unsigned int level,
std::vector<unsigned int>& component,
unsigned int& component_elements,
Partition::Cell*& sh_return);
public:
/**
* Create a new directed graph with \a N vertices and no edges.
*/
Digraph(const unsigned int N = 0);
/**
* Destroy the graph.
*/
~Digraph();
/**
* \copydoc AbstractGraph::is_automorphism(const std::vector<unsigned int>& perm) const
*/
bool is_automorphism(const std::vector<unsigned int>& perm) const;
/**
* \copydoc AbstractGraph::get_hash()
*/
virtual unsigned int get_hash();
/**
* Return the number of vertices in the graph.
*/
unsigned int get_nof_vertices() const { return static_cast<unsigned int>(vertices.size()); }
/**
* Add a new vertex with color 'color' in the graph and return its index.
*/
unsigned int add_vertex(const unsigned int 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(const unsigned int source, const unsigned int target);
/**
* Change the color of the vertex 'vertex' to 'color'.
*/
void change_color(const unsigned int vertex, const unsigned int 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; }
/**
* \copydoc AbstractGraph::permute(const unsigned int* const perm) const
*/
Digraph* permute(const unsigned int* const perm) const;
Digraph* permute(const std::vector<unsigned int>& perm) const;
};
} // namespace bliss
#endif // BLISS_GRAPH_HH
@@ -0,0 +1,111 @@
#include "heap.hh"
#include <new>
#include <cassert>
/* Allow using 'and' instead of '&&' with MSVC */
#if _MSC_VER
#include <ciso646>
#endif
/*
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 <http://www.gnu.org/licenses/>.
*/
namespace bliss {
Heap::Heap() {
array = nullptr;
n = 0;
N = 0;
}
Heap::~Heap()
{
delete[] array;
array = nullptr;
n = 0;
N = 0;
}
void Heap::upheap(unsigned int index)
{
assert(n >= 1);
assert(index >= 1 and index <= n);
const unsigned int v = array[index];
array[0] = 0;
while(array[index/2] > v)
{
array[index] = array[index/2];
index = index/2;
}
array[index] = v;
}
void Heap::downheap(unsigned int index)
{
const unsigned int v = array[index];
const unsigned int lim = n/2;
while(index <= lim)
{
unsigned int new_index = index + index;
if((new_index < n) and (array[new_index] > array[new_index+1]))
new_index++;
if(v <= array[new_index])
break;
array[index] = array[new_index];
index = new_index;
}
array[index] = v;
}
void Heap::init(const unsigned int size)
{
assert(size > 0);
if(size > N)
{
delete[] array;
array = new unsigned int[size + 1];
N = size;
}
n = 0;
}
void Heap::insert(const unsigned int v)
{
assert(n < N);
array[++n] = v;
upheap(n);
}
unsigned int Heap::smallest() const
{
assert(n >= 1 and n <= N);
return array[1];
}
unsigned int Heap::remove()
{
assert(n >= 1 and n <= N);
const unsigned int v = array[1];
array[1] = array[n--];
downheap(1);
return v;
}
} // namespace bliss
@@ -0,0 +1,89 @@
#ifndef BLISS_HEAP_HH
#define BLISS_HEAP_HH
/*
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 <http://www.gnu.org/licenses/>.
*/
namespace bliss {
/**
* \brief A capacity bounded heap data structure.
*/
class Heap
{
unsigned int N;
unsigned int n;
unsigned int *array;
void upheap(unsigned int k);
void downheap(unsigned int k);
public:
/**
* Create a new heap.
* init() must be called after this.
*/
Heap();
~Heap();
/**
* Initialize the heap to have the capacity to hold \e size elements.
*/
void init(const unsigned int size);
/**
* Is the heap empty?
* Time complexity is O(1).
*/
bool is_empty() const {return n == 0; }
/**
* Remove all the elements in the heap.
* Time complexity is O(1).
*/
void clear() {n = 0; }
/**
* Insert the element \a e in the heap.
* Time complexity is O(log(N)), where N is the number of elements
* currently in the heap.
*/
void insert(const unsigned int e);
/**
* Return the smallest element in the heap.
* Time complexity is O(1).
*/
unsigned int smallest() const;
/**
* Remove and return the smallest element in the heap.
* Time complexity is O(log(N)), where N is the number of elements
* currently in the heap.
*/
unsigned int remove();
/**
* Get the number of elements in the heap.
*/
unsigned int size() const {return n; }
};
} // namespace bliss
#endif // BLISS_HEAP_HH
@@ -0,0 +1,34 @@
This file lists changes that were made to the original Bliss package (version 0.75) to integrate it into igraph.
Exclude `CMakeLists.txt`, `Doxyfile`, `Makefile-manual`, `readme.txt`. Make sure not to accidentally overwrite igraph's own `bliss/CMakeLists.txt`.
Removed `bliss.cc`, `bliss_C.cc`, `bliss_C.h`.
Remove `timer.hh`. Remove references to `timer.hh` and `Timer` class in `graph.cc`.
Replace `#pragma once` by traditional header guards in all headers.
### In `bignum.hh`:
Replace `#include <gmp.h>` by `#include "internal/gmp_internal.h"`.
At the beginning, add `#define BLISS_USE_GMP`. Verify that this macro is only used in this file.
### In `defs.cc` and `defs.hh`:
Remove the `...` argument from `fatal_error` for simplicity, and make the function simply invoke `IGRAPH_FATAL`.
### In `graph.cc`:
Define `_INTERNAL_ERROR` in terms of `IGRAPH_FATAL`.
### MSVC compatibility
Bliss uses `and`, `or`, etc. instead of `&&`, `||`, etc. These are not supported by MSVC by default. Bliss 0.74 uses the `/permissive` option to enable support in MSVC, but this option is only supported wit VS2019. Instead, in igraph we add the following where relevant:
```
/* Allow using 'and' instead of '&&' with MSVC */
#if _MSC_VER
#include <ciso646>
#endif
```
@@ -0,0 +1,168 @@
#ifndef BLISS_KQUEUE_HH
#define BLISS_KQUEUE_HH
/*
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 <http://www.gnu.org/licenses/>.
*/
#include <new>
#include <cassert>
namespace bliss {
/**
* \brief A simple implementation of queues with fixed maximum capacity.
*/
template <class Type>
class KQueue
{
public:
/**
* Create a new queue with capacity zero.
* The function init() should be called next.
*/
KQueue();
~KQueue();
/**
* Initialize the queue to have the capacity to hold at most \a N elements.
*/
void init(const unsigned int N);
/** Is the queue empty? */
bool is_empty() const;
/** Return the number of elements in the queue. */
unsigned int size() const;
/** Remove all the elements in the queue. */
void clear();
/** Return (but don't remove) the first element in the queue. */
Type front() const;
/** Remove and return the first element of the queue. */
Type pop_front();
/** Push the element \a e in the front of the queue. */
void push_front(Type e);
/** Remove and return the last element of the queue. */
Type pop_back();
/** Push the element \a e in the back of the queue. */
void push_back(Type e);
private:
Type *entries, *end;
Type *head, *tail;
};
template <class Type>
KQueue<Type>::KQueue()
{
entries = nullptr;
end = nullptr;
head = nullptr;
tail = nullptr;
}
template <class Type>
KQueue<Type>::~KQueue()
{
delete[] entries;
entries = nullptr;
end = nullptr;
head = nullptr;
tail = nullptr;
}
template <class Type>
void KQueue<Type>::init(const unsigned int k)
{
assert(k > 0);
delete[] entries;
entries = new Type[k+1];
end = entries + k + 1;
head = entries;
tail = head;
}
template <class Type>
void KQueue<Type>::clear()
{
head = entries;
tail = head;
}
template <class Type>
bool KQueue<Type>::is_empty() const
{
return head == tail;
}
template <class Type>
unsigned int KQueue<Type>::size() const
{
if(tail >= head)
return(tail - head);
return (end - head) + (tail - entries);
}
template <class Type>
Type KQueue<Type>::front() const
{
assert(head != tail);
return *head;
}
template <class Type>
Type KQueue<Type>::pop_front()
{
assert(head != tail);
Type *old_head = head;
head++;
if(head == end)
head = entries;
return *old_head;
}
template <class Type>
void KQueue<Type>::push_front(Type e)
{
if(head == entries)
head = end - 1;
else
head--;
assert(head != tail);
*head = e;
}
template <class Type>
void KQueue<Type>::push_back(Type e)
{
*tail = e;
tail++;
if(tail == end)
tail = entries;
assert(head != tail);
}
} // namespace bliss
#endif // BLISS_KQUEUE_HH
@@ -0,0 +1,145 @@
#ifndef BLISS_KSTACK_HH
#define BLISS_KSTACK_HH
/*
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 <http://www.gnu.org/licenses/>.
*/
#include <new>
#include <cassert>
namespace bliss {
/**
* \brief A simple implementation of a stack with fixed maximum capacity.
*/
template <class Type>
class KStack {
public:
/**
* Create a new stack with zero capacity.
* The function init() should be called next.
*/
KStack();
/**
* Create a new stack with the capacity to hold at most \a N elements.
*/
KStack(int N);
~KStack();
/**
* Initialize the stack to have the capacity to hold at most \a N elements.
*/
void init(int N);
/**
* Is the stack empty?
*/
bool is_empty() const {return cursor == entries; }
/**
* Return (but don't remove) the top element of the stack.
*/
Type top() const {assert(cursor > entries); return *cursor; }
/**
* Pop (remove) the top element of the stack.
*/
Type pop()
{
assert(cursor > entries);
return *cursor--;
}
/**
* Push the element \a e in the stack.
*/
void push(Type e)
{
assert(cursor < entries + kapacity);
*(++cursor) = e;
}
/** Remove all the elements in the stack. */
void clean() {cursor = entries; }
/**
* Get the number of elements in the stack.
*/
unsigned int size() const {return cursor - entries; }
/**
* Return the i:th element in the stack, where \a i is in the range
* 0,...,this.size()-1; the 0:th element is the bottom element
* in the stack.
*/
Type element_at(unsigned int i)
{
assert(i < size());
return entries[i+1];
}
/** Return the capacity (NOT the number of elements) of the stack. */
int capacity() const {return kapacity; }
private:
int kapacity;
Type *entries;
Type *cursor;
};
template <class Type>
KStack<Type>::KStack()
{
kapacity = 0;
entries = nullptr;
cursor = nullptr;
}
template <class Type>
KStack<Type>::KStack(int k)
{
assert(k > 0);
kapacity = k;
entries = new Type[k+1];
cursor = entries;
}
template <class Type>
void KStack<Type>::init(int k)
{
assert(k > 0);
delete[] entries;
kapacity = k;
entries = new Type[k+1];
cursor = entries;
}
template <class Type>
KStack<Type>::~KStack()
{
delete[] entries;
kapacity = 0;
entries = nullptr;
cursor = nullptr;
}
} // namespace bliss
#endif // BLISS_KSTACK_HH
@@ -0,0 +1,151 @@
#include <cassert>
#include "orbit.hh"
/*
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 <http://www.gnu.org/licenses/>.
*/
namespace bliss {
Orbit::Orbit()
{
orbits = 0;
in_orbit = 0;
nof_elements = 0;
}
Orbit::~Orbit()
{
delete[] orbits;
orbits = 0;
/*
if(orbits)
{
free(orbits);
orbits = 0;
}
*/
delete[] in_orbit;
in_orbit = 0;
/*
if(in_orbit)
{
free(in_orbit);
in_orbit = 0;
}
*/
nof_elements = 0;
_nof_orbits = 0;
}
void Orbit::init(const unsigned int n)
{
assert(n > 0);
if(orbits) delete[] orbits;
orbits = new OrbitEntry[n];
delete[] in_orbit;
in_orbit = new OrbitEntry*[n];
nof_elements = n;
reset();
}
void Orbit::reset()
{
assert(orbits);
assert(in_orbit);
for(unsigned int i = 0; i < nof_elements; i++)
{
orbits[i].element = i;
orbits[i].next = 0;
orbits[i].size = 1;
in_orbit[i] = &orbits[i];
}
_nof_orbits = nof_elements;
}
void Orbit::merge_orbits(OrbitEntry *orbit1, OrbitEntry *orbit2)
{
if(orbit1 != orbit2)
{
_nof_orbits--;
/* Only update the elements in the smaller orbit */
if(orbit1->size > orbit2->size)
{
OrbitEntry * const temp = orbit2;
orbit2 = orbit1;
orbit1 = temp;
}
/* Link the elements of orbit1 to the almost beginning of orbit2 */
OrbitEntry *e = orbit1;
while(e->next)
{
in_orbit[e->element] = orbit2;
e = e->next;
}
in_orbit[e->element] = orbit2;
e->next = orbit2->next;
orbit2->next = orbit1;
/* Keep the minimal orbit representative in the beginning */
if(orbit1->element < orbit2->element)
{
const unsigned int temp = orbit1->element;
orbit1->element = orbit2->element;
orbit2->element = temp;
}
orbit2->size += orbit1->size;
}
}
void Orbit::merge_orbits(unsigned int e1, unsigned int e2)
{
merge_orbits(in_orbit[e1], in_orbit[e2]);
}
bool Orbit::is_minimal_representative(unsigned int element) const
{
return(get_minimal_representative(element) == element);
}
unsigned int Orbit::get_minimal_representative(unsigned int element) const
{
OrbitEntry * const orbit = in_orbit[element];
return(orbit->element);
}
unsigned int Orbit::orbit_size(unsigned int element) const
{
return(in_orbit[element]->size);
}
} // namespace bliss
@@ -0,0 +1,112 @@
#ifndef BLISS_ORBIT_HH
#define BLISS_ORBIT_HH
/*
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 <http://www.gnu.org/licenses/>.
*/
namespace bliss {
/**
* \brief A class for representing orbit information.
*
* Given a set {0,...,N-1} of N elements, represent equivalence
* classes (that is, unordered partitions) of the elements.
* Supports only equivalence class merging, not splitting.
* Merging two classes requires time O(k), where k is the number of
* the elements in the smaller of the merged classes.
* Getting the smallest representative in a class
* (and thus testing whether two elements belong to the same class)
* is a constant time operation.
*/
class Orbit
{
class OrbitEntry
{
public:
unsigned int element;
OrbitEntry *next;
unsigned int size;
};
OrbitEntry *orbits;
OrbitEntry **in_orbit;
unsigned int nof_elements;
unsigned int _nof_orbits;
void merge_orbits(OrbitEntry *o1, OrbitEntry *o2);
public:
/**
* Create a new orbit information object.
* The init() function must be called next to actually initialize
* the object.
*/
Orbit();
~Orbit();
/**
* Initialize the orbit information to consider sets of \a N elements.
* It is required that \a N > 0.
* The orbit information is reset so that each element forms
* an orbit of its own.
* Time complexity is O(N).
* \sa reset()
*/
void init(const unsigned int N);
/**
* Reset the orbits so that each element forms an orbit of its own.
* Time complexity is O(N).
*/
void reset();
/**
* Merge the orbits of the elements \a e1 and \a e2.
* Time complexity is O(k), where k is the number of elements in
* the smaller of the merged orbits.
*/
void merge_orbits(unsigned int e1, unsigned int e2);
/**
* Is the element \a e the smallest element in its orbit?
* Time complexity is O(1).
*/
bool is_minimal_representative(unsigned int e) const;
/**
* Get the smallest element in the orbit of the element \a e.
* Time complexity is O(1).
*/
unsigned int get_minimal_representative(unsigned int e) const;
/**
* Get the number of elements in the orbit of the element \a e.
* Time complexity is O(1).
*/
unsigned int orbit_size(unsigned int e) const;
/**
* Get the number of orbits.
* Time complexity is O(1).
*/
unsigned int nof_orbits() const {return _nof_orbits; }
};
} // namespace bliss
#endif // BLISS_ORBIT_HH
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,299 @@
#ifndef BLISS_PARTITION_HH
#define BLISS_PARTITION_HH
/*
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 <http://www.gnu.org/licenses/>.
*/
namespace bliss {
class Partition;
}
#include <vector>
#include <climits>
#include "kstack.hh"
#include "kqueue.hh"
#include "graph.hh"
namespace bliss {
/**
* \brief A class for refinable, backtrackable ordered partitions.
*
* This is rather a data structure with some helper functions than
* a proper self-contained class.
* That is, for efficiency reasons the fields of this class are directly
* manipulated from bliss::AbstractGraph and its subclasses.
* Conversely, some methods of this class modify the fields of
* bliss::AbstractGraph, too.
*/
class Partition
{
public:
/**
* \brief Data structure for holding information about a cell in a Partition.
*/
class Cell
{
friend class Partition;
public:
unsigned int length;
/* Index of the first element of the cell in
the Partition::elements array */
unsigned int first;
unsigned int max_ival;
unsigned int max_ival_count;
private:
bool in_splitting_queue;
public:
bool in_neighbour_heap;
/* Pointer to the next cell, null if this is the last one. */
Cell* next;
Cell* prev;
Cell* next_nonsingleton;
Cell* prev_nonsingleton;
unsigned int split_level;
/** Is this a unit cell? */
bool is_unit() const {return(length == 1); }
/** Is this cell in splitting queue? */
bool is_in_splitting_queue() const {return(in_splitting_queue); }
};
private:
/** \internal
* Data structure for remembering information about splits in order to
* perform efficient backtracking over the splits.
*/
class RefInfo {
public:
unsigned int split_cell_first;
int prev_nonsingleton_first;
int next_nonsingleton_first;
};
/** \internal
* A stack for remembering the splits, used for backtracking.
*/
KStack<RefInfo> refinement_stack;
class BacktrackInfo {
public:
unsigned int refinement_stack_size;
unsigned int cr_backtrack_point;
};
/** \internal
* The main stack for enabling backtracking.
*/
std::vector<BacktrackInfo> bt_stack;
public:
AbstractGraph* graph;
/* Used during equitable partition refinement */
KQueue<Cell*> splitting_queue;
void splitting_queue_add(Cell* const cell);
Cell* splitting_queue_pop();
bool splitting_queue_is_empty() const;
void splitting_queue_clear();
/** Type for backtracking points. */
typedef unsigned int BacktrackPoint;
/**
* Get a new backtrack point for the current partition
*/
BacktrackPoint set_backtrack_point();
/**
* Backtrack to the point \a p and remove it.
*/
void goto_backtrack_point(BacktrackPoint p);
/**
* Split the non-unit Cell \a cell = {\a element,e1,e2,...,en} containing
* the element \a element in two:
* \a cell = {e1,...,en} and \a newcell = {\a element}.
* @param cell a non-unit Cell
* @param element an element in \a cell
* @return the new unit Cell \a newcell
*/
Cell* individualize(Cell* const cell,
const unsigned int element);
Cell* aux_split_in_two(Cell* const cell,
const unsigned int first_half_size);
private:
unsigned int N;
Cell* cells;
Cell* free_cells;
unsigned int discrete_cell_count;
public:
Cell* first_cell;
Cell* first_nonsingleton_cell;
unsigned int *elements;
/* invariant_values[e] gives the invariant value of the element e */
unsigned int *invariant_values;
/* element_to_cell_map[e] gives the cell of the element e */
Cell **element_to_cell_map;
/** Get the cell of the element \a e */
Cell* get_cell(const unsigned int e) const {
assert(e < N);
return element_to_cell_map[e];
}
/* in_pos[e] points to the elements array s.t. *in_pos[e] = e */
unsigned int **in_pos;
Partition();
~Partition();
/**
* Initialize the partition to the unit partition (all elements in one cell)
* over the \a N > 0 elements {0,...,\a N-1}.
*/
void init(const unsigned int N);
/**
* Returns true iff the partition is discrete, meaning that all
* the elements are in their own cells.
*/
bool is_discrete() const {return(free_cells == 0); }
unsigned int nof_discrete_cells() const {return(discrete_cell_count); }
/*
* Splits the Cell \a cell into [cell_1,...,cell_n]
* according to the invariant_values of the elements in \a cell.
* After splitting, cell_1 == \a cell.
* Returns the pointer to the Cell cell_n;
* cell_n != cell iff the Cell \a cell was actually splitted.
* The flag \a max_ival_info_ok indicates whether the max_ival and
* max_ival_count fields of the Cell \a cell have consistent values
* when the method is called.
* Clears the invariant values of elements in the Cell \a cell as well as
* the max_ival and max_ival_count fields of the Cell \a cell.
*/
Cell *zplit_cell(Cell * const cell, const bool max_ival_info_ok);
/*
* Routines for component recursion
*/
void cr_init();
void cr_free();
unsigned int cr_get_level(const unsigned int cell_index) const;
unsigned int cr_split_level(const unsigned int level,
const std::vector<unsigned int>& cells);
/** Clear the invariant_values of the elements in the Cell \a cell. */
void clear_ivs(Cell* const cell);
private:
/*
* Component recursion data structures
*/
/* Is component recursion support in use? */
bool cr_enabled;
class CRCell {
public:
unsigned int level;
CRCell* next;
CRCell** prev_next_ptr;
void detach() {
if(next)
next->prev_next_ptr = prev_next_ptr;
*(prev_next_ptr) = next;
level = UINT_MAX;
next = 0;
prev_next_ptr = 0;
}
};
CRCell* cr_cells;
CRCell** cr_levels;
class CR_BTInfo {
public:
unsigned int created_trail_index;
unsigned int splitted_level_trail_index;
};
std::vector<unsigned int> cr_created_trail;
std::vector<unsigned int> cr_splitted_level_trail;
std::vector<CR_BTInfo> cr_bt_info;
unsigned int cr_max_level;
void cr_create_at_level(const unsigned int cell_index, unsigned int level);
void cr_create_at_level_trailed(const unsigned int cell_index, unsigned int level);
unsigned int cr_get_backtrack_point();
void cr_goto_backtrack_point(const unsigned int btpoint);
/*
*
* Auxiliary routines for sorting and splitting cells
*
*/
Cell* sort_and_split_cell1(Cell* cell);
Cell* sort_and_split_cell255(Cell* const cell, const unsigned int max_ival);
bool shellsort_cell(Cell* cell);
Cell* split_cell(Cell* const cell);
/*
* Some auxiliary stuff needed for distribution count sorting.
* To make the code thread-safe (modulo the requirement that each graph is
* only accessed in one thread at a time), the arrays are owned by
* the partition instance, not statically defined.
*/
unsigned int dcs_count[256];
unsigned int dcs_start[256];
void dcs_cumulate_count(const unsigned int max);
};
inline Partition::Cell*
Partition::splitting_queue_pop()
{
assert(!splitting_queue.is_empty());
Cell* const cell = splitting_queue.pop_front();
assert(cell->in_splitting_queue);
cell->in_splitting_queue = false;
return cell;
}
inline bool
Partition::splitting_queue_is_empty() const
{
return splitting_queue.is_empty();
}
inline unsigned int
Partition::cr_get_level(const unsigned int cell_index) const
{
assert(cr_enabled);
assert(cell_index < N);
assert(cr_cells[cell_index].level != UINT_MAX);
return(cr_cells[cell_index].level);
}
} // namespace bliss
#endif // BLISS_PARTITION_HH
@@ -0,0 +1,87 @@
#ifndef BLISS_STATS_HH
#define BLISS_STATS_HH
/*
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 <http://www.gnu.org/licenses/>.
*/
#include "graph.hh"
#include "bignum.hh"
namespace bliss {
/**
* \brief Statistics returned by the bliss search algorithm.
*/
class Stats
{
friend class 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. */
long double group_size_approx;
/** \internal The number of nodes in the search tree. */
long unsigned int nof_nodes;
/** \internal The number of leaf nodes in the search tree. */
long unsigned int nof_leaf_nodes;
/** \internal The number of bad nodes in the search tree. */
long unsigned int nof_bad_nodes;
/** \internal The number of canonical representative updates. */
long unsigned int nof_canupdates;
/** \internal The number of generator permutations. */
long unsigned int nof_generators;
/** \internal The maximal depth of the search tree. */
unsigned long int max_level;
/** \internal Reset the statistics. */
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(); }
/** The size of the automorphism group. */
const BigNum& get_group_size() const {return group_size;}
/** An approximation (due to possible overflows/rounding errors) of
* the size of the automorphism group. */
long double get_group_size_approx() const {return group_size_approx;}
/** The number of nodes in the search tree. */
long unsigned int get_nof_nodes() const {return nof_nodes;}
/** The number of leaf nodes in the search tree. */
long unsigned int get_nof_leaf_nodes() const {return nof_leaf_nodes;}
/** The number of bad nodes in the search tree. */
long unsigned int get_nof_bad_nodes() const {return nof_bad_nodes;}
/** The number of canonical representative updates. */
long unsigned int get_nof_canupdates() const {return nof_canupdates;}
/** The number of generator permutations. */
long unsigned int get_nof_generators() const {return nof_generators;}
/** The maximal depth of the search tree. */
unsigned long int get_max_level() const {return max_level;}
};
} // namespace bliss
#endif // BLISS_STATS_HH
@@ -0,0 +1,117 @@
#include "uintseqhash.hh"
/*
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 <http://www.gnu.org/licenses/>.
*/
namespace bliss {
/*
* Random bits generated by
* http://www.fourmilab.ch/hotbits/
*/
static unsigned int rtab[256] = {
0xAEAA35B8, 0x65632E16, 0x155EDBA9, 0x01349B39,
0x8EB8BD97, 0x8E4C5367, 0x8EA78B35, 0x2B1B4072,
0xC1163893, 0x269A8642, 0xC79D7F6D, 0x6A32DEA0,
0xD4D2DA56, 0xD96D4F47, 0x47B5F48A, 0x2587C6BF,
0x642B71D8, 0x5DBBAF58, 0x5C178169, 0xA16D9279,
0x75CDA063, 0x291BC48B, 0x01AC2F47, 0x5416DF7C,
0x45307514, 0xB3E1317B, 0xE1C7A8DE, 0x3ACDAC96,
0x11B96831, 0x32DE22DD, 0x6A1DA93B, 0x58B62381,
0x283810E2, 0xBC30E6A6, 0x8EE51705, 0xB06E8DFB,
0x729AB12A, 0xA9634922, 0x1A6E8525, 0x49DD4E19,
0xE5DB3D44, 0x8C5B3A02, 0xEBDE2864, 0xA9146D9F,
0x736D2CB4, 0xF5229F42, 0x712BA846, 0x20631593,
0x89C02603, 0xD5A5BF6A, 0x823F4E18, 0x5BE5DEFF,
0x1C4EBBFA, 0x5FAB8490, 0x6E559B0C, 0x1FE528D6,
0xB3198066, 0x4A965EB5, 0xFE8BB3D5, 0x4D2F6234,
0x5F125AA4, 0xBCC640FA, 0x4F8BC191, 0xA447E537,
0xAC474D3C, 0x703BFA2C, 0x617DC0E7, 0xF26299D7,
0xC90FD835, 0x33B71C7B, 0x6D83E138, 0xCBB1BB14,
0x029CF5FF, 0x7CBD093D, 0x4C9825EF, 0x845C4D6D,
0x124349A5, 0x53942D21, 0x800E60DA, 0x2BA6EB7F,
0xCEBF30D3, 0xEB18D449, 0xE281F724, 0x58B1CB09,
0xD469A13D, 0x9C7495C3, 0xE53A7810, 0xA866C08E,
0x832A038B, 0xDDDCA484, 0xD5FE0DDE, 0x0756002B,
0x2FF51342, 0x60FEC9C8, 0x061A53E3, 0x47B1884E,
0xDC17E461, 0xA17A6A37, 0x3158E7E2, 0xA40D873B,
0x45AE2140, 0xC8F36149, 0x63A4EE2D, 0xD7107447,
0x6F90994F, 0x5006770F, 0xC1F3CA9A, 0x91B317B2,
0xF61B4406, 0xA8C9EE8F, 0xC6939B75, 0xB28BBC3B,
0x36BF4AEF, 0x3B12118D, 0x4D536ECF, 0x9CF4B46B,
0xE8AB1E03, 0x8225A360, 0x7AE4A130, 0xC4EE8B50,
0x50651797, 0x5BB4C59F, 0xD120EE47, 0x24F3A386,
0xBE579B45, 0x3A378EFC, 0xC5AB007B, 0x3668942B,
0x2DBDCC3A, 0x6F37F64C, 0xC24F862A, 0xB6F97FCF,
0x9E4FA23D, 0x551AE769, 0x46A8A5A6, 0xDC1BCFDD,
0x8F684CF9, 0x501D811B, 0x84279F80, 0x2614E0AC,
0x86445276, 0xAEA0CE71, 0x0812250F, 0xB586D18A,
0xC68D721B, 0x44514E1D, 0x37CDB99A, 0x24731F89,
0xFA72E589, 0x81E6EBA2, 0x15452965, 0x55523D9D,
0x2DC47E14, 0x2E7FA107, 0xA7790F23, 0x40EBFDBB,
0x77E7906B, 0x6C1DB960, 0x1A8B9898, 0x65FA0D90,
0xED28B4D8, 0x34C3ED75, 0x768FD2EC, 0xFAB60BCB,
0x962C75F4, 0x304F0498, 0x0A41A36B, 0xF7DE2A4A,
0xF4770FE2, 0x73C93BBB, 0xD21C82C5, 0x6C387447,
0x8CDB4CB9, 0x2CC243E8, 0x41859E3D, 0xB667B9CB,
0x89681E8A, 0x61A0526C, 0x883EDDDC, 0x539DE9A4,
0xC29E1DEC, 0x97C71EC5, 0x4A560A66, 0xBD7ECACF,
0x576AE998, 0x31CE5616, 0x97172A6C, 0x83D047C4,
0x274EA9A8, 0xEB31A9DA, 0x327209B5, 0x14D1F2CB,
0x00FE1D96, 0x817DBE08, 0xD3E55AED, 0xF2D30AFC,
0xFB072660, 0x866687D6, 0x92552EB9, 0xEA8219CD,
0xF7927269, 0xF1948483, 0x694C1DF5, 0xB7D8B7BF,
0xFFBC5D2F, 0x2E88B849, 0x883FD32B, 0xA0331192,
0x8CB244DF, 0x41FAF895, 0x16902220, 0x97FB512A,
0x2BEA3CC4, 0xAF9CAE61, 0x41ACD0D5, 0xFD2F28FF,
0xE780ADFA, 0xB3A3A76E, 0x7112AD87, 0x7C3D6058,
0x69E64FFF, 0xE5F8617C, 0x8580727C, 0x41F54F04,
0xD72BE498, 0x653D1795, 0x1275A327, 0x14B499D4,
0x4E34D553, 0x4687AA39, 0x68B64292, 0x5C18ABC3,
0x41EABFCC, 0x92A85616, 0x82684CF8, 0x5B9F8A4E,
0x35382FFE, 0xFB936318, 0x52C08E15, 0x80918B2E,
0x199EDEE0, 0xA9470163, 0xEC44ACDD, 0x612D6735,
0x8F88EA7D, 0x759F5EA4, 0xE5CC7240, 0x68CFEB8B,
0x04725601, 0x0C22C23E, 0x5BC97174, 0x89965841,
0x5D939479, 0x690F338A, 0x3C2D4380, 0xDAE97F2B
};
void UintSeqHash::update(unsigned int i)
{
i++;
while(i > 0)
{
h ^= rtab[i & 0xff];
#if 1
const unsigned int b = (h & 0x80000000) >> 31;
i = i >> 8;
h = (h << 1) | b;
#else
const unsigned int b = h & 0x80000000;
h = h << 1;
if(b != 0)
h++;
i = i >> 8;
#endif
}
}
} // namespace bliss
@@ -0,0 +1,63 @@
#ifndef BLISS_UINTSEQHASH_HH
#define BLISS_UINTSEQHASH_HH
/*
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 <http://www.gnu.org/licenses/>.
*/
namespace bliss {
/**
* \brief A updatable hash for sequences of unsigned ints.
*/
class UintSeqHash
{
protected:
unsigned int h;
public:
UintSeqHash() {h = 0; }
UintSeqHash(const UintSeqHash &other) {h = other.h; }
UintSeqHash& operator=(const UintSeqHash &other) {h = other.h; return *this; }
/** Reset the hash value. */
void reset() {h = 0; }
/** Add the unsigned int \a n to the sequence. */
void update(unsigned int n);
/** Get the hash value of the sequence seen so far. */
unsigned int get_value() const {return h; }
/** Compare the hash values of this and \a other.
* Return -1/0/1 if the value of this is smaller/equal/greater than
* that of \a other. */
int cmp(const UintSeqHash &other) const {
return (h < other.h)?-1:((h == other.h)?0:1);
}
/** An abbreviation for cmp(other) < 0 */
bool is_lt(const UintSeqHash &other) const {return cmp(other) < 0; }
/** An abbreviation for cmp(other) <= 0 */
bool is_le(const UintSeqHash &other) const {return cmp(other) <= 0; }
/** An abbreviation for cmp(other) == 0 */
bool is_equal(const UintSeqHash &other) const {return cmp(other) == 0; }
};
} // namespace bliss
#endif // BLISS_UINTSEQHASH_HH
@@ -0,0 +1,60 @@
#include <vector>
#include "utils.hh"
/* Allow using 'and' instead of '&&' with MSVC */
#if _MSC_VER
#include <ciso646>
#endif
/*
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 <http://www.gnu.org/licenses/>.
*/
namespace bliss {
bool
is_permutation(const unsigned int N, const unsigned int* perm)
{
if(N == 0)
return true;
std::vector<bool> m(N, false);
for(unsigned int i = 0; i < N; i++) {
if(perm[i] >= N) return false;
if(m[perm[i]]) return false;
m[perm[i]] = true;
}
return true;
}
bool
is_permutation(const std::vector<unsigned int>& perm)
{
const unsigned int N = perm.size();
if(N == 0)
return true;
std::vector<bool> m(N, false);
for(unsigned int i = 0; i < N; i++) {
if(perm[i] >= N) return false;
if(m[perm[i]]) return false;
m[perm[i]] = true;
}
return true;
}
} // namespace bliss
@@ -0,0 +1,46 @@
#ifndef BLISS_UTILS_HH
#define BLISS_UTILS_HH
/*
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 <http://www.gnu.org/licenses/>.
*/
/**
* \file
* \brief Some small utilities.
*/
#include <vector>
namespace bliss {
/**
* Check whether \a perm is a valid permutation on {0,...,N-1}.
* Slow, mainly for debugging and validation purposes.
*/
bool is_permutation(const unsigned int N, const unsigned int* perm);
/**
* Check whether \a perm is a valid permutation on {0,...,N-1}.
* Slow, mainly for debugging and validation purposes.
*/
bool is_permutation(const std::vector<unsigned int>& perm);
} // namespace bliss
#endif // BLISS_UTILS_HH
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,45 @@
/*
igraph library.
Copyright (C) 2008-2020 The igraph development team
334 Harvard street, Cambridge, MA 02139 USA
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#ifndef IGRAPH_ISOCLASSES_H
#define IGRAPH_ISOCLASSES_H
#include "igraph_decls.h"
IGRAPH_BEGIN_C_DECLS
extern const unsigned int igraph_i_isoclass2_3[];
extern const unsigned int igraph_i_isoclass2_4[];
extern const unsigned int igraph_i_isoclass2_3u[];
extern const unsigned int igraph_i_isoclass2_4u[];
extern const unsigned int igraph_i_isoclass2_5u[];
extern const unsigned int igraph_i_isoclass2_6u[];
extern const unsigned int igraph_i_isoclass_3_idx[];
extern const unsigned int igraph_i_isoclass_4_idx[];
extern const unsigned int igraph_i_isoclass_3u_idx[];
extern const unsigned int igraph_i_isoclass_4u_idx[];
extern const unsigned int igraph_i_isoclass_5u_idx[];
extern const unsigned int igraph_i_isoclass_6u_idx[];
IGRAPH_END_C_DECLS
#endif
@@ -0,0 +1,114 @@
/*
igraph library.
Copyright (C) 2006-2012 Gabor Csardi <csardi.gabor@gmail.com>
334 Harvard street, Cambridge, MA 02139 USA
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "igraph_isomorphism.h"
#include "igraph_constructors.h"
#include "igraph_interface.h"
#include "igraph_iterators.h"
/**
* \function igraph_simplify_and_colorize
* \brief Simplify the graph and compute self-loop and edge multiplicities.
*
* </para><para>
* This function creates a vertex and edge colored simple graph from the input
* graph. The vertex colors are computed as the number of incident self-loops
* to each vertex in the input graph. The edge colors are computed as the number of
* parallel edges in the input graph that were merged to create each edge
* in the simple graph.
*
* </para><para>
* The resulting colored simple graph is suitable for use by isomorphism checking
* algorithms such as VF2, which only support simple graphs, but can consider
* vertex and edge colors.
*
* \param graph The graph object, typically having self-loops or multi-edges.
* \param res An uninitialized graph object. The result will be stored here
* \param vertex_color Computed vertex colors corresponding to self-loop multiplicities.
* \param edge_color Computed edge colors corresponding to edge multiplicities
* \return Error code.
*
* \sa \ref igraph_simplify(), \ref igraph_isomorphic_vf2(), \ref igraph_subisomorphic_vf2()
*
*/
igraph_error_t igraph_simplify_and_colorize(
const igraph_t *graph, igraph_t *res,
igraph_vector_int_t *vertex_color, igraph_vector_int_t *edge_color) {
igraph_es_t es;
igraph_eit_t eit;
igraph_vector_int_t edges;
igraph_int_t no_of_nodes = igraph_vcount(graph);
igraph_int_t no_of_edges = igraph_ecount(graph);
igraph_int_t pto = -1, pfrom = -1;
igraph_int_t i;
IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_FROM));
IGRAPH_FINALLY(igraph_es_destroy, &es);
IGRAPH_CHECK(igraph_eit_create(graph, es, &eit));
IGRAPH_FINALLY(igraph_eit_destroy, &eit);
IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0);
IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2));
IGRAPH_CHECK(igraph_vector_int_resize(vertex_color, no_of_nodes));
igraph_vector_int_null(vertex_color);
IGRAPH_CHECK(igraph_vector_int_resize(edge_color, no_of_edges));
igraph_vector_int_null(edge_color);
i = -1;
for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) {
igraph_int_t edge = IGRAPH_EIT_GET(eit);
igraph_int_t from = IGRAPH_FROM(graph, edge);
igraph_int_t to = IGRAPH_TO(graph, edge);
if (to == from) {
VECTOR(*vertex_color)[to]++;
continue;
}
if (to == pto && from == pfrom) {
VECTOR(*edge_color)[i]++;
} else {
igraph_vector_int_push_back(&edges, from);
igraph_vector_int_push_back(&edges, to);
i++;
VECTOR(*edge_color)[i] = 1;
}
pfrom = from; pto = to;
}
igraph_vector_int_resize(edge_color, i + 1);
igraph_eit_destroy(&eit);
igraph_es_destroy(&es);
IGRAPH_FINALLY_CLEAN(2);
IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, igraph_is_directed(graph)));
igraph_vector_int_destroy(&edges);
IGRAPH_FINALLY_CLEAN(1);
return IGRAPH_SUCCESS;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,215 @@
/*
igraph library.
Copyright (C) 2006-2020 The igraph development team
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "igraph_isomorphism.h"
#include "igraph_interface.h"
#include "igraph_structural.h"
/**
* \section about_graph_isomorphism
*
* <para>igraph provides four set of functions to deal with graph
* isomorphism problems.</para>
*
* <para>The \ref igraph_isomorphic() and \ref igraph_subisomorphic()
* functions make up the first set (in addition with the \ref
* igraph_permute_vertices() function). These functions choose the
* algorithm which is best for the supplied input graph. (The choice is
* not very sophisticated though, see their documentation for
* details.)</para>
*
* <para>The VF2 graph (and subgraph) isomorphism algorithm is implemented in
* igraph, these functions are the second set. See \ref
* igraph_isomorphic_vf2() and \ref igraph_subisomorphic_vf2() for
* starters.</para>
*
* <para>Functions for the Bliss algorithm constitute the third set,
* see \ref igraph_isomorphic_bliss().</para>
*
* <para>Finally, the isomorphism classes of all directed graphs with three and
* four vertices and all undirected graphs with 3-6 vertices are precomputed
* and stored in igraph, so for these small graphs there is a separate fast
* path in the code that does not use more complex, generic isomorphism
* algorithms.</para>
*/
static igraph_error_t igraph_i_isomorphic_small(
const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso
);
/**
* \function igraph_isomorphic
* \brief Are two graphs isomorphic?
*
* In simple terms, two graphs are isomorphic if they become indistinguishable
* from each other once their vertex labels are removed (rendering the vertices
* within each graph indistiguishable). More precisely, two graphs are isomorphic
* if there is a one-to-one mapping from the vertices of the first one
* to the vertices of the second such that it transforms the edge set of the
* first graph into the edge set of the second. This mapping is called
* an \em isomorphism.
*
* </para><para>This function decides which graph isomorphism algorithm to be
* used based on the input graphs. Right now it does the following:
* \olist
* \oli If one graph is directed and the other undirected then an
* error is triggered.
* \oli If one of the graphs has multi-edges then both graphs are
* simplified and colorized using \ref igraph_simplify_and_colorize() and sent to VF2.
* \oli If the two graphs does not have the same number of vertices
* and edges it returns with \c false.
* \oli Otherwise, if the \ref igraph_isoclass() function supports both
* graphs (which is true for directed graphs with 3 and 4 vertices, and
* undirected graphs with 3-6 vertices), an O(1) algorithm is used with
* precomputed data.
* \oli Otherwise Bliss is used, see \ref igraph_isomorphic_bliss().
* \endolist
*
* </para><para>Please call the VF2 and Bliss functions directly if you need
* something more sophisticated, e.g. you need the isomorphic mapping.
*
* \param graph1 The first graph.
* \param graph2 The second graph.
* \param iso Pointer to a Boolean variable, will be set to \c true
* if the two graphs are isomorphic, and \c false otherwise.
* \return Error code.
* \sa \ref igraph_isoclass(), \ref igraph_isoclass_subgraph(),
* \ref igraph_isoclass_create().
*
* Time complexity: exponential.
*/
igraph_error_t igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2,
igraph_bool_t *iso) {
igraph_int_t nodes1 = igraph_vcount(graph1), nodes2 = igraph_vcount(graph2);
igraph_int_t edges1 = igraph_ecount(graph1), edges2 = igraph_ecount(graph2);
igraph_bool_t dir1 = igraph_is_directed(graph1), dir2 = igraph_is_directed(graph2);
igraph_bool_t loop1, loop2, multi1, multi2;
if (dir1 != dir2) {
IGRAPH_ERROR("Cannot compare directed and undirected graphs for isomorphism.", IGRAPH_EINVAL);
}
IGRAPH_CHECK(igraph_has_multiple(graph1, &multi1));
IGRAPH_CHECK(igraph_has_multiple(graph2, &multi2));
if (multi1 || multi2) {
igraph_t r1;
igraph_t r2;
igraph_vector_int_t vc1;
igraph_vector_int_t vc2;
igraph_vector_int_t ec1;
igraph_vector_int_t ec2;
IGRAPH_VECTOR_INT_INIT_FINALLY(&vc1, 0);
IGRAPH_VECTOR_INT_INIT_FINALLY(&vc2, 0);
IGRAPH_VECTOR_INT_INIT_FINALLY(&ec1, 0);
IGRAPH_VECTOR_INT_INIT_FINALLY(&ec2, 0);
IGRAPH_CHECK(igraph_simplify_and_colorize(graph1, &r1, &vc1, &ec1));
IGRAPH_FINALLY(igraph_destroy, &r1);
IGRAPH_CHECK(igraph_simplify_and_colorize(graph2, &r2, &vc2, &ec2));
IGRAPH_FINALLY(igraph_destroy, &r2);
IGRAPH_CHECK(igraph_isomorphic_vf2(&r1, &r2, &vc1, &vc2, &ec1, &ec2, iso,
NULL, NULL, NULL, NULL, NULL));
igraph_destroy(&r2);
igraph_destroy(&r1);
igraph_vector_int_destroy(&ec2);
igraph_vector_int_destroy(&ec1);
igraph_vector_int_destroy(&vc2);
igraph_vector_int_destroy(&vc1);
IGRAPH_FINALLY_CLEAN(6);
return IGRAPH_SUCCESS;
}
if (nodes1 != nodes2 || edges1 != edges2) {
*iso = false;
} else if (nodes1 >= 3 && nodes1 <= (dir1 ? 4 : 6)) {
IGRAPH_CHECK(igraph_has_loop(graph1, &loop1));
IGRAPH_CHECK(igraph_has_loop(graph2, &loop2));
if (!loop1 && !loop2) {
IGRAPH_CHECK(igraph_i_isomorphic_small(graph1, graph2, iso));
} else {
IGRAPH_CHECK(igraph_isomorphic_bliss(graph1, graph2, NULL, NULL, iso,
NULL, NULL, /*sh=*/ IGRAPH_BLISS_FL, NULL, NULL));
}
} else {
IGRAPH_CHECK(igraph_isomorphic_bliss(graph1, graph2, NULL, NULL, iso,
NULL, NULL, /*sh=*/ IGRAPH_BLISS_FL, NULL, NULL));
}
return IGRAPH_SUCCESS;
}
/**
* \function igraph_i_isomorphic_small
* \brief Graph isomorphism for small graphs.
*
* This function uses precomputed indices to decide isomorphism
* problems for directed graphs with only 3 or 4 vertices, or for undirected
* graphs with 3, 4, 5 or 6 vertices. Multi-edges and self-loops are ignored by
* this function.
*
* \param graph1 The first input graph.
* \param graph2 The second input graph. Must have the same
* directedness as \p graph1.
* \param iso Pointer to a boolean, the result is stored here.
* \return Error code.
*
* Time complexity: O(1).
*/
igraph_error_t igraph_i_isomorphic_small(
const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso
) {
igraph_int_t class1, class2;
IGRAPH_CHECK(igraph_isoclass(graph1, &class1));
IGRAPH_CHECK(igraph_isoclass(graph2, &class2));
*iso = (class1 == class2);
return IGRAPH_SUCCESS;
}
/**
* \function igraph_subisomorphic
* \brief Decide subgraph isomorphism.
*
* Check whether \p graph2 is isomorphic to a subgraph of \p graph1.
* Currently this function just calls \ref igraph_subisomorphic_vf2()
* for all graphs.
*
* </para><para>
* Currently this function does not support non-simple graphs.
*
* \param graph1 The first input graph, may be directed or
* undirected. This is supposed to be the bigger graph.
* \param graph2 The second input graph, it must have the same
* directedness as \p graph2, or an error is triggered. This is
* supposed to be the smaller graph.
* \param iso Pointer to a boolean, the result is stored here.
* \return Error code.
*
* Time complexity: exponential.
*/
igraph_error_t igraph_subisomorphic(const igraph_t *graph1, const igraph_t *graph2,
igraph_bool_t *iso) {
return igraph_subisomorphic_vf2(graph1, graph2, NULL, NULL, NULL, NULL, iso, NULL, NULL, NULL, NULL, NULL);
}
File diff suppressed because it is too large Load Diff