Path: blob/master/src/sage/groups/perm_gps/partn_ref/refinement_matrices.pyx
8817 views
"""1Partition backtrack functions for matrices23DOCTEST:4sage: import sage.groups.perm_gps.partn_ref.refinement_matrices56REFERENCE: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.misc.misc import uniq27from sage.matrix.constructor import Matrix2829cdef class MatrixStruct:3031def __cinit__(self, matrix):32cdef int i, j33cdef int *num_rows34self.degree = matrix.ncols()35self.nwords = matrix.nrows()36cdef NonlinearBinaryCodeStruct S_temp37self.matrix = matrix3839self.symbols = uniq(self.matrix.list())40if 0 in self.symbols:41self.symbols.remove(0)42self.nsymbols = len(self.symbols)4344self.symbol_structs = []45num_rows = <int *> sage_malloc(self.nsymbols * sizeof(int))46self.temp_col_ps = PS_new(self.degree, 1)47if num_rows is NULL or self.temp_col_ps is NULL:48sage_free(num_rows)49PS_dealloc(self.temp_col_ps)50raise MemoryError5152for i from 0 <= i < self.nsymbols:53num_rows[i] = 054for row in self.matrix.rows():55row = uniq(row.list())56if 0 in row: row.remove(0)57for s in row:58num_rows[self.symbols.index(s)] += 159for i from 0 <= i < self.nsymbols:60S_temp = NonlinearBinaryCodeStruct( (self.degree, num_rows[i]) )61self.symbol_structs.append(S_temp)6263for i from 0 <= i < self.nsymbols:64num_rows[i] = 065for row in self.matrix.rows():66row_list = row.list()67row_list.reverse()68for i in row.nonzero_positions():69s = row[i]70j = self.symbols.index(s)71S_temp = <NonlinearBinaryCodeStruct>self.symbol_structs[j]72bitset_set( &S_temp.words[num_rows[j]], i)73if row_list.count(s) == 1 or row_list.index(s) == self.degree - i - 1:74num_rows[j] += 175sage_free(num_rows)76self.output = NULL7778def __dealloc__(self):79PS_dealloc(self.temp_col_ps)80if self.output is not NULL:81deallocate_agcl_output(self.output)8283def display(self):84"""85Display the matrix, and associated data.8687EXAMPLE::8889sage: from sage.groups.perm_gps.partn_ref.refinement_matrices import MatrixStruct90sage: M = MatrixStruct(Matrix(GF(5), [[0,1,1,4,4],[0,4,4,1,1]]))91sage: M.display()92[0 1 1 4 4]93[0 4 4 1 1]94<BLANKLINE>9501100960001197198<BLANKLINE>9900011100011001014102103"""104print self.matrix105106cdef int i,j=0107cdef NonlinearBinaryCodeStruct S_temp108for S in self.symbol_structs:109S_temp = <NonlinearBinaryCodeStruct>S110for i from 0 <= i < S_temp.nwords:111print bitset_string(&S_temp.words[i])112print self.symbols[j]113114j += 1115116def run(self, partition=None):117"""118Perform the canonical labeling and automorphism group computation,119storing results to self.120121INPUT:122partition -- an optional list of lists partition of the columns.123default is the unit partition.124125EXAMPLES:126sage: from sage.groups.perm_gps.partn_ref.refinement_matrices import MatrixStruct127128sage: M = MatrixStruct(matrix(GF(3),[[0,1,2],[0,2,1]]))129sage: M.run()130sage: M.automorphism_group()131([[0, 2, 1]], 2, [1])132sage: M.canonical_relabeling()133[0, 1, 2]134135sage: M = MatrixStruct(matrix(GF(3),[[0,1,2],[0,2,1],[1,0,2],[1,2,0],[2,0,1],[2,1,0]]))136sage: M.automorphism_group()[1] == 6137True138139sage: M = MatrixStruct(matrix(GF(3),[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2]]))140sage: M.automorphism_group()[1] == factorial(14)141True142143"""144cdef int i, n = self.degree145cdef PartitionStack *part146cdef NonlinearBinaryCodeStruct S_temp147for i from 0 <= i < self.nsymbols:148S_temp = <NonlinearBinaryCodeStruct> self.symbol_structs[i]149S_temp.first_time = 1150151if partition is None:152part = PS_new(n, 1)153else:154part = PS_from_list(partition)155if part is NULL:156raise MemoryError157158self.output = get_aut_gp_and_can_lab(<void *> self, part, self.degree, &all_matrix_children_are_equivalent, &refine_matrix, &compare_matrices, 1, NULL, NULL, NULL)159160PS_dealloc(part)161162163def automorphism_group(self):164"""165Returns a list of generators of the automorphism group, along with its166order and a base for which the list of generators is a strong generating167set.168169EXAMPLE: (For more examples, see self.run())170sage: from sage.groups.perm_gps.partn_ref.refinement_matrices import MatrixStruct171172sage: M = MatrixStruct(matrix(GF(3),[[0,1,2],[0,2,1]]))173sage: M.automorphism_group()174([[0, 2, 1]], 2, [1])175176"""177cdef int i, j178cdef list generators, base179cdef Integer order180if self.output is NULL:181self.run()182generators = []183for i from 0 <= i < self.output.num_gens:184generators.append([self.output.generators[i*self.degree + j] for j from 0 <= j < self.degree])185order = Integer()186SC_order(self.output.group, 0, order.value)187base = [self.output.group.base_orbits[i][0] for i from 0 <= i < self.output.group.base_size]188return generators, order, base189190def canonical_relabeling(self):191"""192Returns a canonical relabeling (in list permutation format).193194EXAMPLES: (For more examples, see self.run())195sage: from sage.groups.perm_gps.partn_ref.refinement_matrices import MatrixStruct196197sage: M = MatrixStruct(matrix(GF(3),[[0,1,2],[0,2,1]]))198sage: M.canonical_relabeling()199[0, 1, 2]200201"""202cdef int i203if self.output is NULL:204self.run()205return [self.output.relabeling[i] for i from 0 <= i < self.degree]206207def is_isomorphic(self, MatrixStruct other):208"""209Calculate whether self is isomorphic to other.210211EXAMPLES:212sage: from sage.groups.perm_gps.partn_ref.refinement_matrices import MatrixStruct213sage: M = MatrixStruct(Matrix(GF(11), [[1,2,3,0,0,0],[0,0,0,1,2,3]]))214sage: N = MatrixStruct(Matrix(GF(11), [[0,1,0,2,0,3],[1,0,2,0,3,0]]))215sage: M.is_isomorphic(N)216[0, 2, 4, 1, 3, 5]217218"""219cdef int i, j, n = self.degree220cdef int *output, *ordering221cdef PartitionStack *part222cdef NonlinearBinaryCodeStruct S_temp223for i from 0 <= i < self.nsymbols:224S_temp = self.symbol_structs[i]225S_temp.first_time = 1226S_temp = other.symbol_structs[i]227S_temp.first_time = 1228part = PS_new(n, 1)229ordering = <int *> sage_malloc(self.degree * sizeof(int))230output = <int *> sage_malloc(self.degree * sizeof(int))231if part is NULL or ordering is NULL or output is NULL:232PS_dealloc(part)233sage_free(ordering)234sage_free(output)235raise MemoryError236for i from 0 <= i < self.degree:237ordering[i] = i238239cdef bint isomorphic = double_coset(<void *> self, <void *> other, part, ordering, self.degree, &all_matrix_children_are_equivalent, &refine_matrix, &compare_matrices, NULL, NULL, output)240241PS_dealloc(part)242sage_free(ordering)243if isomorphic:244output_py = [output[i] for i from 0 <= i < self.degree]245else:246output_py = False247sage_free(output)248return output_py249250cdef int refine_matrix(PartitionStack *PS, void *S, int *cells_to_refine_by, int ctrb_len):251cdef MatrixStruct M = <MatrixStruct> S252cdef int i, temp_inv, invariant = 1253cdef bint changed = 1254while changed:255PS_copy_from_to(PS, M.temp_col_ps)256for BCS in M.symbol_structs:257temp_inv = refine_by_bip_degree(PS, <void *> BCS, cells_to_refine_by, ctrb_len)258invariant *= temp_inv + 1259if memcmp(PS.entries, M.temp_col_ps.entries, 2*M.degree * sizeof(int)) == 0:260changed = 0261return invariant262263cdef int compare_matrices(int *gamma_1, int *gamma_2, void *S1, void *S2, int degree):264cdef MatrixStruct MS1 = <MatrixStruct> S1265cdef MatrixStruct MS2 = <MatrixStruct> S2266M1 = MS1.matrix267M2 = MS2.matrix268cdef int i269MM1 = Matrix(M1.base_ring(), M1.nrows(), M1.ncols(), sparse=M1.is_sparse())270MM2 = Matrix(M2.base_ring(), M2.nrows(), M2.ncols(), sparse=M2.is_sparse())271for i from 0 <= i < degree:272MM1.set_column(i, M1.column(gamma_1[i]))273MM2.set_column(i, M2.column(gamma_2[i]))274return cmp(sorted(MM1.rows()), sorted(MM2.rows()))275276cdef bint all_matrix_children_are_equivalent(PartitionStack *PS, void *S):277return 0278279def random_tests(n=10, nrows_max=50, ncols_max=50, nsymbols_max=10, perms_per_matrix=5, density_range=(.1,.9)):280"""281Tests to make sure that C(gamma(M)) == C(M) for random permutations gamma282and random matrices M, and that M.is_isomorphic(gamma(M)) returns an283isomorphism.284285INPUT:286287- n -- run tests on this many matrices288- nrows_max -- test matrices with at most this many rows289- ncols_max -- test matrices with at most this many columns290- perms_per_matrix -- test each matrix with this many random permutations291- nsymbols_max -- maximum number of distinct symbols in the matrix292293This code generates n random matrices M on at most ncols_max columns and at294most nrows_max rows. The density of entries in the basis is chosen randomly295between 0 and 1.296297For each matrix M generated, we uniformly generate perms_per_matrix random298permutations and verify that the canonical labels of M and the image of M299under the generated permutation are equal, and that the isomorphism is300discovered by the double coset function.301302TESTS::303304sage: import sage.groups.perm_gps.partn_ref.refinement_matrices305sage: sage.groups.perm_gps.partn_ref.refinement_matrices.random_tests() # long time (up to 30s on sage.math, 2011)306All passed: ... random tests on ... matrices.307308"""309from sage.misc.misc import walltime310from sage.misc.prandom import random, randint311from sage.combinat.permutation import Permutations312from sage.matrix.constructor import random_matrix, matrix313from sage.rings.finite_rings.constructor import FiniteField as GF314from sage.rings.arith import next_prime315cdef int h, i, j, nrows, k, num_tests = 0, num_matrices = 0316cdef MatrixStruct M, N317for m in range(n):318p = random()*(density_range[1]-density_range[0]) + density_range[0]319nrows = randint(1, nrows_max)320ncols = randint(1, ncols_max)321nsymbols = next_prime(randint(1, nsymbols_max))322S = Permutations(ncols)323MM = random_matrix(GF(nsymbols), nrows, ncols, sparse=False, density=p)324M = MatrixStruct( MM )325M.run()326327for i from 0 <= i < perms_per_matrix:328perm = [a-1 for a in list(S.random_element())]329NN = matrix(GF(nsymbols), nrows, ncols)330for j from 0 <= j < ncols:331NN.set_column(perm[j], MM.column(j))332N = MatrixStruct(NN)333# now N is a random permutation of M334N.run()335336M_relab = M.canonical_relabeling()337N_relab = N.canonical_relabeling()338339M_C = matrix(GF(nsymbols), nrows, ncols)340N_C = matrix(GF(nsymbols), nrows, ncols)341342for j from 0 <= j < ncols:343M_C.set_column(M_relab[j], MM.column(j))344N_C.set_column(N_relab[j], NN.column(j))345346M_C = matrix(GF(nsymbols), sorted(M_C.rows()))347N_C = matrix(GF(nsymbols), sorted(N_C.rows()))348349if M_C != N_C:350print "M:"351print M.matrix.str()352print "perm:"353print perm354return355356isom = M.is_isomorphic(N)357if not isom:358print "isom FAILURE: M:"359print M.matrix.str()360print "isom FAILURE: N:"361print N.matrix.str()362return363364num_tests += perms_per_matrix365num_matrices += 2366print "All passed: %d random tests on %d matrices."%(num_tests, num_matrices)367368369370371372373