/src/igraph/src/isomorphism/bliss.cc
Line | Count | Source |
1 | | /* |
2 | | Copyright (C) 2003-2006 Tommi Junttila |
3 | | |
4 | | This program is free software; you can redistribute it and/or modify |
5 | | it under the terms of the GNU General Public License version 2 |
6 | | as published by the Free Software Foundation. |
7 | | |
8 | | This program is distributed in the hope that it will be useful, |
9 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | | GNU General Public License for more details. |
12 | | |
13 | | You should have received a copy of the GNU General Public License |
14 | | along with this program; if not, write to the Free Software |
15 | | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | /* FSF address fixed in the above notice on 1 Oct 2009 by Tamas Nepusz */ |
19 | | |
20 | | #include "bliss/graph.hh" |
21 | | |
22 | | #include "igraph_isomorphism.h" |
23 | | #include "igraph_conversion.h" |
24 | | #include "igraph_interface.h" |
25 | | #include "igraph_interrupt.h" |
26 | | #include "igraph_memory.h" |
27 | | #include "igraph_vector.h" |
28 | | |
29 | | #include "core/exceptions.h" |
30 | | |
31 | | #include <climits> |
32 | | #include <cmath> |
33 | | #include <stdexcept> |
34 | | |
35 | | using namespace bliss; |
36 | | using namespace std; |
37 | | |
38 | | /** |
39 | | * \section about_bliss |
40 | | * |
41 | | * <para> |
42 | | * Bliss is a successor of the famous NAUTY algorithm and |
43 | | * implementation. While using the same ideas in general, with better |
44 | | * heuristics and data structures Bliss outperforms NAUTY on most |
45 | | * graphs. |
46 | | * </para> |
47 | | * |
48 | | * <para> |
49 | | * Bliss was developed and implemented by Tommi Junttila and Petteri Kaski at |
50 | | * Helsinki University of Technology, Finland. For more information, |
51 | | * see the Bliss homepage at https://users.aalto.fi/~tjunttil/bliss/ and the following |
52 | | * publication: |
53 | | * </para> |
54 | | * |
55 | | * <para> |
56 | | * Tommi Junttila and Petteri Kaski: "Engineering an Efficient Canonical Labeling |
57 | | * Tool for Large and Sparse Graphs" In ALENEX 2007, pages 135–149, 2007 |
58 | | * https://doi.org/10.1137/1.9781611972870.13 |
59 | | * </para> |
60 | | * |
61 | | * <para> |
62 | | * Tommi Junttila and Petteri Kaski: "Conflict Propagation and Component Recursion |
63 | | * for Canonical Labeling" in TAPAS 2011, pages 151–162, 2011. |
64 | | * https://doi.org/10.1007/978-3-642-19754-3_16 |
65 | | * </para> |
66 | | * |
67 | | * <para> |
68 | | * Bliss works with both directed graphs and undirected graphs. It supports graphs with |
69 | | * self-loops, but not graphs with multi-edges. |
70 | | * </para> |
71 | | * |
72 | | * <para> |
73 | | * Bliss version 0.75 is included in igraph. |
74 | | * </para> |
75 | | */ |
76 | | |
77 | | namespace { // unnamed namespace |
78 | | |
79 | 4.58k | inline AbstractGraph *bliss_from_igraph(const igraph_t *graph) { |
80 | 4.58k | igraph_int_t nof_vertices = igraph_vcount(graph); |
81 | 4.58k | igraph_int_t nof_edges = igraph_ecount(graph); |
82 | | |
83 | 4.58k | if (nof_vertices > UINT_MAX || nof_edges > UINT_MAX) { |
84 | 0 | throw std::runtime_error("Graph too large for BLISS"); |
85 | 0 | } |
86 | | |
87 | 4.58k | AbstractGraph *g; |
88 | | |
89 | 4.58k | if (igraph_is_directed(graph)) { |
90 | 2.41k | g = new Digraph(static_cast<int>(nof_vertices)); |
91 | 2.41k | } else { |
92 | 2.16k | g = new Graph(static_cast<int>(nof_vertices)); |
93 | 2.16k | } |
94 | | |
95 | | /* g->set_verbose_level(0); */ |
96 | | |
97 | 84.6k | for (unsigned int i = 0; i < static_cast<unsigned int>(nof_edges); i++) { |
98 | 80.0k | g->add_edge( |
99 | 80.0k | static_cast<unsigned int>(IGRAPH_FROM(graph, i)), |
100 | 80.0k | static_cast<unsigned int>(IGRAPH_TO(graph, i)) |
101 | 80.0k | ); |
102 | 80.0k | } |
103 | | |
104 | 4.58k | return g; |
105 | 4.58k | } |
106 | | |
107 | | |
108 | 0 | void bliss_free_graph(AbstractGraph *g) { |
109 | 0 | delete g; |
110 | 0 | } |
111 | | |
112 | | |
113 | 4.58k | inline igraph_error_t bliss_set_sh(AbstractGraph *g, igraph_bliss_sh_t sh, bool directed) { |
114 | 4.58k | if (directed) { |
115 | 2.41k | Digraph::SplittingHeuristic gsh = Digraph::shs_fsm; |
116 | 2.41k | switch (sh) { |
117 | 289 | case IGRAPH_BLISS_F: gsh = Digraph::shs_f; break; |
118 | 331 | case IGRAPH_BLISS_FL: gsh = Digraph::shs_fl; break; |
119 | 347 | case IGRAPH_BLISS_FS: gsh = Digraph::shs_fs; break; |
120 | 414 | case IGRAPH_BLISS_FM: gsh = Digraph::shs_fm; break; |
121 | 501 | case IGRAPH_BLISS_FLM: gsh = Digraph::shs_flm; break; |
122 | 537 | case IGRAPH_BLISS_FSM: gsh = Digraph::shs_fsm; break; |
123 | 0 | default: IGRAPH_ERROR("Invalid splitting heuristic.", IGRAPH_EINVAL); |
124 | 2.41k | } |
125 | 2.41k | static_cast<Digraph *>(g)->set_splitting_heuristic(gsh); |
126 | 2.41k | } else { |
127 | 2.16k | Graph::SplittingHeuristic gsh = Graph::shs_fsm; |
128 | 2.16k | switch (sh) { |
129 | 241 | case IGRAPH_BLISS_F: gsh = Graph::shs_f; break; |
130 | 294 | case IGRAPH_BLISS_FL: gsh = Graph::shs_fl; break; |
131 | 327 | case IGRAPH_BLISS_FS: gsh = Graph::shs_fs; break; |
132 | 369 | case IGRAPH_BLISS_FM: gsh = Graph::shs_fm; break; |
133 | 444 | case IGRAPH_BLISS_FLM: gsh = Graph::shs_flm; break; |
134 | 493 | case IGRAPH_BLISS_FSM: gsh = Graph::shs_fsm; break; |
135 | 0 | default: IGRAPH_ERROR("Invalid splitting heuristic.", IGRAPH_EINVAL); |
136 | 2.16k | } |
137 | 2.16k | static_cast<Graph *>(g)->set_splitting_heuristic(gsh); |
138 | 2.16k | } |
139 | 4.58k | return IGRAPH_SUCCESS; |
140 | 4.58k | } |
141 | | |
142 | | |
143 | 4.58k | inline igraph_error_t bliss_set_colors(AbstractGraph *g, const igraph_vector_int_t *colors) { |
144 | 4.58k | if (colors == NULL) { |
145 | 4.58k | return IGRAPH_SUCCESS; |
146 | 4.58k | } |
147 | 0 | const int n = g->get_nof_vertices(); |
148 | 0 | if (n != igraph_vector_int_size(colors)) { |
149 | 0 | IGRAPH_ERROR("Invalid vertex color vector length.", IGRAPH_EINVAL); |
150 | 0 | } |
151 | 0 | for (int i = 0; i < n; ++i) { |
152 | 0 | igraph_int_t color = VECTOR(*colors)[i]; |
153 | 0 | if (color < INT_MIN || color > INT_MAX) { |
154 | 0 | IGRAPH_ERRORF("Invalid vertex color index %" IGRAPH_PRId " for vertex %d.", IGRAPH_EOVERFLOW, color, i); |
155 | 0 | } |
156 | 0 | g->change_color(i, static_cast<int>(color)); |
157 | 0 | } |
158 | 0 | return IGRAPH_SUCCESS; |
159 | 0 | } |
160 | | |
161 | | |
162 | 4.58k | inline igraph_error_t bliss_info_to_igraph(igraph_bliss_info_t *info, const Stats &stats) { |
163 | 4.58k | if (info) { |
164 | 4.58k | size_t group_size_strlen; |
165 | | |
166 | 4.58k | info->max_level = stats.get_max_level(); |
167 | 4.58k | info->nof_nodes = stats.get_nof_nodes(); |
168 | 4.58k | info->nof_leaf_nodes = stats.get_nof_leaf_nodes(); |
169 | 4.58k | info->nof_bad_nodes = stats.get_nof_bad_nodes(); |
170 | 4.58k | info->nof_canupdates = stats.get_nof_canupdates(); |
171 | 4.58k | info->nof_generators = stats.get_nof_generators(); |
172 | | |
173 | 4.58k | mpz_t group_size; |
174 | 4.58k | mpz_init(group_size); |
175 | 4.58k | stats.get_group_size().get(group_size); |
176 | 4.58k | group_size_strlen = mpz_sizeinbase(group_size, /* base */ 10) + 2; |
177 | 4.58k | info->group_size = IGRAPH_CALLOC(group_size_strlen, char); |
178 | 4.58k | if (! info->group_size) { |
179 | 0 | IGRAPH_ERROR("Insufficient memory to retrieve automotphism group size.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ |
180 | 0 | } |
181 | 4.58k | mpz_get_str(info->group_size, /* base */ 10, group_size); |
182 | 4.58k | mpz_clear(group_size); |
183 | 4.58k | } |
184 | | |
185 | 4.58k | return IGRAPH_SUCCESS; |
186 | 4.58k | } |
187 | | |
188 | | |
189 | | // This is the callback function that can tell Bliss to terminate early. |
190 | | struct AbortChecker { |
191 | | bool aborted; |
192 | | |
193 | 4.58k | AbortChecker() : aborted(false) { } |
194 | 4.93M | bool operator()() { |
195 | 4.93M | if (igraph_allow_interruption() != IGRAPH_SUCCESS) { |
196 | 0 | aborted = true; |
197 | 0 | return true; |
198 | 0 | } |
199 | 4.93M | return false; |
200 | 4.93M | } |
201 | | }; |
202 | | |
203 | | |
204 | | // This is the callback function used with AbstractGraph::find_automorphisms(). |
205 | | // It collects the automorphism group generators into a pointer vector. |
206 | | class AutCollector { |
207 | | igraph_vector_int_list_t *generators; |
208 | | |
209 | | public: |
210 | 4.58k | AutCollector(igraph_vector_int_list_t *generators_) : generators(generators_) { } |
211 | | |
212 | 130k | void operator ()(unsigned int n, const unsigned int *aut) { |
213 | 130k | igraph_vector_int_t newvector; |
214 | 130k | igraph_error_t err; |
215 | | |
216 | 130k | err = igraph_vector_int_init(&newvector, n); |
217 | 130k | if (err != IGRAPH_SUCCESS) { |
218 | 0 | throw bad_alloc(); |
219 | 0 | } |
220 | | |
221 | 130k | copy(aut, aut + n, VECTOR(newvector)); // takes care of unsigned int -> igraph_int_t conversion |
222 | | |
223 | 130k | err = igraph_vector_int_list_push_back(generators, &newvector); |
224 | 130k | if (err != IGRAPH_SUCCESS) { |
225 | 0 | throw bad_alloc(); |
226 | 0 | } |
227 | 130k | } |
228 | | }; |
229 | | |
230 | | } // end unnamed namespace |
231 | | |
232 | | |
233 | | /** |
234 | | * \function igraph_canonical_permutation |
235 | | * \brief Canonical permutation of a graph. |
236 | | * |
237 | | * This function computes the vertex permutation which transforms |
238 | | * the graph into a canonical form. Two graphs have the same canonical form if |
239 | | * and only if they are isomorphic. Use \ref igraph_is_same_graph() to compare |
240 | | * two canonical forms. |
241 | | * |
242 | | * </para><para> |
243 | | * The current implementation uses the BLISS isomorphism algorithms with |
244 | | * sensible defaults. Use \ref igraph_canonical_permutation_bliss() to fine-tune |
245 | | * the parameters. |
246 | | * |
247 | | * \param graph The input graph. Multiple edges between the same nodes |
248 | | * are not supported and will cause an incorrect result to be returned. |
249 | | * \param colors An optional vertex color vector for the graph. Supply a |
250 | | * null pointer is the graph is not colored. |
251 | | * \param labeling Pointer to a vector, the result is stored here. The |
252 | | * permutation takes vertex 0 to the first element of the vector, |
253 | | * vertex 1 to the second, etc. The vector will be resized as |
254 | | * needed. |
255 | | * \return Error code. |
256 | | * |
257 | | * \sa \ref igraph_is_same_graph() |
258 | | * |
259 | | * Time complexity: exponential, in practice it is fast for many graphs. |
260 | | */ |
261 | | igraph_error_t igraph_canonical_permutation( |
262 | | const igraph_t *graph, const igraph_vector_int_t *colors, |
263 | | igraph_vector_int_t *labeling |
264 | 0 | ) { |
265 | 0 | return igraph_canonical_permutation_bliss( |
266 | 0 | graph, colors, labeling, IGRAPH_BLISS_FL, NULL |
267 | 0 | ); |
268 | 0 | } |
269 | | |
270 | | static igraph_error_t igraph_i_canonical_permutation_bliss( |
271 | | const igraph_t *graph, const igraph_vector_int_t *colors, |
272 | | igraph_vector_int_t *labeling, igraph_bliss_sh_t sh, |
273 | | igraph_bliss_info_t *info |
274 | | ); |
275 | | |
276 | | /** |
277 | | * \function igraph_canonical_permutation_bliss |
278 | | * \brief Canonical permutation using Bliss. |
279 | | * |
280 | | * This function computes the vertex permutation which transforms |
281 | | * the graph into a canonical form, using the Bliss algorithm. |
282 | | * Two graphs have the same canonical form if and only if they |
283 | | * are isomorphic. Use \ref igraph_is_same_graph() to compare |
284 | | * two canonical forms. |
285 | | * |
286 | | * \param graph The input graph. Multiple edges between the same nodes |
287 | | * are not supported and will cause an incorrect result to be returned. |
288 | | * \param colors An optional vertex color vector for the graph. Supply a |
289 | | * null pointer is the graph is not colored. |
290 | | * \param labeling Pointer to a vector, the result is stored here. The |
291 | | * permutation takes vertex 0 to the first element of the vector, |
292 | | * vertex 1 to the second, etc. The vector will be resized as |
293 | | * needed. |
294 | | * \param sh The splitting heuristics to be used in Bliss. See \ref |
295 | | * igraph_bliss_sh_t. |
296 | | * \param info If not \c NULL then information on Bliss internals is |
297 | | * stored here. The memory used by this structure must to be freed |
298 | | * when no longer needed, see \ref igraph_bliss_info_t. |
299 | | * \return Error code. |
300 | | * |
301 | | * \sa \ref igraph_is_same_graph() |
302 | | * |
303 | | * Time complexity: exponential, in practice it is fast for many graphs. |
304 | | */ |
305 | | igraph_error_t igraph_canonical_permutation_bliss( |
306 | | const igraph_t *graph, const igraph_vector_int_t *colors, |
307 | | igraph_vector_int_t *labeling, igraph_bliss_sh_t sh, |
308 | | igraph_bliss_info_t *info |
309 | 0 | ) { |
310 | 0 | igraph_vector_int_t inv_permutation; |
311 | |
|
312 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&inv_permutation, igraph_vcount(graph)); |
313 | 0 | IGRAPH_CHECK(igraph_i_canonical_permutation_bliss(graph, colors, &inv_permutation, sh, info)); |
314 | 0 | IGRAPH_CHECK(igraph_invert_permutation(&inv_permutation, labeling)); |
315 | 0 | igraph_vector_int_destroy(&inv_permutation); |
316 | 0 | IGRAPH_FINALLY_CLEAN(1); |
317 | |
|
318 | 0 | return IGRAPH_SUCCESS; |
319 | 0 | } |
320 | | |
321 | | static igraph_error_t igraph_i_canonical_permutation_bliss( |
322 | | const igraph_t *graph, const igraph_vector_int_t *colors, |
323 | | igraph_vector_int_t *labeling, igraph_bliss_sh_t sh, |
324 | | igraph_bliss_info_t *info |
325 | 0 | ) { |
326 | 0 | IGRAPH_HANDLE_EXCEPTIONS( |
327 | 0 | AbstractGraph *g = bliss_from_igraph(graph); |
328 | 0 | IGRAPH_FINALLY(bliss_free_graph, g); |
329 | 0 | const unsigned int N = g->get_nof_vertices(); |
330 | |
|
331 | 0 | IGRAPH_CHECK(bliss_set_sh(g, sh, igraph_is_directed(graph))); |
332 | 0 | IGRAPH_CHECK(bliss_set_colors(g, colors)); |
333 | |
|
334 | 0 | Stats stats; |
335 | 0 | AbortChecker checker; |
336 | 0 | const unsigned int *cl = g->canonical_form(stats, /* report */ nullptr, /* terminate */ checker); |
337 | 0 | if (checker.aborted) { |
338 | 0 | return IGRAPH_INTERRUPTED; |
339 | 0 | } |
340 | |
|
341 | 0 | IGRAPH_CHECK(igraph_vector_int_resize(labeling, N)); |
342 | 0 | for (unsigned int i = 0; i < N; i++) { |
343 | 0 | VECTOR(*labeling)[i] = cl[i]; |
344 | 0 | } |
345 | |
|
346 | 0 | IGRAPH_CHECK(bliss_info_to_igraph(info, stats)); |
347 | |
|
348 | 0 | delete g; |
349 | 0 | IGRAPH_FINALLY_CLEAN(1); |
350 | 0 | ); |
351 | |
|
352 | 0 | return IGRAPH_SUCCESS; |
353 | 0 | } |
354 | | |
355 | | /** |
356 | | * \function igraph_count_automorphisms |
357 | | * \brief Number of automorphisms of a graph. |
358 | | * |
359 | | * This function computes the number of automorphisms of a graph. Since the |
360 | | * number of automorphisms may be very large, the result is returned as an |
361 | | * \c igraph_real_t instead of an integer. If the number of automorphisms |
362 | | * is larger than what can be represented in an \c igraph_real_t and you need |
363 | | * the exact number, use \ref igraph_count_automorphisms_bliss(), which can |
364 | | * return the number as a string. |
365 | | * |
366 | | * \param graph The input graph. Multiple edges between the same nodes |
367 | | * are not supported and will cause an incorrect result to be returned. |
368 | | * \param colors An optional vertex color vector for the graph. Supply a |
369 | | * null pointer is the graph is not colored. |
370 | | * \param result Pointer to an \c igraph_real_t, the number of automorphisms |
371 | | * will be returned here. |
372 | | * \return Error code. \c IGRAPH_EOVERFLOW if the number of automorphisms is |
373 | | * too large to be represented in an \c igraph_real_t . |
374 | | * |
375 | | * Time complexity: exponential, in practice it is fast for many graphs. |
376 | | */ |
377 | | igraph_error_t igraph_count_automorphisms( |
378 | | const igraph_t *graph, const igraph_vector_int_t *colors, |
379 | | igraph_real_t *result |
380 | 0 | ) { |
381 | 0 | igraph_bliss_info_t info; |
382 | 0 | double x; |
383 | |
|
384 | 0 | IGRAPH_CHECK(igraph_count_automorphisms_bliss(graph, colors, IGRAPH_BLISS_FL, &info)); |
385 | | |
386 | 0 | x = strtod(info.group_size, NULL); |
387 | 0 | igraph_free(info.group_size); |
388 | |
|
389 | 0 | if (x == 0) { |
390 | 0 | return IGRAPH_FAILURE; |
391 | 0 | } else if (x == HUGE_VAL) { |
392 | 0 | return IGRAPH_EOVERFLOW; |
393 | 0 | } else { |
394 | 0 | if (result) { |
395 | 0 | *result = x; |
396 | 0 | } |
397 | 0 | return IGRAPH_SUCCESS; |
398 | 0 | } |
399 | 0 | } |
400 | | |
401 | | /** |
402 | | * \function igraph_count_automorphisms_bliss |
403 | | * \brief Number of automorphisms using Bliss. |
404 | | * |
405 | | * The number of automorphisms of a graph is computed using Bliss. The |
406 | | * result is returned as part of the \p info structure, in tag \c |
407 | | * group_size. It is returned as a string, as it can be very high even |
408 | | * for relatively small graphs. See also \ref igraph_bliss_info_t. |
409 | | * |
410 | | * \param graph The input graph. Multiple edges between the same nodes |
411 | | * are not supported and will cause an incorrect result to be returned. |
412 | | * \param colors An optional vertex color vector for the graph. Supply a |
413 | | * null pointer is the graph is not colored. |
414 | | * \param sh The splitting heuristics to be used in Bliss. See \ref |
415 | | * igraph_bliss_sh_t. |
416 | | * \param info The result is stored here, in particular in the \c |
417 | | * group_size tag of \p info. The memory used by this structure must be |
418 | | * released when no longer needed, see \ref igraph_bliss_info_t. |
419 | | * \return Error code. |
420 | | * |
421 | | * Time complexity: exponential, in practice it is fast for many graphs. |
422 | | */ |
423 | | igraph_error_t igraph_count_automorphisms_bliss( |
424 | | const igraph_t *graph, const igraph_vector_int_t *colors, |
425 | | igraph_bliss_sh_t sh, igraph_bliss_info_t *info |
426 | 0 | ) { |
427 | 0 | IGRAPH_HANDLE_EXCEPTIONS( |
428 | 0 | AbstractGraph *g = bliss_from_igraph(graph); |
429 | 0 | IGRAPH_FINALLY(bliss_free_graph, g); |
430 | |
|
431 | 0 | IGRAPH_CHECK(bliss_set_sh(g, sh, igraph_is_directed(graph))); |
432 | 0 | IGRAPH_CHECK(bliss_set_colors(g, colors)); |
433 | |
|
434 | 0 | Stats stats; |
435 | 0 | AbortChecker checker; |
436 | 0 | g->find_automorphisms(stats, /* report */ nullptr, /* terminate */ checker); |
437 | 0 | if (checker.aborted) { |
438 | 0 | return IGRAPH_INTERRUPTED; |
439 | 0 | } |
440 | |
|
441 | 0 | IGRAPH_CHECK(bliss_info_to_igraph(info, stats)); |
442 | |
|
443 | 0 | delete g; |
444 | 0 | IGRAPH_FINALLY_CLEAN(1); |
445 | 0 | ); |
446 | |
|
447 | 0 | return IGRAPH_SUCCESS; |
448 | 0 | } |
449 | | |
450 | | /** |
451 | | * \function igraph_automorphism_group |
452 | | * \brief Automorphism group generators of a graph. |
453 | | * |
454 | | * This function computes the generators of the automorphism group of a graph. |
455 | | * The generator set may not be minimal and may depend on the specific parameters |
456 | | * of the algorithm under the hood. The generators are permutations represented |
457 | | * using zero-based indexing. |
458 | | * |
459 | | * </para><para> |
460 | | * The current implementation uses BLISS behind the scenes and the result may |
461 | | * be dependent on the splitting heuristics. Use \ref igraph_automorphism_group_bliss() |
462 | | * if you want to fine-tune the splitting heuristics. |
463 | | * |
464 | | * \param graph The input graph. Multiple edges between the same nodes |
465 | | * are not supported and will cause an incorrect result to be returned. |
466 | | * \param colors An optional vertex color vector for the graph. Supply a |
467 | | * null pointer is the graph is not colored. |
468 | | * \param generators Must be an initialized interger vector list. |
469 | | * The generators of the automorphism group will be stored here. |
470 | | * \return Error code. |
471 | | * |
472 | | * Time complexity: exponential, in practice it is fast for many graphs. |
473 | | */ |
474 | | igraph_error_t igraph_automorphism_group( |
475 | | const igraph_t *graph, const igraph_vector_int_t *colors, |
476 | | igraph_vector_int_list_t *generators |
477 | 0 | ) { |
478 | 0 | return igraph_automorphism_group_bliss( |
479 | 0 | graph, colors, generators, IGRAPH_BLISS_FL, NULL |
480 | 0 | ); |
481 | 0 | } |
482 | | |
483 | | /** |
484 | | * \function igraph_automorphism_group_bliss |
485 | | * \brief Automorphism group generators using Bliss. |
486 | | * |
487 | | * The generators of the automorphism group of a graph are computed |
488 | | * using Bliss. The generator set may not be minimal and may depend on |
489 | | * the splitting heuristics. The generators are permutations represented |
490 | | * using zero-based indexing. |
491 | | * |
492 | | * \param graph The input graph. Multiple edges between the same nodes |
493 | | * are not supported and will cause an incorrect result to be returned. |
494 | | * \param colors An optional vertex color vector for the graph. Supply a |
495 | | * null pointer is the graph is not colored. |
496 | | * \param generators Must be an initialized interger vector list. |
497 | | * The generators of the automorphism group will be stored here. |
498 | | * \param sh The splitting heuristics to be used in Bliss. See \ref |
499 | | * igraph_bliss_sh_t. |
500 | | * \param info If not \c NULL then information on Bliss internals is |
501 | | * stored here. The memory used by this structure must to be freed |
502 | | * when no longer needed, see \ref igraph_bliss_info_t. |
503 | | * \return Error code. |
504 | | * |
505 | | * Time complexity: exponential, in practice it is fast for many graphs. |
506 | | */ |
507 | | igraph_error_t igraph_automorphism_group_bliss( |
508 | | const igraph_t *graph, const igraph_vector_int_t *colors, |
509 | | igraph_vector_int_list_t *generators, igraph_bliss_sh_t sh, |
510 | | igraph_bliss_info_t *info |
511 | 4.58k | ) { |
512 | 4.58k | IGRAPH_HANDLE_EXCEPTIONS( |
513 | 4.58k | AbstractGraph *g = bliss_from_igraph(graph); |
514 | 4.58k | IGRAPH_FINALLY(bliss_free_graph, g); |
515 | | |
516 | 4.58k | IGRAPH_CHECK(bliss_set_sh(g, sh, igraph_is_directed(graph))); |
517 | 4.58k | IGRAPH_CHECK(bliss_set_colors(g, colors)); |
518 | | |
519 | 4.58k | Stats stats; |
520 | 4.58k | igraph_vector_int_list_clear(generators); |
521 | 4.58k | AutCollector collector(generators); |
522 | 4.58k | AbortChecker checker; |
523 | 4.58k | g->find_automorphisms(stats, collector, checker); |
524 | 4.58k | if (checker.aborted) { |
525 | 4.58k | return IGRAPH_INTERRUPTED; |
526 | 4.58k | } |
527 | 4.58k | IGRAPH_CHECK(bliss_info_to_igraph(info, stats)); |
528 | | |
529 | 4.58k | delete g; |
530 | 4.58k | IGRAPH_FINALLY_CLEAN(1); |
531 | 4.58k | ); |
532 | | |
533 | 4.58k | return IGRAPH_SUCCESS; |
534 | 4.58k | } |
535 | | |
536 | | |
537 | | /* The following license notice applies to the rest of this file */ |
538 | | |
539 | | /* |
540 | | igraph library. |
541 | | Copyright (C) 2006-2021 The igraph development team <igraph@igraph.org> |
542 | | |
543 | | This program is free software; you can redistribute it and/or modify |
544 | | it under the terms of the GNU General Public License as published by |
545 | | the Free Software Foundation; either version 2 of the License, or |
546 | | (at your option) any later version. |
547 | | |
548 | | This program is distributed in the hope that it will be useful, |
549 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
550 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
551 | | GNU General Public License for more details. |
552 | | |
553 | | You should have received a copy of the GNU General Public License |
554 | | along with this program. If not, see <https://www.gnu.org/licenses/>. |
555 | | */ |
556 | | |
557 | | /** |
558 | | * \function igraph_isomorphic_bliss |
559 | | * \brief Graph isomorphism via Bliss. |
560 | | * |
561 | | * This function uses the Bliss graph isomorphism algorithm, a |
562 | | * successor of the famous NAUTY algorithm and implementation. Bliss |
563 | | * is open source and licensed according to the GNU LGPL. See |
564 | | * https://users.aalto.fi/~tjunttil/bliss/ for |
565 | | * details. Currently the 0.75 version of Bliss is included in igraph. |
566 | | * |
567 | | * </para><para> |
568 | | * Isomorphism testing is implemented by producing the canonical form |
569 | | * of both graphs using \ref igraph_canonical_permutation_bliss() and |
570 | | * comparing them. |
571 | | * |
572 | | * \param graph1 The first input graph. Multiple edges between the same nodes |
573 | | * are not supported and will cause an incorrect result to be returned. |
574 | | * \param graph2 The second input graph. Multiple edges between the same nodes |
575 | | * are not supported and will cause an incorrect result to be returned. |
576 | | * \param colors1 An optional vertex color vector for the first graph. Supply a |
577 | | * null pointer if your graph is not colored. |
578 | | * \param colors2 An optional vertex color vector for the second graph. Supply a |
579 | | * null pointer if your graph is not colored. |
580 | | * \param iso Pointer to a boolean, the result is stored here. |
581 | | * \param map12 A vector or \c NULL pointer. If not \c NULL then an |
582 | | * isomorphic mapping from \p graph1 to \p graph2 is stored here. |
583 | | * If the input graphs are not isomorphic then this vector is |
584 | | * cleared, i.e. it will have length zero. |
585 | | * \param map21 Similar to \p map12, but for the mapping from \p |
586 | | * graph2 to \p graph1. |
587 | | * \param sh Splitting heuristics to be used for the graphs. See |
588 | | * \ref igraph_bliss_sh_t. |
589 | | * \param info1 If not \c NULL, information about the canonization of |
590 | | * the first input graph is stored here. Note that if the two graphs |
591 | | * have different number of vertices or edges, then this is only |
592 | | * partially filled. The memory used by this structure should be |
593 | | * released when no longer needed, see \ref igraph_bliss_info_t |
594 | | * for details. |
595 | | * \param info2 Same as \p info1, but for the second graph. |
596 | | * \return Error code. |
597 | | * |
598 | | * Time complexity: exponential, but in practice it is quite fast. |
599 | | */ |
600 | | igraph_error_t igraph_isomorphic_bliss(const igraph_t *graph1, const igraph_t *graph2, |
601 | | const igraph_vector_int_t *colors1, const igraph_vector_int_t *colors2, |
602 | | igraph_bool_t *iso, igraph_vector_int_t *map12, |
603 | | igraph_vector_int_t *map21, igraph_bliss_sh_t sh, |
604 | 0 | igraph_bliss_info_t *info1, igraph_bliss_info_t *info2) { |
605 | |
|
606 | 0 | igraph_int_t no_of_nodes = igraph_vcount(graph1); |
607 | 0 | igraph_int_t no_of_edges = igraph_ecount(graph1); |
608 | 0 | igraph_vector_int_t perm1, perm2; |
609 | 0 | igraph_vector_int_t vmap12, *mymap12 = &vmap12; |
610 | 0 | igraph_vector_int_t from, to, index; |
611 | 0 | igraph_vector_int_t from2, to2, index2; |
612 | 0 | igraph_bool_t directed; |
613 | 0 | igraph_int_t i, j; |
614 | |
|
615 | 0 | *iso = 0; |
616 | 0 | if (info1) { |
617 | 0 | info1->nof_nodes = info1->nof_leaf_nodes = info1->nof_bad_nodes = |
618 | 0 | info1->nof_canupdates = info1->max_level = info1->nof_generators = 0; |
619 | 0 | info1->group_size = 0; |
620 | 0 | } |
621 | 0 | if (info2) { |
622 | 0 | info2->nof_nodes = info2->nof_leaf_nodes = info2->nof_bad_nodes = |
623 | 0 | info2->nof_canupdates = info2->max_level = info2->nof_generators = 0; |
624 | 0 | info2->group_size = 0; |
625 | 0 | } |
626 | |
|
627 | 0 | directed = igraph_is_directed(graph1); |
628 | 0 | if (igraph_is_directed(graph2) != directed) { |
629 | 0 | IGRAPH_ERROR("Cannot compare directed and undirected graphs.", |
630 | 0 | IGRAPH_EINVAL); |
631 | 0 | } |
632 | 0 | if ((colors1 == NULL || colors2 == NULL) && colors1 != colors2) { |
633 | 0 | IGRAPH_WARNING("Only one of the graphs is vertex colored, colors will be ignored."); |
634 | 0 | colors1 = NULL; colors2 = NULL; |
635 | 0 | } |
636 | |
|
637 | 0 | if (no_of_nodes != igraph_vcount(graph2) || |
638 | 0 | no_of_edges != igraph_ecount(graph2)) { |
639 | 0 | if (map12) { |
640 | 0 | igraph_vector_int_clear(map12); |
641 | 0 | } |
642 | 0 | if (map21) { |
643 | 0 | igraph_vector_int_clear(map21); |
644 | 0 | } |
645 | 0 | return IGRAPH_SUCCESS; |
646 | 0 | } |
647 | | |
648 | 0 | if (map12) { |
649 | 0 | mymap12 = map12; |
650 | 0 | } else { |
651 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(mymap12, 0); |
652 | 0 | } |
653 | | |
654 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&perm1, no_of_nodes); |
655 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&perm2, no_of_nodes); |
656 | | |
657 | 0 | IGRAPH_CHECK(igraph_i_canonical_permutation_bliss(graph1, colors1, &perm1, sh, info1)); |
658 | 0 | IGRAPH_CHECK(igraph_i_canonical_permutation_bliss(graph2, colors2, &perm2, sh, info2)); |
659 | | |
660 | 0 | IGRAPH_CHECK(igraph_vector_int_resize(mymap12, no_of_nodes)); |
661 | | |
662 | | /* The inverse of perm2 is produced in mymap12 */ |
663 | 0 | for (i = 0; i < no_of_nodes; i++) { |
664 | 0 | VECTOR(*mymap12)[ VECTOR(perm2)[i] ] = i; |
665 | 0 | } |
666 | | /* Now we produce perm2^{-1} o perm1 in perm2 */ |
667 | 0 | for (i = 0; i < no_of_nodes; i++) { |
668 | 0 | VECTOR(perm2)[i] = VECTOR(*mymap12)[ VECTOR(perm1)[i] ]; |
669 | 0 | } |
670 | | /* Copy it to mymap12 */ |
671 | 0 | IGRAPH_CHECK(igraph_vector_int_update(mymap12, &perm2)); |
672 | | |
673 | 0 | igraph_vector_int_destroy(&perm1); |
674 | 0 | igraph_vector_int_destroy(&perm2); |
675 | 0 | IGRAPH_FINALLY_CLEAN(2); |
676 | | |
677 | | /* Check isomorphism, we apply the permutation in mymap12 to graph1 |
678 | | and should get graph2 */ |
679 | |
|
680 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&from, no_of_edges); |
681 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&to, no_of_edges); |
682 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&index, no_of_edges); |
683 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&from2, no_of_edges * 2); |
684 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&to2, no_of_edges); |
685 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&index2, no_of_edges); |
686 | | |
687 | 0 | for (i = 0; i < no_of_edges; i++) { |
688 | 0 | VECTOR(from)[i] = VECTOR(*mymap12)[ IGRAPH_FROM(graph1, i) ]; |
689 | 0 | VECTOR(to)[i] = VECTOR(*mymap12)[ IGRAPH_TO (graph1, i) ]; |
690 | 0 | if (! directed && VECTOR(from)[i] < VECTOR(to)[i]) { |
691 | 0 | igraph_int_t tmp = VECTOR(from)[i]; |
692 | 0 | VECTOR(from)[i] = VECTOR(to)[i]; |
693 | 0 | VECTOR(to)[i] = tmp; |
694 | 0 | } |
695 | 0 | } |
696 | 0 | igraph_vector_int_pair_order(&from, &to, &index, no_of_nodes); |
697 | |
|
698 | 0 | igraph_get_edgelist(graph2, &from2, /*bycol=*/ 1); |
699 | 0 | for (i = 0, j = no_of_edges; i < no_of_edges; i++, j++) { |
700 | 0 | VECTOR(to2)[i] = VECTOR(from2)[j]; |
701 | 0 | if (! directed && VECTOR(from2)[i] < VECTOR(to2)[i]) { |
702 | 0 | igraph_int_t tmp = VECTOR(from2)[i]; |
703 | 0 | VECTOR(from2)[i] = VECTOR(to2)[i]; |
704 | 0 | VECTOR(to2)[i] = tmp; |
705 | 0 | } |
706 | 0 | } |
707 | 0 | igraph_vector_int_resize(&from2, no_of_edges); |
708 | 0 | igraph_vector_int_pair_order(&from2, &to2, &index2, no_of_nodes); |
709 | |
|
710 | 0 | *iso = 1; |
711 | 0 | for (i = 0; i < no_of_edges; i++) { |
712 | 0 | igraph_int_t i1 = VECTOR(index)[i]; |
713 | 0 | igraph_int_t i2 = VECTOR(index2)[i]; |
714 | 0 | if (VECTOR(from)[i1] != VECTOR(from2)[i2] || |
715 | 0 | VECTOR(to)[i1] != VECTOR(to2)[i2]) { |
716 | 0 | *iso = 0; |
717 | 0 | break; |
718 | 0 | } |
719 | 0 | } |
720 | | |
721 | | /* If the graphs are coloured, we also need to check that applying the |
722 | | permutation mymap12 to colors1 gives colors2. */ |
723 | |
|
724 | 0 | if (*iso && colors1 != NULL) { |
725 | 0 | for (i = 0; i < no_of_nodes; i++) { |
726 | 0 | if (VECTOR(*colors1)[i] != VECTOR(*colors2)[ VECTOR(*mymap12)[i] ]) { |
727 | 0 | *iso = 0; |
728 | 0 | break; |
729 | 0 | } |
730 | 0 | } |
731 | 0 | } |
732 | |
|
733 | 0 | igraph_vector_int_destroy(&index2); |
734 | 0 | igraph_vector_int_destroy(&to2); |
735 | 0 | igraph_vector_int_destroy(&from2); |
736 | 0 | igraph_vector_int_destroy(&index); |
737 | 0 | igraph_vector_int_destroy(&to); |
738 | 0 | igraph_vector_int_destroy(&from); |
739 | 0 | IGRAPH_FINALLY_CLEAN(6); |
740 | |
|
741 | 0 | if (*iso) { |
742 | | /* The inverse of mymap12 */ |
743 | 0 | if (map21) { |
744 | 0 | IGRAPH_CHECK(igraph_vector_int_resize(map21, no_of_nodes)); |
745 | 0 | for (i = 0; i < no_of_nodes; i++) { |
746 | 0 | VECTOR(*map21)[ VECTOR(*mymap12)[i] ] = i; |
747 | 0 | } |
748 | 0 | } |
749 | 0 | } else { |
750 | 0 | if (map12) { |
751 | 0 | igraph_vector_int_clear(map12); |
752 | 0 | } |
753 | 0 | if (map21) { |
754 | 0 | igraph_vector_int_clear(map21); |
755 | 0 | } |
756 | 0 | } |
757 | | |
758 | 0 | if (!map12) { |
759 | 0 | igraph_vector_int_destroy(mymap12); |
760 | 0 | IGRAPH_FINALLY_CLEAN(1); |
761 | 0 | } |
762 | |
|
763 | 0 | return IGRAPH_SUCCESS; |
764 | 0 | } |