Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/groups/perm_gps/partn_ref/refinement_python.pyx
4057 views
1
"""
2
Python interface to partition backtrack functions
3
4
DOCTEST:
5
sage: import sage.groups.perm_gps.partn_ref.refinement_python
6
7
This module provides Python frontends to the Cython-based partition backtrack
8
functions. This allows one to write the three input functions
9
(all_children_are_equivalent, refine_and_return_invariant, and compare_structures)
10
in pure Python, and still use the Cython algorithms. Experimentation with
11
specific partition backtrack implementations no longer requires compilation, as
12
the input functions can be dynamically changed at runtime.
13
14
NOTE:
15
16
This is not intended for production quality implementations of partition
17
refinement, but instead for experimentation, learning, and use of the Python
18
debugger.
19
20
"""
21
22
#*****************************************************************************
23
# Copyright (C) 2006 - 2011 Robert L. Miller <[email protected]>
24
#
25
# Distributed under the terms of the GNU General Public License (GPL)
26
# http://www.gnu.org/licenses/
27
#*****************************************************************************
28
29
include 'data_structures_pyx.pxi' # includes bitsets
30
31
cdef class PythonPartitionStack:
32
"""
33
Instances of this class wrap a (Cython) PartitionStack.
34
"""
35
36
def __init__(self, int n):
37
"""
38
Initialize a PartitionStack.
39
40
EXAMPLE::
41
42
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack
43
sage: P = PythonPartitionStack(7) # implicit doctest
44
45
"""
46
self.c_ps = PS_new(n, 1)
47
48
def __dealloc__(self):
49
"""
50
Deallocate the PartitionStack.
51
52
EXAMPLE::
53
54
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack
55
sage: P = PythonPartitionStack(7)
56
sage: del(P) # implicit doctest
57
58
"""
59
PS_dealloc(self.c_ps)
60
61
def __repr__(self):
62
"""
63
Returns a string representing the stack.
64
65
EXAMPLE::
66
67
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack
68
sage: P = PythonPartitionStack(7)
69
sage: P # implicit doctest
70
PythonPartitionStack of degree 7 and depth 0.
71
72
"""
73
return "PythonPartitionStack of degree %d and depth %d."%(self.c_ps.degree, self.c_ps.depth)
74
75
def display(self):
76
"""
77
Prints a representation of the stack.
78
79
EXAMPLE::
80
81
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack
82
sage: P = PythonPartitionStack(7)
83
sage: P.depth(1)
84
1
85
sage: P.set_level(2,1)
86
sage: P.display()
87
(0 1 2 3 4 5 6)
88
(0 1 2|3 4 5 6)
89
90
"""
91
PS_print(self.c_ps)
92
93
def is_discrete(self):
94
"""
95
Returns whether the deepest partition consists only of singleton cells.
96
97
EXAMPLE::
98
99
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack
100
sage: P = PythonPartitionStack(7)
101
sage: P.is_discrete()
102
False
103
sage: [P.set_level(i,0) for i in xrange(7)]
104
[None, None, None, None, None, None, None]
105
sage: P.is_discrete()
106
True
107
108
"""
109
return PS_is_discrete(self.c_ps)
110
111
def num_cells(self):
112
"""
113
Returns the number of cells in the deepest partition.
114
115
EXAMPLE::
116
117
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack
118
sage: P = PythonPartitionStack(7)
119
sage: P.num_cells()
120
1
121
122
"""
123
return PS_num_cells(self.c_ps)
124
125
def move_min_to_front(self, int start, int end):
126
"""
127
Makes sure that the first element of the segment of entries i with
128
start <= i <= end is minimal.
129
130
EXAMPLE::
131
132
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack
133
sage: P = PythonPartitionStack(7)
134
sage: P.set_entry(1,0)
135
sage: P.set_entry(0,1)
136
sage: P.display()
137
(1 0 2 3 4 5 6)
138
sage: P.move_min_to_front(0,1)
139
sage: P.display()
140
(0 1 2 3 4 5 6)
141
142
"""
143
PS_move_min_to_front(self.c_ps, start, end)
144
145
def __copy__(self):
146
"""
147
148
EXAMPLE::
149
150
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack
151
sage: P = PythonPartitionStack(7)
152
sage: Q = copy(P)
153
sage: P.display()
154
(0 1 2 3 4 5 6)
155
sage: Q.display()
156
(0 1 2 3 4 5 6)
157
158
"""
159
cdef PythonPartitionStack cpy
160
cpy = PythonPartitionStack(self.c_ps.degree)
161
PS_copy_from_to(self.c_ps, cpy.c_ps)
162
return cpy
163
164
def clear(self):
165
"""
166
Sets the current partition to the first shallower one, i.e. forgets about
167
boundaries between cells that are new to the current level.
168
169
EXAMPLE::
170
171
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack
172
sage: P = PythonPartitionStack(7)
173
sage: P.depth(1)
174
1
175
sage: P.set_level(2,1)
176
sage: P.display()
177
(0 1 2 3 4 5 6)
178
(0 1 2|3 4 5 6)
179
sage: P.clear()
180
sage: P.display()
181
(0 1 2 3 4 5 6)
182
(0 1 2 3 4 5 6)
183
184
"""
185
PS_clear(self.c_ps)
186
187
def entries(self):
188
"""
189
Returns the entries array as a Python list of ints.
190
191
EXAMPLE::
192
193
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack
194
sage: P = PythonPartitionStack(7)
195
sage: P.entries()
196
[0, 1, 2, 3, 4, 5, 6]
197
sage: P.levels()
198
[7, 7, 7, 7, 7, 7, -1]
199
200
"""
201
cdef int i
202
return [self.c_ps.entries[i] for i from 0 <= i < self.c_ps.degree]
203
204
def set_entry(self, int i, int entry):
205
"""
206
Sets the ith entry of the entries array to entry.
207
208
EXAMPLE::
209
210
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack
211
sage: P = PythonPartitionStack(7)
212
sage: P.set_entry(1,0)
213
sage: P.set_entry(0,1)
214
sage: P.display()
215
(1 0 2 3 4 5 6)
216
217
"""
218
self.c_ps.entries[i] = entry
219
220
def get_entry(self, int i):
221
"""
222
Gets the ith entry of the entries array.
223
224
EXAMPLE::
225
226
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack
227
sage: P = PythonPartitionStack(7)
228
sage: P.get_entry(0)
229
0
230
231
"""
232
return self.c_ps.entries[i]
233
234
def levels(self):
235
"""
236
Return the levels array as a Python list of ints.
237
238
EXAMPLE::
239
240
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack
241
sage: P = PythonPartitionStack(7)
242
sage: P.entries()
243
[0, 1, 2, 3, 4, 5, 6]
244
sage: P.levels()
245
[7, 7, 7, 7, 7, 7, -1]
246
247
"""
248
return [self.c_ps.levels[i] for i from 0 <= i < self.c_ps.degree]
249
250
def set_level(self, int i, int level):
251
"""
252
Sets the ith entry of the levels array to entry.
253
254
EXAMPLE::
255
256
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack
257
sage: P = PythonPartitionStack(7)
258
sage: P.depth(1)
259
1
260
sage: P.set_level(2,1)
261
sage: P.display()
262
(0 1 2 3 4 5 6)
263
(0 1 2|3 4 5 6)
264
265
"""
266
self.c_ps.levels[i] = level
267
268
def get_level(self, int i):
269
"""
270
Gets the ith entry of the levels array.
271
272
EXAMPLE::
273
274
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack
275
sage: P = PythonPartitionStack(7)
276
sage: P.get_level(0)
277
7
278
279
"""
280
return self.c_ps.levels[i]
281
282
def depth(self, new=None):
283
"""
284
Returns the depth of the deepest partition in the stack, setting it to
285
new if new is not None.
286
287
EXAMPLE::
288
289
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack
290
sage: P = PythonPartitionStack(7)
291
sage: P.depth()
292
0
293
294
"""
295
if new is not None:
296
self.c_ps.depth = new
297
return self.c_ps.depth
298
299
def degree(self, new=None):
300
"""
301
Returns the degree of the partition stack, setting it to
302
new if new is not None.
303
304
EXAMPLE::
305
306
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack
307
sage: P = PythonPartitionStack(7)
308
sage: P.degree()
309
7
310
311
"""
312
if new is not None:
313
self.c_ps.degree = new
314
return self.c_ps.degree
315
316
def partition(self, int k):
317
"""
318
Return the partition at level k, as a Python list of lists.
319
320
EXAMPLE::
321
322
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack
323
sage: P = PythonPartitionStack(7)
324
sage: P.depth(1)
325
1
326
sage: P.set_level(2,1)
327
sage: P.partition(0)
328
[[0, 1, 2, 3, 4, 5, 6]]
329
sage: P.partition(1)
330
[[0, 1, 2], [3, 4, 5, 6]]
331
332
"""
333
cdef int i
334
cdef list partition = [], cell = []
335
for i from 0 <= i < self.c_ps.degree:
336
cell.append(self.c_ps.entries[i])
337
if self.c_ps.levels[i] <= k:
338
partition.append(cell)
339
if i < self.c_ps.degree:
340
cell = []
341
return partition
342
343
class PythonObjectWrapper:
344
"""
345
Instances of this class wrap a Python object and the refinement functions.
346
"""
347
def __init__(self, obj, acae_fn, rari_fn, cs_fn, int degree):
348
"""
349
Initialize a PythonObjectWrapper.
350
351
EXAMPLE::
352
353
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonObjectWrapper
354
sage: def acae(a,b):
355
... return 0
356
sage: def rari(a,b,c):
357
... return 0
358
sage: def cs(a,b,c,d):
359
... return 0
360
sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonObjectWrapper
361
sage: P = PythonObjectWrapper(None, acae, rari, cs, 7) # implicit doctest
362
sage: P.obj
363
sage: P.degree
364
7
365
sage: P.acae_fn
366
<function acae at ...>
367
sage: P.rari_fn
368
<function rari at ...>
369
sage: P.cs_fn
370
<function cs at ...>
371
372
"""
373
self.degree = degree
374
self.obj = obj
375
self.acae_fn = acae_fn
376
self.rari_fn = rari_fn
377
self.cs_fn = cs_fn
378
379
cdef bint all_children_are_equivalent_python(PartitionStack *PS, void *S):
380
"""
381
Python conversion of all_children_are_equivalent function.
382
"""
383
cdef PythonPartitionStack Py_PS = PythonPartitionStack(PS.degree)
384
cdef object S_obj = <object> S
385
PS_copy_from_to(PS, Py_PS.c_ps)
386
return S_obj.acae_fn(Py_PS, S_obj.obj)
387
388
cdef int refine_and_return_invariant_python(PartitionStack *PS, void *S, int *cells_to_refine_by, int ctrb_len):
389
"""
390
Python conversion of refine_and_return_invariant function.
391
"""
392
cdef PythonPartitionStack Py_PS = PythonPartitionStack(PS.degree)
393
cdef object S_obj = <object> S
394
PS_copy_from_to(PS, Py_PS.c_ps)
395
cdef int i
396
cdef list ctrb_py = [cells_to_refine_by[i] for i from 0 <= i < ctrb_len]
397
return S_obj.rari_fn(Py_PS, S_obj.obj, ctrb_py)
398
399
cdef int compare_structures_python(int *gamma_1, int *gamma_2, void *S1, void *S2):
400
"""
401
Python conversion of compare_structures function.
402
"""
403
cdef int i
404
cdef object S1_obj = <object> S1, S2_obj = <object> S2
405
cdef list gamma_1_py = [gamma_1[i] for i from 0 <= i < S1_obj.degree]
406
cdef list gamma_2_py = [gamma_2[i] for i from 0 <= i < S1_obj.degree]
407
return S1_obj.cs_fn(gamma_1_py, gamma_2_py, S1_obj.obj, S2_obj.obj)
408
409
def aut_gp_and_can_lab_python(S, partition, n,
410
all_children_are_equivalent,
411
refine_and_return_invariant,
412
compare_structures,
413
canonical_label, base, order):
414
"""
415
Calls the automorphism group and canonical label function.
416
417
INPUT::
418
419
S -- the object to examine
420
partition -- an ordered partition, as a list of lists
421
n -- the degree of the automorphism group to be computed
422
423
::
424
425
all_children_are_equivalent -- Python function of "signature":
426
bool all_children_are_equivalent(PythonPartitionStack, object)
427
refine_and_return_invariant -- Python function of "signature":
428
int refine_and_return_invariant(PythonPartitionStack, object, list)
429
compare_structures -- Python function of "signature":
430
int compare_structures(list, list, object, object)
431
(see automorphism_group_canonical_label.pyx for more documentation)
432
433
::
434
435
canonical_label -- boolean; whether to search for a canonical label
436
base -- boolean; whether to return a base for the automorphism group
437
order -- boolean; whether to return the order of the automorphism group
438
439
EXAMPLE::
440
441
sage: from sage.groups.perm_gps.partn_ref.refinement_python import aut_gp_and_can_lab_python
442
sage: def acae(a,b):
443
... return 0
444
sage: def rari(a,b,c):
445
... return 0
446
sage: def cs(a,b,c,d):
447
... return 0
448
sage: aut_gp_and_can_lab_python(None, [[0,1,2,3],[4,5]], 6, acae, rari, cs, True, True, True)
449
([[0, 1, 3, 2, 4, 5],
450
[0, 2, 1, 3, 4, 5],
451
[1, 0, 2, 3, 4, 5],
452
[0, 1, 2, 3, 5, 4]],
453
[0, 1, 2, 3, 4, 5],
454
[4, 0, 1, 2],
455
48)
456
sage: factorial(4)*factorial(2)
457
48
458
459
"""
460
obj_wrapper = PythonObjectWrapper(S, all_children_are_equivalent, refine_and_return_invariant, compare_structures, n)
461
cdef aut_gp_and_can_lab *output
462
cdef PythonPartitionStack Py_PS = PythonPartitionStack(n)
463
cdef int i, j
464
cdef Integer I
465
466
cdef PartitionStack *part = PS_from_list(partition)
467
if part is NULL:
468
raise MemoryError
469
470
output = get_aut_gp_and_can_lab(<void *> obj_wrapper, part, n,
471
&all_children_are_equivalent_python,
472
&refine_and_return_invariant_python,
473
&compare_structures_python,
474
canonical_label, NULL)
475
476
list_of_gens = []
477
for i from 0 <= i < output.num_gens:
478
list_of_gens.append([output.generators[j+i*n] for j from 0 <= j < n])
479
return_tuple = [list_of_gens]
480
if canonical_label:
481
return_tuple.append([output.relabeling[i] for i from 0 <= i < n])
482
if base:
483
return_tuple.append([output.group.base_orbits[i][0] for i from 0 <= i < output.group.base_size])
484
if order:
485
I = Integer()
486
SC_order(output.group, 0, I.value)
487
return_tuple.append(I)
488
PS_dealloc(part)
489
sage_free(output.generators)
490
SC_dealloc(output.group)
491
if canonical_label:
492
sage_free(output.relabeling)
493
sage_free(output)
494
if len(return_tuple) == 1:
495
return return_tuple[0]
496
else:
497
return tuple(return_tuple)
498
499
500
def double_coset_python(S1, S2, partition1, ordering2, n,
501
all_children_are_equivalent,
502
refine_and_return_invariant,
503
compare_structures):
504
"""
505
Calls the double coset function.
506
507
INPUT::
508
509
S1, S2 -- the objects to examine
510
partition1 -- an ordered partition, as a list of lists
511
ordering2 -- represents a partition of the points of S2,
512
as a relabeling of partition1
513
n -- the degree
514
515
::
516
517
all_children_are_equivalent -- Python function of "signature":
518
bool all_children_are_equivalent(PythonPartitionStack, object)
519
refine_and_return_invariant -- Python function of "signature":
520
int refine_and_return_invariant(PythonPartitionStack, object, list)
521
compare_structures -- Python function of "signature":
522
int compare_structures(list, list, object, object)
523
(see double_coset.pyx for more documentation)
524
525
EXAMPLE::
526
527
sage: from sage.groups.perm_gps.partn_ref.refinement_python import double_coset_python
528
sage: def acae(a,b):
529
... return 0
530
sage: def rari(a,b,c):
531
... return 0
532
sage: def cs(a,b,c,d):
533
... return 0
534
sage: double_coset_python(None, None, [[0,1,2,3],[4,5]], [2,3,1,5,0,4], 6, acae, rari, cs)
535
[1, 2, 3, 5, 0, 4]
536
537
sage: def compare_lists(p1,p2,l1,l2):
538
... for i in xrange(len(l1)):
539
... j = cmp(l1[p1[i]], l2[p2[i]])
540
... if j != 0: return j
541
... return 0
542
543
sage: double_coset_python([0,0,1], [1,0,0], [[0,1,2]], [0,1,2], 3, acae, rari, compare_lists)
544
[1, 2, 0]
545
546
"""
547
obj_wrapper1 = PythonObjectWrapper(S1, all_children_are_equivalent, refine_and_return_invariant, compare_structures, n)
548
obj_wrapper2 = PythonObjectWrapper(S2, all_children_are_equivalent, refine_and_return_invariant, compare_structures, n)
549
550
cdef PartitionStack *part = PS_from_list(partition1)
551
cdef int *ordering = <int *> sage_malloc(n * sizeof(int))
552
if part is NULL or ordering is NULL:
553
if part is not NULL: PS_dealloc(part)
554
if ordering is not NULL: sage_free(ordering)
555
raise MemoryError
556
for i from 0 <= i < n:
557
ordering[i] = ordering2[i]
558
559
cdef int *output = double_coset(<void *> obj_wrapper1, <void *> obj_wrapper2,
560
part, ordering, n,
561
&all_children_are_equivalent_python,
562
&refine_and_return_invariant_python,
563
&compare_structures_python, NULL)
564
565
PS_dealloc(part)
566
sage_free(ordering)
567
568
if output is NULL:
569
return False
570
else:
571
output_py = [output[i] for i from 0 <= i < n]
572
sage_free(output)
573
return output_py
574
575
576
577