Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/groups/perm_gps/partn_ref/double_coset.pyx
8815 views
1
r"""
2
Double cosets
3
4
This module implements a general algorithm for computing double coset problems
5
for pairs of objects. The class of objects in question must be some kind
6
of structure for which an isomorphism is a permutation in $S_n$ for some $n$,
7
which we call here the order of the object. Given objects $X$ and $Y$,
8
the program returns an isomorphism in list permutation form if $X \cong Y$, and
9
a NULL pointer otherwise.
10
11
In order to take advantage of the algorithms in this module for a specific kind
12
of object, one must implement (in Cython) three functions which will be specific
13
to the kind of objects in question. Pointers to these functions are passed to
14
the main function of the module, which is \code{double_coset}. For specific
15
examples of implementations of these functions, see any of the files in
16
\code{sage.groups.perm_gps.partn_ref} beginning with "refinement." They are:
17
18
A. \code{refine_and_return_invariant}:
19
20
Signature:
21
22
\code{int refine_and_return_invariant(PartitionStack *PS, void *S, int *cells_to_refine_by, int ctrb_len)}
23
24
This function should split up cells in the partition at the top of the
25
partition stack in such a way that any automorphism that respects the
26
partition also respects the resulting partition. The array
27
cells_to_refine_by is a list of the beginning positions of some cells which
28
have been changed since the last refinement. It is not necessary to use
29
this in an implementation of this function, but it will affect performance.
30
One should consult \code{refinement_graphs} for more details and ideas for
31
particular implementations.
32
33
Output:
34
35
An integer $I$ invariant under the orbits of $S_n$. That is, if
36
$\gamma \in S_n$, then
37
$$ I(G, PS, cells_to_refine_by) = I( \gamma(G), \gamma(PS), \gamma(cells_to_refine_by) ) .$$
38
39
40
B. \code{compare_structures}:
41
42
Signature:
43
44
\code{int compare_structures(int *gamma_1, int *gamma_2, void *S1, void *S2, int degree)}
45
46
This function must implement a total ordering on the set of objects of fixed
47
order. Return:
48
-1 if \code{gamma_1^{-1}(S1) < gamma_2^{-1}(S2)},
49
0 if \code{gamma_1^{-1}(S1) == gamma_2^{-1}(S2)},
50
1 if \code{gamma_1^{-1}(S1) > gamma_2^{-1}(S2)}.
51
52
Important note:
53
54
The permutations are thought of as being input in inverse form, and this can
55
lead to subtle bugs. One is encouraged to consult existing implementations
56
to make sure the right thing is being done: this is so that you can avoid
57
*actually* needing to compute the inverse.
58
59
C. \code{all_children_are_equivalent}:
60
61
Signature:
62
63
\code{bint all_children_are_equivalent(PartitionStack *PS, void *S)}
64
65
This function must return False unless it is the case that each discrete
66
partition finer than the top of the partition stack is equivalent to the
67
others under some automorphism of S. The converse need not hold: if this is
68
indeed the case, it still may return False. This function is originally used
69
as a consequence of Lemma 2.25 in [1].
70
71
DOCTEST:
72
sage: import sage.groups.perm_gps.partn_ref.double_coset
73
74
REFERENCE:
75
76
[1] McKay, Brendan D. Practical Graph Isomorphism. Congressus Numerantium,
77
Vol. 30 (1981), pp. 45-87.
78
79
[2] Leon, Jeffrey. Permutation Group Algorithms Based on Partitions, I:
80
Theory and Algorithms. J. Symbolic Computation, Vol. 12 (1991), pp.
81
533-583.
82
83
"""
84
85
#*****************************************************************************
86
# Copyright (C) 2006 - 2011 Robert L. Miller <[email protected]>
87
#
88
# Distributed under the terms of the GNU General Public License (GPL)
89
# http://www.gnu.org/licenses/
90
#*****************************************************************************
91
92
include 'data_structures_pyx.pxi' # includes bitsets
93
94
cdef inline int cmp(int a, int b):
95
if a < b: return -1
96
elif a == b: return 0
97
else: return 1
98
99
# Functions
100
101
cdef bint all_children_are_equivalent_trivial(PartitionStack *PS, void *S):
102
return 0
103
104
cdef int refine_and_return_invariant_trivial(PartitionStack *PS, void *S, int *cells_to_refine_by, int ctrb_len):
105
return 0
106
107
cdef int compare_perms(int *gamma_1, int *gamma_2, void *S1, void *S2, int degree):
108
cdef list MS1 = <list> S1
109
cdef list MS2 = <list> S2
110
cdef int i, j
111
for i from 0 <= i < degree:
112
j = cmp(MS1[gamma_1[i]], MS2[gamma_2[i]])
113
if j != 0: return j
114
return 0
115
116
def coset_eq(list perm1=[0,1,2,3,4,5], list perm2=[1,2,3,4,5,0], list gens=[[1,2,3,4,5,0]]):
117
"""
118
Given a group G generated by the given generators, tests whether the given
119
permutations are in the same right coset of G. Tests nontrivial input group
120
when using double_coset. If they are, return an element g so that
121
g.perm1 = perm2 (composing left to right).
122
123
TESTS::
124
125
sage: from sage.groups.perm_gps.partn_ref.double_coset import coset_eq
126
sage: coset_eq()
127
[5, 0, 1, 2, 3, 4]
128
sage: gens = [[1,2,3,0]]
129
sage: reps = [[0,1,2,3]]
130
sage: for p in SymmetricGroup(4):
131
... p = [p(i)-1 for i in range(1,5)]
132
... found = False
133
... for r in reps:
134
... if coset_eq(p, r, gens):
135
... found = True
136
... break
137
... if not found:
138
... reps.append(p)
139
sage: len(reps)
140
6
141
sage: gens = [[1,0,2,3],[0,1,3,2]]
142
sage: reps = [[0,1,2,3]]
143
sage: for p in SymmetricGroup(4):
144
... p = [p(i)-1 for i in range(1,5)]
145
... found = False
146
... for r in reps:
147
... if coset_eq(p, r, gens):
148
... found = True
149
... break
150
... if not found:
151
... reps.append(p)
152
sage: len(reps)
153
6
154
sage: gens = [[1,2,0,3]]
155
sage: reps = [[0,1,2,3]]
156
sage: for p in SymmetricGroup(4):
157
... p = [p(i)-1 for i in range(1,5)]
158
... found = False
159
... for r in reps:
160
... if coset_eq(p, r, gens):
161
... found = True
162
... break
163
... if not found:
164
... reps.append(p)
165
sage: len(reps)
166
8
167
168
"""
169
cdef int i, n = len(perm1)
170
assert all(len(g) == n for g in gens+[perm2])
171
cdef PartitionStack *part = PS_new(n, 1)
172
cdef int *c_perm = <int *> sage_malloc(n * sizeof(int))
173
cdef StabilizerChain *group = SC_new(n, 1)
174
cdef int *isomorphism = <int *> sage_malloc(n * sizeof(int))
175
if part is NULL or c_perm is NULL or group is NULL or isomorphism is NULL:
176
sage_free(c_perm)
177
PS_dealloc(part)
178
SC_dealloc(group)
179
sage_free(isomorphism)
180
raise MemoryError
181
for g in gens:
182
for i from 0 <= i < n:
183
c_perm[i] = g[i]
184
SC_insert(group, 0, c_perm, 1)
185
for i from 0 <= i < n:
186
c_perm[i] = i
187
cdef bint isomorphic = double_coset(<void *> perm1, <void *> perm2, part, c_perm, n, &all_children_are_equivalent_trivial, &refine_and_return_invariant_trivial, &compare_perms, group, NULL, isomorphism)
188
sage_free(c_perm)
189
PS_dealloc(part)
190
SC_dealloc(group)
191
if isomorphic:
192
x = [isomorphism[i] for i from 0 <= i < n]
193
else:
194
x = False
195
sage_free(isomorphism)
196
return x
197
198
cdef dc_work_space *allocate_dc_work_space(int n):
199
r"""
200
Allocates work space for the double_coset function. It can be
201
input to the function in which case it must be deallocated after the
202
function is called.
203
"""
204
cdef int *int_array
205
206
cdef dc_work_space *work_space
207
work_space = <dc_work_space *> sage_malloc(sizeof(dc_work_space))
208
if work_space is NULL:
209
return NULL
210
211
work_space.degree = n
212
int_array = <int *> sage_malloc((n*n + # for perm_stack
213
5*n # for int_array
214
)*sizeof(int))
215
work_space.group1 = SC_new(n)
216
work_space.group2 = SC_new(n)
217
work_space.current_ps = PS_new(n,0)
218
work_space.first_ps = PS_new(n,0)
219
work_space.bitset_array = <bitset_t *> sage_calloc((n + 2*len_of_fp_and_mcr + 1), sizeof(bitset_t))
220
work_space.orbits_of_subgroup = OP_new(n)
221
work_space.perm_stack = NULL
222
223
if int_array is NULL or \
224
work_space.group1 is NULL or \
225
work_space.group2 is NULL or \
226
work_space.current_ps is NULL or \
227
work_space.first_ps is NULL or \
228
work_space.bitset_array is NULL or \
229
work_space.orbits_of_subgroup is NULL:
230
sage_free(int_array)
231
deallocate_dc_work_space(work_space)
232
return NULL
233
234
work_space.perm_stack = int_array
235
work_space.int_array = int_array + n*n
236
237
cdef int i
238
for i from 0 <= i < n + 2*len_of_fp_and_mcr + 1:
239
work_space.bitset_array[i].bits = NULL
240
try:
241
for i from 0 <= i < n + 2*len_of_fp_and_mcr + 1:
242
bitset_init(work_space.bitset_array[i], n)
243
except MemoryError:
244
deallocate_dc_work_space(work_space)
245
return NULL
246
return work_space
247
248
cdef void deallocate_dc_work_space(dc_work_space *work_space):
249
r"""
250
Deallocates work space for the double_coset function.
251
"""
252
if work_space is NULL:
253
return
254
cdef int i, n = work_space.degree
255
if work_space.bitset_array is not NULL:
256
for i from 0 <= i < n + 2*len_of_fp_and_mcr + 1:
257
bitset_free(work_space.bitset_array[i])
258
sage_free(work_space.perm_stack)
259
SC_dealloc(work_space.group1)
260
SC_dealloc(work_space.group2)
261
PS_dealloc(work_space.current_ps)
262
PS_dealloc(work_space.first_ps)
263
sage_free(work_space.bitset_array)
264
OP_dealloc(work_space.orbits_of_subgroup)
265
sage_free(work_space)
266
267
cdef int double_coset(void *S1, void *S2, PartitionStack *partition1, int *ordering2,
268
int n, bint (*all_children_are_equivalent)(PartitionStack *PS, void *S),
269
int (*refine_and_return_invariant)\
270
(PartitionStack *PS, void *S, int *cells_to_refine_by, int ctrb_len),
271
int (*compare_structures)(int *gamma_1, int *gamma_2, void *S1, void *S2, int degree),
272
StabilizerChain *input_group,
273
dc_work_space *work_space_prealloc, int *isom) except -1:
274
"""
275
Traverse the search space for double coset calculation.
276
277
INPUT:
278
S1, S2 -- pointers to the structures
279
partition1 -- PartitionStack of depth 0 and degree n,
280
whose first partition is of the points of S1
281
ordering2 -- an ordering of the points of S2 representing a second partition
282
n -- the number of points (points are assumed to be 0,1,...,n-1)
283
all_children_are_equivalent -- pointer to a function
284
INPUT:
285
PS -- pointer to a partition stack
286
S -- pointer to the structure
287
OUTPUT:
288
bint -- returns True if it can be determined that all refinements below
289
the current one will result in an equivalent discrete partition
290
refine_and_return_invariant -- pointer to a function
291
INPUT:
292
PS -- pointer to a partition stack
293
S -- pointer to the structure
294
alpha -- an array consisting of numbers, which indicate the starting
295
positions of the cells to refine against (will likely be modified)
296
OUTPUT:
297
int -- returns an invariant under application of arbitrary permutations
298
compare_structures -- pointer to a function
299
INPUT:
300
gamma_1, gamma_2 -- (list) permutations of the points of S1 and S2
301
S1, S2 -- pointers to the structures
302
degree -- degree of gamma_1 and 2
303
OUTPUT:
304
int -- 0 if gamma_1(S1) = gamma_2(S2), otherwise -1 or 1 (see docs for cmp),
305
such that the set of all structures is well-ordered
306
input_group -- either a specified group to limit the search to,
307
or NULL for the full symmetric group
308
isom -- space to store the isomorphism to,
309
or NULL if isomorphism is not needed
310
311
NOTE:
312
The partition ``partition1`` and the resulting partition from ``ordering2``
313
*must* satisfy the property that in each cell, the smallest element occurs
314
first!
315
316
OUTPUT:
317
1 if S1 and S2 are isomorphic, otherwise 0.
318
319
"""
320
cdef PartitionStack *current_ps, *first_ps, *left_ps
321
cdef int first_meets_current = -1
322
cdef int current_kids_are_same = 1
323
cdef int first_kids_are_same
324
325
cdef int *indicators
326
327
cdef OrbitPartition *orbits_of_subgroup, *orbits_of_supergroup
328
cdef int subgroup_primary_orbit_size = 0
329
cdef int minimal_in_primary_orbit
330
331
cdef bitset_t *fixed_points_of_generators # i.e. fp
332
cdef bitset_t *minimal_cell_reps_of_generators # i.e. mcr
333
cdef int len_of_fp_and_mcr = 100
334
cdef int index_in_fp_and_mcr = -1
335
336
cdef bitset_t *vertices_to_split
337
cdef bitset_t *vertices_have_been_reduced
338
cdef int *permutation, *id_perm, *cells_to_refine_by
339
cdef int *vertices_determining_current_stack
340
cdef int *perm_stack
341
cdef StabilizerChain *group, *old_group, *tmp_gp
342
343
cdef int i, j, k, ell, b
344
cdef bint discrete, automorphism, update_label
345
cdef bint backtrack, new_vertex, narrow, mem_err = 0
346
347
if n == 0:
348
return 0
349
350
if work_space_prealloc is not NULL:
351
work_space = work_space_prealloc
352
else:
353
work_space = allocate_dc_work_space(n)
354
if work_space is NULL:
355
raise MemoryError
356
357
# Allocate:
358
if input_group is not NULL:
359
perm_stack = work_space.perm_stack
360
group = work_space.group1
361
old_group = work_space.group2
362
orbits_of_supergroup = input_group.OP_scratch
363
SC_copy_nomalloc(group, input_group, n)
364
SC_identify(perm_stack, n)
365
366
current_ps = work_space.current_ps
367
first_ps = work_space.first_ps
368
orbits_of_subgroup = work_space.orbits_of_subgroup
369
370
indicators = work_space.int_array
371
permutation = work_space.int_array + n
372
id_perm = work_space.int_array + 2*n
373
cells_to_refine_by = work_space.int_array + 3*n
374
vertices_determining_current_stack = work_space.int_array + 4*n
375
376
fixed_points_of_generators = work_space.bitset_array
377
minimal_cell_reps_of_generators = work_space.bitset_array + len_of_fp_and_mcr
378
vertices_to_split = work_space.bitset_array + 2*len_of_fp_and_mcr
379
vertices_have_been_reduced = work_space.bitset_array + 2*len_of_fp_and_mcr + n
380
381
if work_space_prealloc is not NULL:
382
OP_clear(orbits_of_subgroup)
383
384
bitset_zero(vertices_have_been_reduced[0])
385
left_ps = partition1
386
387
cdef bint possible = 1
388
cdef bint unknown = 1
389
390
# set up the identity permutation
391
for i from 0 <= i < n:
392
id_perm[i] = i
393
if ordering2 is NULL:
394
ordering2 = id_perm
395
396
# Copy reordering of left_ps coming from ordering2 to current_ps.
397
memcpy(current_ps.entries, ordering2, n*sizeof(int))
398
memcpy(current_ps.levels, left_ps.levels, n*sizeof(int))
399
current_ps.depth = left_ps.depth
400
401
# default values of "infinity"
402
for i from 0 <= i < n:
403
indicators[i] = -1
404
405
# Our first refinement needs to check every cell of the partition,
406
# so cells_to_refine_by needs to be a list of pointers to each cell.
407
j = 1
408
cells_to_refine_by[0] = 0
409
for i from 0 < i < n:
410
if left_ps.levels[i-1] == 0:
411
cells_to_refine_by[j] = i
412
j += 1
413
if input_group is NULL:
414
k = refine_and_return_invariant(left_ps, S1, cells_to_refine_by, j)
415
else:
416
k = refine_also_by_orbits(left_ps, S1, refine_and_return_invariant,
417
cells_to_refine_by, j, group, perm_stack)
418
j = 1
419
cells_to_refine_by[0] = 0
420
for i from 0 < i < n:
421
if current_ps.levels[i-1] == 0:
422
cells_to_refine_by[j] = i
423
j += 1
424
if input_group is NULL:
425
j = refine_and_return_invariant(current_ps, S2, cells_to_refine_by, j)
426
else:
427
j = refine_also_by_orbits(current_ps, S2, refine_and_return_invariant,
428
cells_to_refine_by, j, group, perm_stack)
429
if k != j:
430
possible = 0; unknown = 0
431
elif not stacks_are_equivalent(left_ps, current_ps):
432
possible = 0; unknown = 0
433
else:
434
PS_move_all_mins_to_front(current_ps)
435
436
# Refine down to a discrete partition
437
while not PS_is_discrete(left_ps):
438
i = left_ps.depth
439
k = PS_first_smallest(left_ps, vertices_to_split[i]) # writes to vertices_to_split, but this is never used
440
if input_group is not NULL:
441
OP_clear(orbits_of_supergroup)
442
for j from i <= j < group.base_size:
443
for ell from 0 <= ell < group.num_gens[j]:
444
OP_merge_list_perm(orbits_of_supergroup, group.generators[j] + n*ell)
445
b = orbits_of_supergroup.mcr[OP_find(orbits_of_supergroup, perm_stack[i*n + k])]
446
tmp_gp = group
447
group = old_group
448
old_group = tmp_gp
449
if SC_insert_base_point_nomalloc(group, old_group, i, b):
450
mem_err = 1
451
break
452
indicators[i] = split_point_and_refine_by_orbits(left_ps, k, S1, refine_and_return_invariant, cells_to_refine_by, group, perm_stack)
453
else:
454
indicators[i] = split_point_and_refine(left_ps, k, S1, refine_and_return_invariant, cells_to_refine_by)
455
bitset_unset(vertices_have_been_reduced[0], left_ps.depth)
456
457
if not mem_err:
458
while not PS_is_discrete(current_ps) and possible:
459
i = current_ps.depth
460
vertices_determining_current_stack[i] = PS_first_smallest(current_ps, vertices_to_split[i])
461
if input_group is not NULL:
462
if group.parents[i][perm_stack[n*i + vertices_determining_current_stack[i]]] == -1:
463
possible = 0
464
while possible:
465
i = current_ps.depth
466
if input_group is not NULL:
467
j = split_point_and_refine_by_orbits(current_ps, vertices_determining_current_stack[i],
468
S2, refine_and_return_invariant, cells_to_refine_by, group, perm_stack)
469
else:
470
j = split_point_and_refine(current_ps,
471
vertices_determining_current_stack[i], S2,
472
refine_and_return_invariant, cells_to_refine_by)
473
if indicators[i] != j:
474
possible = 0
475
elif not stacks_are_equivalent(left_ps, current_ps):
476
possible = 0
477
else:
478
PS_move_all_mins_to_front(current_ps)
479
if not all_children_are_equivalent(current_ps, S2):
480
current_kids_are_same = current_ps.depth + 1
481
break
482
current_ps.depth -= 1
483
while current_ps.depth >= 0: # not possible, so look for another
484
i = current_ps.depth
485
j = vertices_determining_current_stack[i] + 1
486
j = bitset_next(vertices_to_split[i], j)
487
if j == -1:
488
current_ps.depth -= 1 # backtrack
489
else:
490
possible = 1
491
vertices_determining_current_stack[i] = j
492
break # found another
493
if possible:
494
if input_group is NULL:
495
if compare_structures(left_ps.entries, current_ps.entries, S1, S2, n) == 0:
496
unknown = 0
497
else:
498
PS_get_perm_from(left_ps, current_ps, permutation)
499
if SC_contains(group, 0, permutation, 0) and compare_structures(permutation, id_perm, S1, S2, n) == 0:
500
# TODO: might be slight optimization for containment using perm_stack
501
unknown = 0
502
if unknown:
503
first_meets_current = current_ps.depth
504
first_kids_are_same = current_ps.depth
505
PS_copy_from_to(current_ps, first_ps)
506
current_ps.depth -= 1
507
508
if mem_err:
509
if work_space_prealloc is NULL:
510
deallocate_dc_work_space(work_space)
511
raise MemoryError
512
513
# Main loop:
514
while possible and unknown and current_ps.depth != -1:
515
516
# I. Search for a new vertex to split, and update subgroup information
517
new_vertex = 0
518
if current_ps.depth > first_meets_current:
519
# If we are not at a node of the first stack, reduce size of
520
# vertices_to_split by using the symmetries we already know.
521
if not bitset_check(vertices_have_been_reduced[0], current_ps.depth):
522
for i from 0 <= i <= index_in_fp_and_mcr:
523
j = 0
524
while j < current_ps.depth and bitset_check(fixed_points_of_generators[i], vertices_determining_current_stack[j]):
525
j += 1
526
# If each vertex split so far is fixed by generator i,
527
# then remove elements of vertices_to_split which are
528
# not minimal in their orbits under generator i.
529
if j == current_ps.depth:
530
for k from 0 <= k < n:
531
if bitset_check(vertices_to_split[current_ps.depth], k) and not bitset_check(minimal_cell_reps_of_generators[i], k):
532
bitset_flip(vertices_to_split[current_ps.depth], k)
533
bitset_flip(vertices_have_been_reduced[0], current_ps.depth)
534
# Look for a new point to split.
535
i = vertices_determining_current_stack[current_ps.depth] + 1
536
i = bitset_next(vertices_to_split[current_ps.depth], i)
537
if i != -1:
538
# There is a new point.
539
vertices_determining_current_stack[current_ps.depth] = i
540
new_vertex = 1
541
else:
542
# No new point: backtrack.
543
current_ps.depth -= 1
544
else:
545
# If we are at a node of the first stack, the above reduction
546
# will not help. Also, we must update information about
547
# primary orbits here.
548
if current_ps.depth < first_meets_current:
549
# If we are done searching under this part of the first
550
# stack, then first_meets_current is one higher, and we
551
# are looking at a new primary orbit (corresponding to a
552
# larger subgroup in the stabilizer chain).
553
first_meets_current = current_ps.depth
554
for i from 0 <= i < n:
555
if bitset_check(vertices_to_split[current_ps.depth], i):
556
minimal_in_primary_orbit = i
557
break
558
while 1:
559
i = vertices_determining_current_stack[current_ps.depth]
560
# This was the last point to be split here.
561
# If it is in the same orbit as minimal_in_primary_orbit,
562
# then count it as an element of the primary orbit.
563
if OP_find(orbits_of_subgroup, i) == OP_find(orbits_of_subgroup, minimal_in_primary_orbit):
564
subgroup_primary_orbit_size += 1
565
# Look for a new point to split.
566
i += 1
567
i = bitset_next(vertices_to_split[current_ps.depth], i)
568
if i != -1:
569
# There is a new point.
570
vertices_determining_current_stack[current_ps.depth] = i
571
if orbits_of_subgroup.mcr[OP_find(orbits_of_subgroup, i)] == i:
572
new_vertex = 1
573
break
574
else:
575
# No new point: backtrack.
576
# Note that now, we are backtracking up the first stack.
577
vertices_determining_current_stack[current_ps.depth] = -1
578
# If every choice of point to split gave something in the
579
# (same!) primary orbit, then all children of the first
580
# stack at this point are equivalent.
581
j = 0
582
for i from 0 <= i < n:
583
if bitset_check(vertices_to_split[current_ps.depth], i):
584
j += 1
585
if j == subgroup_primary_orbit_size and first_kids_are_same == current_ps.depth+1:
586
first_kids_are_same = current_ps.depth
587
# Backtrack.
588
subgroup_primary_orbit_size = 0
589
current_ps.depth -= 1
590
break
591
if not new_vertex:
592
continue
593
594
if current_kids_are_same > current_ps.depth + 1:
595
current_kids_are_same = current_ps.depth + 1
596
597
# II. Refine down to a discrete partition, or until
598
# we leave the part of the tree we are interested in
599
discrete = 0
600
while 1:
601
i = current_ps.depth
602
while 1:
603
if input_group is not NULL:
604
k = split_point_and_refine_by_orbits(current_ps,
605
vertices_determining_current_stack[i], S2,
606
refine_and_return_invariant, cells_to_refine_by,
607
group, perm_stack)
608
update_perm_stack(group, current_ps.depth, vertices_determining_current_stack[i], perm_stack)
609
else:
610
k = split_point_and_refine(current_ps,
611
vertices_determining_current_stack[i], S2,
612
refine_and_return_invariant, cells_to_refine_by)
613
PS_move_all_mins_to_front(current_ps)
614
if indicators[i] != k:
615
possible = 0
616
elif not stacks_are_equivalent(left_ps, current_ps):
617
possible = 0
618
if PS_is_discrete(current_ps):
619
break
620
vertices_determining_current_stack[current_ps.depth] = PS_first_smallest(current_ps, vertices_to_split[current_ps.depth])
621
if input_group is not NULL:
622
if group.parents[current_ps.depth][perm_stack[n*current_ps.depth + vertices_determining_current_stack[current_ps.depth]]] == -1:
623
possible = 0
624
if not possible:
625
j = vertices_determining_current_stack[i] + 1
626
j = bitset_next(vertices_to_split[i], j)
627
if j == -1:
628
break
629
else:
630
possible = 1
631
vertices_determining_current_stack[i] = j
632
current_ps.depth -= 1 # reset for next refinement
633
else: break
634
if not possible:
635
break
636
if PS_is_discrete(current_ps):
637
break
638
bitset_unset(vertices_have_been_reduced[0], current_ps.depth)
639
if not all_children_are_equivalent(current_ps, S2):
640
current_kids_are_same = current_ps.depth + 1
641
642
# III. Check for automorphisms and isomorphisms
643
automorphism = 0
644
if possible:
645
PS_get_perm_from(first_ps, current_ps, permutation)
646
if compare_structures(permutation, id_perm, S2, S2, n) == 0:
647
if input_group is NULL or SC_contains(group, 0, permutation, 0):
648
# TODO: might be slight optimization for containment using perm_stack
649
automorphism = 1
650
if not automorphism and possible:
651
# if we get here, discrete must be true
652
if current_ps.depth != left_ps.depth:
653
possible = 0
654
elif input_group is NULL:
655
if compare_structures(left_ps.entries, current_ps.entries, S1, S2, n) == 0:
656
unknown = 0
657
break
658
else:
659
possible = 0
660
else:
661
PS_get_perm_from(left_ps, current_ps, permutation)
662
if SC_contains(group, 0, permutation, 0) and compare_structures(permutation, id_perm, S1, S2, n) == 0:
663
# TODO: might be slight optimization for containment using perm_stack
664
unknown = 0
665
break
666
else:
667
possible = 0
668
if automorphism:
669
if index_in_fp_and_mcr < len_of_fp_and_mcr - 1:
670
index_in_fp_and_mcr += 1
671
bitset_zero(fixed_points_of_generators[index_in_fp_and_mcr])
672
bitset_zero(minimal_cell_reps_of_generators[index_in_fp_and_mcr])
673
for i from 0 <= i < n:
674
if permutation[i] == i:
675
bitset_set(fixed_points_of_generators[index_in_fp_and_mcr], i)
676
bitset_set(minimal_cell_reps_of_generators[index_in_fp_and_mcr], i)
677
else:
678
bitset_unset(fixed_points_of_generators[index_in_fp_and_mcr], i)
679
k = i
680
j = permutation[i]
681
while j != i:
682
if j < k: k = j
683
j = permutation[j]
684
if k == i:
685
bitset_set(minimal_cell_reps_of_generators[index_in_fp_and_mcr], i)
686
else:
687
bitset_unset(minimal_cell_reps_of_generators[index_in_fp_and_mcr], i)
688
current_ps.depth = first_meets_current
689
if OP_merge_list_perm(orbits_of_subgroup, permutation): # if permutation made orbits coarser
690
if orbits_of_subgroup.mcr[OP_find(orbits_of_subgroup, minimal_in_primary_orbit)] != minimal_in_primary_orbit:
691
continue # main loop
692
if bitset_check(vertices_have_been_reduced[0], current_ps.depth):
693
bitset_and(vertices_to_split[current_ps.depth], vertices_to_split[current_ps.depth], minimal_cell_reps_of_generators[index_in_fp_and_mcr])
694
continue # main loop
695
if not possible:
696
possible = 1
697
i = current_ps.depth
698
current_ps.depth = current_kids_are_same-1
699
if i == current_kids_are_same:
700
continue # main loop
701
if index_in_fp_and_mcr < len_of_fp_and_mcr - 1:
702
index_in_fp_and_mcr += 1
703
bitset_zero(fixed_points_of_generators[index_in_fp_and_mcr])
704
bitset_zero(minimal_cell_reps_of_generators[index_in_fp_and_mcr])
705
j = current_ps.depth
706
current_ps.depth = i # just for mcr and fixed functions...
707
for i from 0 <= i < n:
708
if PS_is_mcr(current_ps, i):
709
bitset_set(minimal_cell_reps_of_generators[index_in_fp_and_mcr], i)
710
if PS_is_fixed(current_ps, i):
711
bitset_set(fixed_points_of_generators[index_in_fp_and_mcr], i)
712
current_ps.depth = j
713
if bitset_check(vertices_have_been_reduced[0], current_ps.depth):
714
bitset_and(vertices_to_split[current_ps.depth], vertices_to_split[current_ps.depth], minimal_cell_reps_of_generators[index_in_fp_and_mcr])
715
716
# End of main loop.
717
if possible and not unknown and isom is not NULL:
718
for i from 0 <= i < n:
719
isom[left_ps.entries[i]] = current_ps.entries[i]
720
721
# Deallocate:
722
if work_space_prealloc is NULL:
723
deallocate_dc_work_space(work_space)
724
return 1 if (possible and not unknown) else 0
725
726
727
728
729
730
731