Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/groups/perm_gps/partn_ref/refinement_matrices.pyx
8817 views
1
"""
2
Partition backtrack functions for matrices
3
4
DOCTEST:
5
sage: import sage.groups.perm_gps.partn_ref.refinement_matrices
6
7
REFERENCE:
8
9
[1] McKay, Brendan D. Practical Graph Isomorphism. Congressus Numerantium,
10
Vol. 30 (1981), pp. 45-87.
11
12
[2] Leon, Jeffrey. Permutation Group Algorithms Based on Partitions, I:
13
Theory and Algorithms. J. Symbolic Computation, Vol. 12 (1991), pp.
14
533-583.
15
16
"""
17
18
#*****************************************************************************
19
# Copyright (C) 2006 - 2011 Robert L. Miller <[email protected]>
20
#
21
# Distributed under the terms of the GNU General Public License (GPL)
22
# http://www.gnu.org/licenses/
23
#*****************************************************************************
24
25
include 'data_structures_pyx.pxi' # includes bitsets
26
27
from sage.misc.misc import uniq
28
from sage.matrix.constructor import Matrix
29
30
cdef class MatrixStruct:
31
32
def __cinit__(self, matrix):
33
cdef int i, j
34
cdef int *num_rows
35
self.degree = matrix.ncols()
36
self.nwords = matrix.nrows()
37
cdef NonlinearBinaryCodeStruct S_temp
38
self.matrix = matrix
39
40
self.symbols = uniq(self.matrix.list())
41
if 0 in self.symbols:
42
self.symbols.remove(0)
43
self.nsymbols = len(self.symbols)
44
45
self.symbol_structs = []
46
num_rows = <int *> sage_malloc(self.nsymbols * sizeof(int))
47
self.temp_col_ps = PS_new(self.degree, 1)
48
if num_rows is NULL or self.temp_col_ps is NULL:
49
sage_free(num_rows)
50
PS_dealloc(self.temp_col_ps)
51
raise MemoryError
52
53
for i from 0 <= i < self.nsymbols:
54
num_rows[i] = 0
55
for row in self.matrix.rows():
56
row = uniq(row.list())
57
if 0 in row: row.remove(0)
58
for s in row:
59
num_rows[self.symbols.index(s)] += 1
60
for i from 0 <= i < self.nsymbols:
61
S_temp = NonlinearBinaryCodeStruct( (self.degree, num_rows[i]) )
62
self.symbol_structs.append(S_temp)
63
64
for i from 0 <= i < self.nsymbols:
65
num_rows[i] = 0
66
for row in self.matrix.rows():
67
row_list = row.list()
68
row_list.reverse()
69
for i in row.nonzero_positions():
70
s = row[i]
71
j = self.symbols.index(s)
72
S_temp = <NonlinearBinaryCodeStruct>self.symbol_structs[j]
73
bitset_set( &S_temp.words[num_rows[j]], i)
74
if row_list.count(s) == 1 or row_list.index(s) == self.degree - i - 1:
75
num_rows[j] += 1
76
sage_free(num_rows)
77
self.output = NULL
78
79
def __dealloc__(self):
80
PS_dealloc(self.temp_col_ps)
81
if self.output is not NULL:
82
deallocate_agcl_output(self.output)
83
84
def display(self):
85
"""
86
Display the matrix, and associated data.
87
88
EXAMPLE::
89
90
sage: from sage.groups.perm_gps.partn_ref.refinement_matrices import MatrixStruct
91
sage: M = MatrixStruct(Matrix(GF(5), [[0,1,1,4,4],[0,4,4,1,1]]))
92
sage: M.display()
93
[0 1 1 4 4]
94
[0 4 4 1 1]
95
<BLANKLINE>
96
01100
97
00011
98
1
99
<BLANKLINE>
100
00011
101
01100
102
4
103
104
"""
105
print self.matrix
106
print
107
cdef int i,j=0
108
cdef NonlinearBinaryCodeStruct S_temp
109
for S in self.symbol_structs:
110
S_temp = <NonlinearBinaryCodeStruct>S
111
for i from 0 <= i < S_temp.nwords:
112
print bitset_string(&S_temp.words[i])
113
print self.symbols[j]
114
print
115
j += 1
116
117
def run(self, partition=None):
118
"""
119
Perform the canonical labeling and automorphism group computation,
120
storing results to self.
121
122
INPUT:
123
partition -- an optional list of lists partition of the columns.
124
default is the unit partition.
125
126
EXAMPLES:
127
sage: from sage.groups.perm_gps.partn_ref.refinement_matrices import MatrixStruct
128
129
sage: M = MatrixStruct(matrix(GF(3),[[0,1,2],[0,2,1]]))
130
sage: M.run()
131
sage: M.automorphism_group()
132
([[0, 2, 1]], 2, [1])
133
sage: M.canonical_relabeling()
134
[0, 1, 2]
135
136
sage: M = MatrixStruct(matrix(GF(3),[[0,1,2],[0,2,1],[1,0,2],[1,2,0],[2,0,1],[2,1,0]]))
137
sage: M.automorphism_group()[1] == 6
138
True
139
140
sage: M = MatrixStruct(matrix(GF(3),[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2]]))
141
sage: M.automorphism_group()[1] == factorial(14)
142
True
143
144
"""
145
cdef int i, n = self.degree
146
cdef PartitionStack *part
147
cdef NonlinearBinaryCodeStruct S_temp
148
for i from 0 <= i < self.nsymbols:
149
S_temp = <NonlinearBinaryCodeStruct> self.symbol_structs[i]
150
S_temp.first_time = 1
151
152
if partition is None:
153
part = PS_new(n, 1)
154
else:
155
part = PS_from_list(partition)
156
if part is NULL:
157
raise MemoryError
158
159
self.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)
160
161
PS_dealloc(part)
162
163
164
def automorphism_group(self):
165
"""
166
Returns a list of generators of the automorphism group, along with its
167
order and a base for which the list of generators is a strong generating
168
set.
169
170
EXAMPLE: (For more examples, see self.run())
171
sage: from sage.groups.perm_gps.partn_ref.refinement_matrices import MatrixStruct
172
173
sage: M = MatrixStruct(matrix(GF(3),[[0,1,2],[0,2,1]]))
174
sage: M.automorphism_group()
175
([[0, 2, 1]], 2, [1])
176
177
"""
178
cdef int i, j
179
cdef list generators, base
180
cdef Integer order
181
if self.output is NULL:
182
self.run()
183
generators = []
184
for i from 0 <= i < self.output.num_gens:
185
generators.append([self.output.generators[i*self.degree + j] for j from 0 <= j < self.degree])
186
order = Integer()
187
SC_order(self.output.group, 0, order.value)
188
base = [self.output.group.base_orbits[i][0] for i from 0 <= i < self.output.group.base_size]
189
return generators, order, base
190
191
def canonical_relabeling(self):
192
"""
193
Returns a canonical relabeling (in list permutation format).
194
195
EXAMPLES: (For more examples, see self.run())
196
sage: from sage.groups.perm_gps.partn_ref.refinement_matrices import MatrixStruct
197
198
sage: M = MatrixStruct(matrix(GF(3),[[0,1,2],[0,2,1]]))
199
sage: M.canonical_relabeling()
200
[0, 1, 2]
201
202
"""
203
cdef int i
204
if self.output is NULL:
205
self.run()
206
return [self.output.relabeling[i] for i from 0 <= i < self.degree]
207
208
def is_isomorphic(self, MatrixStruct other):
209
"""
210
Calculate whether self is isomorphic to other.
211
212
EXAMPLES:
213
sage: from sage.groups.perm_gps.partn_ref.refinement_matrices import MatrixStruct
214
sage: M = MatrixStruct(Matrix(GF(11), [[1,2,3,0,0,0],[0,0,0,1,2,3]]))
215
sage: N = MatrixStruct(Matrix(GF(11), [[0,1,0,2,0,3],[1,0,2,0,3,0]]))
216
sage: M.is_isomorphic(N)
217
[0, 2, 4, 1, 3, 5]
218
219
"""
220
cdef int i, j, n = self.degree
221
cdef int *output, *ordering
222
cdef PartitionStack *part
223
cdef NonlinearBinaryCodeStruct S_temp
224
for i from 0 <= i < self.nsymbols:
225
S_temp = self.symbol_structs[i]
226
S_temp.first_time = 1
227
S_temp = other.symbol_structs[i]
228
S_temp.first_time = 1
229
part = PS_new(n, 1)
230
ordering = <int *> sage_malloc(self.degree * sizeof(int))
231
output = <int *> sage_malloc(self.degree * sizeof(int))
232
if part is NULL or ordering is NULL or output is NULL:
233
PS_dealloc(part)
234
sage_free(ordering)
235
sage_free(output)
236
raise MemoryError
237
for i from 0 <= i < self.degree:
238
ordering[i] = i
239
240
cdef bint isomorphic = double_coset(<void *> self, <void *> other, part, ordering, self.degree, &all_matrix_children_are_equivalent, &refine_matrix, &compare_matrices, NULL, NULL, output)
241
242
PS_dealloc(part)
243
sage_free(ordering)
244
if isomorphic:
245
output_py = [output[i] for i from 0 <= i < self.degree]
246
else:
247
output_py = False
248
sage_free(output)
249
return output_py
250
251
cdef int refine_matrix(PartitionStack *PS, void *S, int *cells_to_refine_by, int ctrb_len):
252
cdef MatrixStruct M = <MatrixStruct> S
253
cdef int i, temp_inv, invariant = 1
254
cdef bint changed = 1
255
while changed:
256
PS_copy_from_to(PS, M.temp_col_ps)
257
for BCS in M.symbol_structs:
258
temp_inv = refine_by_bip_degree(PS, <void *> BCS, cells_to_refine_by, ctrb_len)
259
invariant *= temp_inv + 1
260
if memcmp(PS.entries, M.temp_col_ps.entries, 2*M.degree * sizeof(int)) == 0:
261
changed = 0
262
return invariant
263
264
cdef int compare_matrices(int *gamma_1, int *gamma_2, void *S1, void *S2, int degree):
265
cdef MatrixStruct MS1 = <MatrixStruct> S1
266
cdef MatrixStruct MS2 = <MatrixStruct> S2
267
M1 = MS1.matrix
268
M2 = MS2.matrix
269
cdef int i
270
MM1 = Matrix(M1.base_ring(), M1.nrows(), M1.ncols(), sparse=M1.is_sparse())
271
MM2 = Matrix(M2.base_ring(), M2.nrows(), M2.ncols(), sparse=M2.is_sparse())
272
for i from 0 <= i < degree:
273
MM1.set_column(i, M1.column(gamma_1[i]))
274
MM2.set_column(i, M2.column(gamma_2[i]))
275
return cmp(sorted(MM1.rows()), sorted(MM2.rows()))
276
277
cdef bint all_matrix_children_are_equivalent(PartitionStack *PS, void *S):
278
return 0
279
280
def random_tests(n=10, nrows_max=50, ncols_max=50, nsymbols_max=10, perms_per_matrix=5, density_range=(.1,.9)):
281
"""
282
Tests to make sure that C(gamma(M)) == C(M) for random permutations gamma
283
and random matrices M, and that M.is_isomorphic(gamma(M)) returns an
284
isomorphism.
285
286
INPUT:
287
288
- n -- run tests on this many matrices
289
- nrows_max -- test matrices with at most this many rows
290
- ncols_max -- test matrices with at most this many columns
291
- perms_per_matrix -- test each matrix with this many random permutations
292
- nsymbols_max -- maximum number of distinct symbols in the matrix
293
294
This code generates n random matrices M on at most ncols_max columns and at
295
most nrows_max rows. The density of entries in the basis is chosen randomly
296
between 0 and 1.
297
298
For each matrix M generated, we uniformly generate perms_per_matrix random
299
permutations and verify that the canonical labels of M and the image of M
300
under the generated permutation are equal, and that the isomorphism is
301
discovered by the double coset function.
302
303
TESTS::
304
305
sage: import sage.groups.perm_gps.partn_ref.refinement_matrices
306
sage: sage.groups.perm_gps.partn_ref.refinement_matrices.random_tests() # long time (up to 30s on sage.math, 2011)
307
All passed: ... random tests on ... matrices.
308
309
"""
310
from sage.misc.misc import walltime
311
from sage.misc.prandom import random, randint
312
from sage.combinat.permutation import Permutations
313
from sage.matrix.constructor import random_matrix, matrix
314
from sage.rings.finite_rings.constructor import FiniteField as GF
315
from sage.rings.arith import next_prime
316
cdef int h, i, j, nrows, k, num_tests = 0, num_matrices = 0
317
cdef MatrixStruct M, N
318
for m in range(n):
319
p = random()*(density_range[1]-density_range[0]) + density_range[0]
320
nrows = randint(1, nrows_max)
321
ncols = randint(1, ncols_max)
322
nsymbols = next_prime(randint(1, nsymbols_max))
323
S = Permutations(ncols)
324
MM = random_matrix(GF(nsymbols), nrows, ncols, sparse=False, density=p)
325
M = MatrixStruct( MM )
326
M.run()
327
328
for i from 0 <= i < perms_per_matrix:
329
perm = [a-1 for a in list(S.random_element())]
330
NN = matrix(GF(nsymbols), nrows, ncols)
331
for j from 0 <= j < ncols:
332
NN.set_column(perm[j], MM.column(j))
333
N = MatrixStruct(NN)
334
# now N is a random permutation of M
335
N.run()
336
337
M_relab = M.canonical_relabeling()
338
N_relab = N.canonical_relabeling()
339
340
M_C = matrix(GF(nsymbols), nrows, ncols)
341
N_C = matrix(GF(nsymbols), nrows, ncols)
342
343
for j from 0 <= j < ncols:
344
M_C.set_column(M_relab[j], MM.column(j))
345
N_C.set_column(N_relab[j], NN.column(j))
346
347
M_C = matrix(GF(nsymbols), sorted(M_C.rows()))
348
N_C = matrix(GF(nsymbols), sorted(N_C.rows()))
349
350
if M_C != N_C:
351
print "M:"
352
print M.matrix.str()
353
print "perm:"
354
print perm
355
return
356
357
isom = M.is_isomorphic(N)
358
if not isom:
359
print "isom FAILURE: M:"
360
print M.matrix.str()
361
print "isom FAILURE: N:"
362
print N.matrix.str()
363
return
364
365
num_tests += perms_per_matrix
366
num_matrices += 2
367
print "All passed: %d random tests on %d matrices."%(num_tests, num_matrices)
368
369
370
371
372
373