passagemath-bliss 10.5.45__cp311-cp311-macosx_14_0_arm64.whl → 10.5.46__cp311-cp311-macosx_14_0_arm64.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: passagemath-bliss
3
- Version: 10.5.45
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
@@ -0,0 +1,9 @@
1
+ passagemath_bliss.dylibs/libbliss.dylib,sha256=Ul4sc_txJHQ6sKVoMMMe6BODUGy2g63FcdvrhAuLhl0,215920
2
+ passagemath_bliss-10.5.46.dist-info/RECORD,,
3
+ passagemath_bliss-10.5.46.dist-info/WHEEL,sha256=IDSs5FX6_bA_Je6GNgVyz6rcn2XC7t_ySnnLoSan-5o,136
4
+ passagemath_bliss-10.5.46.dist-info/top_level.txt,sha256=Kmzulf9WsphADFQuqgvdy5mvTLDj_V2zkFHU2s3UXos,6
5
+ passagemath_bliss-10.5.46.dist-info/METADATA,sha256=SEV9QmexCUnCV9xhOO3PNeAmhSFTT2vVijLh3sGSPN8,4379
6
+ sage/all__sagemath_bliss.py,sha256=2p3JOEnbjaX22LWk6f1yfiAvhMMkcvNXwTp_NcGw6pM,44
7
+ sage/graphs/all__sagemath_bliss.py,sha256=2p3JOEnbjaX22LWk6f1yfiAvhMMkcvNXwTp_NcGw6pM,44
8
+ sage/graphs/bliss.pyx,sha256=tdD81xTIfyxo3IgoWfsrTpHE33aTZQFWcAd-quMvzU0,32385
9
+ sage/graphs/bliss.cpython-311-darwin.so,sha256=ytROuqdkrxDjkgtPRj_mTfQmoUh5d10cLYf0eph-W1M,203808
Binary file
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
@@ -1,8 +0,0 @@
1
- passagemath_bliss.dylibs/libbliss.dylib,sha256=Pq7tJB5fl3-Q0As7WEkSrs5PSQsStnd33BZLihVV2EA,215920
2
- passagemath_bliss-10.5.45.dist-info/RECORD,,
3
- passagemath_bliss-10.5.45.dist-info/WHEEL,sha256=IDSs5FX6_bA_Je6GNgVyz6rcn2XC7t_ySnnLoSan-5o,136
4
- passagemath_bliss-10.5.45.dist-info/top_level.txt,sha256=hibFyzQHiLOMK68qL1OWsNKaXOmSXqZjeLTBem6Yy7I,5
5
- passagemath_bliss-10.5.45.dist-info/METADATA,sha256=xmC4_cWkiRyOzRBTdj0-_x-JIwXlJ7SCmM9dl5m8dLk,4379
6
- sage/all__sagemath_bliss.py,sha256=2p3JOEnbjaX22LWk6f1yfiAvhMMkcvNXwTp_NcGw6pM,44
7
- sage/graphs/all__sagemath_bliss.py,sha256=2p3JOEnbjaX22LWk6f1yfiAvhMMkcvNXwTp_NcGw6pM,44
8
- sage/graphs/bliss.cpython-311-darwin.so,sha256=7ucFwUjDc1bgLxtID0qLQJ8QsIAggmNqTLcrR8lszas,203808