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