Add graph references
This commit is contained in:
@@ -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 135–149, 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 151–162, 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
Reference in New Issue
Block a user