Path: blob/master/sage/groups/perm_gps/partn_ref/double_coset.pyx
4097 views
r"""1Double cosets23This module implements a general algorithm for computing double coset problems4for pairs of objects. The class of objects in question must be some kind5of structure for which an isomorphism is a permutation in $S_n$ for some $n$,6which we call here the order of the object. Given objects $X$ and $Y$,7the program returns an isomorphism in list permutation form if $X \cong Y$, and8a NULL pointer otherwise.910In order to take advantage of the algorithms in this module for a specific kind11of object, one must implement (in Cython) three functions which will be specific12to the kind of objects in question. Pointers to these functions are passed to13the main function of the module, which is \code{double_coset}. For specific14examples of implementations of these functions, see any of the files in15\code{sage.groups.perm_gps.partn_ref} beginning with "refinement." They are:1617A. \code{refine_and_return_invariant}:1819Signature:2021\code{int refine_and_return_invariant(PartitionStack *PS, void *S, int *cells_to_refine_by, int ctrb_len)}2223This function should split up cells in the partition at the top of the24partition stack in such a way that any automorphism that respects the25partition also respects the resulting partition. The array26cells_to_refine_by is a list of the beginning positions of some cells which27have been changed since the last refinement. It is not necessary to use28this in an implementation of this function, but it will affect performance.29One should consult \code{refinement_graphs} for more details and ideas for30particular implementations.3132Output:3334An integer $I$ invariant under the orbits of $S_n$. That is, if35$\gamma \in S_n$, then36$$ I(G, PS, cells_to_refine_by) = I( \gamma(G), \gamma(PS), \gamma(cells_to_refine_by) ) .$$373839B. \code{compare_structures}:4041Signature:4243\code{int compare_structures(int *gamma_1, int *gamma_2, void *S1, void *S2)}4445This function must implement a total ordering on the set of objects of fixed46order. Return:47-1 if \code{gamma_1^{-1}(S1) < gamma_2^{-1}(S2)},480 if \code{gamma_1^{-1}(S1) == gamma_2^{-1}(S2)},491 if \code{gamma_1^{-1}(S1) > gamma_2^{-1}(S2)}.5051Important note:5253The permutations are thought of as being input in inverse form, and this can54lead to subtle bugs. One is encouraged to consult existing implementations55to make sure the right thing is being done: this is so that you can avoid56*actually* needing to compute the inverse.5758C. \code{all_children_are_equivalent}:5960Signature:6162\code{bint all_children_are_equivalent(PartitionStack *PS, void *S)}6364This function must return False unless it is the case that each discrete65partition finer than the top of the partition stack is equivalent to the66others under some automorphism of S. The converse need not hold: if this is67indeed the case, it still may return False. This function is originally used68as a consequence of Lemma 2.25 in [1].6970DOCTEST:71sage: import sage.groups.perm_gps.partn_ref.double_coset7273REFERENCE:7475[1] McKay, Brendan D. Practical Graph Isomorphism. Congressus Numerantium,76Vol. 30 (1981), pp. 45-87.7778[2] Leon, Jeffrey. Permutation Group Algorithms Based on Partitions, I:79Theory and Algorithms. J. Symbolic Computation, Vol. 12 (1991), pp.80533-583.8182"""8384#*****************************************************************************85# Copyright (C) 2006 - 2011 Robert L. Miller <[email protected]>86#87# Distributed under the terms of the GNU General Public License (GPL)88# http://www.gnu.org/licenses/89#*****************************************************************************9091include 'data_structures_pyx.pxi' # includes bitsets9293cdef inline int cmp(int a, int b):94if a < b: return -195elif a == b: return 096else: return 19798# Functions99100cdef bint all_children_are_equivalent_trivial(PartitionStack *PS, void *S):101return 0102103cdef int refine_and_return_invariant_trivial(PartitionStack *PS, void *S, int *cells_to_refine_by, int ctrb_len):104return 0105106cdef int compare_perms(int *gamma_1, int *gamma_2, void *S1, void *S2):107cdef list MS1 = <list> S1108cdef list MS2 = <list> S2109cdef int i, j110for i from 0 <= i < len(MS1):111j = cmp(MS1[gamma_1[i]], MS2[gamma_2[i]])112if j != 0: return j113return 0114115def 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]]):116"""117Given a group G generated by the given generators, tests whether the given118permutations are in the same right coset of G. Tests nontrivial input group119when using double_coset. If they are, return an element g so that120g.perm1 = perm2 (composing left to right).121122TESTS::123124sage: from sage.groups.perm_gps.partn_ref.double_coset import coset_eq125sage: coset_eq()126[5, 0, 1, 2, 3, 4]127sage: gens = [[1,2,3,0]]128sage: reps = [[0,1,2,3]]129sage: for p in SymmetricGroup(4):130... p = [a-1 for a in p.list()]131... found = False132... for r in reps:133... if coset_eq(p, r, gens):134... found = True135... break136... if not found:137... reps.append(p)138sage: len(reps)1396140sage: gens = [[1,0,2,3],[0,1,3,2]]141sage: reps = [[0,1,2,3]]142sage: for p in SymmetricGroup(4):143... p = [a-1 for a in p.list()]144... found = False145... for r in reps:146... if coset_eq(p, r, gens):147... found = True148... break149... if not found:150... reps.append(p)151sage: len(reps)1526153sage: gens = [[1,2,0,3]]154sage: reps = [[0,1,2,3]]155sage: for p in SymmetricGroup(4):156... p = [a-1 for a in p.list()]157... found = False158... for r in reps:159... if coset_eq(p, r, gens):160... found = True161... break162... if not found:163... reps.append(p)164sage: len(reps)1658166167"""168cdef int i, n = len(perm1)169assert all(len(g) == n for g in gens+[perm2])170cdef PartitionStack *part = PS_new(n, 1)171cdef int *c_perm = <int *> sage_malloc(n * sizeof(int))172cdef StabilizerChain *group = SC_new(n, 1)173if part is NULL or c_perm is NULL or group is NULL:174sage_free(c_perm)175PS_dealloc(part)176SC_dealloc(group)177raise MemoryError178for g in gens:179for i from 0 <= i < n:180c_perm[i] = g[i]181SC_insert(group, 0, c_perm, 1)182for i from 0 <= i < n:183c_perm[i] = i184cdef int *output = double_coset(<void *> perm1, <void *> perm2, part, c_perm, n, &all_children_are_equivalent_trivial, &refine_and_return_invariant_trivial, &compare_perms, group)185sage_free(c_perm)186PS_dealloc(part)187SC_dealloc(group)188if output is NULL:189return False190else:191x = [output[i] for i from 0 <= i < n]192sage_free(output)193return x194195cdef int *double_coset(void *S1, void *S2, PartitionStack *partition1, int *ordering2,196int n, bint (*all_children_are_equivalent)(PartitionStack *PS, void *S),197int (*refine_and_return_invariant)\198(PartitionStack *PS, void *S, int *cells_to_refine_by, int ctrb_len),199int (*compare_structures)(int *gamma_1, int *gamma_2, void *S1, void *S2),200StabilizerChain *input_group):201"""202Traverse the search space for double coset calculation.203204INPUT:205S1, S2 -- pointers to the structures206partition1 -- PartitionStack of depth 0 and degree n,207whose first partition is of the points of S1208ordering2 -- an ordering of the points of S2 representing a second partition209n -- the number of points (points are assumed to be 0,1,...,n-1)210all_children_are_equivalent -- pointer to a function211INPUT:212PS -- pointer to a partition stack213S -- pointer to the structure214OUTPUT:215bint -- returns True if it can be determined that all refinements below216the current one will result in an equivalent discrete partition217refine_and_return_invariant -- pointer to a function218INPUT:219PS -- pointer to a partition stack220S -- pointer to the structure221alpha -- an array consisting of numbers, which indicate the starting222positions of the cells to refine against (will likely be modified)223OUTPUT:224int -- returns an invariant under application of arbitrary permutations225compare_structures -- pointer to a function226INPUT:227gamma_1, gamma_2 -- (list) permutations of the points of S1 and S2228S1, S2 -- pointers to the structures229OUTPUT:230int -- 0 if gamma_1(S1) = gamma_2(S2), otherwise -1 or 1 (see docs for cmp),231such that the set of all structures is well-ordered232233NOTE:234The partition ``partition1`` and the resulting partition from ``ordering2``235*must* satisfy the property that in each cell, the smallest element occurs236first!237238OUTPUT:239If S1 and S2 are isomorphic, a pointer to an integer array representing an240isomorphism. Otherwise, a NULL pointer.241242"""243cdef PartitionStack *current_ps, *first_ps, *left_ps244cdef int first_meets_current = -1245cdef int current_kids_are_same = 1246cdef int first_kids_are_same247248cdef int *indicators249250cdef OrbitPartition *orbits_of_subgroup251cdef int subgroup_primary_orbit_size = 0252cdef int minimal_in_primary_orbit253254cdef bitset_t *fixed_points_of_generators # i.e. fp255cdef bitset_t *minimal_cell_reps_of_generators # i.e. mcr256cdef int len_of_fp_and_mcr = 100257cdef int index_in_fp_and_mcr = -1258259cdef bitset_t *vertices_to_split260cdef bitset_t vertices_have_been_reduced261cdef int *permutation, *id_perm, *cells_to_refine_by262cdef int *vertices_determining_current_stack263cdef int *perm_stack264cdef StabilizerChain *group, *old_group, *tmp_gp265266cdef int *output267268cdef int i, j, k269cdef bint discrete, automorphism, update_label270cdef bint backtrack, new_vertex, narrow, mem_err = 0271272if n == 0:273return NULL274275cdef int *int_array = <int *> sage_malloc( 5*n * sizeof(int) )276output = <int *> sage_malloc( n * sizeof(int) )277if input_group is not NULL:278perm_stack = <int *> sage_malloc( n*n * sizeof(int) )279group = SC_copy(input_group, n)280old_group = SC_new(n)281if perm_stack is NULL or group is NULL or old_group is NULL:282mem_err = 1283else:284SC_identify(perm_stack, n)285cdef bitset_t *bitset_array = <bitset_t *> sage_malloc( (n+2*len_of_fp_and_mcr) * sizeof(bitset_t) )286left_ps = partition1287current_ps = PS_new(n, 0)288orbits_of_subgroup = OP_new(n)289290if int_array is NULL or output is NULL or bitset_array is NULL or \291current_ps is NULL or orbits_of_subgroup is NULL:292mem_err = 1293294if not mem_err:295indicators = int_array296permutation = int_array + n297id_perm = int_array + 2*n298cells_to_refine_by = int_array + 3*n299vertices_determining_current_stack = int_array + 4*n300301fixed_points_of_generators = bitset_array302minimal_cell_reps_of_generators = bitset_array + len_of_fp_and_mcr303vertices_to_split = bitset_array + 2*len_of_fp_and_mcr304try:305for i from 0 <= i < n+2*len_of_fp_and_mcr:306bitset_init(bitset_array[i], n)307bitset_init(vertices_have_been_reduced, n)308except MemoryError:309mem_err = 1310311cdef bint possible = 1312cdef bint unknown = 1313314if not mem_err:315bitset_zero(vertices_have_been_reduced)316317# set up the identity permutation318for i from 0 <= i < n:319id_perm[i] = i320if ordering2 is NULL:321ordering2 = id_perm322323# Copy reordering of left_ps coming from ordering2 to current_ps.324for i from 0 <= i < n:325current_ps.entries[i] = ordering2[i] # memcpy faster?326current_ps.levels[i] = left_ps.levels[i]327# note that current_ps depth and degree already set328329# default values of "infinity"330for i from 0 <= i < n:331indicators[i] = -1332333# Our first refinement needs to check every cell of the partition,334# so cells_to_refine_by needs to be a list of pointers to each cell.335j = 1336cells_to_refine_by[0] = 0337for i from 0 < i < n:338if left_ps.levels[i-1] == 0:339cells_to_refine_by[j] = i340j += 1341if input_group is NULL:342k = refine_and_return_invariant(left_ps, S1, cells_to_refine_by, j)343else:344k = refine_also_by_orbits(left_ps, S1, refine_and_return_invariant,345cells_to_refine_by, j, group, perm_stack)346347j = 1348cells_to_refine_by[0] = 0349for i from 0 < i < n:350if current_ps.levels[i-1] == 0:351cells_to_refine_by[j] = i352j += 1353if input_group is NULL:354j = refine_and_return_invariant(current_ps, S2, cells_to_refine_by, j)355else:356j = refine_also_by_orbits(current_ps, S2, refine_and_return_invariant,357cells_to_refine_by, j, group, perm_stack)358359if k != j:360possible = 0; unknown = 0361elif not stacks_are_equivalent(left_ps, current_ps):362possible = 0; unknown = 0363else:364PS_move_all_mins_to_front(current_ps)365366first_ps = NULL367# Refine down to a discrete partition368while not PS_is_discrete(left_ps):369i = left_ps.depth370k = PS_first_smallest(left_ps, vertices_to_split[i]) # writes to vertices_to_split, but this is never used371if input_group is not NULL:372# split the point373left_ps.depth += 1374PS_clear(left_ps)375cells_to_refine_by[0] = PS_split_point(left_ps, k)376# update the base377tmp_gp = group378group = old_group379old_group = tmp_gp380if SC_insert_base_point_nomalloc(group, old_group, i, k):381mem_err = 1382break383SC_identify(perm_stack + n*left_ps.depth, n)384# do the refinements385indicators[i] = refine_also_by_orbits(left_ps, S1, refine_and_return_invariant, cells_to_refine_by, 1, group, perm_stack)386else:387indicators[i] = split_point_and_refine(left_ps, k, S1, refine_and_return_invariant, cells_to_refine_by)388bitset_unset(vertices_have_been_reduced, left_ps.depth)389if not mem_err:390while not PS_is_discrete(current_ps) and possible:391i = current_ps.depth392vertices_determining_current_stack[i] = PS_first_smallest(current_ps, vertices_to_split[i])393if input_group is not NULL:394if group.parents[i][perm_stack[n*i + vertices_determining_current_stack[i]]] == -1:395possible = 0396while possible:397i = current_ps.depth398if input_group is not NULL:399# split the point400current_ps.depth += 1401PS_clear(current_ps)402cells_to_refine_by[0] = PS_split_point(current_ps, vertices_determining_current_stack[i])403# update the base404tmp_gp = group405group = old_group406old_group = tmp_gp407if SC_insert_base_point_nomalloc(group, old_group, i, vertices_determining_current_stack[i]):408mem_err = 1409break410# update perm_stack411update_perm_stack(group, current_ps.depth, vertices_determining_current_stack[i], perm_stack)412# do the refinements413j = refine_also_by_orbits(current_ps, S2, refine_and_return_invariant, cells_to_refine_by, 1, group, perm_stack)414else:415j = split_point_and_refine(current_ps,416vertices_determining_current_stack[i], S2,417refine_and_return_invariant, cells_to_refine_by)418if indicators[i] != j:419possible = 0420elif not stacks_are_equivalent(left_ps, current_ps):421possible = 0422else:423PS_move_all_mins_to_front(current_ps)424if not all_children_are_equivalent(current_ps, S2):425current_kids_are_same = current_ps.depth + 1426break427current_ps.depth -= 1428while current_ps.depth >= 0: # not possible, so look for another429i = current_ps.depth430j = vertices_determining_current_stack[i] + 1431j = bitset_next(vertices_to_split[i], j)432if j == -1:433current_ps.depth -= 1 # backtrack434else:435possible = 1436vertices_determining_current_stack[i] = j437break # found another438if possible:439if input_group is NULL:440if compare_structures(left_ps.entries, current_ps.entries, S1, S2) == 0:441unknown = 0442else:443PS_get_perm_from(left_ps, current_ps, permutation)444if SC_contains(group, 0, permutation, 0) and compare_structures(permutation, id_perm, S1, S2) == 0:445# TODO: might be slight optimization for containment using perm_stack446unknown = 0447if unknown:448first_meets_current = current_ps.depth449first_kids_are_same = current_ps.depth450first_ps = PS_copy(current_ps)451if first_ps is NULL:452mem_err = 1453current_ps.depth -= 1454455if mem_err:456sage_free(int_array)457if input_group is not NULL:458sage_free(perm_stack)459SC_dealloc(group)460SC_dealloc(old_group)461OP_dealloc(orbits_of_subgroup)462sage_free(output)463if bitset_array is not NULL:464for i from 0 <= i < n+2*len_of_fp_and_mcr:465bitset_free(bitset_array[i])466bitset_free(vertices_have_been_reduced)467sage_free(bitset_array)468PS_dealloc(current_ps)469raise MemoryError470471# Main loop:472while possible and unknown and current_ps.depth != -1:473474# I. Search for a new vertex to split, and update subgroup information475new_vertex = 0476if current_ps.depth > first_meets_current:477# If we are not at a node of the first stack, reduce size of478# vertices_to_split by using the symmetries we already know.479if not bitset_check(vertices_have_been_reduced, current_ps.depth):480for i from 0 <= i <= index_in_fp_and_mcr:481j = 0482while j < current_ps.depth and bitset_check(fixed_points_of_generators[i], vertices_determining_current_stack[j]):483j += 1484# If each vertex split so far is fixed by generator i,485# then remove elements of vertices_to_split which are486# not minimal in their orbits under generator i.487if j == current_ps.depth:488for k from 0 <= k < n:489if bitset_check(vertices_to_split[current_ps.depth], k) and not bitset_check(minimal_cell_reps_of_generators[i], k):490bitset_flip(vertices_to_split[current_ps.depth], k)491bitset_flip(vertices_have_been_reduced, current_ps.depth)492# Look for a new point to split.493i = vertices_determining_current_stack[current_ps.depth] + 1494i = bitset_next(vertices_to_split[current_ps.depth], i)495if i != -1:496# There is a new point.497vertices_determining_current_stack[current_ps.depth] = i498new_vertex = 1499else:500# No new point: backtrack.501current_ps.depth -= 1502else:503# If we are at a node of the first stack, the above reduction504# will not help. Also, we must update information about505# primary orbits here.506if current_ps.depth < first_meets_current:507# If we are done searching under this part of the first508# stack, then first_meets_current is one higher, and we509# are looking at a new primary orbit (corresponding to a510# larger subgroup in the stabilizer chain).511first_meets_current = current_ps.depth512for i from 0 <= i < n:513if bitset_check(vertices_to_split[current_ps.depth], i):514minimal_in_primary_orbit = i515break516while 1:517i = vertices_determining_current_stack[current_ps.depth]518# This was the last point to be split here.519# If it is in the same orbit as minimal_in_primary_orbit,520# then count it as an element of the primary orbit.521if OP_find(orbits_of_subgroup, i) == OP_find(orbits_of_subgroup, minimal_in_primary_orbit):522subgroup_primary_orbit_size += 1523# Look for a new point to split.524i += 1525i = bitset_next(vertices_to_split[current_ps.depth], i)526if i != -1:527# There is a new point.528vertices_determining_current_stack[current_ps.depth] = i529if orbits_of_subgroup.mcr[OP_find(orbits_of_subgroup, i)] == i:530new_vertex = 1531break532else:533# No new point: backtrack.534# Note that now, we are backtracking up the first stack.535vertices_determining_current_stack[current_ps.depth] = -1536# If every choice of point to split gave something in the537# (same!) primary orbit, then all children of the first538# stack at this point are equivalent.539j = 0540for i from 0 <= i < n:541if bitset_check(vertices_to_split[current_ps.depth], i):542j += 1543if j == subgroup_primary_orbit_size and first_kids_are_same == current_ps.depth+1:544first_kids_are_same = current_ps.depth545# Backtrack.546subgroup_primary_orbit_size = 0547current_ps.depth -= 1548break549if not new_vertex:550continue551552if current_kids_are_same > current_ps.depth + 1:553current_kids_are_same = current_ps.depth + 1554555# II. Refine down to a discrete partition, or until556# we leave the part of the tree we are interested in557discrete = 0558while 1:559i = current_ps.depth560while 1:561if input_group is not NULL:562k = split_point_and_refine_by_orbits(current_ps,563vertices_determining_current_stack[i], S2,564refine_and_return_invariant, cells_to_refine_by,565group, perm_stack)566update_perm_stack(group, current_ps.depth, vertices_determining_current_stack[i], perm_stack)567else:568k = split_point_and_refine(current_ps,569vertices_determining_current_stack[i], S2,570refine_and_return_invariant, cells_to_refine_by)571PS_move_all_mins_to_front(current_ps)572if indicators[i] != k:573possible = 0574elif not stacks_are_equivalent(left_ps, current_ps):575possible = 0576if PS_is_discrete(current_ps):577break578vertices_determining_current_stack[current_ps.depth] = PS_first_smallest(current_ps, vertices_to_split[current_ps.depth])579if input_group is not NULL:580if group.parents[current_ps.depth][perm_stack[n*current_ps.depth + vertices_determining_current_stack[current_ps.depth]]] == -1:581possible = 0582if not possible:583j = vertices_determining_current_stack[i] + 1584j = bitset_next(vertices_to_split[i], j)585if j == -1:586break587else:588possible = 1589vertices_determining_current_stack[i] = j590current_ps.depth -= 1 # reset for next refinement591else: break592if not possible:593break594if PS_is_discrete(current_ps):595break596bitset_unset(vertices_have_been_reduced, current_ps.depth)597if not all_children_are_equivalent(current_ps, S2):598current_kids_are_same = current_ps.depth + 1599600# III. Check for automorphisms and isomorphisms601automorphism = 0602if possible:603PS_get_perm_from(first_ps, current_ps, permutation)604if compare_structures(permutation, id_perm, S2, S2) == 0:605if input_group is NULL or SC_contains(group, 0, permutation, 0):606# TODO: might be slight optimization for containment using perm_stack607automorphism = 1608if not automorphism and possible:609# if we get here, discrete must be true610if current_ps.depth != left_ps.depth:611possible = 0612elif input_group is NULL:613if compare_structures(left_ps.entries, current_ps.entries, S1, S2) == 0:614unknown = 0615break616else:617possible = 0618else:619PS_get_perm_from(left_ps, current_ps, permutation)620if SC_contains(group, 0, permutation, 0) and compare_structures(permutation, id_perm, S1, S2) == 0:621# TODO: might be slight optimization for containment using perm_stack622unknown = 0623break624else:625possible = 0626if automorphism:627if index_in_fp_and_mcr < len_of_fp_and_mcr - 1:628index_in_fp_and_mcr += 1629bitset_zero(fixed_points_of_generators[index_in_fp_and_mcr])630bitset_zero(minimal_cell_reps_of_generators[index_in_fp_and_mcr])631for i from 0 <= i < n:632if permutation[i] == i:633bitset_set(fixed_points_of_generators[index_in_fp_and_mcr], i)634bitset_set(minimal_cell_reps_of_generators[index_in_fp_and_mcr], i)635else:636bitset_unset(fixed_points_of_generators[index_in_fp_and_mcr], i)637k = i638j = permutation[i]639while j != i:640if j < k: k = j641j = permutation[j]642if k == i:643bitset_set(minimal_cell_reps_of_generators[index_in_fp_and_mcr], i)644else:645bitset_unset(minimal_cell_reps_of_generators[index_in_fp_and_mcr], i)646current_ps.depth = first_meets_current647if OP_merge_list_perm(orbits_of_subgroup, permutation): # if permutation made orbits coarser648if orbits_of_subgroup.mcr[OP_find(orbits_of_subgroup, minimal_in_primary_orbit)] != minimal_in_primary_orbit:649continue # main loop650if bitset_check(vertices_have_been_reduced, current_ps.depth):651bitset_and(vertices_to_split[current_ps.depth], vertices_to_split[current_ps.depth], minimal_cell_reps_of_generators[index_in_fp_and_mcr])652continue # main loop653if not possible:654possible = 1655i = current_ps.depth656current_ps.depth = current_kids_are_same-1657if i == current_kids_are_same:658continue # main loop659if index_in_fp_and_mcr < len_of_fp_and_mcr - 1:660index_in_fp_and_mcr += 1661bitset_zero(fixed_points_of_generators[index_in_fp_and_mcr])662bitset_zero(minimal_cell_reps_of_generators[index_in_fp_and_mcr])663j = current_ps.depth664current_ps.depth = i # just for mcr and fixed functions...665for i from 0 <= i < n:666if PS_is_mcr(current_ps, i):667bitset_set(minimal_cell_reps_of_generators[index_in_fp_and_mcr], i)668if PS_is_fixed(current_ps, i):669bitset_set(fixed_points_of_generators[index_in_fp_and_mcr], i)670current_ps.depth = j671if bitset_check(vertices_have_been_reduced, current_ps.depth):672bitset_and(vertices_to_split[current_ps.depth], vertices_to_split[current_ps.depth], minimal_cell_reps_of_generators[index_in_fp_and_mcr])673674# End of main loop.675676if possible and not unknown:677for i from 0 <= i < n:678output[left_ps.entries[i]] = current_ps.entries[i]679else:680sage_free(output)681output = NULL682683# Deallocate:684sage_free(int_array)685OP_dealloc(orbits_of_subgroup)686if bitset_array is not NULL:687for i from 0 <= i < n+2*len_of_fp_and_mcr:688bitset_free(bitset_array[i])689bitset_free(vertices_have_been_reduced)690sage_free(bitset_array)691if input_group is not NULL:692sage_free(perm_stack)693SC_dealloc(group)694SC_dealloc(old_group)695PS_dealloc(first_ps)696PS_dealloc(current_ps)697return output698699700701702703704705