Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx
8817 views
1
"""
2
Graph-theoretic partition backtrack functions
3
4
DOCTEST:
5
sage: import sage.groups.perm_gps.partn_ref.refinement_graphs
6
7
REFERENCE:
8
9
[1] McKay, Brendan D. Practical Graph Isomorphism. Congressus Numerantium,
10
Vol. 30 (1981), pp. 45-87.
11
12
"""
13
14
#*****************************************************************************
15
# Copyright (C) 2006 - 2011 Robert L. Miller <[email protected]>
16
#
17
# Distributed under the terms of the GNU General Public License (GPL)
18
# http://www.gnu.org/licenses/
19
#*****************************************************************************
20
21
include 'data_structures_pyx.pxi' # includes bitsets
22
23
def isomorphic(G1, G2, partn, ordering2, dig, use_indicator_function, sparse=False):
24
"""
25
Tests whether two graphs are isomorphic.
26
27
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import isomorphic
28
29
sage: G = Graph(2)
30
sage: H = Graph(2)
31
sage: isomorphic(G, H, [[0,1]], [0,1], 0, 1)
32
{0: 0, 1: 1}
33
sage: isomorphic(G, H, [[0,1]], [0,1], 0, 1)
34
{0: 0, 1: 1}
35
sage: isomorphic(G, H, [[0],[1]], [0,1], 0, 1)
36
{0: 0, 1: 1}
37
sage: isomorphic(G, H, [[0],[1]], [1,0], 0, 1)
38
{0: 1, 1: 0}
39
40
sage: G = Graph(3)
41
sage: H = Graph(3)
42
sage: isomorphic(G, H, [[0,1,2]], [0,1,2], 0, 1)
43
{0: 0, 1: 1, 2: 2}
44
sage: G.add_edge(0,1)
45
sage: isomorphic(G, H, [[0,1,2]], [0,1,2], 0, 1)
46
False
47
sage: H.add_edge(1,2)
48
sage: isomorphic(G, H, [[0,1,2]], [0,1,2], 0, 1)
49
{0: 1, 1: 2, 2: 0}
50
51
"""
52
cdef PartitionStack *part
53
cdef int *output, *ordering
54
cdef CGraph G
55
cdef GraphStruct GS1 = GraphStruct()
56
cdef GraphStruct GS2 = GraphStruct()
57
cdef GraphStruct GS
58
cdef int i, j, k, n = -1, cell_len
59
cdef list partition, cell
60
cdef bint loops = 0
61
62
from sage.graphs.all import Graph, DiGraph
63
from sage.graphs.generic_graph import GenericGraph
64
from copy import copy
65
which_G = 1
66
for G_in in [G1, G2]:
67
if which_G == 1:
68
GS = GS1
69
first=True
70
else:
71
GS = GS2
72
first=False
73
if isinstance(G_in, GenericGraph):
74
if G_in.has_loops():
75
loops = 1
76
if n == -1:
77
n = G_in.num_verts()
78
elif n != G_in.num_verts():
79
return False
80
if G_in.vertices() != range(n):
81
G_in = copy(G_in)
82
to = G_in.relabel(return_map=True)
83
frm = {}
84
for v in to.iterkeys():
85
frm[to[v]] = v
86
if first:
87
partition = [[to[v] for v in cell] for cell in partn]
88
else:
89
if first:
90
partition = partn
91
to = range(n)
92
frm = to
93
if sparse:
94
G = SparseGraph(n)
95
else:
96
G = DenseGraph(n)
97
if G_in.is_directed():
98
for i,j in G_in.edge_iterator(labels=False):
99
G.add_arc(i,j)
100
else:
101
for i,j in G_in.edge_iterator(labels=False):
102
G.add_arc(i,j)
103
G.add_arc(j,i)
104
elif isinstance(G_in, CGraph):
105
G = <CGraph> G_in
106
if n == -1:
107
n = G.num_verts
108
elif n != G.num_verts:
109
return False
110
if not loops:
111
for i from 0 <= i < n:
112
if G.has_arc_unsafe(i,i):
113
loops = 1
114
to = {}
115
for a in G.verts(): to[a]=a
116
frm = to
117
if first:
118
partition = partn
119
else:
120
raise TypeError("G must be a Sage graph.")
121
if first: frm1=frm;to1=to
122
else: frm2=frm;to2=to
123
GS.G = G
124
GS.directed = 1 if dig else 0
125
GS.loops = 1
126
GS.use_indicator = 1 if use_indicator_function else 0
127
which_G += 1
128
129
if n == 0:
130
return {}
131
132
part = PS_from_list(partition)
133
ordering = <int *> sage_malloc(n * sizeof(int))
134
output = <int *> sage_malloc(n * sizeof(int))
135
if part is NULL or ordering is NULL or output is NULL:
136
PS_dealloc(part)
137
sage_free(ordering)
138
sage_free(output)
139
raise MemoryError
140
for i from 0 <= i < n:
141
ordering[i] = to2[ordering2[i]]
142
143
GS1.scratch = <int *> sage_malloc((3*n+1) * sizeof(int))
144
GS2.scratch = <int *> sage_malloc((3*n+1) * sizeof(int))
145
if GS1.scratch is NULL or GS2.scratch is NULL:
146
sage_free(GS1.scratch)
147
sage_free(GS2.scratch)
148
PS_dealloc(part)
149
sage_free(ordering)
150
raise MemoryError
151
152
cdef bint isomorphic = double_coset(<void *>GS1, <void *>GS2, part, ordering, n, &all_children_are_equivalent, &refine_by_degree, &compare_graphs, NULL, NULL, output)
153
154
PS_dealloc(part)
155
sage_free(ordering)
156
sage_free(GS1.scratch)
157
sage_free(GS2.scratch)
158
if isomorphic:
159
output_py = dict([[frm1[i], frm2[output[i]]] for i from 0 <= i < n])
160
else:
161
output_py = False
162
sage_free(output)
163
return output_py
164
165
def search_tree(G_in, partition, lab=True, dig=False, dict_rep=False, certify=False,
166
verbosity=0, use_indicator_function=True, sparse=True,
167
base=False, order=False):
168
"""
169
Compute canonical labels and automorphism groups of graphs.
170
171
INPUT:
172
G_in -- a Sage graph
173
partition -- a list of lists representing a partition of the vertices
174
lab -- if True, compute and return the canonical label in addition to the
175
automorphism group.
176
dig -- set to True for digraphs and graphs with loops. If True, does not
177
use optimizations based on Lemma 2.25 in [1] that are valid only for
178
simple graphs.
179
dict_rep -- if True, return a dictionary with keys the vertices of the
180
input graph G_in and values elements of the set the permutation group
181
acts on. (The point is that graphs are arbitrarily labelled, often
182
0..n-1, and permutation groups always act on 1..n. This dictionary
183
maps vertex labels (such as 0..n-1) to the domain of the permutations.)
184
certify -- if True, return the permutation from G to its canonical label.
185
verbosity -- currently ignored
186
use_indicator_function -- option to turn off indicator function
187
(True is generally faster)
188
sparse -- whether to use sparse or dense representation of the graph
189
(ignored if G is already a CGraph - see sage.graphs.base)
190
base -- whether to return the first sequence of split vertices (used in
191
computing the order of the group)
192
order -- whether to return the order of the automorphism group
193
194
OUTPUT:
195
Depends on the options. If more than one thing is returned, they are in a
196
tuple in the following order:
197
list of generators in list-permutation format -- always
198
dict -- if dict_rep
199
graph -- if lab
200
dict -- if certify
201
list -- if base
202
integer -- if order
203
204
DOCTEST:
205
sage: st = sage.groups.perm_gps.partn_ref.refinement_graphs.search_tree
206
sage: from sage.graphs.base.dense_graph import DenseGraph
207
sage: from sage.graphs.base.sparse_graph import SparseGraph
208
209
Graphs on zero vertices:
210
sage: G = Graph()
211
sage: st(G, [[]], order=True)
212
([], Graph on 0 vertices, 1)
213
214
Graphs on one vertex:
215
sage: G = Graph(1)
216
sage: st(G, [[0]], order=True)
217
([], Graph on 1 vertex, 1)
218
219
Graphs on two vertices:
220
sage: G = Graph(2)
221
sage: st(G, [[0,1]], order=True)
222
([[1, 0]], Graph on 2 vertices, 2)
223
sage: st(G, [[0],[1]], order=True)
224
([], Graph on 2 vertices, 1)
225
sage: G.add_edge(0,1)
226
sage: st(G, [[0,1]], order=True)
227
([[1, 0]], Graph on 2 vertices, 2)
228
sage: st(G, [[0],[1]], order=True)
229
([], Graph on 2 vertices, 1)
230
231
Graphs on three vertices:
232
sage: G = Graph(3)
233
sage: st(G, [[0,1,2]], order=True)
234
([[0, 2, 1], [1, 0, 2]], Graph on 3 vertices, 6)
235
sage: st(G, [[0],[1,2]], order=True)
236
([[0, 2, 1]], Graph on 3 vertices, 2)
237
sage: st(G, [[0],[1],[2]], order=True)
238
([], Graph on 3 vertices, 1)
239
sage: G.add_edge(0,1)
240
sage: st(G, [range(3)], order=True)
241
([[1, 0, 2]], Graph on 3 vertices, 2)
242
sage: st(G, [[0],[1,2]], order=True)
243
([], Graph on 3 vertices, 1)
244
sage: st(G, [[0,1],[2]], order=True)
245
([[1, 0, 2]], Graph on 3 vertices, 2)
246
247
The Dodecahedron has automorphism group of size 120:
248
sage: G = graphs.DodecahedralGraph()
249
sage: Pi = [range(20)]
250
sage: st(G, Pi, order=True)[2]
251
120
252
253
The three-cube has automorphism group of size 48:
254
sage: G = graphs.CubeGraph(3)
255
sage: G.relabel()
256
sage: Pi = [G.vertices()]
257
sage: st(G, Pi, order=True)[2]
258
48
259
260
We obtain the same output using different types of Sage graphs:
261
sage: G = graphs.DodecahedralGraph()
262
sage: GD = DenseGraph(20)
263
sage: GS = SparseGraph(20)
264
sage: for i,j,_ in G.edge_iterator():
265
... GD.add_arc(i,j); GD.add_arc(j,i)
266
... GS.add_arc(i,j); GS.add_arc(j,i)
267
sage: Pi=[range(20)]
268
sage: a,b = st(G, Pi)
269
sage: asp,bsp = st(GS, Pi)
270
sage: ade,bde = st(GD, Pi)
271
sage: bsg = Graph()
272
sage: bdg = Graph()
273
sage: for i in range(20):
274
... for j in range(20):
275
... if bsp.has_arc(i,j):
276
... bsg.add_edge(i,j)
277
... if bde.has_arc(i,j):
278
... bdg.add_edge(i,j)
279
sage: print a, b.graph6_string()
280
[[0, 19, 3, 2, 6, 5, 4, 17, 18, 11, 10, 9, 13, 12, 16, 15, 14, 7, 8, 1], [0, 1, 8, 9, 13, 14, 7, 6, 2, 3, 19, 18, 17, 4, 5, 15, 16, 12, 11, 10], [1, 8, 9, 10, 11, 12, 13, 14, 7, 6, 2, 3, 4, 5, 15, 16, 17, 18, 19, 0]] S?[PG__OQ@?_?_?P?CO?_?AE?EC?Ac?@O
281
sage: a == asp
282
True
283
sage: a == ade
284
True
285
sage: b == bsg
286
True
287
sage: b == bdg
288
True
289
290
Cubes!
291
sage: C = graphs.CubeGraph(1)
292
sage: gens, order = st(C, [C.vertices()], lab=False, order=True); order
293
2
294
sage: C = graphs.CubeGraph(2)
295
sage: gens, order = st(C, [C.vertices()], lab=False, order=True); order
296
8
297
sage: C = graphs.CubeGraph(3)
298
sage: gens, order = st(C, [C.vertices()], lab=False, order=True); order
299
48
300
sage: C = graphs.CubeGraph(4)
301
sage: gens, order = st(C, [C.vertices()], lab=False, order=True); order
302
384
303
sage: C = graphs.CubeGraph(5)
304
sage: gens, order = st(C, [C.vertices()], lab=False, order=True); order
305
3840
306
sage: C = graphs.CubeGraph(6)
307
sage: gens, order = st(C, [C.vertices()], lab=False, order=True); order
308
46080
309
310
One can also turn off the indicator function (note- this will take longer)
311
sage: D1 = DiGraph({0:[2],2:[0],1:[1]}, loops=True)
312
sage: D2 = DiGraph({1:[2],2:[1],0:[0]}, loops=True)
313
sage: a,b = st(D1, [D1.vertices()], dig=True, use_indicator_function=False)
314
sage: c,d = st(D2, [D2.vertices()], dig=True, use_indicator_function=False)
315
sage: b==d
316
True
317
318
This example is due to Chris Godsil:
319
sage: HS = graphs.HoffmanSingletonGraph()
320
sage: alqs = [Set(c) for c in (HS.complement()).cliques_maximum()]
321
sage: Y = Graph([alqs, lambda s,t: len(s.intersection(t))==0])
322
sage: Y0,Y1 = Y.connected_components_subgraphs()
323
sage: st(Y0, [Y0.vertices()])[1] == st(Y1, [Y1.vertices()])[1]
324
True
325
sage: st(Y0, [Y0.vertices()])[1] == st(HS, [HS.vertices()])[1]
326
True
327
sage: st(HS, [HS.vertices()])[1] == st(Y1, [Y1.vertices()])[1]
328
True
329
330
Certain border cases need to be tested as well:
331
sage: G = Graph('Fll^G')
332
sage: a,b,c = st(G, [range(G.num_verts())], order=True); b
333
Graph on 7 vertices
334
sage: c
335
48
336
sage: G = Graph(21)
337
sage: st(G, [range(G.num_verts())], order=True)[2] == factorial(21)
338
True
339
340
sage: G = Graph('^????????????????????{??N??@w??FaGa?PCO@CP?AGa?_QO?Q@G?CcA??cc????Bo????{????F_')
341
sage: perm = {3:15, 15:3}
342
sage: H = G.relabel(perm, inplace=False)
343
sage: st(G, [range(G.num_verts())])[1] == st(H, [range(H.num_verts())])[1]
344
True
345
346
sage: st(Graph(':Dkw'), [range(5)], lab=False, dig=True)
347
[[4, 1, 2, 3, 0], [0, 2, 1, 3, 4]]
348
349
"""
350
cdef CGraph G
351
cdef int i, j, n
352
cdef Integer I
353
cdef bint loops
354
cdef aut_gp_and_can_lab *output
355
cdef PartitionStack *part
356
from sage.graphs.all import Graph, DiGraph
357
from sage.graphs.generic_graph import GenericGraph
358
from copy import copy
359
if isinstance(G_in, GenericGraph):
360
loops = G_in.has_loops()
361
n = G_in.num_verts()
362
if G_in.vertices() != range(n):
363
G_in = copy(G_in)
364
to = G_in.relabel(return_map=True)
365
frm = {}
366
for v in to.iterkeys():
367
frm[to[v]] = v
368
partition = [[to[v] for v in cell] for cell in partition]
369
else:
370
to = dict(enumerate(range(n)))
371
frm = to
372
if sparse:
373
G = SparseGraph(n)
374
else:
375
G = DenseGraph(n)
376
if G_in.is_directed():
377
for i,j in G_in.edge_iterator(labels=False):
378
G.add_arc(i,j)
379
else:
380
for i,j in G_in.edge_iterator(labels=False):
381
G.add_arc(i,j)
382
G.add_arc(j,i)
383
elif isinstance(G_in, CGraph):
384
G = <CGraph> G_in
385
n = G.num_verts
386
loops = 0
387
for i from 0 <= i < n:
388
if G.has_arc_unsafe(i,i):
389
loops = 1
390
to = {}
391
for a in G.verts(): to[a]=a
392
frm = to
393
else:
394
raise TypeError("G must be a Sage graph.")
395
396
cdef GraphStruct GS = GraphStruct()
397
GS.G = G
398
GS.directed = 1 if dig else 0
399
GS.loops = loops
400
GS.use_indicator = 1 if use_indicator_function else 0
401
402
if n == 0:
403
return_tuple = [[]]
404
if dict_rep:
405
return_tuple.append({})
406
if lab:
407
if isinstance(G_in, GenericGraph):
408
G_C = copy(G_in)
409
else:
410
if isinstance(G, SparseGraph):
411
G_C = SparseGraph(n)
412
else:
413
G_C = DenseGraph(n)
414
return_tuple.append(G_C)
415
if certify:
416
return_tuple.append({})
417
if base:
418
return_tuple.append([])
419
if order:
420
return_tuple.append(Integer(1))
421
if len(return_tuple) == 1:
422
return return_tuple[0]
423
else:
424
return tuple(return_tuple)
425
426
GS.scratch = <int *> sage_malloc( (3*G.num_verts + 1) * sizeof(int) )
427
part = PS_from_list(partition)
428
if GS.scratch is NULL or part is NULL:
429
PS_dealloc(part)
430
sage_free(GS.scratch)
431
raise MemoryError
432
433
lab_new = lab or certify
434
output = get_aut_gp_and_can_lab(<void *>GS, part, G.num_verts, &all_children_are_equivalent, &refine_by_degree, &compare_graphs, lab, NULL, NULL, NULL)
435
sage_free( GS.scratch )
436
# prepare output
437
list_of_gens = []
438
for i from 0 <= i < output.num_gens:
439
list_of_gens.append([output.generators[j+i*G.num_verts] for j from 0 <= j < G.num_verts])
440
return_tuple = [list_of_gens]
441
if dict_rep:
442
ddd = {}
443
for v in frm.iterkeys():
444
ddd[frm[v]] = v if v != 0 else n
445
return_tuple.append(ddd)
446
if lab:
447
if isinstance(G_in, GenericGraph):
448
G_C = copy(G_in)
449
G_C.relabel([output.relabeling[i] for i from 0 <= i < n])
450
else:
451
if isinstance(G, SparseGraph):
452
G_C = SparseGraph(n)
453
else:
454
G_C = DenseGraph(n)
455
for i from 0 <= i < n:
456
for j in G.out_neighbors(i):
457
G_C.add_arc(output.relabeling[i],output.relabeling[j])
458
return_tuple.append(G_C)
459
if certify:
460
dd = {}
461
for i from 0 <= i < G.num_verts:
462
dd[frm[i]] = output.relabeling[i]
463
return_tuple.append(dd)
464
if base:
465
return_tuple.append([output.group.base_orbits[i][0] for i from 0 <= i < output.group.base_size])
466
if order:
467
I = Integer()
468
SC_order(output.group, 0, I.value)
469
return_tuple.append(I)
470
PS_dealloc(part)
471
deallocate_agcl_output(output)
472
if len(return_tuple) == 1:
473
return return_tuple[0]
474
else:
475
return tuple(return_tuple)
476
477
cdef int refine_by_degree(PartitionStack *PS, void *S, int *cells_to_refine_by, int ctrb_len):
478
r"""
479
Refines the input partition by checking degrees of vertices to the given
480
cells.
481
482
INPUT:
483
PS -- a partition stack, whose finest partition is the partition to be
484
refined.
485
S -- a graph struct object, which contains scratch space, the graph in
486
question, and some flags.
487
cells_to_refine_by -- a list of pointers to cells to check degrees against
488
in refining the other cells (updated in place). Must be allocated to
489
length at least the degree of PS, since the array may grow
490
ctrb_len -- how many cells in cells_to_refine_by
491
492
OUTPUT:
493
494
An integer invariant under the orbits of $S_n$. That is, if $\gamma$ is a
495
permutation of the vertices, then
496
$$ I(G, PS, cells_to_refine_by) = I( \gamma(G), \gamma(PS), \gamma(cells_to_refine_by) ) .$$
497
498
"""
499
cdef GraphStruct GS = <GraphStruct> S
500
cdef CGraph G = GS.G
501
cdef int current_cell_against = 0
502
cdef int current_cell, i, r
503
cdef int first_largest_subcell
504
cdef int invariant = 1
505
cdef int max_degree
506
cdef int *degrees = GS.scratch # length 3n+1
507
cdef bint necessary_to_split_cell
508
cdef int against_index
509
if G.num_verts != PS.degree and PS.depth == 0:
510
# should be less verts, then, so place the "nonverts" in separate cell at the end
511
current_cell = 0
512
while current_cell < PS.degree:
513
i = current_cell
514
r = 0
515
while 1:
516
if G.has_vertex(PS.entries[i]):
517
degrees[i-current_cell] = 0
518
else:
519
r = 1
520
degrees[i-current_cell] = 1
521
i += 1
522
if PS.levels[i-1] <= PS.depth:
523
break
524
if r != 0:
525
sort_by_function(PS, current_cell, degrees)
526
current_cell = i
527
while not PS_is_discrete(PS) and current_cell_against < ctrb_len:
528
invariant += 1
529
current_cell = 0
530
while current_cell < PS.degree:
531
invariant += 50
532
i = current_cell
533
necessary_to_split_cell = 0
534
max_degree = 0
535
while 1:
536
degrees[i-current_cell] = degree(PS, G, i, cells_to_refine_by[current_cell_against], 0)
537
if degrees[i-current_cell] != degrees[0]:
538
necessary_to_split_cell = 1
539
if degrees[i-current_cell] > max_degree:
540
max_degree = degrees[i-current_cell]
541
i += 1
542
if PS.levels[i-1] <= PS.depth:
543
break
544
# now, i points to the next cell (before refinement)
545
if necessary_to_split_cell:
546
invariant += 10
547
first_largest_subcell = sort_by_function(PS, current_cell, degrees)
548
invariant += first_largest_subcell + max_degree
549
against_index = current_cell_against
550
while against_index < ctrb_len:
551
if cells_to_refine_by[against_index] == current_cell:
552
cells_to_refine_by[against_index] = first_largest_subcell
553
break
554
against_index += 1
555
r = current_cell
556
while 1:
557
if r == current_cell or PS.levels[r-1] == PS.depth:
558
if r != first_largest_subcell:
559
cells_to_refine_by[ctrb_len] = r
560
ctrb_len += 1
561
r += 1
562
if r >= i:
563
break
564
invariant += (i - current_cell)
565
current_cell = i
566
if GS.directed:
567
# if we are looking at a digraph, also compute
568
# the reverse degrees and sort by them
569
current_cell = 0
570
while current_cell < PS.degree: # current_cell is still a valid cell
571
invariant += 20
572
i = current_cell
573
necessary_to_split_cell = 0
574
max_degree = 0
575
while 1:
576
degrees[i-current_cell] = degree(PS, G, i, cells_to_refine_by[current_cell_against], 1)
577
if degrees[i-current_cell] != degrees[0]:
578
necessary_to_split_cell = 1
579
if degrees[i-current_cell] > max_degree:
580
max_degree = degrees[i-current_cell]
581
i += 1
582
if PS.levels[i-1] <= PS.depth:
583
break
584
# now, i points to the next cell (before refinement)
585
if necessary_to_split_cell:
586
invariant += 7
587
first_largest_subcell = sort_by_function(PS, current_cell, degrees)
588
invariant += first_largest_subcell + max_degree
589
against_index = current_cell_against
590
while against_index < ctrb_len:
591
if cells_to_refine_by[against_index] == current_cell:
592
cells_to_refine_by[against_index] = first_largest_subcell
593
break
594
against_index += 1
595
against_index = ctrb_len
596
r = current_cell
597
while 1:
598
if r == current_cell or PS.levels[r-1] == PS.depth:
599
if r != first_largest_subcell:
600
cells_to_refine_by[against_index] = r
601
against_index += 1
602
ctrb_len += 1
603
r += 1
604
if r >= i:
605
break
606
invariant += (i - current_cell)
607
current_cell = i
608
current_cell_against += 1
609
if GS.use_indicator:
610
return invariant
611
else:
612
return 0
613
614
cdef int compare_graphs(int *gamma_1, int *gamma_2, void *S1, void *S2, int degree):
615
r"""
616
Compare gamma_1(S1) and gamma_2(S2).
617
618
Return return -1 if gamma_1(S1) < gamma_2(S2), 0 if gamma_1(S1) ==
619
gamma_2(S2), 1 if gamma_1(S1) > gamma_2(S2). (Just like the python
620
\code{cmp}) function.
621
622
INPUT:
623
gamma_1, gamma_2 -- list permutations (inverse)
624
S1, S2 -- graph struct objects
625
626
"""
627
cdef int i, j, m
628
cdef GraphStruct GS1 = <GraphStruct> S1
629
cdef GraphStruct GS2 = <GraphStruct> S2
630
cdef CGraph G1 = GS1.G
631
cdef CGraph G2 = GS2.G
632
if G1.active_vertices.size != G2.active_vertices.size or \
633
not bitset_cmp(G1.active_vertices, G2.active_vertices):
634
for i from 0 <= i < degree:
635
if G1.has_vertex(gamma_1[i]) != G2.has_vertex(gamma_2[i]):
636
return G1.has_vertex(gamma_1[i]) - G2.has_vertex(gamma_2[i])
637
for i from 0 <= i < G1.num_verts:
638
for j from 0 <= j < G1.num_verts:
639
if G1.has_arc_unsafe(gamma_1[i], gamma_1[j]):
640
if not G2.has_arc_unsafe(gamma_2[i], gamma_2[j]):
641
return 1
642
elif G2.has_arc_unsafe(gamma_2[i], gamma_2[j]):
643
return -1
644
return 0
645
646
cdef bint all_children_are_equivalent(PartitionStack *PS, void *S):
647
"""
648
Return True if every refinement of the current partition results in the
649
same structure.
650
651
WARNING:
652
Converse does not hold in general! See Lemma 2.25 of [1] for details.
653
654
INPUT:
655
PS -- the partition stack to be checked
656
S -- a graph struct object
657
"""
658
cdef GraphStruct GS = <GraphStruct> S
659
if GS.directed or GS.loops:
660
return 0
661
cdef CGraph G = GS.G
662
cdef int i, n = PS.degree
663
cdef bint in_cell = 0
664
cdef int nontrivial_cells = 0
665
cdef int total_cells = PS_num_cells(PS)
666
if n <= total_cells + 4:
667
return 1
668
for i from 0 <= i < n-1:
669
if PS.levels[i] <= PS.depth:
670
if in_cell:
671
nontrivial_cells += 1
672
in_cell = 0
673
else:
674
in_cell = 1
675
if in_cell:
676
nontrivial_cells += 1
677
if n == total_cells + nontrivial_cells:
678
return 1
679
if n == total_cells + nontrivial_cells + 1:
680
return 1
681
return 0
682
683
cdef inline int degree(PartitionStack *PS, CGraph G, int entry, int cell_index, bint reverse):
684
"""
685
Returns the number of edges from the vertex corresponding to entry to
686
vertices in the cell corresponding to cell_index.
687
688
INPUT:
689
PS -- the partition stack to be checked
690
S -- a graph struct object
691
entry -- the position of the vertex in question in the entries of PS
692
cell_index -- the starting position of the cell in question in the entries
693
of PS
694
reverse -- whether to check for arcs in the other direction
695
"""
696
cdef int num_arcs = 0
697
entry = PS.entries[entry]
698
if not reverse:
699
while 1:
700
if G.has_arc_unsafe(PS.entries[cell_index], entry):
701
num_arcs += 1
702
if PS.levels[cell_index] > PS.depth:
703
cell_index += 1
704
else:
705
break
706
else:
707
while 1:
708
if G.has_arc_unsafe(entry, PS.entries[cell_index]):
709
num_arcs += 1
710
if PS.levels[cell_index] > PS.depth:
711
cell_index += 1
712
else:
713
break
714
return num_arcs
715
716
def all_labeled_graphs(n):
717
"""
718
Returns all labeled graphs on n vertices {0,1,...,n-1}. Used in
719
classifying isomorphism types (naive approach), and more importantly
720
in benchmarking the search algorithm.
721
722
EXAMPLE::
723
724
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import all_labeled_graphs
725
sage: st = sage.groups.perm_gps.partn_ref.refinement_graphs.search_tree
726
sage: Glist = {}
727
sage: Giso = {}
728
sage: for n in [1..5]: # long time (4s on sage.math, 2011)
729
... Glist[n] = all_labeled_graphs(n)
730
... Giso[n] = []
731
... for g in Glist[n]:
732
... a, b = st(g, [range(n)])
733
... inn = False
734
... for gi in Giso[n]:
735
... if b == gi:
736
... inn = True
737
... if not inn:
738
... Giso[n].append(b)
739
sage: for n in Giso: # long time
740
... print n, len(Giso[n])
741
1 1
742
2 2
743
3 4
744
4 11
745
5 34
746
747
"""
748
from sage.graphs.all import Graph
749
TE = []
750
for i in range(n):
751
for j in range(i):
752
TE.append((i, j))
753
m = len(TE)
754
Glist= []
755
for i in range(2**m):
756
G = Graph(n)
757
b = Integer(i).binary()
758
b = '0'*(m-len(b)) + b
759
for i in range(m):
760
if int(b[i]):
761
G.add_edge(TE[i])
762
Glist.append(G)
763
return Glist
764
765
766
def random_tests(num=10, n_max=60, perms_per_graph=5):
767
"""
768
Tests to make sure that C(gamma(G)) == C(G) for random permutations gamma
769
and random graphs G, and that isomorphic returns an isomorphism.
770
771
INPUT:
772
num -- run tests for this many graphs
773
n_max -- test graphs with at most this many vertices
774
perms_per_graph -- test each graph with this many random permutations
775
776
DISCUSSION:
777
778
This code generates num random graphs G on at most n_max vertices. The
779
density of edges is chosen randomly between 0 and 1.
780
781
For each graph G generated, we uniformly generate perms_per_graph random
782
permutations and verify that the canonical labels of G and the image of G
783
under the generated permutation are equal, and that the isomorphic function
784
returns an isomorphism.
785
786
TESTS::
787
788
sage: import sage.groups.perm_gps.partn_ref.refinement_graphs
789
sage: sage.groups.perm_gps.partn_ref.refinement_graphs.random_tests() # long time
790
All passed: 200 random tests on 20 graphs.
791
"""
792
from sage.misc.prandom import random, randint
793
from sage.graphs.graph_generators import GraphGenerators
794
from sage.graphs.digraph_generators import DiGraphGenerators
795
from sage.combinat.permutation import Permutations
796
from copy import copy
797
cdef int i, j, num_tests = 0, num_graphs = 0
798
GG = GraphGenerators()
799
DGG = DiGraphGenerators()
800
for mmm in range(num):
801
p = random()
802
n = randint(1, n_max)
803
S = Permutations(n)
804
805
G = GG.RandomGNP(n, p)
806
H = copy(G)
807
for i from 0 <= i < perms_per_graph:
808
G = copy(H)
809
G1 = search_tree(G, [G.vertices()])[1]
810
perm = list(S.random_element())
811
perm = [perm[j]-1 for j from 0 <= j < n]
812
G.relabel(perm)
813
G2 = search_tree(G, [G.vertices()])[1]
814
if G1 != G2:
815
print "search_tree FAILURE: graph6-"
816
print H.graph6_string()
817
print perm
818
return
819
isom = isomorphic(G, H, [range(n)], range(n), 0, 1)
820
if not isom or G.relabel(isom, inplace=False) != H:
821
print "isom FAILURE: graph6-"
822
print H.graph6_string()
823
print perm
824
return
825
826
D = DGG.RandomDirectedGNP(n, p)
827
D.allow_loops(True)
828
for i from 0 <= i < n:
829
if random() < p:
830
D.add_edge(i,i)
831
E = copy(D)
832
for i from 0 <= i < perms_per_graph:
833
D = copy(E)
834
D1 = search_tree(D, [D.vertices()], dig=True)[1]
835
perm = list(S.random_element())
836
perm = [perm[j]-1 for j from 0 <= j < n]
837
D.relabel(perm)
838
D2 = search_tree(D, [D.vertices()], dig=True)[1]
839
if D1 != D2:
840
print "search_tree FAILURE: dig6-"
841
print E.dig6_string()
842
print perm
843
return
844
isom = isomorphic(D, E, [range(n)], range(n), 1, 1)
845
if not isom or D.relabel(isom, inplace=False) != E:
846
print "isom FAILURE: dig6-"
847
print E.dig6_string()
848
print perm
849
print isom
850
return
851
num_tests += 4*perms_per_graph
852
num_graphs += 2
853
print "All passed: %d random tests on %d graphs."%(num_tests, num_graphs)
854
855
def orbit_partition(gamma, list_perm=False):
856
r"""
857
Assuming that G is a graph on vertices 0,1,...,n-1, and gamma is an
858
element of SymmetricGroup(n), returns the partition of the vertex
859
set determined by the orbits of gamma, considered as action on the
860
set 1,2,...,n where we take 0 = n. In other words, returns the
861
partition determined by a cyclic representation of gamma.
862
863
INPUT:
864
865
866
- ``list_perm`` - if True, assumes
867
``gamma`` is a list representing the map
868
`i \mapsto ``gamma``[i]`.
869
870
871
EXAMPLES::
872
873
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import orbit_partition
874
sage: G = graphs.PetersenGraph()
875
sage: S = SymmetricGroup(10)
876
sage: gamma = S('(10,1,2,3,4)(5,6,7)(8,9)')
877
sage: orbit_partition(gamma)
878
[[1, 2, 3, 4, 0], [5, 6, 7], [8, 9]]
879
sage: gamma = S('(10,5)(1,6)(2,7)(3,8)(4,9)')
880
sage: orbit_partition(gamma)
881
[[1, 6], [2, 7], [3, 8], [4, 9], [5, 0]]
882
"""
883
if list_perm:
884
n = len(gamma)
885
seen = [1] + [0]*(n-1)
886
i = 0
887
p = 0
888
partition = [[0]]
889
while sum(seen) < n:
890
if gamma[i] != partition[p][0]:
891
partition[p].append(gamma[i])
892
i = gamma[i]
893
seen[i] = 1
894
else:
895
for j in range(n):
896
if seen[j]==0:
897
i = j
898
break
899
partition.append([i])
900
p += 1
901
seen[i] = 1
902
return partition
903
else:
904
n = len(gamma.domain())
905
l = []
906
for i in range(1,n+1):
907
orb = gamma.orbit(i)
908
if orb not in l: l.append(orb)
909
for i in l:
910
for j in range(len(i)):
911
if i[j] == n:
912
i[j] = 0
913
return l
914
915
def coarsest_equitable_refinement(CGraph G, list partition, bint directed):
916
"""
917
Returns the coarsest equitable refinement of ``partition`` for ``G``.
918
919
This is a helper function for the graph function of the same name.
920
921
DOCTEST (More thorough testing in ``sage/graphs/graph.py``)::
922
923
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import coarsest_equitable_refinement
924
sage: from sage.graphs.base.sparse_graph import SparseGraph
925
sage: coarsest_equitable_refinement(SparseGraph(7), [[0], [1,2,3,4], [5,6]], 0)
926
[[0], [1, 2, 3, 4], [5, 6]]
927
928
"""
929
cdef int i, j = 0, k = 0, n = G.num_verts
930
931
# set up partition stack and graph struct
932
cdef PartitionStack *nu = PS_new(n, 0)
933
for cell in partition:
934
for i in cell:
935
nu.entries[j] = i
936
nu.levels[j] = n
937
j += 1
938
nu.levels[j-1] = 0
939
PS_move_min_to_front(nu, k, j-1)
940
k = j
941
942
cdef GraphStruct GS = GraphStruct()
943
GS.G = G
944
GS.scratch = <int *> sage_malloc((3*n+1) * sizeof(int))
945
if GS.scratch is NULL:
946
PS_dealloc(nu)
947
raise MemoryError
948
GS.directed = directed
949
GS.use_indicator = 0
950
951
# set up cells to refine by
952
cdef int num_cells = len(partition)
953
cdef int *alpha = <int *>sage_malloc(n * sizeof(int))
954
if alpha is NULL:
955
PS_dealloc(nu)
956
sage_free(GS.scratch)
957
raise MemoryError
958
j = 0
959
for i from 0 <= i < num_cells:
960
alpha[i] = j
961
j += len(partition[i])
962
963
# refine, and get the result
964
refine_by_degree(nu, <void *>GS, alpha, num_cells)
965
966
eq_part = []
967
cell = []
968
for i from 0 <= i < n:
969
cell.append(nu.entries[i])
970
if nu.levels[i] <= 0:
971
eq_part.append(cell)
972
cell = []
973
974
PS_dealloc(nu)
975
sage_free(GS.scratch)
976
sage_free(alpha)
977
978
return eq_part
979
980
def get_orbits(list gens, int n):
981
"""
982
Compute orbits given a list of generators of a permutation group, in list
983
format.
984
985
This is a helper function for automorphism groups of graphs.
986
987
DOCTEST (More thorough testing in ``sage/graphs/graph.py``)::
988
989
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import get_orbits
990
sage: get_orbits([[1,2,3,0,4,5], [0,1,2,3,5,4]], 6)
991
[[0, 1, 2, 3], [4, 5]]
992
993
"""
994
cdef int i, j
995
if len(gens) == 0:
996
return [[i] for i from 0 <= i < n]
997
998
cdef OrbitPartition *OP = OP_new(n)
999
cdef int *perm_ints = <int *> sage_malloc(n * sizeof(int))
1000
if perm_ints is NULL:
1001
OP_dealloc(OP)
1002
raise MemoryError
1003
1004
for gen in gens:
1005
for i from 0 <= i < n:
1006
perm_ints[i] = gen[i]
1007
OP_merge_list_perm(OP, perm_ints)
1008
1009
orbit_dict = {}
1010
for i from 0 <= i < n:
1011
j = OP_find(OP, i)
1012
if orbit_dict.has_key(j):
1013
orbit_dict[j].append(i)
1014
else:
1015
orbit_dict[j] = [i]
1016
1017
OP_dealloc(OP)
1018
sage_free(perm_ints)
1019
1020
return orbit_dict.values()
1021
1022
1023
1024
1025
1026
1027
1028
# Canonical augmentation
1029
from cpython.ref cimport *
1030
1031
# Dense graphs: adding edges
1032
1033
# This implements an augmentation scheme as follows:
1034
# * Seed objects are graphs with n vertices and no edges.
1035
# * Augmentations consist of adding a single edge, or a loop.
1036
1037
cdef void *dg_edge_gen_next(void *data, int *degree, bint *mem_err):
1038
r"""
1039
The ``next`` function in an edge iterator. The iterator generates unique
1040
representatives under the action of the automorphism group of the parent
1041
graph on edges not in the graph, which are to considered for adding to the
1042
graph.
1043
"""
1044
cdef dg_edge_gen_data *degd = <dg_edge_gen_data *> data
1045
cdef GraphStruct graph = <GraphStruct> degd.graph
1046
cdef subset *edge_candidate
1047
cdef int u, v, reject
1048
cdef bint mem_err_sub = 0
1049
if mem_err[0]:
1050
(<canonical_generator_data *> degd.edge_iterator.data).mem_err = 1
1051
while 1:
1052
edge_candidate = <subset *> degd.edge_iterator.next(degd.edge_iterator.data, NULL, &mem_err_sub)
1053
if edge_candidate is NULL:
1054
break
1055
reject = 0
1056
if bitset_len(&edge_candidate.bits) < (1 if graph.loops else 2):
1057
reject = 1
1058
else:
1059
u = bitset_first(&edge_candidate.bits)
1060
v = bitset_next(&edge_candidate.bits, u+1)
1061
if v == -1: v = u
1062
if graph.G.has_arc_unsafe(u, v):
1063
reject = 1
1064
if not reject:
1065
break
1066
if mem_err_sub:
1067
mem_err[0] = 1
1068
return edge_candidate
1069
1070
cdef void *allocate_degd(int degree):
1071
r"""
1072
Allocate the data part of the iterator over edges to add to the graph.
1073
"""
1074
cdef dg_edge_gen_data *degd = <dg_edge_gen_data *> sage_malloc(sizeof(dg_edge_gen_data))
1075
cdef iterator *edge_iterator = allocate_subset_gen(degree, 2)
1076
if degd is NULL or edge_iterator is NULL:
1077
sage_free(degd)
1078
free_subset_gen(edge_iterator)
1079
return NULL
1080
edge_iterator = setup_set_gen(edge_iterator, degree, 2)
1081
if edge_iterator is NULL:
1082
sage_free(degd)
1083
return NULL
1084
degd.edge_iterator = edge_iterator
1085
return degd
1086
1087
cdef void deallocate_degd(void *data):
1088
r"""
1089
Deallocate the data part of the iterator over edges to add to the graph.
1090
"""
1091
cdef dg_edge_gen_data *degd = <dg_edge_gen_data *> data
1092
free_subset_gen(degd.edge_iterator)
1093
sage_free(degd)
1094
1095
cdef int gen_children_dg_edge(void *S, aut_gp_and_can_lab *group, iterator *it):
1096
r"""
1097
Setup an iterator over edges to be added.
1098
"""
1099
cdef GraphStruct GS = <GraphStruct> S
1100
cdef int n = GS.G.num_verts
1101
(<dg_edge_gen_data *> it.data).graph = <void *> GS
1102
cdef iterator *edge_iterator = setup_set_gen((<dg_edge_gen_data *> it.data).edge_iterator, n, 2)
1103
if edge_iterator is not NULL:
1104
start_canonical_generator(group.group, NULL, n, edge_iterator)
1105
return (edge_iterator is NULL)
1106
1107
cdef void copy_dense_graph(DenseGraph dest, DenseGraph src):
1108
r"""
1109
caution! active_vertices must be same size!
1110
"""
1111
memcpy(dest.edges, src.edges, src.active_vertices.size * src.num_longs * sizeof(unsigned long))
1112
memcpy(dest.in_degrees, src.in_degrees, src.active_vertices.size * sizeof(int))
1113
memcpy(dest.out_degrees, src.out_degrees, src.active_vertices.size * sizeof(int))
1114
bitset_copy(dest.active_vertices, src.active_vertices)
1115
dest.num_verts = src.num_verts
1116
dest.num_arcs = src.num_arcs
1117
1118
cdef void *apply_dg_edge_aug(void *parent, void *aug, void *child, int *degree, bint *mem_err):
1119
r"""
1120
Apply the augmentation to ``parent`` storing the result in ``child``. Here
1121
``aug`` represents an edge to be added.
1122
"""
1123
cdef GraphStruct GS_child = <GraphStruct> child, GS_par = <GraphStruct> parent
1124
cdef DenseGraph DG = <DenseGraph> GS_child.G, DG_par = <DenseGraph> GS_par.G
1125
cdef subset *edge = <subset *> aug
1126
cdef int u, v, n = DG_par.num_verts
1127
1128
# copy DG_par edges to DG
1129
copy_dense_graph(DG, DG_par)
1130
1131
# add the edge
1132
u = bitset_first(&edge.bits)
1133
v = bitset_next(&edge.bits, u+1)
1134
if v == -1:
1135
DG.add_arc_unsafe(u, u)
1136
else:
1137
DG.add_arc_unsafe(u, v)
1138
DG.add_arc_unsafe(v, u)
1139
1140
degree[0] = DG.num_verts
1141
return <void *> GS_child
1142
1143
cdef void *allocate_dg_edge(int n, bint loops):
1144
r"""
1145
Allocates an object for this augmentation scheme.
1146
"""
1147
cdef GraphStruct GS
1148
cdef DenseGraph G
1149
cdef int *scratch
1150
try:
1151
GS = GraphStruct()
1152
G = DenseGraph(n)
1153
scratch = <int *> sage_malloc((3*n+1) * sizeof(int))
1154
if scratch is NULL:
1155
raise MemoryError
1156
except MemoryError:
1157
return NULL
1158
Py_INCREF(GS)
1159
Py_INCREF(G)
1160
GS.G = G
1161
GS.directed = 0
1162
GS.loops = loops
1163
GS.use_indicator = 1
1164
GS.scratch = scratch
1165
return <void *> GS
1166
1167
cdef void free_dg_edge(void *child):
1168
r"""
1169
Deallocates an object for this augmentation scheme.
1170
"""
1171
cdef GraphStruct GS = <GraphStruct> child
1172
sage_free(GS.scratch)
1173
Py_DECREF(GS.G)
1174
Py_DECREF(GS)
1175
1176
cdef void *canonical_dg_edge_parent(void *child, void *parent, int *permutation, int *degree, bint *mem_err):
1177
r"""
1178
Applies ``permutation`` to ``child``, determines an arbitrary parent by
1179
deleting the lexicographically largest edge, applies the inverse of
1180
``permutation`` to the result and stores the result in ``parent``.
1181
"""
1182
cdef GraphStruct GS_par = <GraphStruct> parent, GS = <GraphStruct> child
1183
cdef DenseGraph DG_par = <DenseGraph> GS_par.G, DG = <DenseGraph> GS.G
1184
cdef int u, v, n = DG.num_verts
1185
cdef int *scratch = GS_par.scratch
1186
1187
# copy DG edges to DG_par
1188
copy_dense_graph(DG_par, DG)
1189
1190
# remove the right edge
1191
for u from 0 <= u < n:
1192
scratch[permutation[u]] = u
1193
for u from n > u >= 0:
1194
if DG.in_degrees[scratch[u]] != 0:
1195
break
1196
for v from u >= v >= 0:
1197
if DG.has_arc_unsafe(scratch[u], scratch[v]):
1198
break
1199
DG_par.del_arc_unsafe(scratch[u], scratch[v])
1200
if u != v:
1201
DG_par.del_arc_unsafe(scratch[v], scratch[u])
1202
1203
degree[0] = n
1204
return <void *> GS_par
1205
1206
cdef iterator *allocate_dg_edge_gen(int degree, int depth, bint loops):
1207
r"""
1208
Allocates the iterator for generating graphs.
1209
"""
1210
cdef iterator *dg_edge_gen = <iterator *> sage_malloc(sizeof(iterator))
1211
cdef canonical_generator_data *cgd = allocate_cgd(depth, degree)
1212
if dg_edge_gen is NULL or cgd is NULL:
1213
sage_free(dg_edge_gen)
1214
deallocate_cgd(cgd)
1215
return NULL
1216
cdef int i, j
1217
for i from 0 <= i < depth:
1218
cgd.object_stack[i] = allocate_dg_edge(degree, loops)
1219
cgd.parent_stack[i] = allocate_dg_edge(degree, loops)
1220
cgd.iterator_stack[i].data = allocate_degd(degree)
1221
cgd.iterator_stack[i].next = &dg_edge_gen_next
1222
if cgd.iterator_stack[i].data is NULL or \
1223
cgd.object_stack[i] is NULL or \
1224
cgd.parent_stack[i] is NULL:
1225
for j from 0 <= j <= i:
1226
deallocate_degd(cgd.iterator_stack[j].data)
1227
free_dg_edge(cgd.object_stack[j])
1228
free_dg_edge(cgd.parent_stack[j])
1229
sage_free(dg_edge_gen)
1230
deallocate_cgd(cgd)
1231
return NULL
1232
dg_edge_gen.data = <void *> cgd
1233
dg_edge_gen.next = &canonical_generator_next
1234
return dg_edge_gen
1235
1236
cdef void free_dg_edge_gen(iterator *dg_edge_gen):
1237
r"""
1238
Deallocates the iterator for generating graphs.
1239
"""
1240
cdef canonical_generator_data *cgd = <canonical_generator_data *> dg_edge_gen.data
1241
deallocate_cgd(cgd)
1242
sage_free(dg_edge_gen)
1243
1244
1245
def generate_dense_graphs_edge_addition(int n, bint loops, G = None, depth = None, bint construct = False,
1246
bint indicate_mem_err = True):
1247
r"""
1248
1249
EXAMPLES::
1250
1251
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import generate_dense_graphs_edge_addition
1252
1253
::
1254
1255
sage: for n in [0..6]:
1256
... print generate_dense_graphs_edge_addition(n,1)
1257
1
1258
2
1259
6
1260
20
1261
90
1262
544
1263
5096
1264
1265
::
1266
1267
sage: for n in [0..7]:
1268
... print generate_dense_graphs_edge_addition(n,0)
1269
1
1270
1
1271
2
1272
4
1273
11
1274
34
1275
156
1276
1044
1277
sage: generate_dense_graphs_edge_addition(8,0) # long time - about 14 seconds at 2.4 GHz
1278
12346
1279
1280
"""
1281
from sage.graphs.all import Graph
1282
cdef iterator *graph_iterator
1283
cdef DenseGraph DG, ODG
1284
cdef GraphStruct GS
1285
if n < 0:
1286
return [] if construct else Integer(0)
1287
if n == 0:
1288
return [Graph(0, implementation='c_graph', sparse=False, loops=loops)] if construct else Integer(1)
1289
if n == 1:
1290
if not loops:
1291
return [Graph(1, implementation='c_graph', sparse=False, loops=loops)] if construct else Integer(1)
1292
else:
1293
if construct:
1294
G = Graph(1, implementation='c_graph', sparse=False, loops=loops)
1295
(<CGraph>G._backend._cg).add_arc_unsafe(0,0)
1296
return [G, Graph(1, implementation='c_graph', sparse=False, loops=loops)]
1297
else:
1298
return Integer(2)
1299
1300
if depth is None:
1301
depth = n*n
1302
1303
graph_iterator = allocate_dg_edge_gen(n, depth, loops)
1304
if graph_iterator is NULL:
1305
raise MemoryError
1306
1307
GS = (<GraphStruct> (<canonical_generator_data *> graph_iterator.data).object_stack[0])
1308
if G is not None:
1309
DG = GS.G
1310
for u,v in G.edges(labels=False):
1311
DG.add_arc(u,v)
1312
if u != v:
1313
DG.add_arc(v,u)
1314
1315
graph_iterator = setup_canonical_generator(n,
1316
&all_children_are_equivalent,
1317
&refine_by_degree,
1318
&compare_graphs,
1319
&gen_children_dg_edge,
1320
&apply_dg_edge_aug,
1321
&free_dg_edge,
1322
&deallocate_degd,
1323
&free_subset,
1324
&canonical_dg_edge_parent,
1325
depth, 0, graph_iterator)
1326
1327
start_canonical_generator(NULL, <void *> GS, n, graph_iterator)
1328
1329
cdef list out_list
1330
cdef void *thing
1331
cdef GraphStruct thing_gs
1332
cdef Integer number
1333
cdef bint mem_err = 0
1334
if construct:
1335
out_list = []
1336
else:
1337
number = Integer(0)
1338
if construct:
1339
while 1:
1340
thing = graph_iterator.next(graph_iterator.data, NULL, &mem_err)
1341
if thing is NULL: break
1342
ODG = (<GraphStruct>thing).G
1343
G = Graph(0, implementation='c_graph', sparse=False)
1344
DG = DenseGraph(ODG.active_vertices.size, extra_vertices=0)
1345
copy_dense_graph(DG, ODG)
1346
G._backend._cg = DG
1347
out_list.append(G)
1348
else:
1349
while 1:
1350
thing = graph_iterator.next(graph_iterator.data, NULL, &mem_err)
1351
if thing is NULL: break
1352
number += 1
1353
1354
free_dg_edge_gen(graph_iterator)
1355
if mem_err:
1356
if indicate_mem_err:
1357
raise MemoryError
1358
else:
1359
out_list.append(MemoryError())
1360
if construct:
1361
return out_list
1362
else:
1363
return number
1364
1365
1366
1367
# Dense graphs: adding vertices
1368
1369
# This implements an augmentation scheme as follows:
1370
# * Seed objects are graphs with one verticex and no edges.
1371
# * Augmentations consist of adding a single vertex connected to some subset of
1372
# the previous vertices.
1373
1374
cdef int gen_children_dg_vert(void *S, aut_gp_and_can_lab *group, iterator *it):
1375
r"""
1376
Setup an iterator over subsets to join a new vertex to.
1377
"""
1378
cdef GraphStruct GS = <GraphStruct> S
1379
cdef int n = GS.G.num_verts
1380
cdef iterator *subset_iterator = setup_set_gen(it, n, n)
1381
if subset_iterator is not NULL:
1382
start_canonical_generator(group.group, NULL, n, subset_iterator)
1383
return (subset_iterator is NULL)
1384
1385
cdef void *apply_dg_vert_aug(void *parent, void *aug, void *child, int *degree, bint *mem_err):
1386
r"""
1387
Apply the augmentation to ``parent`` storing the result in ``child``. Here
1388
``aug`` represents a subset to join to a new vertex.
1389
"""
1390
cdef GraphStruct GS_child = <GraphStruct> child, GS_par = <GraphStruct> parent
1391
cdef DenseGraph DG = <DenseGraph> GS_child.G, DG_par = <DenseGraph> GS_par.G
1392
cdef subset *set1 = <subset *> aug
1393
cdef int u, n = DG_par.num_verts
1394
1395
# copy DG_par edges to DG
1396
copy_dense_graph(DG, DG_par)
1397
DG.add_vertex_unsafe(n)
1398
1399
# add the edges
1400
u = bitset_first(&set1.bits)
1401
while u != -1:
1402
DG.add_arc_unsafe(u, n)
1403
DG.add_arc_unsafe(n, u)
1404
u = bitset_next(&set1.bits, u+1)
1405
1406
degree[0] = n+1
1407
return <void *> GS_child
1408
1409
cdef void *allocate_dg_vert(int n, int depth):
1410
r"""
1411
Allocates an object for this augmentation scheme.
1412
"""
1413
cdef GraphStruct GS
1414
cdef DenseGraph G
1415
cdef int *scratch
1416
try:
1417
GS = GraphStruct()
1418
G = DenseGraph(0, extra_vertices=depth)
1419
bitset_set_first_n(G.active_vertices, n)
1420
G.num_verts = n
1421
scratch = <int *> sage_malloc((3*depth+1) * sizeof(int))
1422
if scratch is NULL:
1423
raise MemoryError
1424
except MemoryError:
1425
return NULL
1426
Py_INCREF(GS)
1427
Py_INCREF(G)
1428
GS.G = G
1429
GS.directed = 0
1430
GS.loops = 0
1431
GS.use_indicator = 1
1432
GS.scratch = scratch
1433
return <void *> GS
1434
1435
cdef void free_dg_vert(void *child):
1436
r"""
1437
Deallocates an object for this augmentation scheme.
1438
"""
1439
cdef GraphStruct GS = <GraphStruct> child
1440
sage_free(GS.scratch)
1441
Py_DECREF(GS.G)
1442
Py_DECREF(GS)
1443
1444
cdef void *canonical_dg_vert_parent(void *child, void *parent, int *permutation, int *degree, bint *mem_err):
1445
r"""
1446
Applies ``permutation`` to ``child``, determines an arbitrary parent by
1447
deleting the lexicographically largest vertex, applies the inverse of
1448
``permutation`` to the result and stores the result in ``parent``.
1449
"""
1450
cdef GraphStruct GS_par = <GraphStruct> parent, GS = <GraphStruct> child
1451
cdef DenseGraph DG_par = <DenseGraph> GS_par.G, DG = <DenseGraph> GS.G
1452
cdef int u, v, n = DG_par.num_verts
1453
cdef int *scratch = GS.scratch
1454
1455
# copy DG edges to DG_par
1456
copy_dense_graph(DG_par, DG)
1457
1458
# remove the right vertex
1459
for u from 0 <= u <= n:
1460
scratch[permutation[u]] = u
1461
DG_par.del_vertex_unsafe(scratch[n])
1462
1463
degree[0] = n
1464
return <void *> GS_par
1465
1466
cdef iterator *allocate_dg_vert_gen(int degree, int depth):
1467
r"""
1468
Allocates the iterator for generating graphs.
1469
"""
1470
cdef iterator *dg_vert_gen = <iterator *> sage_malloc(sizeof(iterator))
1471
cdef canonical_generator_data *cgd = allocate_cgd(depth, degree), *cgd2
1472
if dg_vert_gen is NULL or cgd is NULL:
1473
sage_free(dg_vert_gen)
1474
deallocate_cgd(cgd)
1475
return NULL
1476
cdef int i, j
1477
for i from 0 <= i < depth:
1478
cgd.object_stack[i] = allocate_dg_vert(i+degree,depth+degree-1)
1479
cgd.parent_stack[i] = allocate_dg_vert(i+degree,depth+degree-1)
1480
if cgd.object_stack[i] is NULL or \
1481
cgd.parent_stack[i] is NULL:
1482
for j from 0 <= j <= i:
1483
free_dg_vert(cgd.object_stack[j])
1484
free_dg_vert(cgd.parent_stack[j])
1485
sage_free(dg_vert_gen)
1486
deallocate_cgd(cgd)
1487
return NULL
1488
for i from 0 <= i < depth-1:
1489
# TODO: in fact, should this not happen in
1490
# dg_vert_gen_children!? otherwise iterator[i].data will be NULL
1491
# and no problems.....
1492
if allocate_subset_gen_2(i+degree, i+degree, cgd.iterator_stack+i):
1493
for j from 0 <= j < depth:
1494
free_dg_vert(cgd.object_stack[j])
1495
free_dg_vert(cgd.parent_stack[j])
1496
for j from 0 <= j < i:
1497
cgd2 = <canonical_generator_data *> cgd.iterator_stack[j].data
1498
deallocate_cgd(cgd2)
1499
sage_free(dg_vert_gen)
1500
deallocate_cgd(cgd)
1501
return NULL
1502
dg_vert_gen.data = <void *> cgd
1503
dg_vert_gen.next = &canonical_generator_next
1504
return dg_vert_gen
1505
1506
cdef void free_dg_vert_gen(iterator *dg_vert_gen):
1507
r"""
1508
Deallocates the iterator for generating graphs.
1509
"""
1510
cdef canonical_generator_data *cgd = <canonical_generator_data *> dg_vert_gen.data
1511
deallocate_cgd(cgd)
1512
sage_free(dg_vert_gen)
1513
1514
cdef void free_cgd_2(void *data):
1515
r"""
1516
A simpler alternative to ``free_cgd``.
1517
"""
1518
cdef canonical_generator_data *cgd = <canonical_generator_data *> data
1519
deallocate_cgd(cgd)
1520
1521
def generate_dense_graphs_vert_addition(int n, base_G = None, bint construct = False, bint indicate_mem_err = True):
1522
r"""
1523
1524
EXAMPLES::
1525
1526
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import generate_dense_graphs_vert_addition
1527
1528
::
1529
1530
sage: for n in [0..7]:
1531
... generate_dense_graphs_vert_addition(n)
1532
1
1533
2
1534
4
1535
8
1536
19
1537
53
1538
209
1539
1253
1540
sage: generate_dense_graphs_vert_addition(8) # long time
1541
13599
1542
1543
TEST::
1544
1545
sage: from sage.groups.perm_gps.partn_ref.refinement_graphs import generate_dense_graphs_vert_addition
1546
sage: generate_dense_graphs_vert_addition(10, base_G=Graph('HEhf^rs'))
1547
11
1548
1549
"""
1550
from sage.graphs.all import Graph
1551
cdef iterator *graph_iterator
1552
cdef DenseGraph DG, ODG
1553
cdef GraphStruct GS
1554
if n < 2:
1555
if construct:
1556
L = []
1557
if n < 0:
1558
return L
1559
L.append(Graph(0, implementation='c_graph', sparse=False))
1560
if n == 0:
1561
return L
1562
L.append(Graph(0, implementation='c_graph', sparse=False))
1563
L.reverse()
1564
return L
1565
else:
1566
if n < 0:
1567
return Integer(0)
1568
if n == 0:
1569
return Integer(1)
1570
return Integer(2)
1571
1572
cdef int start_deg = 1 if base_G is None else base_G.num_verts()
1573
graph_iterator = allocate_dg_vert_gen(start_deg, n+1-start_deg)
1574
if graph_iterator is NULL:
1575
raise MemoryError
1576
1577
GS = (<GraphStruct> (<canonical_generator_data *> graph_iterator.data).object_stack[0])
1578
DG = GS.G
1579
if base_G is not None:
1580
for v in base_G.vertices():
1581
DG.add_vertex(v)
1582
for u,v in base_G.edges(labels=False):
1583
DG.add_arc(u,v)
1584
DG.add_arc(v,u)
1585
1586
graph_iterator = setup_canonical_generator(start_deg,
1587
&all_children_are_equivalent,
1588
&refine_by_degree,
1589
&compare_graphs,
1590
&gen_children_dg_vert,
1591
&apply_dg_vert_aug,
1592
&free_dg_vert,
1593
&free_cgd_2,
1594
free_subset,
1595
&canonical_dg_vert_parent,
1596
n+1-start_deg, 0, graph_iterator)
1597
1598
start_canonical_generator(NULL, <void *> GS, DG.num_verts, graph_iterator)
1599
1600
cdef list out_list
1601
cdef void *thing
1602
cdef GraphStruct thing_gs
1603
cdef Integer number
1604
cdef bint mem_err = 0
1605
if construct:
1606
out_list = []
1607
else:
1608
number = Integer(0)
1609
if construct:
1610
while 1:
1611
thing = graph_iterator.next(graph_iterator.data, NULL, &mem_err)
1612
if thing is NULL: break
1613
ODG = (<GraphStruct>thing).G
1614
G = Graph(0, implementation='c_graph', sparse=False)
1615
DG = DenseGraph(ODG.active_vertices.size, extra_vertices=0)
1616
copy_dense_graph(DG, ODG)
1617
G._backend._cg = DG
1618
out_list.append(G)
1619
else:
1620
while 1:
1621
thing = graph_iterator.next(graph_iterator.data, NULL, &mem_err)
1622
if thing is NULL: break
1623
number += 1
1624
1625
free_dg_vert_gen(graph_iterator)
1626
if mem_err:
1627
if indicate_mem_err:
1628
raise MemoryError
1629
else:
1630
out_list.append(MemoryError())
1631
if construct:
1632
if base_G is None:
1633
out_list = [Graph(0, implementation='c_graph', sparse=False)] + out_list
1634
return out_list
1635
else:
1636
if base_G is None:
1637
number += 1
1638
return number
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653