passagemath-bliss 10.5.45__cp311-cp311-musllinux_1_2_x86_64.whl → 10.5.46__cp311-cp311-musllinux_1_2_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {passagemath_bliss-10.5.45.dist-info → passagemath_bliss-10.5.46.dist-info}/METADATA +1 -1
- {passagemath_bliss-10.5.45.dist-info → passagemath_bliss-10.5.46.dist-info}/RECORD +6 -5
- {passagemath_bliss-10.5.45.dist-info → passagemath_bliss-10.5.46.dist-info}/top_level.txt +1 -0
- sage/graphs/bliss.cpython-311-x86_64-linux-musl.so +0 -0
- sage/graphs/bliss.pyx +915 -0
- {passagemath_bliss-10.5.45.dist-info → passagemath_bliss-10.5.46.dist-info}/WHEEL +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: passagemath-bliss
|
3
|
-
Version: 10.5.
|
3
|
+
Version: 10.5.46
|
4
4
|
Summary: passagemath: Graph (iso/auto)morphisms with bliss
|
5
5
|
Author-email: The Sage Developers <sage-support@googlegroups.com>
|
6
6
|
Maintainer: Matthias Köppe, passagemath contributors
|
@@ -3,8 +3,9 @@ passagemath_bliss.libs/libgcc_s-2298274a.so.1,sha256=V2IJwfUuwSSQhJqB29JtMx8hH79
|
|
3
3
|
passagemath_bliss.libs/libstdc++-08d5c7eb.so.6.0.33,sha256=k0S_imrCh_IE6WpvxrLoVPYiRyMVf15RDWRiXPIywV8,3562401
|
4
4
|
sage/all__sagemath_bliss.py,sha256=2p3JOEnbjaX22LWk6f1yfiAvhMMkcvNXwTp_NcGw6pM,44
|
5
5
|
sage/graphs/all__sagemath_bliss.py,sha256=2p3JOEnbjaX22LWk6f1yfiAvhMMkcvNXwTp_NcGw6pM,44
|
6
|
-
sage/graphs/bliss.cpython-311-x86_64-linux-musl.so,sha256=
|
7
|
-
|
8
|
-
passagemath_bliss-10.5.
|
9
|
-
passagemath_bliss-10.5.
|
10
|
-
passagemath_bliss-10.5.
|
6
|
+
sage/graphs/bliss.cpython-311-x86_64-linux-musl.so,sha256=GuAh9JvczjIh3BHfaqEehohB8r6WFoP2Qv9cuuw8QWI,1114937
|
7
|
+
sage/graphs/bliss.pyx,sha256=tdD81xTIfyxo3IgoWfsrTpHE33aTZQFWcAd-quMvzU0,32385
|
8
|
+
passagemath_bliss-10.5.46.dist-info/METADATA,sha256=SEV9QmexCUnCV9xhOO3PNeAmhSFTT2vVijLh3sGSPN8,4379
|
9
|
+
passagemath_bliss-10.5.46.dist-info/WHEEL,sha256=kA_iIvT-cxTFNl4I8QDfFHN1DAyqZDYakVXCaObxeLo,112
|
10
|
+
passagemath_bliss-10.5.46.dist-info/top_level.txt,sha256=Kmzulf9WsphADFQuqgvdy5mvTLDj_V2zkFHU2s3UXos,6
|
11
|
+
passagemath_bliss-10.5.46.dist-info/RECORD,,
|
Binary file
|
sage/graphs/bliss.pyx
ADDED
@@ -0,0 +1,915 @@
|
|
1
|
+
# distutils: language = c++
|
2
|
+
# distutils: extra_compile_args = -std=c++11
|
3
|
+
# distutils: libraries = bliss
|
4
|
+
# sage_setup: distribution = sagemath-bliss
|
5
|
+
|
6
|
+
r"""
|
7
|
+
Interface with bliss: graph (iso/auto)morphism
|
8
|
+
|
9
|
+
Implemented functions:
|
10
|
+
|
11
|
+
.. csv-table::
|
12
|
+
:class: contentstable
|
13
|
+
:widths: 30, 70
|
14
|
+
:delim: |
|
15
|
+
|
16
|
+
:meth:`automorphism_group` | Return the automorphism group of the given (di)graph
|
17
|
+
:meth:`canonical_form` | Return a canonical label for the given (di)graph
|
18
|
+
|
19
|
+
AUTHORS:
|
20
|
+
|
21
|
+
- Jernej Azarija
|
22
|
+
"""
|
23
|
+
|
24
|
+
# ****************************************************************************
|
25
|
+
# Copyright (C) 2015 Jernej Azarija
|
26
|
+
# Copyright (C) 2015 Nathann Cohen <nathann.cohen@gail.com>
|
27
|
+
# Copyright (C) 2018 Christian Stump <christian.stump@gmail.com>
|
28
|
+
#
|
29
|
+
# This program is free software: you can redistribute it and/or modify
|
30
|
+
# it under the terms of the GNU General Public License as published by
|
31
|
+
# the Free Software Foundation, either version 2 of the License, or
|
32
|
+
# (at your option) any later version.
|
33
|
+
# https://www.gnu.org/licenses/
|
34
|
+
# ****************************************************************************
|
35
|
+
|
36
|
+
from libc.limits cimport LONG_MAX
|
37
|
+
|
38
|
+
from cysignals.memory cimport check_calloc, sig_free
|
39
|
+
|
40
|
+
cdef extern from "bliss/graph.hh" namespace "bliss":
|
41
|
+
|
42
|
+
cdef cppclass Stats:
|
43
|
+
pass
|
44
|
+
|
45
|
+
cdef cppclass AbstractGraph:
|
46
|
+
pass
|
47
|
+
|
48
|
+
cdef cppclass Graph(AbstractGraph):
|
49
|
+
Graph(const unsigned int)
|
50
|
+
void add_edge(const unsigned int, const unsigned int)
|
51
|
+
void change_color(const unsigned int, const unsigned int)
|
52
|
+
const unsigned int* canonical_form(Stats&)
|
53
|
+
|
54
|
+
cdef extern from "bliss/digraph.hh" namespace "bliss":
|
55
|
+
|
56
|
+
cdef cppclass Digraph(AbstractGraph):
|
57
|
+
Digraph(const unsigned int)
|
58
|
+
void add_edge(const unsigned int, const unsigned int)
|
59
|
+
void change_color(const unsigned int, const unsigned int)
|
60
|
+
const unsigned int* canonical_form(Stats&)
|
61
|
+
unsigned int get_hash()
|
62
|
+
|
63
|
+
cdef extern from "bliss_cpp/bliss_find_automorphisms.h":
|
64
|
+
|
65
|
+
void bliss_find_automorphisms(Graph*, void (*)(void*, unsigned int, const unsigned int*), void*, Stats&)
|
66
|
+
void bliss_find_automorphisms(Digraph*, void (*)(void*, unsigned int, const unsigned int*), void*, Stats&)
|
67
|
+
|
68
|
+
cdef int encoding_numbits(int n) noexcept:
|
69
|
+
r"""
|
70
|
+
Return the number of bits needed to encode the `n` numbers from `1` to
|
71
|
+
`n`. In other words, the last bit set in `n`.
|
72
|
+
"""
|
73
|
+
if n <= 0:
|
74
|
+
return 0
|
75
|
+
cdef int i = 0
|
76
|
+
while n:
|
77
|
+
n >>= 1
|
78
|
+
i += 1
|
79
|
+
return i
|
80
|
+
|
81
|
+
|
82
|
+
cdef void add_gen(void *user_param, unsigned int n, const unsigned int *aut) noexcept:
|
83
|
+
r"""
|
84
|
+
Function called each time a new generator of the automorphism group is
|
85
|
+
found.
|
86
|
+
|
87
|
+
This function is used to append the new generators to a Python list. Its
|
88
|
+
main job is to translate a permutation into disjoint cycles.
|
89
|
+
|
90
|
+
INPUT:
|
91
|
+
|
92
|
+
- ``user_param`` -- ``void *``; in the current implementation, points toward
|
93
|
+
a Python object which is a pair ``(list_of_current_generators,
|
94
|
+
vert_to_integer_labelling)``.
|
95
|
+
|
96
|
+
- ``n`` -- integer; number of points in the graph
|
97
|
+
|
98
|
+
- ``aut`` -- ``int *``; an automorphism of the graph
|
99
|
+
"""
|
100
|
+
cdef int N
|
101
|
+
cdef int tmp = 0
|
102
|
+
cdef int cur = 0
|
103
|
+
cdef list perm = []
|
104
|
+
cdef bint* done = <bint*> check_calloc(n, sizeof(bint))
|
105
|
+
|
106
|
+
gens, int_to_vertex, N = <object>user_param
|
107
|
+
|
108
|
+
while cur < N:
|
109
|
+
if not done[cur]:
|
110
|
+
tmp = cur
|
111
|
+
cycle = [int_to_vertex[cur]]
|
112
|
+
done[cur] = True
|
113
|
+
|
114
|
+
while aut[tmp] != cur:
|
115
|
+
tmp = aut[tmp]
|
116
|
+
done[tmp] = True
|
117
|
+
cycle.append(int_to_vertex[tmp])
|
118
|
+
|
119
|
+
perm.append(tuple(cycle))
|
120
|
+
|
121
|
+
cur += 1
|
122
|
+
|
123
|
+
gens.append(perm)
|
124
|
+
|
125
|
+
sig_free(done)
|
126
|
+
|
127
|
+
#####################################################
|
128
|
+
# constructing bliss graphs from edge lists
|
129
|
+
#####################################################
|
130
|
+
|
131
|
+
cdef Graph *bliss_graph_from_labelled_edges(int Vnr, int Lnr, Vout, Vin, labels, partition) noexcept:
|
132
|
+
r"""
|
133
|
+
Return a bliss graph from the input data
|
134
|
+
|
135
|
+
For edge labelled graphs, the bliss graph is constructed using `Vnr *
|
136
|
+
\log(Lnr)` many vertices as described in Sec. 14 of the `nauty reference
|
137
|
+
manual <http://pallini.di.uniroma1.it/Guide.html>`_.
|
138
|
+
|
139
|
+
More precisely, let `V` the vertices of the original graph. We
|
140
|
+
construct the new graph on the vertex set
|
141
|
+
`V \times \{0, \ldots, log(Lnr)\}`. The integers in the second factor of
|
142
|
+
`V \times \{0, \ldots, \log(Lnr)` encode the coloring of the edges. Then
|
143
|
+
|
144
|
+
- for each vertex `v` in `G` and each `i` in `0`, ..., `log(Lnr)-1`, we
|
145
|
+
add an edge from `(v, i)` to `(v, i+1)`
|
146
|
+
|
147
|
+
- for each edge `e` from `u` to `v` with label `lab` in `G` we add edges
|
148
|
+
from `(u, i)` to `(v, i)` for each `i` so that the `i`-th bit of `lab+1`
|
149
|
+
is set (recall that the labels range from `0` to `Lnr-1` and hence the
|
150
|
+
binary encoding of `1` to `Lnr` is so that at least one bit is set and
|
151
|
+
their binary encoding has length at most `log(Lnr)`)
|
152
|
+
|
153
|
+
.. WARNING::
|
154
|
+
|
155
|
+
the input is not checked for correctness, any wrong input will result in
|
156
|
+
a segfault
|
157
|
+
|
158
|
+
INPUT:
|
159
|
+
|
160
|
+
- ``Vnr`` -- integer; number of vertices, such that the vertices are `0,
|
161
|
+
\ldots, Vnr-1`
|
162
|
+
|
163
|
+
- ``Lnr`` -- integer; number of labels, such that the labels are `0, \ldots,
|
164
|
+
Lnr-1`
|
165
|
+
|
166
|
+
- ``Vout`` -- list; the list of vertices of outgoing edges
|
167
|
+
|
168
|
+
- ``Vin`` -- list; the list of vertices of ingoing edges
|
169
|
+
|
170
|
+
- ``labels`` -- list; the list of edge labels
|
171
|
+
|
172
|
+
- ``partition`` -- an ordered partition of the vertex set
|
173
|
+
"""
|
174
|
+
cdef Graph * g
|
175
|
+
cdef int i, j, x, y, lab, Pnr, Enr, logLnr = 1
|
176
|
+
|
177
|
+
if Lnr <= 1:
|
178
|
+
g = new Graph(Vnr)
|
179
|
+
else:
|
180
|
+
logLnr = encoding_numbits(Lnr)
|
181
|
+
g = new Graph(Vnr * logLnr)
|
182
|
+
if not g:
|
183
|
+
raise MemoryError("allocation failed")
|
184
|
+
|
185
|
+
Enr = len(Vout)
|
186
|
+
|
187
|
+
if Lnr <= 1:
|
188
|
+
for i in range(Enr):
|
189
|
+
g.add_edge(Vout[i], Vin[i])
|
190
|
+
|
191
|
+
else:
|
192
|
+
# arrows going up in layers
|
193
|
+
for i in range(Vnr * (logLnr - 1)):
|
194
|
+
g.add_edge(i, i + Vnr)
|
195
|
+
|
196
|
+
# arrows inside layers shadowing the original graph
|
197
|
+
for i in range(Enr):
|
198
|
+
x = Vout[i]
|
199
|
+
y = Vin[i]
|
200
|
+
lab = labels[i] + 1
|
201
|
+
j = 0
|
202
|
+
while lab:
|
203
|
+
if lab & 1:
|
204
|
+
g.add_edge(j * Vnr + x, j * Vnr + y)
|
205
|
+
j += 1
|
206
|
+
lab >>= 1
|
207
|
+
|
208
|
+
# vertex partition gives colors
|
209
|
+
if partition:
|
210
|
+
Pnr = len(partition)
|
211
|
+
for i in range(Pnr):
|
212
|
+
for v in partition[i]:
|
213
|
+
for j in range(logLnr):
|
214
|
+
g.change_color(j * Vnr + v, j * Pnr + i)
|
215
|
+
else:
|
216
|
+
for j in range(logLnr):
|
217
|
+
for v in range(Vnr):
|
218
|
+
g.change_color(j * Vnr + v, j)
|
219
|
+
|
220
|
+
return g
|
221
|
+
|
222
|
+
cdef Digraph *bliss_digraph_from_labelled_edges(int Vnr, int Lnr, Vout, Vin, labels, partition) noexcept:
|
223
|
+
r"""
|
224
|
+
Return a bliss digraph from the input data
|
225
|
+
|
226
|
+
For edge labelled graphs, the bliss graph is constructed using `Vnr *
|
227
|
+
\log(Lnr)` many vertices as described in Sec 14 in the `nauty reference
|
228
|
+
manual <http://pallini.di.uniroma1.it/Guide.html>`_.
|
229
|
+
|
230
|
+
.. WARNING::
|
231
|
+
|
232
|
+
the input is not checked for correctness, any wrong input will result in
|
233
|
+
a segfault
|
234
|
+
|
235
|
+
INPUT:
|
236
|
+
|
237
|
+
- ``Vnr`` -- integer; number of vertices, such that the vertices are `0,
|
238
|
+
\ldots, Vnr-1`
|
239
|
+
|
240
|
+
- ``Lnr`` -- integer; number of labels, such that the labels are `0, \ldots,
|
241
|
+
Lnr-1`
|
242
|
+
|
243
|
+
- ``Vout`` -- list; the list of vertices of outgoing edges
|
244
|
+
|
245
|
+
- ``Vin`` -- list; the list of vertices of ingoing edges
|
246
|
+
|
247
|
+
- ``labels`` -- list; the list of edge labels
|
248
|
+
|
249
|
+
- ``partition`` -- a partition of the vertex set
|
250
|
+
"""
|
251
|
+
cdef Digraph *g
|
252
|
+
cdef int i, j, x, y, lab, Pnr, Enr, logLnr = 1
|
253
|
+
|
254
|
+
if Lnr <= 1:
|
255
|
+
g = new Digraph(Vnr)
|
256
|
+
else:
|
257
|
+
logLnr = encoding_numbits(Lnr)
|
258
|
+
g = new Digraph(Vnr * logLnr)
|
259
|
+
if not g:
|
260
|
+
raise MemoryError("allocation failed")
|
261
|
+
|
262
|
+
Enr = len(Vout)
|
263
|
+
|
264
|
+
if Lnr <= 1:
|
265
|
+
for i in range(Enr):
|
266
|
+
g.add_edge(Vout[i], Vin[i])
|
267
|
+
else:
|
268
|
+
# arrows going up in layers
|
269
|
+
for i in range(Vnr * (logLnr - 1)):
|
270
|
+
g.add_edge(i, i + Vnr)
|
271
|
+
|
272
|
+
# arrows inside layers shadowing the original graph
|
273
|
+
for i in range(Enr):
|
274
|
+
x = Vout[i]
|
275
|
+
y = Vin[i]
|
276
|
+
lab = labels[i] + 1
|
277
|
+
j = 0
|
278
|
+
while lab:
|
279
|
+
if lab & 1:
|
280
|
+
g.add_edge(j * Vnr + x, j * Vnr + y)
|
281
|
+
j += 1
|
282
|
+
lab >>= 1
|
283
|
+
|
284
|
+
# vertex partition gives color
|
285
|
+
if partition:
|
286
|
+
Pnr = len(partition)
|
287
|
+
for i in range(Pnr):
|
288
|
+
for v in partition[i]:
|
289
|
+
for j in range(logLnr):
|
290
|
+
g.change_color(j * Vnr + v, j * Pnr + i)
|
291
|
+
else:
|
292
|
+
for j in range(logLnr):
|
293
|
+
for v in range(Vnr):
|
294
|
+
g.change_color(j * Vnr + v, j)
|
295
|
+
|
296
|
+
return g
|
297
|
+
|
298
|
+
#####################################################
|
299
|
+
# canonical form from graph or edge list
|
300
|
+
#####################################################
|
301
|
+
|
302
|
+
cdef canonical_form_from_edge_list(int Vnr, list Vout, list Vin, int Lnr=1, list labels=[],
|
303
|
+
list partition=None, bint directed=False, bint certificate=False) noexcept:
|
304
|
+
r"""
|
305
|
+
Return an unsorted list of labelled edges of a canonical form.
|
306
|
+
|
307
|
+
INPUT:
|
308
|
+
|
309
|
+
- ``Vnr`` -- integer; number of vertices, such that the vertices are `0,
|
310
|
+
\ldots, Vnr-1`
|
311
|
+
|
312
|
+
- ``Vout`` -- list; the list of vertices of outgoing edges
|
313
|
+
|
314
|
+
- ``Vin`` -- list; the list of vertices of ingoing edges
|
315
|
+
|
316
|
+
- ``Lnr`` -- integer (default: 1); number of labels, such that the labels
|
317
|
+
are `0, \ldots, Lnr-1`
|
318
|
+
|
319
|
+
- ``labels`` -- list (default: ``[]``); the list of edge labels
|
320
|
+
|
321
|
+
- ``partition`` -- list (default: ``None``); a partition of the vertex
|
322
|
+
set
|
323
|
+
|
324
|
+
- ``directed`` -- boolean (default: ``False``); whether the edges are
|
325
|
+
directed or not
|
326
|
+
|
327
|
+
- ``certificate`` -- boolean 'default: ``False``); whether to return the
|
328
|
+
isomorphism to obtain the canonical labelling
|
329
|
+
"""
|
330
|
+
# We need this to convert the numbers from <unsigned int> to
|
331
|
+
# <long>. This assertion should be true simply for memory reasons.
|
332
|
+
assert <unsigned long>(Vnr) <= <unsigned long>LONG_MAX
|
333
|
+
|
334
|
+
cdef const unsigned int* aut
|
335
|
+
cdef Graph* g
|
336
|
+
cdef Digraph* d
|
337
|
+
cdef Stats s
|
338
|
+
cdef dict relabel
|
339
|
+
|
340
|
+
cdef list new_edges = []
|
341
|
+
cdef long e, f
|
342
|
+
|
343
|
+
if directed:
|
344
|
+
d = bliss_digraph_from_labelled_edges(Vnr, Lnr, Vout, Vin, labels, partition)
|
345
|
+
aut = d.canonical_form(s)
|
346
|
+
else:
|
347
|
+
g = bliss_graph_from_labelled_edges(Vnr, Lnr, Vout, Vin, labels, partition)
|
348
|
+
aut = g.canonical_form(s)
|
349
|
+
|
350
|
+
for i in range(len(Vout)):
|
351
|
+
x = Vout[i]
|
352
|
+
y = Vin[i]
|
353
|
+
e = aut[x]
|
354
|
+
f = aut[y]
|
355
|
+
if Lnr == 1:
|
356
|
+
if not bool(labels):
|
357
|
+
lab = 0
|
358
|
+
else:
|
359
|
+
lab = labels[0]
|
360
|
+
else:
|
361
|
+
lab = labels[i]
|
362
|
+
if directed:
|
363
|
+
new_edges.append((e, f, lab))
|
364
|
+
else:
|
365
|
+
new_edges.append((e, f, lab) if e > f else (f, e, lab))
|
366
|
+
|
367
|
+
if certificate:
|
368
|
+
relabel = {v: <long>aut[v] for v in range(Vnr)}
|
369
|
+
|
370
|
+
if directed:
|
371
|
+
del d
|
372
|
+
else:
|
373
|
+
del g
|
374
|
+
|
375
|
+
if certificate:
|
376
|
+
return new_edges, relabel
|
377
|
+
return new_edges
|
378
|
+
|
379
|
+
|
380
|
+
cpdef canonical_form(G, partition=None, return_graph=False, use_edge_labels=True, certificate=False) noexcept:
|
381
|
+
r"""
|
382
|
+
Return a canonical label for the given (di)graph.
|
383
|
+
|
384
|
+
A canonical label ``canonical_form(G)`` of ``G`` is a (di)graph defined on
|
385
|
+
`\{0,...,n-1\}` such that ``G`` is isomorphic to ``H`` if and only if
|
386
|
+
``canonical_form(G)`` is equal to ``canonical_form(H)``.
|
387
|
+
|
388
|
+
INPUT:
|
389
|
+
|
390
|
+
- ``G`` -- a Sage (Di)Graph
|
391
|
+
|
392
|
+
- ``partition`` -- list (default: ``None``); a partition of the vertices
|
393
|
+
of ``G`` into color classes
|
394
|
+
|
395
|
+
- ``return_graph`` -- boolean (default: ``False``); whether to return the
|
396
|
+
canonical graph of ``G`` or its set of edges
|
397
|
+
|
398
|
+
- ``use_edge_labels`` -- boolean (default: ``True``); whether to consider
|
399
|
+
edge labels. The edge labels are assumed to be hashable and
|
400
|
+
sortable. If this is not the case (ie a :exc:`TypeError` is
|
401
|
+
raised), the algorithm will consider the string representations
|
402
|
+
of the labels instead of the labels.
|
403
|
+
|
404
|
+
- ``certificate`` -- boolean (default: ``False``); when set to ``True``,
|
405
|
+
returns the labeling of G into a canonical graph
|
406
|
+
|
407
|
+
TESTS::
|
408
|
+
|
409
|
+
sage: from sage.graphs.bliss import canonical_form # optional - bliss
|
410
|
+
sage: G = graphs.PetersenGraph() # optional - bliss
|
411
|
+
sage: canonical_form(G) # optional - bliss
|
412
|
+
[(2, 0, None),
|
413
|
+
(2, 1, None),
|
414
|
+
(3, 0, None),
|
415
|
+
(4, 1, None),
|
416
|
+
(5, 3, None),
|
417
|
+
(5, 4, None),
|
418
|
+
(6, 0, None),
|
419
|
+
(6, 4, None),
|
420
|
+
(7, 1, None),
|
421
|
+
(7, 3, None),
|
422
|
+
(8, 2, None),
|
423
|
+
(8, 5, None),
|
424
|
+
(9, 6, None),
|
425
|
+
(9, 7, None),
|
426
|
+
(9, 8, None)]
|
427
|
+
|
428
|
+
sage: P = graphs.GeneralizedPetersenGraph(5, 2) # optional - bliss
|
429
|
+
sage: Q = graphs.PetersenGraph() # optional - bliss
|
430
|
+
sage: canonical_form(P) == canonical_form(Q) # optional - bliss
|
431
|
+
True
|
432
|
+
|
433
|
+
sage: canonical_form(Graph(15), return_graph=True) # optional - bliss
|
434
|
+
Graph on 15 vertices
|
435
|
+
sage: g = digraphs.RandomTournament(40) # optional - bliss
|
436
|
+
sage: g.is_isomorphic(canonical_form(g, return_graph=True)) # optional - bliss
|
437
|
+
True
|
438
|
+
|
439
|
+
sage: # optional - bliss
|
440
|
+
sage: g1 = graphs.RandomGNP(100, .4)
|
441
|
+
sage: r = Permutations(range(100)).random_element()
|
442
|
+
sage: g2 = Graph([(r[u],r[v]) for u,v in g1.edges(sort=True, labels=False)])
|
443
|
+
sage: g1 = canonical_form(g1, return_graph=True)
|
444
|
+
sage: g2 = canonical_form(g2, return_graph=True)
|
445
|
+
sage: g2 == g2
|
446
|
+
True
|
447
|
+
|
448
|
+
sage: g = Graph({1: [2]})
|
449
|
+
sage: g_ = canonical_form(g, return_graph=True, certificate=True) # optional - bliss
|
450
|
+
sage: 0 in g_[0] # optional - bliss
|
451
|
+
True
|
452
|
+
|
453
|
+
Check that parameter ``use_edge_labels`` can be used (:issue:`27571`)::
|
454
|
+
|
455
|
+
sage: g = Graph({1: {2: 'a'}})
|
456
|
+
sage: canonical_form(g, use_edge_labels=True) # optional - bliss
|
457
|
+
[(1, 0, 'a')]
|
458
|
+
sage: canonical_form(g, use_edge_labels=False) # optional - bliss
|
459
|
+
[(1, 0, None)]
|
460
|
+
|
461
|
+
Check that :issue:`28531` is fixed::
|
462
|
+
|
463
|
+
sage: from itertools import product, permutations
|
464
|
+
sage: edges_list = [[(0,1), (1,2)],
|
465
|
+
....: [(0,1),(1,2),(2,3)],
|
466
|
+
....: [(0,1),(1,2),(2,3),(3,0)]]
|
467
|
+
sage: for edges in edges_list: # optional - bliss
|
468
|
+
....: for labels in product([0,1], repeat=len(edges)):
|
469
|
+
....: g = Graph([(u,v,l) for ((u,v),l) in zip(edges, labels)])
|
470
|
+
....: gcan = canonical_form(g, use_edge_labels=True)
|
471
|
+
....: for p in permutations(range(g.num_verts())):
|
472
|
+
....: h = Graph([(p[u], p[v], lab) for u,v,lab in g.edges(sort=True)])
|
473
|
+
....: hcan = canonical_form(h, use_edge_labels=True)
|
474
|
+
....: if gcan != hcan: print(edges, labels, p)
|
475
|
+
|
476
|
+
Check that it works with non hashable non sortable edge labels (relying
|
477
|
+
on string representations of the labels)::
|
478
|
+
|
479
|
+
sage: # needs sage.modules
|
480
|
+
sage: g1 = Graph([(0, 1, matrix(ZZ, 2)), (0, 2, RDF.pi()), (1, 2, 'a')])
|
481
|
+
sage: g2 = Graph([(1, 2, matrix(ZZ, 2)), (2, 0, RDF.pi()), (0, 1, 'a')])
|
482
|
+
sage: g1can = canonical_form(g1, use_edge_labels=True) # optional - bliss
|
483
|
+
sage: g2can = canonical_form(g2, use_edge_labels=True) # optional - bliss
|
484
|
+
sage: g1can == g2can # optional - bliss
|
485
|
+
True
|
486
|
+
|
487
|
+
Check that :issue:`32395` is fixed::
|
488
|
+
|
489
|
+
sage: g = Graph([[0, 2]]) # 1 is not a vertex!
|
490
|
+
sage: g.canonical_label(partition=[[0], [1], [2]], algorithm='bliss') # optional - bliss
|
491
|
+
Traceback (most recent call last):
|
492
|
+
...
|
493
|
+
ValueError: vertex 1 of the partition is not a vertex of the graph
|
494
|
+
sage: g.canonical_label(partition=[[0], [0, 2]], algorithm='bliss') # optional - bliss
|
495
|
+
Traceback (most recent call last):
|
496
|
+
...
|
497
|
+
ValueError: vertex 0 can appear only once in the partition
|
498
|
+
sage: g.canonical_label(partition=[[0, 0], [2]], algorithm='bliss') # optional - bliss
|
499
|
+
Traceback (most recent call last):
|
500
|
+
...
|
501
|
+
ValueError: vertex 0 can appear only once in the partition
|
502
|
+
sage: g.canonical_label(partition=[[0]], algorithm='bliss') # optional - bliss
|
503
|
+
Traceback (most recent call last):
|
504
|
+
...
|
505
|
+
ValueError: some vertices of the graph are not in the partition
|
506
|
+
"""
|
507
|
+
# We need this to convert the numbers from <unsigned int> to <long>.
|
508
|
+
# This assertion should be true simply for memory reasons.
|
509
|
+
cdef unsigned long Vnr = G.order()
|
510
|
+
assert Vnr <= <unsigned long> LONG_MAX
|
511
|
+
|
512
|
+
cdef bint directed = G.is_directed()
|
513
|
+
|
514
|
+
cdef list Vout = []
|
515
|
+
cdef list Vin = []
|
516
|
+
cdef list labels = []
|
517
|
+
|
518
|
+
cdef list int2vert
|
519
|
+
cdef dict vert2int
|
520
|
+
cdef dict lab_to_index
|
521
|
+
cdef list edge_labels = [] if use_edge_labels else [None]
|
522
|
+
cdef int Lnr = 1
|
523
|
+
|
524
|
+
if partition:
|
525
|
+
from itertools import chain
|
526
|
+
int2vert = list(chain(*partition))
|
527
|
+
# We check that the partition contains only vertices of the graph
|
528
|
+
# and that it is actually a partition
|
529
|
+
seen = set()
|
530
|
+
for u in int2vert:
|
531
|
+
if u not in G:
|
532
|
+
raise ValueError("vertex {} of the partition is not a vertex of the graph".format(u))
|
533
|
+
if u in seen:
|
534
|
+
raise ValueError("vertex {} can appear only once in the partition".format(u))
|
535
|
+
seen.add(u)
|
536
|
+
if len(seen) != G.order():
|
537
|
+
raise ValueError("some vertices of the graph are not in the partition")
|
538
|
+
else:
|
539
|
+
int2vert = list(G)
|
540
|
+
vert2int = {v: i for i, v in enumerate(int2vert)}
|
541
|
+
if partition:
|
542
|
+
partition = [[vert2int[i] for i in part] for part in partition]
|
543
|
+
|
544
|
+
# Create 3 lists to represent edges
|
545
|
+
# - Vout[i] : source of the ith edge
|
546
|
+
# - Vin[i] : destination of the ith edge
|
547
|
+
# - labels[i] : label of the ith edge if use_edge_labels is True
|
548
|
+
# On the way, assign a unique integer to each distinct label
|
549
|
+
if use_edge_labels:
|
550
|
+
try:
|
551
|
+
edge_labels = sorted(set(G.edge_labels()))
|
552
|
+
except TypeError:
|
553
|
+
# NOTE: use edge labels might not be hashable or sortable...
|
554
|
+
# rely loosely on string representation
|
555
|
+
edge_labels = sorted(set(map(str, G.edge_labels())))
|
556
|
+
lab_to_index = {lab: i for i, lab in enumerate(edge_labels)}
|
557
|
+
for x, y, lab in G.edge_iterator(labels=True):
|
558
|
+
Vout.append(vert2int[x])
|
559
|
+
Vin.append(vert2int[y])
|
560
|
+
labels.append(lab_to_index[str(lab)])
|
561
|
+
|
562
|
+
else:
|
563
|
+
lab_to_index = {lab: i for i, lab in enumerate(edge_labels)}
|
564
|
+
for x, y, lab in G.edge_iterator(labels=True):
|
565
|
+
Vout.append(vert2int[x])
|
566
|
+
Vin.append(vert2int[y])
|
567
|
+
labels.append(lab_to_index[lab])
|
568
|
+
|
569
|
+
Lnr = len(lab_to_index)
|
570
|
+
|
571
|
+
else:
|
572
|
+
for x, y in G.edge_iterator(labels=False):
|
573
|
+
Vout.append(vert2int[x])
|
574
|
+
Vin.append(vert2int[y])
|
575
|
+
|
576
|
+
new_edges, relabel = canonical_form_from_edge_list(Vnr, Vout, Vin, Lnr, labels, partition, directed, certificate=True)
|
577
|
+
|
578
|
+
new_edges = [(x, y, edge_labels[lab]) for x, y, lab in new_edges]
|
579
|
+
relabel = {int2vert[i]: j for i, j in relabel.items()}
|
580
|
+
|
581
|
+
if return_graph:
|
582
|
+
if directed:
|
583
|
+
from sage.graphs.digraph import DiGraph
|
584
|
+
H = DiGraph(new_edges, loops=G.allows_loops(), multiedges=G.allows_multiple_edges())
|
585
|
+
else:
|
586
|
+
from sage.graphs.graph import Graph
|
587
|
+
H = Graph(new_edges, loops=G.allows_loops(), multiedges=G.allows_multiple_edges())
|
588
|
+
|
589
|
+
H.add_vertices(range(G.order()))
|
590
|
+
return (H, relabel) if certificate else H
|
591
|
+
|
592
|
+
# Warning: this may break badly in Python 3 if the graph is not simple
|
593
|
+
return (sorted(new_edges), relabel) if certificate else sorted(new_edges)
|
594
|
+
|
595
|
+
|
596
|
+
#####################################################
|
597
|
+
# automorphism group from graphs
|
598
|
+
#####################################################
|
599
|
+
|
600
|
+
cdef automorphism_group_gens_from_edge_list(int Vnr, Vout, Vin, int Lnr=1, labels=[],
|
601
|
+
int2vert=[], partition=None, bint directed=False) noexcept:
|
602
|
+
r"""
|
603
|
+
Return an unsorted list of labelled edges of a canonical form.
|
604
|
+
|
605
|
+
INPUT:
|
606
|
+
|
607
|
+
- ``Vnr`` -- integer; number of vertices, such that the vertices are `0,
|
608
|
+
\ldots, Vnr-1`
|
609
|
+
|
610
|
+
- ``Vout`` -- list; the list of vertices of outgoing edges
|
611
|
+
|
612
|
+
- ``Vin`` -- list; the list of vertices of ingoing edges
|
613
|
+
|
614
|
+
- ``Lnr`` -- integer (default: 1); number of labels, such that the labels
|
615
|
+
are `0, \ldots, Lnr-1`
|
616
|
+
|
617
|
+
- ``labels`` -- list (default: ``[]``); the list of edge labels
|
618
|
+
|
619
|
+
- ``int2vert`` -- list (default: ``[]``); ordering of the vertices
|
620
|
+
|
621
|
+
- ``partition`` -- list (default: ``None``); a partition of the vertex
|
622
|
+
set
|
623
|
+
|
624
|
+
- ``directed`` -- boolean (default: ``False``); whether the edges are
|
625
|
+
directed or not
|
626
|
+
"""
|
627
|
+
# We need this to convert the numbers from <unsigned int> to
|
628
|
+
# <long>. This assertion should be true simply for memory reasons.
|
629
|
+
assert <unsigned long>(Vnr) <= <unsigned long>LONG_MAX
|
630
|
+
|
631
|
+
cdef Graph* g
|
632
|
+
cdef Digraph* d
|
633
|
+
cdef Stats s
|
634
|
+
|
635
|
+
if not int2vert:
|
636
|
+
int2vert = list(range(Vnr))
|
637
|
+
|
638
|
+
cdef list gens = []
|
639
|
+
cdef tuple data = (gens, int2vert, Vnr)
|
640
|
+
|
641
|
+
if directed:
|
642
|
+
d = bliss_digraph_from_labelled_edges(Vnr, Lnr, Vout, Vin, labels, partition)
|
643
|
+
bliss_find_automorphisms(d, add_gen, <void*>data, s)
|
644
|
+
del d
|
645
|
+
else:
|
646
|
+
g = bliss_graph_from_labelled_edges(Vnr, Lnr, Vout, Vin, labels, partition)
|
647
|
+
bliss_find_automorphisms(g, add_gen, <void*>data, s)
|
648
|
+
del g
|
649
|
+
|
650
|
+
return [[cyc for cyc in gen if cyc[0] is not None] for gen in gens]
|
651
|
+
|
652
|
+
cpdef automorphism_group(G, partition=None, use_edge_labels=True) noexcept:
|
653
|
+
"""
|
654
|
+
Return the automorphism group of the given (di)graph.
|
655
|
+
|
656
|
+
Compute the automorphism group of ``G`` subject to the vertex coloring
|
657
|
+
``partition``, if given. The graph ``G`` can be a directed or undirected
|
658
|
+
graph with or without edge labellings.
|
659
|
+
|
660
|
+
Observe the neither the vertex colorings nor the edge colorings are
|
661
|
+
interchangeable.
|
662
|
+
|
663
|
+
INPUT:
|
664
|
+
|
665
|
+
- ``G`` -- a Sage graph
|
666
|
+
|
667
|
+
- ``partition`` -- list (default: ``None``); a partition of the vertices
|
668
|
+
of ``G`` into color classes. Defaults to ``None``, which is equivalent to
|
669
|
+
a partition of size 1.
|
670
|
+
|
671
|
+
- ``use_edge_labels`` -- boolean (default: ``True``); whether to consider
|
672
|
+
edge labels
|
673
|
+
|
674
|
+
EXAMPLES::
|
675
|
+
|
676
|
+
sage: from sage.graphs.bliss import automorphism_group # optional - bliss
|
677
|
+
|
678
|
+
Computing the automorphism group of a graph or digraph::
|
679
|
+
|
680
|
+
sage: # optional - bliss
|
681
|
+
sage: G = graphs.CompleteMultipartiteGraph([1, 1, 1, 2])
|
682
|
+
sage: automorphism_group(G).cardinality()
|
683
|
+
12
|
684
|
+
sage: D = DiGraph(G.edges(sort=True))
|
685
|
+
sage: automorphism_group(D).cardinality()
|
686
|
+
2
|
687
|
+
|
688
|
+
Observe that the order 12 is given by permuting the first three vertices, or
|
689
|
+
the last two in the case of a graph, while only the latter two are possible
|
690
|
+
in the case of a directed graph.
|
691
|
+
|
692
|
+
Partitioning the vertices into classes::
|
693
|
+
|
694
|
+
sage: # optional - bliss
|
695
|
+
sage: G = graphs.CompleteMultipartiteGraph([3, 2])
|
696
|
+
sage: automorphism_group(G).cardinality()
|
697
|
+
12
|
698
|
+
sage: automorphism_group(G,partition=[[0],[1],[2],[3,4]]).cardinality()
|
699
|
+
2
|
700
|
+
sage: automorphism_group(G,partition=[[0],[1,2],[3,4]]).cardinality()
|
701
|
+
4
|
702
|
+
sage: automorphism_group(G,partition=[[1,2],[0,3],[4]]).cardinality()
|
703
|
+
2
|
704
|
+
|
705
|
+
Partitioning the edges into classes::
|
706
|
+
|
707
|
+
sage: # optional - bliss
|
708
|
+
sage: G = Graph(graphs.CompleteMultipartiteGraph([8, 2]), sparse=True)
|
709
|
+
sage: for i,j in G.edges(labels=False, sort=False):
|
710
|
+
....: if 0 <= i < 3:
|
711
|
+
....: G.set_edge_label(i, j, "A")
|
712
|
+
....: if 3 <= i < 6:
|
713
|
+
....: G.set_edge_label(i, j, "B")
|
714
|
+
....: if 6 <= i < 8:
|
715
|
+
....: G.set_edge_label(i, j, "C")
|
716
|
+
sage: factor(automorphism_group(G).cardinality())
|
717
|
+
2^4 * 3^2
|
718
|
+
sage: automorphism_group(G,[[0],[1],[2,3],[4,5],[6,7],[8],[9]]).cardinality()
|
719
|
+
4
|
720
|
+
|
721
|
+
TESTS::
|
722
|
+
|
723
|
+
sage: from sage.graphs.bliss import automorphism_group # optional - bliss
|
724
|
+
sage: G = graphs.PetersenGraph() # optional - bliss
|
725
|
+
sage: automorphism_group(G).is_isomorphic(G.automorphism_group()) # optional - bliss
|
726
|
+
True
|
727
|
+
|
728
|
+
sage: # optional - bliss
|
729
|
+
sage: G = graphs.HeawoodGraph()
|
730
|
+
sage: p = G.bipartite_sets()
|
731
|
+
sage: A = G.automorphism_group(partition=[list(p[0]), list(p[1])])
|
732
|
+
sage: automorphism_group(G, partition=p).is_isomorphic(A)
|
733
|
+
True
|
734
|
+
|
735
|
+
sage: G = graphs.CompleteMultipartiteGraph([5, 7, 11])
|
736
|
+
sage: B = automorphism_group(G) # optional - bliss
|
737
|
+
sage: B.cardinality() == prod(factorial(n) for n in [5, 7, 11]) # optional - bliss
|
738
|
+
True
|
739
|
+
|
740
|
+
sage: # optional - bliss
|
741
|
+
sage: G = Graph(graphs.CompleteMultipartiteGraph([8,8,8,5]),sparse=True)
|
742
|
+
sage: for i,j in G.edges(labels=False, sort=False):
|
743
|
+
....: if 0 <= i < 3:
|
744
|
+
....: G.set_edge_label(i, j, "A")
|
745
|
+
....: if 3 <= i < 6:
|
746
|
+
....: G.set_edge_label(i, j, "B")
|
747
|
+
....: if 6 <= i < 8:
|
748
|
+
....: G.set_edge_label(i, j, "C")
|
749
|
+
sage: card = automorphism_group(G).cardinality()
|
750
|
+
sage: card == prod(factorial(n) for n in [3, 3, 2, 8, 8, 5, 2])
|
751
|
+
True
|
752
|
+
sage: card = automorphism_group(G, use_edge_labels=False).cardinality()
|
753
|
+
sage: card == prod(factorial(n) for n in [8, 8, 8, 5, 3])
|
754
|
+
True
|
755
|
+
sage: card = automorphism_group(G, [[0 .. 7], [8 .. 11] ,[12 .. 28]]).cardinality()
|
756
|
+
sage: card == prod(factorial(n) for n in [3, 3, 2, 4, 4, 8, 5])
|
757
|
+
True
|
758
|
+
|
759
|
+
sage: # optional - bliss
|
760
|
+
sage: G = Graph()
|
761
|
+
sage: G.add_edges((i,j,"A") for i in range(0, 2) for j in range(14,20))
|
762
|
+
sage: G.add_edges((i,j,"B") for i in range(2, 5) for j in range(14,20))
|
763
|
+
sage: G.add_edges((i,j,"C") for i in range(5, 9) for j in range(14,20))
|
764
|
+
sage: G.add_edges((i,j,"D") for i in range(9,14) for j in range(14,20))
|
765
|
+
sage: A = automorphism_group(G)
|
766
|
+
sage: print(A.gens()) # random
|
767
|
+
((12,13), (11,12), (10,11), (9,10), (7,8), (6,7), (5,6), (3,4),
|
768
|
+
(2,3), (1,0), (18,19), (17,18), (16,17), (15,16), (14,15))
|
769
|
+
sage: A.cardinality() == prod(factorial(n) for n in [2,3,4,5,6])
|
770
|
+
True
|
771
|
+
|
772
|
+
sage: # optional - bliss
|
773
|
+
sage: G = Graph()
|
774
|
+
sage: alpha = "abcdefghijklmnopqrstuvwxyz"
|
775
|
+
sage: G.add_edges((alpha[i],alpha[j],"A") for i in range(0, 2) for j in range(14,20))
|
776
|
+
sage: G.add_edges((alpha[i],alpha[j],"B") for i in range(2, 5) for j in range(14,20))
|
777
|
+
sage: G.add_edges((alpha[i],alpha[j],"C") for i in range(5, 9) for j in range(14,20))
|
778
|
+
sage: G.add_edges((alpha[i],alpha[j],"D") for i in range(9,14) for j in range(14,20))
|
779
|
+
sage: A = automorphism_group(G)
|
780
|
+
sage: print(A.gens())
|
781
|
+
(('m','n'), ('l','m'), ('k','l'), ('j','k'), ('h','i'),
|
782
|
+
('g','h'), ('f','g'), ('d','e'), ('c','d'), ('s','t'),
|
783
|
+
('r','s'), ('q','r'), ('p','q'), ('o','p'), ('a','b'))
|
784
|
+
sage: A.cardinality() == prod(factorial(n) for n in [2, 3, 4, 5, 6])
|
785
|
+
True
|
786
|
+
|
787
|
+
sage: # optional - bliss
|
788
|
+
sage: gg = graphs.CompleteGraph(5)
|
789
|
+
sage: gg.allow_loops(True)
|
790
|
+
sage: gg.add_edge(0, 0)
|
791
|
+
sage: gg.add_edge(1, 1)
|
792
|
+
sage: automorphism_group(gg).cardinality()
|
793
|
+
12
|
794
|
+
sage: automorphism_group(gg, [[0], [1, 2, 3, 4]]).cardinality()
|
795
|
+
6
|
796
|
+
"""
|
797
|
+
# We need this to convert the numbers from <unsigned int> to
|
798
|
+
# <long>. This assertion should be true simply for memory reasons.
|
799
|
+
cdef unsigned long Vnr = G.order()
|
800
|
+
assert Vnr <= <unsigned long>LONG_MAX
|
801
|
+
|
802
|
+
cdef bint directed = G.is_directed()
|
803
|
+
|
804
|
+
cdef int labInd
|
805
|
+
cdef list Vout = []
|
806
|
+
cdef list Vin = []
|
807
|
+
cdef list labels = []
|
808
|
+
|
809
|
+
cdef list int2vert
|
810
|
+
cdef dict vert2int
|
811
|
+
cdef list edge_labels = []
|
812
|
+
cdef int Lnr = 0 if use_edge_labels else 1
|
813
|
+
|
814
|
+
if partition:
|
815
|
+
from itertools import chain
|
816
|
+
int2vert = list(chain(*partition))
|
817
|
+
else:
|
818
|
+
int2vert = list(G)
|
819
|
+
vert2int = {v: i for i, v in enumerate(int2vert)}
|
820
|
+
if partition:
|
821
|
+
partition = [[vert2int[i] for i in part] for part in partition]
|
822
|
+
|
823
|
+
# Create 3 lists to represent edges
|
824
|
+
# - Vout[i] : source of the ith edge
|
825
|
+
# - Vin[i] : destination of the ith edge
|
826
|
+
# - labels[i] : label of the ith edge if use_edge_labels is True
|
827
|
+
# On the way, assign a unique integer to each distinct label
|
828
|
+
for x, y, lab in G.edge_iterator(labels=True):
|
829
|
+
Vout.append(vert2int[x])
|
830
|
+
Vin.append(vert2int[y])
|
831
|
+
if use_edge_labels:
|
832
|
+
try:
|
833
|
+
labInd = edge_labels.index(lab)
|
834
|
+
except ValueError:
|
835
|
+
labInd = Lnr
|
836
|
+
Lnr += 1
|
837
|
+
edge_labels.append(lab)
|
838
|
+
labels.append(labInd)
|
839
|
+
|
840
|
+
gens = automorphism_group_gens_from_edge_list(Vnr, Vout, Vin, Lnr, labels, int2vert, partition, directed)
|
841
|
+
|
842
|
+
from sage.groups.perm_gps.permgroup import PermutationGroup
|
843
|
+
return PermutationGroup(gens, domain=int2vert[:G.order()])
|
844
|
+
|
845
|
+
|
846
|
+
#####################################################
|
847
|
+
# old direct interactions graphs <-> bliss graphs
|
848
|
+
#####################################################
|
849
|
+
|
850
|
+
cdef Graph *bliss_graph(G, partition, vert2int, int2vert) noexcept:
|
851
|
+
r"""
|
852
|
+
Return a bliss copy of a graph G
|
853
|
+
|
854
|
+
INPUT:
|
855
|
+
|
856
|
+
- ``G`` -- a Sage Graph
|
857
|
+
|
858
|
+
- ``partition`` -- list; a partition of the vertex set
|
859
|
+
|
860
|
+
- ``vert2int, int2vert`` -- a empty ``dict`` and a empty list; the
|
861
|
+
entries of the dictionary are later set to record the labeling of our
|
862
|
+
graph. They are taken as arguments to avoid technicalities of returning
|
863
|
+
Python objects in Cython functions.
|
864
|
+
"""
|
865
|
+
cdef Graph *g = new Graph(G.order())
|
866
|
+
|
867
|
+
if not g:
|
868
|
+
raise MemoryError("allocation failed")
|
869
|
+
|
870
|
+
for i, v in enumerate(G):
|
871
|
+
vert2int[v] = i
|
872
|
+
int2vert[i] = v
|
873
|
+
|
874
|
+
for x, y in G.edge_iterator(labels=False):
|
875
|
+
g.add_edge(vert2int[x], vert2int[y])
|
876
|
+
|
877
|
+
if partition:
|
878
|
+
for i in range(1, len(partition)):
|
879
|
+
for v in partition[i]:
|
880
|
+
g.change_color(vert2int[v], i)
|
881
|
+
return g
|
882
|
+
|
883
|
+
|
884
|
+
cdef Digraph *bliss_digraph(G, partition, vert2int, int2vert) noexcept:
|
885
|
+
r"""
|
886
|
+
Return a bliss copy of a digraph G
|
887
|
+
|
888
|
+
INPUT:
|
889
|
+
|
890
|
+
- ``G`` -- a Sage DiGraph
|
891
|
+
|
892
|
+
- ``partition`` -- list; a partition of the vertex set
|
893
|
+
|
894
|
+
- ``vert2int, int2vert`` -- a empty ``dict`` and a empty list; the
|
895
|
+
entries of the dictionary are later set to record the labeling of our
|
896
|
+
graph. They are taken as arguments to avoid technicalities of returning
|
897
|
+
Python objects in Cython functions.
|
898
|
+
"""
|
899
|
+
cdef Digraph *g = new Digraph(G.order())
|
900
|
+
|
901
|
+
if not g:
|
902
|
+
raise MemoryError("allocation failed")
|
903
|
+
|
904
|
+
for i, v in enumerate(G):
|
905
|
+
vert2int[v] = i
|
906
|
+
int2vert[i] = v
|
907
|
+
|
908
|
+
for x, y in G.edge_iterator(labels=False):
|
909
|
+
g.add_edge(vert2int[x], vert2int[y])
|
910
|
+
|
911
|
+
if partition:
|
912
|
+
for i in range(1, len(partition)):
|
913
|
+
for v in partition[i]:
|
914
|
+
g.change_color(vert2int[v], i)
|
915
|
+
return g
|
File without changes
|