Path: blob/master/sage/groups/perm_gps/partn_ref/refinement_python.pyx
4057 views
"""1Python interface to partition backtrack functions23DOCTEST:4sage: import sage.groups.perm_gps.partn_ref.refinement_python56This module provides Python frontends to the Cython-based partition backtrack7functions. This allows one to write the three input functions8(all_children_are_equivalent, refine_and_return_invariant, and compare_structures)9in pure Python, and still use the Cython algorithms. Experimentation with10specific partition backtrack implementations no longer requires compilation, as11the input functions can be dynamically changed at runtime.1213NOTE:1415This is not intended for production quality implementations of partition16refinement, but instead for experimentation, learning, and use of the Python17debugger.1819"""2021#*****************************************************************************22# Copyright (C) 2006 - 2011 Robert L. Miller <[email protected]>23#24# Distributed under the terms of the GNU General Public License (GPL)25# http://www.gnu.org/licenses/26#*****************************************************************************2728include 'data_structures_pyx.pxi' # includes bitsets2930cdef class PythonPartitionStack:31"""32Instances of this class wrap a (Cython) PartitionStack.33"""3435def __init__(self, int n):36"""37Initialize a PartitionStack.3839EXAMPLE::4041sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack42sage: P = PythonPartitionStack(7) # implicit doctest4344"""45self.c_ps = PS_new(n, 1)4647def __dealloc__(self):48"""49Deallocate the PartitionStack.5051EXAMPLE::5253sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack54sage: P = PythonPartitionStack(7)55sage: del(P) # implicit doctest5657"""58PS_dealloc(self.c_ps)5960def __repr__(self):61"""62Returns a string representing the stack.6364EXAMPLE::6566sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack67sage: P = PythonPartitionStack(7)68sage: P # implicit doctest69PythonPartitionStack of degree 7 and depth 0.7071"""72return "PythonPartitionStack of degree %d and depth %d."%(self.c_ps.degree, self.c_ps.depth)7374def display(self):75"""76Prints a representation of the stack.7778EXAMPLE::7980sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack81sage: P = PythonPartitionStack(7)82sage: P.depth(1)83184sage: P.set_level(2,1)85sage: P.display()86(0 1 2 3 4 5 6)87(0 1 2|3 4 5 6)8889"""90PS_print(self.c_ps)9192def is_discrete(self):93"""94Returns whether the deepest partition consists only of singleton cells.9596EXAMPLE::9798sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack99sage: P = PythonPartitionStack(7)100sage: P.is_discrete()101False102sage: [P.set_level(i,0) for i in xrange(7)]103[None, None, None, None, None, None, None]104sage: P.is_discrete()105True106107"""108return PS_is_discrete(self.c_ps)109110def num_cells(self):111"""112Returns the number of cells in the deepest partition.113114EXAMPLE::115116sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack117sage: P = PythonPartitionStack(7)118sage: P.num_cells()1191120121"""122return PS_num_cells(self.c_ps)123124def move_min_to_front(self, int start, int end):125"""126Makes sure that the first element of the segment of entries i with127start <= i <= end is minimal.128129EXAMPLE::130131sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack132sage: P = PythonPartitionStack(7)133sage: P.set_entry(1,0)134sage: P.set_entry(0,1)135sage: P.display()136(1 0 2 3 4 5 6)137sage: P.move_min_to_front(0,1)138sage: P.display()139(0 1 2 3 4 5 6)140141"""142PS_move_min_to_front(self.c_ps, start, end)143144def __copy__(self):145"""146147EXAMPLE::148149sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack150sage: P = PythonPartitionStack(7)151sage: Q = copy(P)152sage: P.display()153(0 1 2 3 4 5 6)154sage: Q.display()155(0 1 2 3 4 5 6)156157"""158cdef PythonPartitionStack cpy159cpy = PythonPartitionStack(self.c_ps.degree)160PS_copy_from_to(self.c_ps, cpy.c_ps)161return cpy162163def clear(self):164"""165Sets the current partition to the first shallower one, i.e. forgets about166boundaries between cells that are new to the current level.167168EXAMPLE::169170sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack171sage: P = PythonPartitionStack(7)172sage: P.depth(1)1731174sage: P.set_level(2,1)175sage: P.display()176(0 1 2 3 4 5 6)177(0 1 2|3 4 5 6)178sage: P.clear()179sage: P.display()180(0 1 2 3 4 5 6)181(0 1 2 3 4 5 6)182183"""184PS_clear(self.c_ps)185186def entries(self):187"""188Returns the entries array as a Python list of ints.189190EXAMPLE::191192sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack193sage: P = PythonPartitionStack(7)194sage: P.entries()195[0, 1, 2, 3, 4, 5, 6]196sage: P.levels()197[7, 7, 7, 7, 7, 7, -1]198199"""200cdef int i201return [self.c_ps.entries[i] for i from 0 <= i < self.c_ps.degree]202203def set_entry(self, int i, int entry):204"""205Sets the ith entry of the entries array to entry.206207EXAMPLE::208209sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack210sage: P = PythonPartitionStack(7)211sage: P.set_entry(1,0)212sage: P.set_entry(0,1)213sage: P.display()214(1 0 2 3 4 5 6)215216"""217self.c_ps.entries[i] = entry218219def get_entry(self, int i):220"""221Gets the ith entry of the entries array.222223EXAMPLE::224225sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack226sage: P = PythonPartitionStack(7)227sage: P.get_entry(0)2280229230"""231return self.c_ps.entries[i]232233def levels(self):234"""235Return the levels array as a Python list of ints.236237EXAMPLE::238239sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack240sage: P = PythonPartitionStack(7)241sage: P.entries()242[0, 1, 2, 3, 4, 5, 6]243sage: P.levels()244[7, 7, 7, 7, 7, 7, -1]245246"""247return [self.c_ps.levels[i] for i from 0 <= i < self.c_ps.degree]248249def set_level(self, int i, int level):250"""251Sets the ith entry of the levels array to entry.252253EXAMPLE::254255sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack256sage: P = PythonPartitionStack(7)257sage: P.depth(1)2581259sage: P.set_level(2,1)260sage: P.display()261(0 1 2 3 4 5 6)262(0 1 2|3 4 5 6)263264"""265self.c_ps.levels[i] = level266267def get_level(self, int i):268"""269Gets the ith entry of the levels array.270271EXAMPLE::272273sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack274sage: P = PythonPartitionStack(7)275sage: P.get_level(0)2767277278"""279return self.c_ps.levels[i]280281def depth(self, new=None):282"""283Returns the depth of the deepest partition in the stack, setting it to284new if new is not None.285286EXAMPLE::287288sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack289sage: P = PythonPartitionStack(7)290sage: P.depth()2910292293"""294if new is not None:295self.c_ps.depth = new296return self.c_ps.depth297298def degree(self, new=None):299"""300Returns the degree of the partition stack, setting it to301new if new is not None.302303EXAMPLE::304305sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack306sage: P = PythonPartitionStack(7)307sage: P.degree()3087309310"""311if new is not None:312self.c_ps.degree = new313return self.c_ps.degree314315def partition(self, int k):316"""317Return the partition at level k, as a Python list of lists.318319EXAMPLE::320321sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonPartitionStack322sage: P = PythonPartitionStack(7)323sage: P.depth(1)3241325sage: P.set_level(2,1)326sage: P.partition(0)327[[0, 1, 2, 3, 4, 5, 6]]328sage: P.partition(1)329[[0, 1, 2], [3, 4, 5, 6]]330331"""332cdef int i333cdef list partition = [], cell = []334for i from 0 <= i < self.c_ps.degree:335cell.append(self.c_ps.entries[i])336if self.c_ps.levels[i] <= k:337partition.append(cell)338if i < self.c_ps.degree:339cell = []340return partition341342class PythonObjectWrapper:343"""344Instances of this class wrap a Python object and the refinement functions.345"""346def __init__(self, obj, acae_fn, rari_fn, cs_fn, int degree):347"""348Initialize a PythonObjectWrapper.349350EXAMPLE::351352sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonObjectWrapper353sage: def acae(a,b):354... return 0355sage: def rari(a,b,c):356... return 0357sage: def cs(a,b,c,d):358... return 0359sage: from sage.groups.perm_gps.partn_ref.refinement_python import PythonObjectWrapper360sage: P = PythonObjectWrapper(None, acae, rari, cs, 7) # implicit doctest361sage: P.obj362sage: P.degree3637364sage: P.acae_fn365<function acae at ...>366sage: P.rari_fn367<function rari at ...>368sage: P.cs_fn369<function cs at ...>370371"""372self.degree = degree373self.obj = obj374self.acae_fn = acae_fn375self.rari_fn = rari_fn376self.cs_fn = cs_fn377378cdef bint all_children_are_equivalent_python(PartitionStack *PS, void *S):379"""380Python conversion of all_children_are_equivalent function.381"""382cdef PythonPartitionStack Py_PS = PythonPartitionStack(PS.degree)383cdef object S_obj = <object> S384PS_copy_from_to(PS, Py_PS.c_ps)385return S_obj.acae_fn(Py_PS, S_obj.obj)386387cdef int refine_and_return_invariant_python(PartitionStack *PS, void *S, int *cells_to_refine_by, int ctrb_len):388"""389Python conversion of refine_and_return_invariant function.390"""391cdef PythonPartitionStack Py_PS = PythonPartitionStack(PS.degree)392cdef object S_obj = <object> S393PS_copy_from_to(PS, Py_PS.c_ps)394cdef int i395cdef list ctrb_py = [cells_to_refine_by[i] for i from 0 <= i < ctrb_len]396return S_obj.rari_fn(Py_PS, S_obj.obj, ctrb_py)397398cdef int compare_structures_python(int *gamma_1, int *gamma_2, void *S1, void *S2):399"""400Python conversion of compare_structures function.401"""402cdef int i403cdef object S1_obj = <object> S1, S2_obj = <object> S2404cdef list gamma_1_py = [gamma_1[i] for i from 0 <= i < S1_obj.degree]405cdef list gamma_2_py = [gamma_2[i] for i from 0 <= i < S1_obj.degree]406return S1_obj.cs_fn(gamma_1_py, gamma_2_py, S1_obj.obj, S2_obj.obj)407408def aut_gp_and_can_lab_python(S, partition, n,409all_children_are_equivalent,410refine_and_return_invariant,411compare_structures,412canonical_label, base, order):413"""414Calls the automorphism group and canonical label function.415416INPUT::417418S -- the object to examine419partition -- an ordered partition, as a list of lists420n -- the degree of the automorphism group to be computed421422::423424all_children_are_equivalent -- Python function of "signature":425bool all_children_are_equivalent(PythonPartitionStack, object)426refine_and_return_invariant -- Python function of "signature":427int refine_and_return_invariant(PythonPartitionStack, object, list)428compare_structures -- Python function of "signature":429int compare_structures(list, list, object, object)430(see automorphism_group_canonical_label.pyx for more documentation)431432::433434canonical_label -- boolean; whether to search for a canonical label435base -- boolean; whether to return a base for the automorphism group436order -- boolean; whether to return the order of the automorphism group437438EXAMPLE::439440sage: from sage.groups.perm_gps.partn_ref.refinement_python import aut_gp_and_can_lab_python441sage: def acae(a,b):442... return 0443sage: def rari(a,b,c):444... return 0445sage: def cs(a,b,c,d):446... return 0447sage: aut_gp_and_can_lab_python(None, [[0,1,2,3],[4,5]], 6, acae, rari, cs, True, True, True)448([[0, 1, 3, 2, 4, 5],449[0, 2, 1, 3, 4, 5],450[1, 0, 2, 3, 4, 5],451[0, 1, 2, 3, 5, 4]],452[0, 1, 2, 3, 4, 5],453[4, 0, 1, 2],45448)455sage: factorial(4)*factorial(2)45648457458"""459obj_wrapper = PythonObjectWrapper(S, all_children_are_equivalent, refine_and_return_invariant, compare_structures, n)460cdef aut_gp_and_can_lab *output461cdef PythonPartitionStack Py_PS = PythonPartitionStack(n)462cdef int i, j463cdef Integer I464465cdef PartitionStack *part = PS_from_list(partition)466if part is NULL:467raise MemoryError468469output = get_aut_gp_and_can_lab(<void *> obj_wrapper, part, n,470&all_children_are_equivalent_python,471&refine_and_return_invariant_python,472&compare_structures_python,473canonical_label, NULL)474475list_of_gens = []476for i from 0 <= i < output.num_gens:477list_of_gens.append([output.generators[j+i*n] for j from 0 <= j < n])478return_tuple = [list_of_gens]479if canonical_label:480return_tuple.append([output.relabeling[i] for i from 0 <= i < n])481if base:482return_tuple.append([output.group.base_orbits[i][0] for i from 0 <= i < output.group.base_size])483if order:484I = Integer()485SC_order(output.group, 0, I.value)486return_tuple.append(I)487PS_dealloc(part)488sage_free(output.generators)489SC_dealloc(output.group)490if canonical_label:491sage_free(output.relabeling)492sage_free(output)493if len(return_tuple) == 1:494return return_tuple[0]495else:496return tuple(return_tuple)497498499def double_coset_python(S1, S2, partition1, ordering2, n,500all_children_are_equivalent,501refine_and_return_invariant,502compare_structures):503"""504Calls the double coset function.505506INPUT::507508S1, S2 -- the objects to examine509partition1 -- an ordered partition, as a list of lists510ordering2 -- represents a partition of the points of S2,511as a relabeling of partition1512n -- the degree513514::515516all_children_are_equivalent -- Python function of "signature":517bool all_children_are_equivalent(PythonPartitionStack, object)518refine_and_return_invariant -- Python function of "signature":519int refine_and_return_invariant(PythonPartitionStack, object, list)520compare_structures -- Python function of "signature":521int compare_structures(list, list, object, object)522(see double_coset.pyx for more documentation)523524EXAMPLE::525526sage: from sage.groups.perm_gps.partn_ref.refinement_python import double_coset_python527sage: def acae(a,b):528... return 0529sage: def rari(a,b,c):530... return 0531sage: def cs(a,b,c,d):532... return 0533sage: double_coset_python(None, None, [[0,1,2,3],[4,5]], [2,3,1,5,0,4], 6, acae, rari, cs)534[1, 2, 3, 5, 0, 4]535536sage: def compare_lists(p1,p2,l1,l2):537... for i in xrange(len(l1)):538... j = cmp(l1[p1[i]], l2[p2[i]])539... if j != 0: return j540... return 0541542sage: double_coset_python([0,0,1], [1,0,0], [[0,1,2]], [0,1,2], 3, acae, rari, compare_lists)543[1, 2, 0]544545"""546obj_wrapper1 = PythonObjectWrapper(S1, all_children_are_equivalent, refine_and_return_invariant, compare_structures, n)547obj_wrapper2 = PythonObjectWrapper(S2, all_children_are_equivalent, refine_and_return_invariant, compare_structures, n)548549cdef PartitionStack *part = PS_from_list(partition1)550cdef int *ordering = <int *> sage_malloc(n * sizeof(int))551if part is NULL or ordering is NULL:552if part is not NULL: PS_dealloc(part)553if ordering is not NULL: sage_free(ordering)554raise MemoryError555for i from 0 <= i < n:556ordering[i] = ordering2[i]557558cdef int *output = double_coset(<void *> obj_wrapper1, <void *> obj_wrapper2,559part, ordering, n,560&all_children_are_equivalent_python,561&refine_and_return_invariant_python,562&compare_structures_python, NULL)563564PS_dealloc(part)565sage_free(ordering)566567if output is NULL:568return False569else:570output_py = [output[i] for i from 0 <= i < n]571sage_free(output)572return output_py573574575576577