Coverage Report

Created: 2026-08-11 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}