Path: blob/master/sage/groups/perm_gps/partn_ref/refinement_binary.pyx
4057 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:49if self.basis is not NULL: sage_free(self.basis)50if self.scratch_bitsets is not NULL: sage_free(self.scratch_bitsets)51if self.alpha_is_wd is not NULL: sage_free(self.alpha_is_wd)52if self.word_ps is not NULL: PS_dealloc(self.word_ps)53if self.alpha is not NULL: sage_free(self.alpha)54if self.scratch is not NULL: sage_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)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))308if part is NULL or ordering is NULL:309if part is not NULL: PS_dealloc(part)310if ordering is not NULL: sage_free(ordering)311raise MemoryError312for i from 0 <= i < n:313ordering[i] = i314self.first_time = 1315other.first_time = 1316317output = double_coset(<void *> self, <void *> other, part, ordering, n, &all_children_are_equivalent, &refine_by_bip_degree, &compare_linear_codes, NULL)318319PS_dealloc(part)320sage_free(ordering)321322if output is NULL:323return False324else:325output_py = [output[i] for i from 0 <= i < n]326sage_free(output)327return output_py328329def __dealloc__(self):330cdef int j331bitset_free(self.alpha_is_wd)332for j from 0 <= j < 2*self.dimension+2:333bitset_free(&self.scratch_bitsets[j])334for j from 0 <= j < self.dimension:335bitset_free(&self.basis[j])336sage_free(self.basis); sage_free(self.scratch_bitsets)337sage_free(self.alpha_is_wd); PS_dealloc(self.word_ps)338sage_free(self.alpha); sage_free(self.scratch)339if self.output is not NULL:340sage_free(self.output.generators)341SC_dealloc(self.output.group)342sage_free(self.output.relabeling)343sage_free(self.output)344345cdef int ith_word_linear(BinaryCodeStruct self, int i, bitset_s *word):346cdef LinearBinaryCodeStruct LBCS = <LinearBinaryCodeStruct> self347cdef int j348bitset_zero(word)349for j from 0 <= j < LBCS.dimension:350if (1<<j)&i:351bitset_xor(word, word, &LBCS.basis[j])352return 0353354cdef class NonlinearBinaryCodeStruct(BinaryCodeStruct):355356def __cinit__(self, arg):357cdef int i,j358if is_Matrix(arg):359self.degree = arg.ncols()360self.nwords = arg.nrows()361elif isinstance(arg, tuple):362assert len(arg) == 2363self.degree, self.nwords = arg364else:365raise NotImplementedError366367self.words = <bitset_s *> sage_malloc(self.nwords * sizeof(bitset_s))368self.scratch_bitsets = <bitset_s *> sage_malloc((4*self.nwords+1) * sizeof(bitset_s))369self.alpha_is_wd = <bitset_s *> sage_malloc(sizeof(bitset_s))370self.word_ps = PS_new(self.nwords, 1)371self.alpha = <int *> sage_malloc((self.nwords+self.degree) * sizeof(int))372self.scratch = <int *> sage_malloc((3*self.nwords+3*self.degree+2) * sizeof(int))373if self.words is NULL or self.scratch_bitsets is NULL \374or self.alpha_is_wd is NULL or self.word_ps is NULL \375or self.alpha is NULL or self.scratch is NULL:376if self.words is not NULL: sage_free(self.words)377if self.scratch_bitsets is not NULL: sage_free(self.scratch_bitsets)378if self.alpha_is_wd is not NULL: sage_free(self.alpha_is_wd)379if self.word_ps is not NULL: PS_dealloc(self.word_ps)380if self.alpha is not NULL: sage_free(self.alpha)381if self.scratch is not NULL: sage_free(self.scratch)382raise MemoryError383384cdef bint memerr = 0385for i from 0 <= i < self.nwords:386try: bitset_init(&self.words[i], self.degree)387except MemoryError:388for j from 0 <= j < i:389bitset_free(&self.words[j])390memerr = 1391break392if not memerr:393for i from 0 <= i < 4*self.nwords:394try: bitset_init(&self.scratch_bitsets[i], self.degree)395except MemoryError:396for j from 0 <= j < i:397bitset_free(&self.scratch_bitsets[j])398for j from 0 <= j < self.nwords:399bitset_free(&self.words[j])400memerr = 1401break402if not memerr:403try: bitset_init(&self.scratch_bitsets[4*self.nwords], self.nwords)404except MemoryError:405for j from 0 <= j < 4*self.nwords:406bitset_free(&self.scratch_bitsets[j])407for j from 0 <= j < self.nwords:408bitset_free(&self.words[j])409memerr = 1410if not memerr:411try: bitset_init(self.alpha_is_wd, self.nwords + self.degree)412except MemoryError:413for j from 0 <= j < 4*self.nwords + 1:414bitset_free(&self.scratch_bitsets[j])415for j from 0 <= j < self.nwords:416bitset_free(&self.words[j])417memerr = 1418if memerr:419sage_free(self.words); sage_free(self.scratch_bitsets)420sage_free(self.alpha_is_wd); PS_dealloc(self.word_ps)421sage_free(self.alpha); sage_free(self.scratch)422raise MemoryError423else:424bitset_zero(self.alpha_is_wd)425for j from 0 <= j < self.nwords:426bitset_zero(&self.words[j])427428if is_Matrix(arg):429for i,j in arg.nonzero_positions():430bitset_set(&self.words[i], j)431432self.output = NULL433self.ith_word = &ith_word_nonlinear434435def __dealloc__(self):436cdef int j437bitset_free(self.alpha_is_wd)438for j from 0 <= j < 4*self.nwords + 1:439bitset_free(&self.scratch_bitsets[j])440for j from 0 <= j < self.nwords:441bitset_free(&self.words[j])442sage_free(self.words); sage_free(self.scratch_bitsets)443sage_free(self.alpha_is_wd); PS_dealloc(self.word_ps)444sage_free(self.alpha); sage_free(self.scratch)445if self.output is not NULL:446sage_free(self.output.generators)447SC_dealloc(self.output.group)448sage_free(self.output.relabeling)449sage_free(self.output)450451def run(self, partition=None):452"""453Perform the canonical labeling and automorphism group computation,454storing results to self.455456INPUT:457partition -- an optional list of lists partition of the columns.458default is the unit partition.459460EXAMPLES:461sage: from sage.groups.perm_gps.partn_ref.refinement_binary import NonlinearBinaryCodeStruct462463sage: B = NonlinearBinaryCodeStruct(Matrix(GF(2), [[1,0,0,0],[0,0,1,0]]))464sage: B.run()465sage: B.automorphism_group()466([[2, 1, 0, 3], [0, 3, 2, 1]], 4, [1, 0])467sage: B.canonical_relabeling()468[2, 0, 3, 1]469470sage: B = NonlinearBinaryCodeStruct(Matrix(GF(2), [[1,1,1,0],[1,1,0,1],[1,0,1,1],[0,1,1,1]]))471sage: B.run()472sage: B.automorphism_group()473([[0, 1, 3, 2], [0, 2, 1, 3], [1, 0, 2, 3]], 24, [0, 1, 2])474sage: B.canonical_relabeling()475[0, 1, 2, 3]476477sage: 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]]))478sage: B.run()479sage: B.automorphism_group()480([[0, 1, 3, 2, 4, 5],481[0, 2, 1, 3, 4, 5],482[1, 0, 2, 3, 4, 5],483[0, 1, 2, 3, 5, 4]],48448,485[4, 0, 1, 2])486sage: B.canonical_relabeling()487[2, 3, 4, 5, 0, 1]488489"""490cdef int n = self.degree491cdef PartitionStack *part492if partition is None:493part = PS_new(n, 1)494else:495part = PS_from_list(partition)496if part is NULL:497raise MemoryError498self.first_time = 1499500self.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)501502PS_dealloc(part)503504def automorphism_group(self):505"""506Returns a list of generators of the automorphism group, along with its507order and a base for which the list of generators is a strong generating508set.509510EXAMPLE: (For more examples, see self.run())511sage: from sage.groups.perm_gps.partn_ref.refinement_binary import NonlinearBinaryCodeStruct512513sage: 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]]))514sage: B.run()515sage: B.automorphism_group()516([[0, 1, 3, 2, 4, 5],517[0, 2, 1, 3, 4, 5],518[1, 0, 2, 3, 4, 5],519[0, 1, 2, 3, 5, 4]],52048,521[4, 0, 1, 2])522523"""524cdef int i, j525cdef list generators, base526cdef Integer order527if self.output is NULL:528self.run()529generators = []530for i from 0 <= i < self.output.num_gens:531generators.append([self.output.generators[i*self.degree + j] for j from 0 <= j < self.degree])532order = Integer()533SC_order(self.output.group, 0, order.value)534base = [self.output.group.base_orbits[i][0] for i from 0 <= i < self.output.group.base_size]535return generators, order, base536537def canonical_relabeling(self):538"""539Returns a canonical relabeling (in list permutation format).540541EXAMPLES: (For more examples, see self.run())542sage: from sage.groups.perm_gps.partn_ref.refinement_binary import NonlinearBinaryCodeStruct543544sage: 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]]))545sage: B.run()546sage: B.canonical_relabeling()547[2, 3, 4, 5, 0, 1]548549"""550cdef int i551if self.output is NULL:552self.run()553return [self.output.relabeling[i] for i from 0 <= i < self.degree]554555def is_isomorphic(self, NonlinearBinaryCodeStruct other):556"""557Calculate whether self is isomorphic to other.558559EXAMPLES:560sage: from sage.groups.perm_gps.partn_ref.refinement_binary import NonlinearBinaryCodeStruct561562sage: B = NonlinearBinaryCodeStruct(Matrix(GF(2), [[1,1,1,1,0,0],[0,0,1,1,1,1]]))563sage: C = NonlinearBinaryCodeStruct(Matrix(GF(2), [[1,1,0,0,1,1],[1,1,1,1,0,0]]))564sage: B.is_isomorphic(C)565[2, 3, 0, 1, 4, 5]566567"""568cdef int i, n = self.degree569cdef int *output, *ordering570cdef PartitionStack *part571part = PS_new(n, 1)572ordering = <int *> sage_malloc(n * sizeof(int))573if part is NULL or ordering is NULL:574if part is not NULL: PS_dealloc(part)575if ordering is not NULL: sage_free(ordering)576raise MemoryError577for i from 0 <= i < n:578ordering[i] = i579self.first_time = 1580other.first_time = 1581582output = double_coset(<void *> self, <void *> other, part, ordering, n, &all_children_are_equivalent, &refine_by_bip_degree, &compare_nonlinear_codes, NULL)583584PS_dealloc(part)585sage_free(ordering)586587if output is NULL:588return False589else:590output_py = [output[i] for i from 0 <= i < self.degree]591sage_free(output)592return output_py593594cdef int ith_word_nonlinear(BinaryCodeStruct self, int i, bitset_s *word):595cdef NonlinearBinaryCodeStruct NBCS = <NonlinearBinaryCodeStruct> self596bitset_copy(word, &NBCS.words[i])597return 0598599cdef int refine_by_bip_degree(PartitionStack *col_ps, void *S, int *cells_to_refine_by, int ctrb_len):600"""601Refines the input partition by checking degrees of vertices to the given602cells in the associated bipartite graph (vertices split into columns and603words).604605INPUT:606col_ps -- a partition stack, whose finest partition is the partition to be607refined.608S -- a binary code struct object609cells_to_refine_by -- a list of pointers to cells to check degrees against610in refining the other cells (updated in place)611ctrb_len -- how many cells in cells_to_refine_by612613OUTPUT:614615An integer $I$ invariant under the orbits of $S_n$. That is, if $\gamma$ is a616permutation of the columns, then617$$ I(G, PS, cells_to_refine_by) = I( \gamma(G), \gamma(PS), \gamma(cells_to_refine_by) ) .$$618619"""620cdef BinaryCodeStruct BCS = <BinaryCodeStruct> S621cdef int current_cell_against = 0622cdef int current_cell, i, r, j623cdef int first_largest_subcell624cdef int invariant = 0625cdef PartitionStack *word_ps = BCS.word_ps626cdef int *ctrb = BCS.alpha627cdef bitset_s *ctrb_is_wd = BCS.alpha_is_wd628629word_ps.depth = col_ps.depth630PS_clear(word_ps)631bitset_zero(ctrb_is_wd)632memcpy(ctrb, cells_to_refine_by, ctrb_len * sizeof(int))633if BCS.first_time:634BCS.first_time = 0635ctrb[ctrb_len] = 0636bitset_set(ctrb_is_wd, ctrb_len)637ctrb_len += 1638cdef int *col_degrees = BCS.scratch # len degree639cdef int *col_counts = &BCS.scratch[BCS.degree] # len nwords+1640cdef int *col_output = &BCS.scratch[BCS.degree + BCS.nwords + 1] # len degree641cdef int *word_degrees = &BCS.scratch[2*BCS.degree + BCS.nwords + 1] # len nwords642cdef int *word_counts = &BCS.scratch[2*BCS.degree + 2*BCS.nwords + 1] # len degree+1643cdef int *word_output = &BCS.scratch[3*BCS.degree + 2*BCS.nwords + 2] # len nwords644cdef bint necessary_to_split_cell645cdef int against_index646while not (PS_is_discrete(col_ps) and PS_is_discrete(word_ps)) and current_cell_against < ctrb_len:647invariant += 1648current_cell = 0649if bitset_check(ctrb_is_wd, current_cell_against):650while current_cell < col_ps.degree:651invariant += 8652i = current_cell653necessary_to_split_cell = 0654while 1:655col_degrees[i-current_cell] = col_degree(col_ps, BCS, i, ctrb[current_cell_against], word_ps)656if col_degrees[i-current_cell] != col_degrees[0]:657necessary_to_split_cell = 1658i += 1659if col_ps.levels[i-1] <= col_ps.depth:660break661# now, i points to the next cell (before refinement)662if necessary_to_split_cell:663invariant += 8664first_largest_subcell = sort_by_function_codes(col_ps, current_cell, col_degrees, col_counts, col_output, BCS.nwords+1)665invariant += col_degree(col_ps, BCS, i-1, ctrb[current_cell_against], word_ps)666invariant += first_largest_subcell667against_index = current_cell_against668while against_index < ctrb_len:669if ctrb[against_index] == current_cell and not bitset_check(ctrb_is_wd, against_index):670ctrb[against_index] = first_largest_subcell671break672against_index += 1673r = current_cell674while 1:675if r == current_cell or col_ps.levels[r-1] == col_ps.depth:676if r != first_largest_subcell:677ctrb[ctrb_len] = r678ctrb_len += 1679r += 1680if r >= i:681break682invariant += (i - current_cell)683current_cell = i684else:685while current_cell < word_ps.degree:686invariant += 64687i = current_cell688necessary_to_split_cell = 0689while 1:690word_degrees[i-current_cell] = word_degree(word_ps, BCS, i, ctrb[current_cell_against], col_ps)691if word_degrees[i-current_cell] != word_degrees[0]:692necessary_to_split_cell = 1693i += 1694if word_ps.levels[i-1] <= col_ps.depth:695break696# now, i points to the next cell (before refinement)697if necessary_to_split_cell:698invariant += 64699first_largest_subcell = sort_by_function_codes(word_ps, current_cell, word_degrees, word_counts, word_output, BCS.degree+1)700invariant += word_degree(word_ps, BCS, i-1, ctrb[current_cell_against], col_ps)701invariant += first_largest_subcell702against_index = current_cell_against703while against_index < ctrb_len:704if ctrb[against_index] == current_cell and bitset_check(ctrb_is_wd, against_index):705ctrb[against_index] = first_largest_subcell706break707against_index += 1708r = current_cell709while 1:710if r == current_cell or word_ps.levels[r-1] == col_ps.depth:711if r != first_largest_subcell:712ctrb[ctrb_len] = r713bitset_set(ctrb_is_wd, ctrb_len)714ctrb_len += 1715r += 1716if r >= i:717break718invariant += (i - current_cell)719current_cell = i720current_cell_against += 1721return invariant722723cdef int compare_linear_codes(int *gamma_1, int *gamma_2, void *S1, void *S2):724"""725Compare gamma_1(S1) and gamma_2(S2).726727Return return -1 if gamma_1(S1) < gamma_2(S2), 0 if gamma_1(S1) == gamma_2(S2),7281 if gamma_1(S1) > gamma_2(S2). (Just like the python \code{cmp}) function.729730Abstractly, what this function does is relabel the basis of B by gamma_1 and731gamma_2, run a row reduction on each, and verify that the matrices are the732same, which holds if and only if the rowspan is the same. In practice, if733the codes are not equal, the reductions (which are carried out in an734interleaved way) will terminate as soon as this is discovered, and whichever735code has a 1 in the entry in which they differ is reported as larger.736737INPUT:738gamma_1, gamma_2 -- list permutations (inverse)739S1, S2 -- binary code struct objects740741"""742cdef int i, piv_loc_1, piv_loc_2, cur_col, cur_row=0743cdef bint is_pivot_1, is_pivot_2744cdef LinearBinaryCodeStruct BCS1 = <LinearBinaryCodeStruct> S1745cdef LinearBinaryCodeStruct BCS2 = <LinearBinaryCodeStruct> S2746cdef bitset_s *basis_1 = BCS1.scratch_bitsets # len = dim747cdef bitset_s *basis_2 = &BCS1.scratch_bitsets[BCS1.dimension] # len = dim748cdef bitset_s *pivots = &BCS1.scratch_bitsets[2*BCS1.dimension] # len 1749cdef bitset_s *temp = &BCS1.scratch_bitsets[2*BCS1.dimension+1] # len 1750for i from 0 <= i < BCS1.dimension:751bitset_copy(&basis_1[i], &BCS1.basis[i])752bitset_copy(&basis_2[i], &BCS2.basis[i])753bitset_zero(pivots)754for cur_col from 0 <= cur_col < BCS1.degree:755is_pivot_1 = 0756is_pivot_2 = 0757for i from cur_row <= i < BCS1.dimension:758if bitset_check(&basis_1[i], gamma_1[cur_col]):759is_pivot_1 = 1760piv_loc_1 = i761break762for i from cur_row <= i < BCS1.dimension:763if bitset_check(&basis_2[i], gamma_2[cur_col]):764is_pivot_2 = 1765piv_loc_2 = i766break767if is_pivot_1 != is_pivot_2:768return <int>is_pivot_2 - <int>is_pivot_1769if is_pivot_1:770bitset_set(pivots, cur_col)771if piv_loc_1 != cur_row:772bitset_copy(temp, &basis_1[piv_loc_1])773bitset_copy(&basis_1[piv_loc_1], &basis_1[cur_row])774bitset_copy(&basis_1[cur_row], temp)775if piv_loc_2 != cur_row:776bitset_copy(temp, &basis_2[piv_loc_2])777bitset_copy(&basis_2[piv_loc_2], &basis_2[cur_row])778bitset_copy(&basis_2[cur_row], temp)779for i from 0 <= i < cur_row:780if bitset_check(&basis_1[i], gamma_1[cur_col]):781bitset_xor(&basis_1[i], &basis_1[i], &basis_1[cur_row])782if bitset_check(&basis_2[i], gamma_2[cur_col]):783bitset_xor(&basis_2[i], &basis_2[i], &basis_2[cur_row])784for i from cur_row < i < BCS1.dimension:785if bitset_check(&basis_1[i], gamma_1[cur_col]):786bitset_xor(&basis_1[i], &basis_1[i], &basis_1[cur_row])787if bitset_check(&basis_2[i], gamma_2[cur_col]):788bitset_xor(&basis_2[i], &basis_2[i], &basis_2[cur_row])789cur_row += 1790else:791for i from 0 <= i < cur_row:792if bitset_check(&basis_1[i], gamma_1[cur_col]) != bitset_check(&basis_2[i], gamma_2[cur_col]):793return <int>bitset_check(&basis_2[i], gamma_2[cur_col]) - <int>bitset_check(&basis_1[i], gamma_1[cur_col])794return 0795796cdef int compare_nonlinear_codes(int *gamma_1, int *gamma_2, void *S1, void *S2):797"""798Compare gamma_1(S1) and gamma_2(S2).799800Return return -1 if gamma_1(S1) < gamma_2(S2), 0 if gamma_1(S1) == gamma_2(S2),8011 if gamma_1(S1) > gamma_2(S2). (Just like the python \code{cmp}) function.802803INPUT:804gamma_1, gamma_2 -- list permutations (inverse)805S1, S2 -- a binary code struct object806807"""808cdef int side=0, i, start, end, n_one_1, n_one_2, cur_col809cdef int where_0, where_1810cdef NonlinearBinaryCodeStruct BCS1 = <NonlinearBinaryCodeStruct> S1811cdef NonlinearBinaryCodeStruct BCS2 = <NonlinearBinaryCodeStruct> S2812cdef bitset_s *B_1_0 = BCS1.scratch_bitsets # nwords of len degree813cdef bitset_s *B_1_1 = &BCS1.scratch_bitsets[BCS1.nwords] # nwords of len degree814cdef bitset_s *B_2_0 = &BCS1.scratch_bitsets[2*BCS1.nwords] # nwords of len degree815cdef bitset_s *B_2_1 = &BCS1.scratch_bitsets[3*BCS1.nwords] # nwords of len degree816cdef bitset_s *dividers = &BCS1.scratch_bitsets[4*BCS1.nwords] # 1 of len nwords817cdef bitset_s *B_1_this, *B_1_other, *B_2_this, *B_2_other818for i from 0 <= i < BCS1.nwords:819bitset_copy(&B_1_0[i], &BCS1.words[i])820bitset_copy(&B_2_0[i], &BCS2.words[i])821bitset_zero(dividers)822bitset_set(dividers, BCS1.nwords-1)823824for cur_col from 0 <= cur_col < BCS1.degree:825if side == 0:826B_1_this = B_1_0827B_1_other = B_1_1828B_2_this = B_2_0829B_2_other = B_2_1830else:831B_1_this = B_1_1832B_1_other = B_1_0833B_2_this = B_2_1834B_2_other = B_2_0835side ^= 1836start = 0837while start < BCS1.nwords:838end = start839while not bitset_check(dividers, end):840end += 1841end += 1842n_one_1 = 0843n_one_2 = 0844for i from start <= i < end:845n_one_1 += bitset_check(&B_1_this[i], gamma_1[cur_col])846n_one_2 += bitset_check(&B_2_this[i], gamma_2[cur_col])847if n_one_1 != n_one_2:848if n_one_1 > n_one_2:849return 1850else:851return -1852where_0 = start853where_1 = end - n_one_1854if start < where_1 and where_1 < end:855bitset_set(dividers, where_1 - 1)856for i from start <= i < end:857if bitset_check(&B_1_this[i], gamma_1[cur_col]):858bitset_copy(&B_1_other[where_1], &B_1_this[i])859where_1 += 1860else:861bitset_copy(&B_1_other[where_0], &B_1_this[i])862where_0 += 1863where_0 = start864where_1 = end - n_one_2865for i from start <= i < end:866if bitset_check(&B_2_this[i], gamma_2[cur_col]):867bitset_copy(&B_2_other[where_1], &B_2_this[i])868where_1 += 1869else:870bitset_copy(&B_2_other[where_0], &B_2_this[i])871where_0 += 1872start = end873874return 0875876cdef bint all_children_are_equivalent(PartitionStack *col_ps, void *S):877"""878Returns True if any refinement of the current partition results in the same879structure.880881WARNING:882Converse does not hold in general! See Lemma 2.25 of [1] for details, noting883that the binary code is interpreted as a bipartite graph (see module docs884for details).885886INPUT:887col_ps -- the partition stack to be checked888S -- a binary code struct object889"""890cdef BinaryCodeStruct BCS = <BinaryCodeStruct> S891cdef PartitionStack *word_ps = BCS.word_ps892cdef int i, n = col_ps.degree + BCS.nwords893cdef bint in_cell = 0894cdef int nontrivial_cells = 0895cdef int total_cells = PS_num_cells(col_ps) + PS_num_cells(word_ps)896if n <= total_cells + 4:897return 1898for i from 0 <= i < BCS.nwords:899if word_ps.levels[i] <= col_ps.depth:900if in_cell:901nontrivial_cells += 1902in_cell = 0903else:904in_cell = 1905in_cell = 0906for i from 0 <= i < BCS.degree:907if col_ps.levels[i] <= col_ps.depth:908if in_cell:909nontrivial_cells += 1910in_cell = 0911else:912in_cell = 1913if n == total_cells + nontrivial_cells:914return 1915if n == total_cells + nontrivial_cells + 1:916return 1917return 0918919cdef inline int word_degree(PartitionStack *word_ps, BinaryCodeStruct BCS, int entry, int cell_index, PartitionStack *col_ps):920"""921Returns the number of edges from the vertex corresponding to entry to922vertices in the cell corresponding to cell_index.923924INPUT:925word_ps -- the partition stack to be checked926col_ps -- corresponding partition stack on columns927BCS -- a binary code struct object928entry -- the position of the vertex in question in the entries of word_ps929cell_index -- the starting position of the cell in question in the entries930of PS931"""932cdef bitset_t cell, word933cdef int i, h934bitset_init(cell, BCS.degree)935bitset_zero(cell)936bitset_init(word, BCS.degree)937entry = word_ps.entries[entry]938bitset_set(cell, col_ps.entries[cell_index])939while col_ps.levels[cell_index] > col_ps.depth:940cell_index += 1941bitset_set(cell, col_ps.entries[cell_index])942BCS.ith_word(BCS, entry, word)943bitset_and(cell, word, cell)944h = bitset_hamming_weight(cell)945bitset_free(cell)946bitset_free(word)947return h948949cdef inline int col_degree(PartitionStack *col_ps, BinaryCodeStruct BCS, int entry, int cell_index, PartitionStack *word_ps):950"""951Returns the number of edges from the vertex corresponding to entry to952vertices in the cell corresponding to cell_index.953954INPUT:955col_ps -- the partition stack to be checked956word_ps -- corresponding partition stack on words957BCS -- a binary code struct object958entry -- the position of the vertex in question in the entries of word_ps959cell_index -- the starting position of the cell in question in the entries960of PS961"""962cdef bitset_t word963bitset_init(word, BCS.degree)964cdef int degree = 0, word_basis, i, b965entry = col_ps.entries[entry]966while 1:967BCS.ith_word(BCS, word_ps.entries[cell_index], word)968degree += bitset_check(word, entry)969if not word_ps.levels[cell_index] > col_ps.depth:970break971cell_index += 1972bitset_free(word)973return degree974975cdef inline int sort_by_function_codes(PartitionStack *PS, int start, int *degrees, int *counts, int *output, int count_max):976"""977A simple counting sort, given the degrees of vertices to a certain cell.978979INPUT:980PS -- the partition stack to be checked981start -- beginning index of the cell to be sorted982degrees -- the values to be sorted by983count, count_max, output -- scratch space984985"""986cdef int n = PS.degree987cdef int i, j, max, max_location988for j from 0 <= j < count_max:989counts[j] = 0990i = 0991while PS.levels[i+start] > PS.depth:992counts[degrees[i]] += 1993i += 1994counts[degrees[i]] += 1995# i+start is the right endpoint of the cell now996max = counts[0]997max_location = 0998for j from 0 < j < count_max:999if counts[j] > max:1000max = counts[j]1001max_location = j1002counts[j] += counts[j - 1]1003for j from i >= j >= 0:1004counts[degrees[j]] -= 11005output[counts[degrees[j]]] = PS.entries[start+j]1006max_location = counts[max_location]+start1007for j from 0 <= j <= i:1008PS.entries[start+j] = output[j]1009j = 11010while j < count_max and counts[j] <= i:1011if counts[j] > 0:1012PS.levels[start + counts[j] - 1] = PS.depth1013PS_move_min_to_front(PS, start + counts[j-1], start + counts[j] - 1)1014j += 11015return max_location101610171018def random_tests(num=50, n_max=50, k_max=6, nwords_max=200, perms_per_code=10, density_range=(.1,.9)):1019"""1020Tests to make sure that C(gamma(B)) == C(B) for random permutations gamma1021and random codes B, and that is_isomorphic returns an isomorphism.10221023INPUT:1024num -- run tests for this many codes1025n_max -- test codes with at most this many columns1026k_max -- test codes with at most this for dimension1027perms_per_code -- test each code with this many random permutations10281029DISCUSSION:10301031This code generates num random linear codes B on at most n_max columns with1032dimension at most k_max, and a random nonlinear code B2 on at most n_max1033columns with number of words at most nwords_max. The density of entries in1034the basis is chosen randomly between 0 and 1.10351036For each code B (B2) generated, we uniformly generate perms_per_code random1037permutations and verify that the canonical labels of B and the image of B1038under the generated permutation are equal, and check that the double coset1039function returns an isomorphism.10401041DOCTEST:1042sage: import sage.groups.perm_gps.partn_ref.refinement_binary1043sage: sage.groups.perm_gps.partn_ref.refinement_binary.random_tests()1044All passed: ... random tests on ... codes.10451046"""1047from sage.misc.misc import walltime1048from sage.misc.prandom import random, randint1049from sage.combinat.permutation import Permutations1050from sage.matrix.constructor import random_matrix, matrix1051from sage.rings.finite_rings.constructor import FiniteField as GF1052cdef int h, i, j, n, k, num_tests = 0, num_codes = 01053cdef LinearBinaryCodeStruct B, C1054cdef NonlinearBinaryCodeStruct B_n, C_n1055for mmm in range(num):1056p = random()*(density_range[1]-density_range[0]) + density_range[0]1057n = randint(2, n_max)1058k = randint(1, min(n-1,k_max) )1059nwords = randint(1, min(n-1,nwords_max) )1060S = Permutations(n)10611062M = random_matrix(GF(2), k, n, sparse=False, density=p).row_space().basis_matrix()1063M_n = random_matrix(GF(2), nwords, n, sparse=False, density=p)1064B = LinearBinaryCodeStruct( M )1065B_n = NonlinearBinaryCodeStruct( M_n )1066B.run()1067B_n.run()10681069for i from 0 <= i < perms_per_code:1070perm = [a-1 for a in list(S.random_element())]1071C = LinearBinaryCodeStruct( matrix(GF(2), B.dimension, B.degree) )1072C_n = NonlinearBinaryCodeStruct( matrix(GF(2), B_n.nwords, B_n.degree) )1073for j from 0 <= j < B.dimension:1074for h from 0 <= h < B.degree:1075bitset_set_to(&C.basis[j], perm[h], bitset_check(&B.basis[j], h))1076for j from 0 <= j < B_n.nwords:1077for h from 0 <= h < B_n.degree:1078bitset_set_to(&C_n.words[j], perm[h], bitset_check(&B_n.words[j], h))1079# now C is a random permutation of B, and C_n of B_n1080C.run()1081C_n.run()1082B_relab = B.canonical_relabeling()1083C_relab = C.canonical_relabeling()1084B_n_relab = B_n.canonical_relabeling()1085C_n_relab = C_n.canonical_relabeling()1086B_M = matrix(GF(2), B.dimension, B.degree)1087C_M = matrix(GF(2), B.dimension, B.degree)1088B_n_M = matrix(GF(2), B_n.nwords, B_n.degree)1089C_n_M = matrix(GF(2), B_n.nwords, B_n.degree)1090for j from 0 <= j < B.dimension:1091for h from 0 <= h < B.degree:1092B_M[j,B_relab[h]] = bitset_check(&B.basis[j], h)1093C_M[j,C_relab[h]] = bitset_check(&C.basis[j], h)1094for j from 0 <= j < B_n.nwords:1095for h from 0 <= h < B_n.degree:1096B_n_M[j,B_n_relab[h]] = bitset_check(&B_n.words[j], h)1097C_n_M[j,C_n_relab[h]] = bitset_check(&C_n.words[j], h)1098if B_M.row_space() != C_M.row_space():1099print "can_lab error -- B:"1100for j from 0 <= j < B.dimension:1101print bitset_string(&B.basis[j])1102print perm1103return1104if sorted(B_n_M.rows()) != sorted(C_n_M.rows()):1105print "can_lab error -- B_n:"1106for j from 0 <= j < B_n.nwords:1107print bitset_string(&B_n.words[j])1108print perm1109return1110isom = B.is_isomorphic(C)1111if not isom:1112print "isom -- B:"1113for j from 0 <= j < B.dimension:1114print bitset_string(&B.basis[j])1115print perm1116print isom1117return1118isom = B_n.is_isomorphic(C_n)1119if not isom:1120print "isom -- B_n:"1121for j from 0 <= j < B_n.nwords:1122print bitset_string(&B_n.words[j])1123print perm1124print isom1125return11261127num_tests += 4*perms_per_code1128num_codes += 211291130print "All passed: %d random tests on %d codes."%(num_tests, num_codes)11311132113311341135