Path: blob/master/src/sage/groups/perm_gps/partn_ref/refinement_binary.pyx
8815 views
"""1Partition backtrack functions for binary codes23DOCTEST:4sage: import sage.groups.perm_gps.partn_ref.refinement_binary56REFERENCE:78[1] McKay, Brendan D. Practical Graph Isomorphism. Congressus Numerantium,9Vol. 30 (1981), pp. 45-87.1011[2] Leon, Jeffrey. Permutation Group Algorithms Based on Partitions, I:12Theory and Algorithms. J. Symbolic Computation, Vol. 12 (1991), pp.13533-583.1415"""1617#*****************************************************************************18# Copyright (C) 2006 - 2011 Robert L. Miller <[email protected]>19#20# Distributed under the terms of the GNU General Public License (GPL)21# http://www.gnu.org/licenses/22#*****************************************************************************2324include 'data_structures_pyx.pxi' # includes bitsets2526from sage.matrix.matrix import is_Matrix2728cdef class LinearBinaryCodeStruct(BinaryCodeStruct):2930def __cinit__(self, matrix):31cdef int i,j32self.degree = matrix.ncols()33self.dimension = matrix.nrows()34if self.dimension >= (sizeof(int) << 3):35raise NotImplementedError36# By the time the dimension gets this big, the computation is infeasible anyway...37self.nwords = 1<<self.dimension3839self.basis = <bitset_s *> sage_malloc(self.dimension * sizeof(bitset_s))40self.scratch_bitsets = <bitset_s *> sage_malloc((2*self.dimension+2) * sizeof(bitset_s))41self.alpha_is_wd = <bitset_s *> sage_malloc(sizeof(bitset_s))42self.word_ps = PS_new(self.nwords, 1)43self.alpha = <int *> sage_malloc((self.nwords+self.degree) * sizeof(int))44self.scratch = <int *> sage_malloc((3*self.nwords+3*self.degree+2) * sizeof(int))4546if self.basis is NULL or self.scratch_bitsets is NULL \47or self.alpha_is_wd is NULL or self.word_ps is NULL \48or self.alpha is NULL or self.scratch is NULL:49sage_free(self.basis)50sage_free(self.scratch_bitsets)51sage_free(self.alpha_is_wd)52PS_dealloc(self.word_ps)53sage_free(self.alpha)54sage_free(self.scratch)55raise MemoryError5657cdef bint memerr = 058for i from 0 <= i < self.dimension:59try: bitset_init(&self.basis[i], self.degree)60except MemoryError:61for j from 0 <= j < i:62bitset_free(&self.basis[j])63memerr = 164break65if not memerr:66for i from 0 <= i < 2*self.dimension+2:67try: bitset_init(&self.scratch_bitsets[i], self.degree)68except MemoryError:69for j from 0 <= j < i:70bitset_free(&self.scratch_bitsets[j])71for j from 0 <= j < self.dimension:72bitset_free(&self.basis[j])73memerr = 174break75if not memerr:76try: bitset_init(self.alpha_is_wd, self.nwords + self.degree)77except MemoryError:78for j from 0 <= j < 2*self.dimension+2:79bitset_free(&self.scratch_bitsets[j])80for j from 0 <= j < self.dimension:81bitset_free(&self.basis[j])82memerr = 183if memerr:84sage_free(self.basis); sage_free(self.scratch_bitsets)85sage_free(self.alpha_is_wd); PS_dealloc(self.word_ps)86sage_free(self.alpha); sage_free(self.scratch)87raise MemoryError88else:89bitset_zero(self.alpha_is_wd)90for j from 0 <= j < self.dimension:91bitset_zero(&self.basis[j])9293for i,j in matrix.nonzero_positions():94bitset_set(&self.basis[i], j)9596self.output = NULL97self.ith_word = &ith_word_linear9899def run(self, partition=None):100"""101Perform the canonical labeling and automorphism group computation,102storing results to self.103104INPUT:105partition -- an optional list of lists partition of the columns.106default is the unit partition.107108EXAMPLES:109sage: from sage.groups.perm_gps.partn_ref.refinement_binary import LinearBinaryCodeStruct110111sage: B = LinearBinaryCodeStruct(matrix(GF(2),[[1,0,1],[0,1,1]]))112sage: B.run()113sage: B.automorphism_group()114([[0, 2, 1], [1, 0, 2]], 6, [0, 1])115sage: B.canonical_relabeling()116[0, 1, 2]117118sage: B = LinearBinaryCodeStruct(matrix(GF(2),[[1,1,1,1]]))119sage: B.automorphism_group()120([[0, 1, 3, 2], [0, 2, 1, 3], [1, 0, 2, 3]], 24, [0, 1, 2])121sage: B.canonical_relabeling()122[0, 1, 2, 3]123124sage: B = LinearBinaryCodeStruct(matrix(GF(2),[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]]))125sage: B.automorphism_group()[1] == factorial(14)126True127128sage: M = Matrix(GF(2),[\129... [1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0],\130... [0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0],\131... [0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1],\132... [0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1],\133... [0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1]])134sage: B = LinearBinaryCodeStruct(M)135sage: B.automorphism_group()[1]136322560137138sage: M = Matrix(GF(2),[\139... [1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0],\140... [0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0],\141... [0,0,0,0,0,1,0,1,0,0,0,1,1,1,1,1,1],\142... [0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,1]])143sage: B = LinearBinaryCodeStruct(M)144sage: B.automorphism_group()[1]1452304146147sage: M=Matrix(GF(2),[\148... [1,0,0,1,1,1,1,0,0,1,0,0,0,0,0,0,0],\149... [0,1,0,0,1,1,1,1,0,0,1,0,0,0,0,0,0],\150... [0,0,1,0,0,1,1,1,1,0,0,1,0,0,0,0,0],\151... [0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0,0],\152... [0,0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0],\153... [0,0,0,0,0,1,0,0,1,1,1,1,0,0,1,0,0],\154... [0,0,0,0,0,0,1,0,0,1,1,1,1,0,0,1,0],\155... [0,0,0,0,0,0,0,1,0,0,1,1,1,1,0,0,1]])156sage: B = LinearBinaryCodeStruct(M)157sage: B.automorphism_group()[1]158136159160sage: M = Matrix(GF(2),[\161... [1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0],162... [0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0],163... [0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1,1,1],164... [0,0,1,1,0,0,0,0,0,0,1,1,1,1,0,0,1,1],165... [0,0,0,1,0,0,0,1,0,1,0,1,0,1,1,1,0,1],166... [0,1,0,0,0,1,0,0,0,1,1,1,0,1,0,1,1,0]])167sage: B = LinearBinaryCodeStruct(M)168sage: B.automorphism_group()[1]1692160170171sage: M=Matrix(GF(2),[\172... [0,1,0,1,1,1,0,0,0,1,0,0,0,1,0,0,0,1,1,1,0,1],\173... [1,0,1,1,1,0,0,0,1,0,0,0,1,0,0,0,1,1,1,0,1,0],\174... [0,1,1,1,0,0,0,1,0,0,1,1,0,0,0,1,1,1,0,1,0,0],\175... [1,1,1,0,0,0,1,0,0,1,0,0,0,0,1,1,1,0,1,0,0,1],\176... [1,1,0,0,0,1,0,0,1,0,1,0,0,1,1,1,0,1,0,0,1,0],\177... [1,0,0,0,1,0,0,1,0,1,1,0,1,1,1,0,1,0,0,1,0,0],\178... [0,0,0,1,0,0,1,0,1,1,1,1,1,1,0,1,0,0,1,0,0,0],\179... [0,0,1,0,0,1,0,1,1,1,0,1,1,0,1,0,0,1,0,0,0,1],\180... [0,1,0,0,1,0,1,1,1,0,0,1,0,1,0,0,1,0,0,0,1,1],\181... [1,0,0,1,0,1,1,1,0,0,0,0,1,0,0,1,0,0,0,1,1,1],\182... [0,0,1,0,1,1,1,0,0,0,1,1,0,0,1,0,0,0,1,1,1,0]])183sage: B = LinearBinaryCodeStruct(M)184sage: B.automorphism_group()[1]185887040186187sage: M = Matrix(GF(2),[\188... [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],189... [0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],190... [0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0],191... [0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0],192... [0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0],193... [0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0],194... [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],195... [1,0,1,0,1,0,1,0,1,1,0,0,0,0,0,0,1,1,0,0],196... [1,1,0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,1,0,0],197... [1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,0,1,0]])198sage: B = LinearBinaryCodeStruct(M)199sage: B.automorphism_group()[1]200294912201202sage: M = Matrix(GF(2), [\203... [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],204... [0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],205... [0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0],206... [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0],207... [0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0],208... [0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0],209... [0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1],210... [0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,0,0,1,1,1,0,1],211... [0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,1,1,1,1,0,0,0,1]])212sage: B = LinearBinaryCodeStruct(M)213sage: B.automorphism_group()[1]214442368215216sage: M = Matrix(GF(2), [\217... [1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0],\218... [1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,1,0,0,0,0,1]])219sage: B = LinearBinaryCodeStruct(M)220sage: B.automorphism_group()[1]22117868913969917295853568000000222223"""224cdef int i, n = self.degree225cdef PartitionStack *part226if partition is None:227part = PS_new(n, 1)228else:229part = PS_from_list(partition)230if part is NULL:231raise MemoryError232233self.first_time = 1234235self.output = get_aut_gp_and_can_lab(<void *>self, part, n, &all_children_are_equivalent, &refine_by_bip_degree, &compare_linear_codes, 1, NULL, NULL, NULL)236237PS_dealloc(part)238239def automorphism_group(self):240"""241Returns a list of generators of the automorphism group, along with its242order and a base for which the list of generators is a strong generating243set.244245EXAMPLE: (For more examples, see self.run())246sage: from sage.groups.perm_gps.partn_ref.refinement_binary import LinearBinaryCodeStruct247248sage: B = LinearBinaryCodeStruct(matrix(GF(2),[[1,1,1,1]]))249sage: B.automorphism_group()250([[0, 1, 3, 2], [0, 2, 1, 3], [1, 0, 2, 3]], 24, [0, 1, 2])251252"""253cdef int i, j254cdef list generators, base255cdef Integer order256if self.output is NULL:257self.run()258generators = []259for i from 0 <= i < self.output.num_gens:260generators.append([self.output.generators[i*self.degree + j] for j from 0 <= j < self.degree])261order = Integer()262SC_order(self.output.group, 0, order.value)263base = [self.output.group.base_orbits[i][0] for i from 0 <= i < self.output.group.base_size]264return generators, order, base265266def canonical_relabeling(self):267"""268Returns a canonical relabeling (in list permutation format).269270EXAMPLES: (For more examples, see self.run())271sage: from sage.groups.perm_gps.partn_ref.refinement_binary import LinearBinaryCodeStruct272273sage: B = LinearBinaryCodeStruct(matrix(GF(2), [[1,1,0]]))274sage: B.automorphism_group()275([[1, 0, 2]], 2, [0])276sage: B.canonical_relabeling()277[1, 2, 0]278sage: B = LinearBinaryCodeStruct(matrix(GF(2), [[1,0,1]]))279sage: B.automorphism_group()280([[2, 1, 0]], 2, [0])281sage: B.canonical_relabeling()282[1, 0, 2]283284"""285cdef int i286if self.output is NULL:287self.run()288return [self.output.relabeling[i] for i from 0 <= i < self.degree]289290def is_isomorphic(self, LinearBinaryCodeStruct other):291"""292Calculate whether self is isomorphic to other.293294EXAMPLES:295sage: from sage.groups.perm_gps.partn_ref.refinement_binary import LinearBinaryCodeStruct296297sage: B = LinearBinaryCodeStruct(Matrix(GF(2), [[1,1,1,1,0,0],[0,0,1,1,1,1]]))298sage: C = LinearBinaryCodeStruct(Matrix(GF(2), [[1,1,1,0,0,1],[1,1,0,1,1,0]]))299sage: B.is_isomorphic(C)300[0, 1, 2, 5, 3, 4]301302"""303cdef int i, n = self.degree304cdef int *output, *ordering305cdef PartitionStack *part306part = PS_new(n, 1)307ordering = <int *> sage_malloc(self.degree * sizeof(int))308output = <int *> sage_malloc(self.degree * sizeof(int))309if part is NULL or ordering is NULL or output is NULL:310PS_dealloc(part)311sage_free(ordering)312sage_free(output)313raise MemoryError314for i from 0 <= i < n:315ordering[i] = i316self.first_time = 1317other.first_time = 1318319cdef bint isomorphic = double_coset(<void *> self, <void *> other, part, ordering, n, &all_children_are_equivalent, &refine_by_bip_degree, &compare_linear_codes, NULL, NULL, output)320321PS_dealloc(part)322sage_free(ordering)323if isomorphic:324output_py = [output[i] for i from 0 <= i < n]325else:326output_py = False327sage_free(output)328return output_py329330def __dealloc__(self):331cdef int j332bitset_free(self.alpha_is_wd)333for j from 0 <= j < 2*self.dimension+2:334bitset_free(&self.scratch_bitsets[j])335for j from 0 <= j < self.dimension:336bitset_free(&self.basis[j])337sage_free(self.basis); sage_free(self.scratch_bitsets)338sage_free(self.alpha_is_wd); PS_dealloc(self.word_ps)339sage_free(self.alpha); sage_free(self.scratch)340if self.output is not NULL:341deallocate_agcl_output(self.output)342343cdef int ith_word_linear(BinaryCodeStruct self, int i, bitset_s *word):344cdef LinearBinaryCodeStruct LBCS = <LinearBinaryCodeStruct> self345cdef int j346bitset_zero(word)347for j from 0 <= j < LBCS.dimension:348if (1<<j)&i:349bitset_xor(word, word, &LBCS.basis[j])350return 0351352cdef class NonlinearBinaryCodeStruct(BinaryCodeStruct):353354def __cinit__(self, arg):355cdef int i,j356if is_Matrix(arg):357self.degree = arg.ncols()358self.nwords = arg.nrows()359elif isinstance(arg, tuple):360assert len(arg) == 2361self.degree, self.nwords = arg362else:363raise NotImplementedError364365self.words = <bitset_s *> sage_malloc(self.nwords * sizeof(bitset_s))366self.scratch_bitsets = <bitset_s *> sage_malloc((4*self.nwords+1) * sizeof(bitset_s))367self.alpha_is_wd = <bitset_s *> sage_malloc(sizeof(bitset_s))368self.word_ps = PS_new(self.nwords, 1)369self.alpha = <int *> sage_malloc((self.nwords+self.degree) * sizeof(int))370self.scratch = <int *> sage_malloc((3*self.nwords+3*self.degree+2) * sizeof(int))371if self.words is NULL or self.scratch_bitsets is NULL \372or self.alpha_is_wd is NULL or self.word_ps is NULL \373or self.alpha is NULL or self.scratch is NULL:374sage_free(self.words)375sage_free(self.scratch_bitsets)376sage_free(self.alpha_is_wd)377PS_dealloc(self.word_ps)378sage_free(self.alpha)379sage_free(self.scratch)380raise MemoryError381382cdef bint memerr = 0383for i from 0 <= i < self.nwords:384try: bitset_init(&self.words[i], self.degree)385except MemoryError:386for j from 0 <= j < i:387bitset_free(&self.words[j])388memerr = 1389break390if not memerr:391for i from 0 <= i < 4*self.nwords:392try: bitset_init(&self.scratch_bitsets[i], self.degree)393except MemoryError:394for j from 0 <= j < i:395bitset_free(&self.scratch_bitsets[j])396for j from 0 <= j < self.nwords:397bitset_free(&self.words[j])398memerr = 1399break400if not memerr:401try: bitset_init(&self.scratch_bitsets[4*self.nwords], self.nwords)402except MemoryError:403for j from 0 <= j < 4*self.nwords:404bitset_free(&self.scratch_bitsets[j])405for j from 0 <= j < self.nwords:406bitset_free(&self.words[j])407memerr = 1408if not memerr:409try: bitset_init(self.alpha_is_wd, self.nwords + self.degree)410except MemoryError:411for j from 0 <= j < 4*self.nwords + 1:412bitset_free(&self.scratch_bitsets[j])413for j from 0 <= j < self.nwords:414bitset_free(&self.words[j])415memerr = 1416if memerr:417sage_free(self.words); sage_free(self.scratch_bitsets)418sage_free(self.alpha_is_wd); PS_dealloc(self.word_ps)419sage_free(self.alpha); sage_free(self.scratch)420raise MemoryError421else:422bitset_zero(self.alpha_is_wd)423for j from 0 <= j < self.nwords:424bitset_zero(&self.words[j])425426if is_Matrix(arg):427for i,j in arg.nonzero_positions():428bitset_set(&self.words[i], j)429430self.output = NULL431self.ith_word = &ith_word_nonlinear432433def __dealloc__(self):434cdef int j435bitset_free(self.alpha_is_wd)436for j from 0 <= j < 4*self.nwords + 1:437bitset_free(&self.scratch_bitsets[j])438for j from 0 <= j < self.nwords:439bitset_free(&self.words[j])440sage_free(self.words); sage_free(self.scratch_bitsets)441sage_free(self.alpha_is_wd); PS_dealloc(self.word_ps)442sage_free(self.alpha); sage_free(self.scratch)443if self.output is not NULL:444deallocate_agcl_output(self.output)445446def run(self, partition=None):447"""448Perform the canonical labeling and automorphism group computation,449storing results to self.450451INPUT:452partition -- an optional list of lists partition of the columns.453default is the unit partition.454455EXAMPLES:456sage: from sage.groups.perm_gps.partn_ref.refinement_binary import NonlinearBinaryCodeStruct457458sage: B = NonlinearBinaryCodeStruct(Matrix(GF(2), [[1,0,0,0],[0,0,1,0]]))459sage: B.run()460sage: B.automorphism_group()461([[2, 1, 0, 3], [0, 3, 2, 1]], 4, [1, 0])462sage: B.canonical_relabeling()463[2, 0, 3, 1]464465sage: B = NonlinearBinaryCodeStruct(Matrix(GF(2), [[1,1,1,0],[1,1,0,1],[1,0,1,1],[0,1,1,1]]))466sage: B.run()467sage: B.automorphism_group()468([[0, 1, 3, 2], [0, 2, 1, 3], [1, 0, 2, 3]], 24, [0, 1, 2])469sage: B.canonical_relabeling()470[0, 1, 2, 3]471472sage: B = NonlinearBinaryCodeStruct(Matrix(GF(2), [[1,1,1,0,0,0],[1,1,0,1,0,0],[1,0,1,1,0,0],[0,1,1,1,0,0],[0,0,0,0,1,0],[0,0,0,0,0,1]]))473sage: B.run()474sage: B.automorphism_group()475([[0, 1, 3, 2, 4, 5],476[0, 2, 1, 3, 4, 5],477[1, 0, 2, 3, 4, 5],478[0, 1, 2, 3, 5, 4]],47948,480[4, 0, 1, 2])481sage: B.canonical_relabeling()482[2, 3, 4, 5, 0, 1]483484"""485cdef int n = self.degree486cdef PartitionStack *part487if partition is None:488part = PS_new(n, 1)489else:490part = PS_from_list(partition)491if part is NULL:492raise MemoryError493self.first_time = 1494495self.output = get_aut_gp_and_can_lab(<void *> self, part, self.degree, &all_children_are_equivalent, &refine_by_bip_degree, &compare_nonlinear_codes, 1, NULL, NULL, NULL)496497PS_dealloc(part)498499def automorphism_group(self):500"""501Returns a list of generators of the automorphism group, along with its502order and a base for which the list of generators is a strong generating503set.504505EXAMPLE: (For more examples, see self.run())506sage: from sage.groups.perm_gps.partn_ref.refinement_binary import NonlinearBinaryCodeStruct507508sage: B = NonlinearBinaryCodeStruct(Matrix(GF(2), [[1,1,1,0,0,0],[1,1,0,1,0,0],[1,0,1,1,0,0],[0,1,1,1,0,0],[0,0,0,0,1,0],[0,0,0,0,0,1]]))509sage: B.run()510sage: B.automorphism_group()511([[0, 1, 3, 2, 4, 5],512[0, 2, 1, 3, 4, 5],513[1, 0, 2, 3, 4, 5],514[0, 1, 2, 3, 5, 4]],51548,516[4, 0, 1, 2])517518"""519cdef int i, j520cdef list generators, base521cdef Integer order522if self.output is NULL:523self.run()524generators = []525for i from 0 <= i < self.output.num_gens:526generators.append([self.output.generators[i*self.degree + j] for j from 0 <= j < self.degree])527order = Integer()528SC_order(self.output.group, 0, order.value)529base = [self.output.group.base_orbits[i][0] for i from 0 <= i < self.output.group.base_size]530return generators, order, base531532def canonical_relabeling(self):533"""534Returns a canonical relabeling (in list permutation format).535536EXAMPLES: (For more examples, see self.run())537sage: from sage.groups.perm_gps.partn_ref.refinement_binary import NonlinearBinaryCodeStruct538539sage: B = NonlinearBinaryCodeStruct(Matrix(GF(2), [[1,1,1,0,0,0],[1,1,0,1,0,0],[1,0,1,1,0,0],[0,1,1,1,0,0],[0,0,0,0,1,0],[0,0,0,0,0,1]]))540sage: B.run()541sage: B.canonical_relabeling()542[2, 3, 4, 5, 0, 1]543544"""545cdef int i546if self.output is NULL:547self.run()548return [self.output.relabeling[i] for i from 0 <= i < self.degree]549550def is_isomorphic(self, NonlinearBinaryCodeStruct other):551"""552Calculate whether self is isomorphic to other.553554EXAMPLES:555sage: from sage.groups.perm_gps.partn_ref.refinement_binary import NonlinearBinaryCodeStruct556557sage: B = NonlinearBinaryCodeStruct(Matrix(GF(2), [[1,1,1,1,0,0],[0,0,1,1,1,1]]))558sage: C = NonlinearBinaryCodeStruct(Matrix(GF(2), [[1,1,0,0,1,1],[1,1,1,1,0,0]]))559sage: B.is_isomorphic(C)560[2, 3, 0, 1, 4, 5]561562"""563cdef int i, n = self.degree564cdef int *output, *ordering565cdef PartitionStack *part566part = PS_new(n, 1)567ordering = <int *> sage_malloc(n * sizeof(int))568output = <int *> sage_malloc(n * sizeof(int))569if part is NULL or ordering is NULL or output is NULL:570PS_dealloc(part)571sage_free(ordering)572sage_free(output)573raise MemoryError574for i from 0 <= i < n:575ordering[i] = i576self.first_time = 1577other.first_time = 1578579cdef bint isomorphic = double_coset(<void *> self, <void *> other, part, ordering, n, &all_children_are_equivalent, &refine_by_bip_degree, &compare_nonlinear_codes, NULL, NULL, output)580581PS_dealloc(part)582sage_free(ordering)583if isomorphic:584output_py = [output[i] for i from 0 <= i < n]585else:586output_py = False587sage_free(output)588return output_py589590cdef int ith_word_nonlinear(BinaryCodeStruct self, int i, bitset_s *word):591cdef NonlinearBinaryCodeStruct NBCS = <NonlinearBinaryCodeStruct> self592bitset_copy(word, &NBCS.words[i])593return 0594595cdef int refine_by_bip_degree(PartitionStack *col_ps, void *S, int *cells_to_refine_by, int ctrb_len):596"""597Refines the input partition by checking degrees of vertices to the given598cells in the associated bipartite graph (vertices split into columns and599words).600601INPUT:602col_ps -- a partition stack, whose finest partition is the partition to be603refined.604S -- a binary code struct object605cells_to_refine_by -- a list of pointers to cells to check degrees against606in refining the other cells (updated in place)607ctrb_len -- how many cells in cells_to_refine_by608609OUTPUT:610611An integer $I$ invariant under the orbits of $S_n$. That is, if $\gamma$ is a612permutation of the columns, then613$$ I(G, PS, cells_to_refine_by) = I( \gamma(G), \gamma(PS), \gamma(cells_to_refine_by) ) .$$614615"""616cdef BinaryCodeStruct BCS = <BinaryCodeStruct> S617cdef int current_cell_against = 0618cdef int current_cell, i, r, j619cdef int first_largest_subcell620cdef int invariant = 0621cdef PartitionStack *word_ps = BCS.word_ps622cdef int *ctrb = BCS.alpha623cdef bitset_s *ctrb_is_wd = BCS.alpha_is_wd624625word_ps.depth = col_ps.depth626PS_clear(word_ps)627bitset_zero(ctrb_is_wd)628memcpy(ctrb, cells_to_refine_by, ctrb_len * sizeof(int))629if BCS.first_time:630BCS.first_time = 0631ctrb[ctrb_len] = 0632bitset_set(ctrb_is_wd, ctrb_len)633ctrb_len += 1634cdef int *col_degrees = BCS.scratch # len degree635cdef int *col_counts = &BCS.scratch[BCS.degree] # len nwords+1636cdef int *col_output = &BCS.scratch[BCS.degree + BCS.nwords + 1] # len degree637cdef int *word_degrees = &BCS.scratch[2*BCS.degree + BCS.nwords + 1] # len nwords638cdef int *word_counts = &BCS.scratch[2*BCS.degree + 2*BCS.nwords + 1] # len degree+1639cdef int *word_output = &BCS.scratch[3*BCS.degree + 2*BCS.nwords + 2] # len nwords640cdef bint necessary_to_split_cell641cdef int against_index642while not (PS_is_discrete(col_ps) and PS_is_discrete(word_ps)) and current_cell_against < ctrb_len:643invariant += 1644current_cell = 0645if bitset_check(ctrb_is_wd, current_cell_against):646while current_cell < col_ps.degree:647invariant += 8648i = current_cell649necessary_to_split_cell = 0650while 1:651col_degrees[i-current_cell] = col_degree(col_ps, BCS, i, ctrb[current_cell_against], word_ps)652if col_degrees[i-current_cell] != col_degrees[0]:653necessary_to_split_cell = 1654i += 1655if col_ps.levels[i-1] <= col_ps.depth:656break657# now, i points to the next cell (before refinement)658if necessary_to_split_cell:659invariant += 8660first_largest_subcell = sort_by_function_codes(col_ps, current_cell, col_degrees, col_counts, col_output, BCS.nwords+1)661invariant += col_degree(col_ps, BCS, i-1, ctrb[current_cell_against], word_ps)662invariant += first_largest_subcell663against_index = current_cell_against664while against_index < ctrb_len:665if ctrb[against_index] == current_cell and not bitset_check(ctrb_is_wd, against_index):666ctrb[against_index] = first_largest_subcell667break668against_index += 1669r = current_cell670while 1:671if r == current_cell or col_ps.levels[r-1] == col_ps.depth:672if r != first_largest_subcell:673ctrb[ctrb_len] = r674ctrb_len += 1675r += 1676if r >= i:677break678invariant += (i - current_cell)679current_cell = i680else:681while current_cell < word_ps.degree:682invariant += 64683i = current_cell684necessary_to_split_cell = 0685while 1:686word_degrees[i-current_cell] = word_degree(word_ps, BCS, i, ctrb[current_cell_against], col_ps)687if word_degrees[i-current_cell] != word_degrees[0]:688necessary_to_split_cell = 1689i += 1690if word_ps.levels[i-1] <= col_ps.depth:691break692# now, i points to the next cell (before refinement)693if necessary_to_split_cell:694invariant += 64695first_largest_subcell = sort_by_function_codes(word_ps, current_cell, word_degrees, word_counts, word_output, BCS.degree+1)696invariant += word_degree(word_ps, BCS, i-1, ctrb[current_cell_against], col_ps)697invariant += first_largest_subcell698against_index = current_cell_against699while against_index < ctrb_len:700if ctrb[against_index] == current_cell and bitset_check(ctrb_is_wd, against_index):701ctrb[against_index] = first_largest_subcell702break703against_index += 1704r = current_cell705while 1:706if r == current_cell or word_ps.levels[r-1] == col_ps.depth:707if r != first_largest_subcell:708ctrb[ctrb_len] = r709bitset_set(ctrb_is_wd, ctrb_len)710ctrb_len += 1711r += 1712if r >= i:713break714invariant += (i - current_cell)715current_cell = i716current_cell_against += 1717return invariant718719cdef int compare_linear_codes(int *gamma_1, int *gamma_2, void *S1, void *S2, int degree):720"""721Compare gamma_1(S1) and gamma_2(S2).722723Return return -1 if gamma_1(S1) < gamma_2(S2), 0 if gamma_1(S1) == gamma_2(S2),7241 if gamma_1(S1) > gamma_2(S2). (Just like the python \code{cmp}) function.725726Abstractly, what this function does is relabel the basis of B by gamma_1 and727gamma_2, run a row reduction on each, and verify that the matrices are the728same, which holds if and only if the rowspan is the same. In practice, if729the codes are not equal, the reductions (which are carried out in an730interleaved way) will terminate as soon as this is discovered, and whichever731code has a 1 in the entry in which they differ is reported as larger.732733INPUT:734gamma_1, gamma_2 -- list permutations (inverse)735S1, S2 -- binary code struct objects736737"""738cdef int i, piv_loc_1, piv_loc_2, cur_col, cur_row=0739cdef bint is_pivot_1, is_pivot_2740cdef LinearBinaryCodeStruct BCS1 = <LinearBinaryCodeStruct> S1741cdef LinearBinaryCodeStruct BCS2 = <LinearBinaryCodeStruct> S2742cdef bitset_s *basis_1 = BCS1.scratch_bitsets # len = dim743cdef bitset_s *basis_2 = &BCS1.scratch_bitsets[BCS1.dimension] # len = dim744cdef bitset_s *pivots = &BCS1.scratch_bitsets[2*BCS1.dimension] # len 1745cdef bitset_s *temp = &BCS1.scratch_bitsets[2*BCS1.dimension+1] # len 1746for i from 0 <= i < BCS1.dimension:747bitset_copy(&basis_1[i], &BCS1.basis[i])748bitset_copy(&basis_2[i], &BCS2.basis[i])749bitset_zero(pivots)750for cur_col from 0 <= cur_col < BCS1.degree:751is_pivot_1 = 0752is_pivot_2 = 0753for i from cur_row <= i < BCS1.dimension:754if bitset_check(&basis_1[i], gamma_1[cur_col]):755is_pivot_1 = 1756piv_loc_1 = i757break758for i from cur_row <= i < BCS1.dimension:759if bitset_check(&basis_2[i], gamma_2[cur_col]):760is_pivot_2 = 1761piv_loc_2 = i762break763if is_pivot_1 != is_pivot_2:764return <int>is_pivot_2 - <int>is_pivot_1765if is_pivot_1:766bitset_set(pivots, cur_col)767if piv_loc_1 != cur_row:768bitset_copy(temp, &basis_1[piv_loc_1])769bitset_copy(&basis_1[piv_loc_1], &basis_1[cur_row])770bitset_copy(&basis_1[cur_row], temp)771if piv_loc_2 != cur_row:772bitset_copy(temp, &basis_2[piv_loc_2])773bitset_copy(&basis_2[piv_loc_2], &basis_2[cur_row])774bitset_copy(&basis_2[cur_row], temp)775for i from 0 <= i < cur_row:776if bitset_check(&basis_1[i], gamma_1[cur_col]):777bitset_xor(&basis_1[i], &basis_1[i], &basis_1[cur_row])778if bitset_check(&basis_2[i], gamma_2[cur_col]):779bitset_xor(&basis_2[i], &basis_2[i], &basis_2[cur_row])780for i from cur_row < i < BCS1.dimension:781if bitset_check(&basis_1[i], gamma_1[cur_col]):782bitset_xor(&basis_1[i], &basis_1[i], &basis_1[cur_row])783if bitset_check(&basis_2[i], gamma_2[cur_col]):784bitset_xor(&basis_2[i], &basis_2[i], &basis_2[cur_row])785cur_row += 1786else:787for i from 0 <= i < cur_row:788if bitset_check(&basis_1[i], gamma_1[cur_col]) != bitset_check(&basis_2[i], gamma_2[cur_col]):789return <int>bitset_check(&basis_2[i], gamma_2[cur_col]) - <int>bitset_check(&basis_1[i], gamma_1[cur_col])790return 0791792cdef int compare_nonlinear_codes(int *gamma_1, int *gamma_2, void *S1, void *S2, int degree):793"""794Compare gamma_1(S1) and gamma_2(S2).795796Return return -1 if gamma_1(S1) < gamma_2(S2), 0 if gamma_1(S1) == gamma_2(S2),7971 if gamma_1(S1) > gamma_2(S2). (Just like the python \code{cmp}) function.798799INPUT:800gamma_1, gamma_2 -- list permutations (inverse)801S1, S2 -- a binary code struct object802803"""804cdef int side=0, i, start, end, n_one_1, n_one_2, cur_col805cdef int where_0, where_1806cdef NonlinearBinaryCodeStruct BCS1 = <NonlinearBinaryCodeStruct> S1807cdef NonlinearBinaryCodeStruct BCS2 = <NonlinearBinaryCodeStruct> S2808cdef bitset_s *B_1_0 = BCS1.scratch_bitsets # nwords of len degree809cdef bitset_s *B_1_1 = &BCS1.scratch_bitsets[BCS1.nwords] # nwords of len degree810cdef bitset_s *B_2_0 = &BCS1.scratch_bitsets[2*BCS1.nwords] # nwords of len degree811cdef bitset_s *B_2_1 = &BCS1.scratch_bitsets[3*BCS1.nwords] # nwords of len degree812cdef bitset_s *dividers = &BCS1.scratch_bitsets[4*BCS1.nwords] # 1 of len nwords813cdef bitset_s *B_1_this, *B_1_other, *B_2_this, *B_2_other814for i from 0 <= i < BCS1.nwords:815bitset_copy(&B_1_0[i], &BCS1.words[i])816bitset_copy(&B_2_0[i], &BCS2.words[i])817bitset_zero(dividers)818bitset_set(dividers, BCS1.nwords-1)819820for cur_col from 0 <= cur_col < BCS1.degree:821if side == 0:822B_1_this = B_1_0823B_1_other = B_1_1824B_2_this = B_2_0825B_2_other = B_2_1826else:827B_1_this = B_1_1828B_1_other = B_1_0829B_2_this = B_2_1830B_2_other = B_2_0831side ^= 1832start = 0833while start < BCS1.nwords:834end = start835while not bitset_check(dividers, end):836end += 1837end += 1838n_one_1 = 0839n_one_2 = 0840for i from start <= i < end:841n_one_1 += bitset_check(&B_1_this[i], gamma_1[cur_col])842n_one_2 += bitset_check(&B_2_this[i], gamma_2[cur_col])843if n_one_1 != n_one_2:844if n_one_1 > n_one_2:845return 1846else:847return -1848where_0 = start849where_1 = end - n_one_1850if start < where_1 and where_1 < end:851bitset_set(dividers, where_1 - 1)852for i from start <= i < end:853if bitset_check(&B_1_this[i], gamma_1[cur_col]):854bitset_copy(&B_1_other[where_1], &B_1_this[i])855where_1 += 1856else:857bitset_copy(&B_1_other[where_0], &B_1_this[i])858where_0 += 1859where_0 = start860where_1 = end - n_one_2861for i from start <= i < end:862if bitset_check(&B_2_this[i], gamma_2[cur_col]):863bitset_copy(&B_2_other[where_1], &B_2_this[i])864where_1 += 1865else:866bitset_copy(&B_2_other[where_0], &B_2_this[i])867where_0 += 1868start = end869870return 0871872cdef bint all_children_are_equivalent(PartitionStack *col_ps, void *S):873"""874Returns True if any refinement of the current partition results in the same875structure.876877WARNING:878Converse does not hold in general! See Lemma 2.25 of [1] for details, noting879that the binary code is interpreted as a bipartite graph (see module docs880for details).881882INPUT:883col_ps -- the partition stack to be checked884S -- a binary code struct object885"""886cdef BinaryCodeStruct BCS = <BinaryCodeStruct> S887cdef PartitionStack *word_ps = BCS.word_ps888cdef int i, n = col_ps.degree + BCS.nwords889cdef bint in_cell = 0890cdef int nontrivial_cells = 0891cdef int total_cells = PS_num_cells(col_ps) + PS_num_cells(word_ps)892if n <= total_cells + 4:893return 1894for i from 0 <= i < BCS.nwords:895if word_ps.levels[i] <= col_ps.depth:896if in_cell:897nontrivial_cells += 1898in_cell = 0899else:900in_cell = 1901in_cell = 0902for i from 0 <= i < BCS.degree:903if col_ps.levels[i] <= col_ps.depth:904if in_cell:905nontrivial_cells += 1906in_cell = 0907else:908in_cell = 1909if n == total_cells + nontrivial_cells:910return 1911if n == total_cells + nontrivial_cells + 1:912return 1913return 0914915cdef inline int word_degree(PartitionStack *word_ps, BinaryCodeStruct BCS, int entry, int cell_index, PartitionStack *col_ps):916"""917Returns the number of edges from the vertex corresponding to entry to918vertices in the cell corresponding to cell_index.919920INPUT:921word_ps -- the partition stack to be checked922col_ps -- corresponding partition stack on columns923BCS -- a binary code struct object924entry -- the position of the vertex in question in the entries of word_ps925cell_index -- the starting position of the cell in question in the entries926of PS927"""928cdef bitset_t cell, word929cdef int i, h930bitset_init(cell, BCS.degree)931bitset_zero(cell)932bitset_init(word, BCS.degree)933entry = word_ps.entries[entry]934bitset_set(cell, col_ps.entries[cell_index])935while col_ps.levels[cell_index] > col_ps.depth:936cell_index += 1937bitset_set(cell, col_ps.entries[cell_index])938BCS.ith_word(BCS, entry, word)939bitset_and(cell, word, cell)940h = bitset_hamming_weight(cell)941bitset_free(cell)942bitset_free(word)943return h944945cdef inline int col_degree(PartitionStack *col_ps, BinaryCodeStruct BCS, int entry, int cell_index, PartitionStack *word_ps):946"""947Returns the number of edges from the vertex corresponding to entry to948vertices in the cell corresponding to cell_index.949950INPUT:951col_ps -- the partition stack to be checked952word_ps -- corresponding partition stack on words953BCS -- a binary code struct object954entry -- the position of the vertex in question in the entries of word_ps955cell_index -- the starting position of the cell in question in the entries956of PS957"""958cdef bitset_t word959bitset_init(word, BCS.degree)960cdef int degree = 0, word_basis, i, b961entry = col_ps.entries[entry]962while 1:963BCS.ith_word(BCS, word_ps.entries[cell_index], word)964degree += bitset_check(word, entry)965if not word_ps.levels[cell_index] > col_ps.depth:966break967cell_index += 1968bitset_free(word)969return degree970971cdef inline int sort_by_function_codes(PartitionStack *PS, int start, int *degrees, int *counts, int *output, int count_max):972"""973A simple counting sort, given the degrees of vertices to a certain cell.974975INPUT:976PS -- the partition stack to be checked977start -- beginning index of the cell to be sorted978degrees -- the values to be sorted by979count, count_max, output -- scratch space980981"""982cdef int n = PS.degree983cdef int i, j, max, max_location984for j from 0 <= j < count_max:985counts[j] = 0986i = 0987while PS.levels[i+start] > PS.depth:988counts[degrees[i]] += 1989i += 1990counts[degrees[i]] += 1991# i+start is the right endpoint of the cell now992max = counts[0]993max_location = 0994for j from 0 < j < count_max:995if counts[j] > max:996max = counts[j]997max_location = j998counts[j] += counts[j - 1]999for j from i >= j >= 0:1000counts[degrees[j]] -= 11001output[counts[degrees[j]]] = PS.entries[start+j]1002max_location = counts[max_location]+start1003for j from 0 <= j <= i:1004PS.entries[start+j] = output[j]1005j = 11006while j < count_max and counts[j] <= i:1007if counts[j] > 0:1008PS.levels[start + counts[j] - 1] = PS.depth1009PS_move_min_to_front(PS, start + counts[j-1], start + counts[j] - 1)1010j += 11011return max_location101210131014def random_tests(num=50, n_max=50, k_max=6, nwords_max=200, perms_per_code=10, density_range=(.1,.9)):1015"""1016Tests to make sure that C(gamma(B)) == C(B) for random permutations gamma1017and random codes B, and that is_isomorphic returns an isomorphism.10181019INPUT:1020num -- run tests for this many codes1021n_max -- test codes with at most this many columns1022k_max -- test codes with at most this for dimension1023perms_per_code -- test each code with this many random permutations10241025DISCUSSION:10261027This code generates num random linear codes B on at most n_max columns with1028dimension at most k_max, and a random nonlinear code B2 on at most n_max1029columns with number of words at most nwords_max. The density of entries in1030the basis is chosen randomly between 0 and 1.10311032For each code B (B2) generated, we uniformly generate perms_per_code random1033permutations and verify that the canonical labels of B and the image of B1034under the generated permutation are equal, and check that the double coset1035function returns an isomorphism.10361037TESTS::10381039sage: import sage.groups.perm_gps.partn_ref.refinement_binary1040sage: sage.groups.perm_gps.partn_ref.refinement_binary.random_tests() # long time (up to 5s on sage.math, 2012)1041All passed: ... random tests on ... codes.10421043"""1044from sage.misc.misc import walltime1045from sage.misc.prandom import random, randint1046from sage.combinat.permutation import Permutations1047from sage.matrix.constructor import random_matrix, matrix1048from sage.rings.finite_rings.constructor import FiniteField as GF1049cdef int h, i, j, n, k, num_tests = 0, num_codes = 01050cdef LinearBinaryCodeStruct B, C1051cdef NonlinearBinaryCodeStruct B_n, C_n1052for mmm in range(num):1053p = random()*(density_range[1]-density_range[0]) + density_range[0]1054n = randint(2, n_max)1055k = randint(1, min(n-1,k_max) )1056nwords = randint(1, min(n-1,nwords_max) )1057S = Permutations(n)10581059M = random_matrix(GF(2), k, n, sparse=False, density=p).row_space().basis_matrix()1060M_n = random_matrix(GF(2), nwords, n, sparse=False, density=p)1061B = LinearBinaryCodeStruct( M )1062B_n = NonlinearBinaryCodeStruct( M_n )1063B.run()1064B_n.run()10651066for i from 0 <= i < perms_per_code:1067perm = [a-1 for a in list(S.random_element())]1068C = LinearBinaryCodeStruct( matrix(GF(2), B.dimension, B.degree) )1069C_n = NonlinearBinaryCodeStruct( matrix(GF(2), B_n.nwords, B_n.degree) )1070for j from 0 <= j < B.dimension:1071for h from 0 <= h < B.degree:1072bitset_set_to(&C.basis[j], perm[h], bitset_check(&B.basis[j], h))1073for j from 0 <= j < B_n.nwords:1074for h from 0 <= h < B_n.degree:1075bitset_set_to(&C_n.words[j], perm[h], bitset_check(&B_n.words[j], h))1076# now C is a random permutation of B, and C_n of B_n1077C.run()1078C_n.run()1079B_relab = B.canonical_relabeling()1080C_relab = C.canonical_relabeling()1081B_n_relab = B_n.canonical_relabeling()1082C_n_relab = C_n.canonical_relabeling()1083B_M = matrix(GF(2), B.dimension, B.degree)1084C_M = matrix(GF(2), B.dimension, B.degree)1085B_n_M = matrix(GF(2), B_n.nwords, B_n.degree)1086C_n_M = matrix(GF(2), B_n.nwords, B_n.degree)1087for j from 0 <= j < B.dimension:1088for h from 0 <= h < B.degree:1089B_M[j,B_relab[h]] = bitset_check(&B.basis[j], h)1090C_M[j,C_relab[h]] = bitset_check(&C.basis[j], h)1091for j from 0 <= j < B_n.nwords:1092for h from 0 <= h < B_n.degree:1093B_n_M[j,B_n_relab[h]] = bitset_check(&B_n.words[j], h)1094C_n_M[j,C_n_relab[h]] = bitset_check(&C_n.words[j], h)1095if B_M.row_space() != C_M.row_space():1096print "can_lab error -- B:"1097for j from 0 <= j < B.dimension:1098print bitset_string(&B.basis[j])1099print perm1100return1101if sorted(B_n_M.rows()) != sorted(C_n_M.rows()):1102print "can_lab error -- B_n:"1103for j from 0 <= j < B_n.nwords:1104print bitset_string(&B_n.words[j])1105print perm1106return1107isom = B.is_isomorphic(C)1108if not isom:1109print "isom -- B:"1110for j from 0 <= j < B.dimension:1111print bitset_string(&B.basis[j])1112print perm1113print isom1114return1115isom = B_n.is_isomorphic(C_n)1116if not isom:1117print "isom -- B_n:"1118for j from 0 <= j < B_n.nwords:1119print bitset_string(&B_n.words[j])1120print perm1121print isom1122return11231124num_tests += 4*perms_per_code1125num_codes += 211261127print "All passed: %d random tests on %d codes."%(num_tests, num_codes)11281129113011311132