Path: blob/master/sage/groups/perm_gps/partn_ref/refinement_matrices.pyx
4069 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:48if num_rows is not NULL:49sage_free(num_rows)50if self.temp_col_ps is not NULL:51PS_dealloc(self.temp_col_ps)52raise MemoryError5354for i from 0 <= i < self.nsymbols:55num_rows[i] = 056for row in self.matrix.rows():57row = uniq(row.list())58if 0 in row: row.remove(0)59for s in row:60num_rows[self.symbols.index(s)] += 161for i from 0 <= i < self.nsymbols:62S_temp = NonlinearBinaryCodeStruct( (self.degree, num_rows[i]) )63self.symbol_structs.append(S_temp)6465for i from 0 <= i < self.nsymbols:66num_rows[i] = 067for row in self.matrix.rows():68row_list = row.list()69row_list.reverse()70for i in row.nonzero_positions():71s = row[i]72j = self.symbols.index(s)73S_temp = <NonlinearBinaryCodeStruct>self.symbol_structs[j]74bitset_set( &S_temp.words[num_rows[j]], i)75if row_list.count(s) == 1 or row_list.index(s) == self.degree - i - 1:76num_rows[j] += 177sage_free(num_rows)78self.output = NULL7980def __dealloc__(self):81PS_dealloc(self.temp_col_ps)82if self.output is not NULL:83sage_free(self.output.generators)84SC_dealloc(self.output.group)85sage_free(self.output.relabeling)86sage_free(self.output)8788def display(self):89"""90Display the matrix, and associated data.9192EXAMPLE::9394sage: from sage.groups.perm_gps.partn_ref.refinement_matrices import MatrixStruct95sage: M = MatrixStruct(Matrix(GF(5), [[0,1,1,4,4],[0,4,4,1,1]]))96sage: M.display()97[0 1 1 4 4]98[0 4 4 1 1]99<BLANKLINE>10001100101000111021103<BLANKLINE>10400011105011001064107108"""109print self.matrix110111cdef int i,j=0112cdef NonlinearBinaryCodeStruct S_temp113for S in self.symbol_structs:114S_temp = <NonlinearBinaryCodeStruct>S115for i from 0 <= i < S_temp.nwords:116print bitset_string(&S_temp.words[i])117print self.symbols[j]118119j += 1120121def run(self, partition=None):122"""123Perform the canonical labeling and automorphism group computation,124storing results to self.125126INPUT:127partition -- an optional list of lists partition of the columns.128default is the unit partition.129130EXAMPLES:131sage: from sage.groups.perm_gps.partn_ref.refinement_matrices import MatrixStruct132133sage: M = MatrixStruct(matrix(GF(3),[[0,1,2],[0,2,1]]))134sage: M.run()135sage: M.automorphism_group()136([[0, 2, 1]], 2, [1])137sage: M.canonical_relabeling()138[0, 1, 2]139140sage: M = MatrixStruct(matrix(GF(3),[[0,1,2],[0,2,1],[1,0,2],[1,2,0],[2,0,1],[2,1,0]]))141sage: M.automorphism_group()[1] == 6142True143144sage: M = MatrixStruct(matrix(GF(3),[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2]]))145sage: M.automorphism_group()[1] == factorial(14)146True147148"""149cdef int i, n = self.degree150cdef PartitionStack *part151cdef NonlinearBinaryCodeStruct S_temp152for i from 0 <= i < self.nsymbols:153S_temp = <NonlinearBinaryCodeStruct> self.symbol_structs[i]154S_temp.first_time = 1155156if partition is None:157part = PS_new(n, 1)158else:159part = PS_from_list(partition)160if part is NULL:161raise MemoryError162163self.output = get_aut_gp_and_can_lab(<void *> self, part, self.degree, &all_matrix_children_are_equivalent, &refine_matrix, &compare_matrices, 1, NULL)164165PS_dealloc(part)166167168def automorphism_group(self):169"""170Returns a list of generators of the automorphism group, along with its171order and a base for which the list of generators is a strong generating172set.173174EXAMPLE: (For more examples, see self.run())175sage: from sage.groups.perm_gps.partn_ref.refinement_matrices import MatrixStruct176177sage: M = MatrixStruct(matrix(GF(3),[[0,1,2],[0,2,1]]))178sage: M.automorphism_group()179([[0, 2, 1]], 2, [1])180181"""182cdef int i, j183cdef list generators, base184cdef Integer order185if self.output is NULL:186self.run()187generators = []188for i from 0 <= i < self.output.num_gens:189generators.append([self.output.generators[i*self.degree + j] for j from 0 <= j < self.degree])190order = Integer()191SC_order(self.output.group, 0, order.value)192base = [self.output.group.base_orbits[i][0] for i from 0 <= i < self.output.group.base_size]193return generators, order, base194195def canonical_relabeling(self):196"""197Returns a canonical relabeling (in list permutation format).198199EXAMPLES: (For more examples, see self.run())200sage: from sage.groups.perm_gps.partn_ref.refinement_matrices import MatrixStruct201202sage: M = MatrixStruct(matrix(GF(3),[[0,1,2],[0,2,1]]))203sage: M.canonical_relabeling()204[0, 1, 2]205206"""207cdef int i208if self.output is NULL:209self.run()210return [self.output.relabeling[i] for i from 0 <= i < self.degree]211212def is_isomorphic(self, MatrixStruct other):213"""214Calculate whether self is isomorphic to other.215216EXAMPLES:217sage: from sage.groups.perm_gps.partn_ref.refinement_matrices import MatrixStruct218sage: M = MatrixStruct(Matrix(GF(11), [[1,2,3,0,0,0],[0,0,0,1,2,3]]))219sage: N = MatrixStruct(Matrix(GF(11), [[0,1,0,2,0,3],[1,0,2,0,3,0]]))220sage: M.is_isomorphic(N)221[0, 2, 4, 1, 3, 5]222223"""224cdef int i, j, n = self.degree225cdef int *output, *ordering226cdef PartitionStack *part227cdef NonlinearBinaryCodeStruct S_temp228for i from 0 <= i < self.nsymbols:229S_temp = self.symbol_structs[i]230S_temp.first_time = 1231S_temp = other.symbol_structs[i]232S_temp.first_time = 1233part = PS_new(n, 1)234ordering = <int *> sage_malloc(self.degree * sizeof(int))235if part is NULL or ordering is NULL:236if part is not NULL: PS_dealloc(part)237if ordering is not NULL: sage_free(ordering)238raise MemoryError239for i from 0 <= i < self.degree:240ordering[i] = i241242output = double_coset(<void *> self, <void *> other, part, ordering, self.degree, &all_matrix_children_are_equivalent, &refine_matrix, &compare_matrices, NULL)243244PS_dealloc(part)245sage_free(ordering)246247if output is NULL:248return False249else:250output_py = [output[i] for i from 0 <= i < self.degree]251sage_free(output)252return output_py253254cdef int refine_matrix(PartitionStack *PS, void *S, int *cells_to_refine_by, int ctrb_len):255cdef MatrixStruct M = <MatrixStruct> S256cdef int i, temp_inv, invariant = 1257cdef bint changed = 1258while changed:259PS_copy_from_to(PS, M.temp_col_ps)260for BCS in M.symbol_structs:261temp_inv = refine_by_bip_degree(PS, <void *> BCS, cells_to_refine_by, ctrb_len)262invariant *= temp_inv + 1263if memcmp(PS.entries, M.temp_col_ps.entries, 2*M.degree * sizeof(int)) == 0:264changed = 0265return invariant266267cdef int compare_matrices(int *gamma_1, int *gamma_2, void *S1, void *S2):268cdef MatrixStruct MS1 = <MatrixStruct> S1269cdef MatrixStruct MS2 = <MatrixStruct> S2270M1 = MS1.matrix271M2 = MS2.matrix272cdef int i273MM1 = Matrix(M1.base_ring(), M1.nrows(), M1.ncols(), sparse=M1.is_sparse())274MM2 = Matrix(M2.base_ring(), M2.nrows(), M2.ncols(), sparse=M2.is_sparse())275for i from 0 <= i < M1.ncols():276MM1.set_column(i, M1.column(gamma_1[i]))277MM2.set_column(i, M2.column(gamma_2[i]))278return cmp(sorted(MM1.rows()), sorted(MM2.rows()))279280cdef bint all_matrix_children_are_equivalent(PartitionStack *PS, void *S):281return 0282283def random_tests(n=10, nrows_max=50, ncols_max=50, nsymbols_max=10, perms_per_matrix=5, density_range=(.1,.9)):284"""285Tests to make sure that C(gamma(M)) == C(M) for random permutations gamma286and random matrices M, and that M.is_isomorphic(gamma(M)) returns an287isomorphism.288289INPUT:290291- n -- run tests on this many matrices292- nrows_max -- test matrices with at most this many rows293- ncols_max -- test matrices with at most this many columns294- perms_per_matrix -- test each matrix with this many random permutations295- nsymbols_max -- maximum number of distinct symbols in the matrix296297This code generates n random matrices M on at most ncols_max columns and at298most nrows_max rows. The density of entries in the basis is chosen randomly299between 0 and 1.300301For each matrix M generated, we uniformly generate perms_per_matrix random302permutations and verify that the canonical labels of M and the image of M303under the generated permutation are equal, and that the isomorphism is304discovered by the double coset function.305306TESTS::307308sage: import sage.groups.perm_gps.partn_ref.refinement_matrices309sage: sage.groups.perm_gps.partn_ref.refinement_matrices.random_tests() # long time (up to 30s on sage.math, 2011)310All passed: ... random tests on ... matrices.311312"""313from sage.misc.misc import walltime314from sage.misc.prandom import random, randint315from sage.combinat.permutation import Permutations316from sage.matrix.constructor import random_matrix, matrix317from sage.rings.finite_rings.constructor import FiniteField as GF318from sage.rings.arith import next_prime319cdef int h, i, j, nrows, k, num_tests = 0, num_matrices = 0320cdef MatrixStruct M, N321for m in range(n):322p = random()*(density_range[1]-density_range[0]) + density_range[0]323nrows = randint(1, nrows_max)324ncols = randint(1, ncols_max)325nsymbols = next_prime(randint(1, nsymbols_max))326S = Permutations(ncols)327MM = random_matrix(GF(nsymbols), nrows, ncols, sparse=False, density=p)328M = MatrixStruct( MM )329M.run()330331for i from 0 <= i < perms_per_matrix:332perm = [a-1 for a in list(S.random_element())]333NN = matrix(GF(nsymbols), nrows, ncols)334for j from 0 <= j < ncols:335NN.set_column(perm[j], MM.column(j))336N = MatrixStruct(NN)337# now N is a random permutation of M338N.run()339340M_relab = M.canonical_relabeling()341N_relab = N.canonical_relabeling()342343M_C = matrix(GF(nsymbols), nrows, ncols)344N_C = matrix(GF(nsymbols), nrows, ncols)345346for j from 0 <= j < ncols:347M_C.set_column(M_relab[j], MM.column(j))348N_C.set_column(N_relab[j], NN.column(j))349350M_C = matrix(GF(nsymbols), sorted(M_C.rows()))351N_C = matrix(GF(nsymbols), sorted(N_C.rows()))352353if M_C != N_C:354print "M:"355print M.matrix.str()356print "perm:"357print perm358return359360isom = M.is_isomorphic(N)361if not isom:362print "isom FAILURE: M:"363print M.matrix.str()364print "isom FAILURE: N:"365print N.matrix.str()366return367368num_tests += perms_per_matrix369num_matrices += 2370print "All passed: %d random tests on %d matrices."%(num_tests, num_matrices)371372373374375376377