Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/coding/binary_code.pyx
8815 views
1
"""
2
Fast binary code routines.
3
4
Some computations with linear binary codes. Fix a basis for $GF(2)^n$.
5
A linear binary code is a linear subspace of $GF(2)^n$, together with
6
this choice of basis. A permutation $g \in S_n$ of the fixed basis
7
gives rise to a permutation of the vectors, or words, in $GF(2)^n$,
8
sending $(w_i)$ to $(w_{g(i)})$. The permutation automorphism group of
9
the code $C$ is the set of permutations of the basis that bijectively
10
map $C$ to itself. Note that if $g$ is such a permutation, then
11
$$g(a_i) + g(b_i) = (a_{g(i)} + b_{g(i)}) = g((a_i) + (b_i)).$$
12
Over other fields, it is also required that the map be linear, which
13
as per above boils down to scalar multiplication. However, over
14
$GF(2),$ the only scalars are 0 and 1, so the linearity condition has
15
trivial effect.
16
17
AUTHOR:
18
Robert L Miller (Oct-Nov 2007)
19
* compiled code data structure
20
* union-find based orbit partition
21
* optimized partition stack class
22
* NICE-based partition refinement algorithm
23
* canonical generation function
24
25
"""
26
27
#*******************************************************************************
28
# Copyright (C) 2007 Robert L. Miller <[email protected]>
29
#
30
# Distributed under the terms of the GNU General Public License (GPL)
31
# http://www.gnu.org/licenses/
32
#*******************************************************************************
33
34
include 'sage/ext/cdefs.pxi'
35
from cpython.mem cimport *
36
include 'sage/ext/stdsage.pxi'
37
include 'sage/ext/interrupt.pxi'
38
from sage.structure.element import is_Matrix
39
from sage.misc.misc import cputime
40
from sage.rings.integer cimport Integer
41
from copy import copy
42
43
WORD_SIZE = sizeof(codeword) << 3
44
45
cdef enum:
46
chunk_size = 8
47
48
cdef inline int min(int a, int b):
49
if a > b:
50
return b
51
else:
52
return a
53
54
## NOTE - Since most of the functions are used from within the module, cdef'd
55
## functions come without an underscore, and the def'd equivalents, which are
56
## essentially only for doctesting and debugging, have underscores.
57
58
cdef int *hamming_weights():
59
cdef int *ham_wts, i
60
ham_wts = <int *> sage_malloc( 65536 * sizeof(int) )
61
if ham_wts is NULL:
62
sage_free(ham_wts)
63
raise MemoryError("Memory.")
64
ham_wts[0] = 0
65
ham_wts[1] = 1
66
ham_wts[2] = 1
67
ham_wts[3] = 2
68
for i from 4 <= i < 16:
69
ham_wts[i] = ham_wts[i & 3] + ham_wts[(i>>2) & 3]
70
for i from 16 <= i < 256:
71
ham_wts[i] = ham_wts[i & 15] + ham_wts[(i>>4) & 15]
72
for i from 256 <= i < 65536:
73
ham_wts[i] = ham_wts[i & 255] + ham_wts[(i>>8) & 255]
74
return ham_wts
75
76
include 'sage/misc/bitset.pxi'
77
def weight_dist(M):
78
"""
79
Computes the weight distribution of the row space of M.
80
81
EXAMPLES:
82
sage: from sage.coding.binary_code import weight_dist
83
sage: M = Matrix(GF(2),[
84
... [1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0],
85
... [0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0],
86
... [0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1],
87
... [0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1],
88
... [0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1]])
89
sage: weight_dist(M)
90
[1, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 1]
91
sage: M = Matrix(GF(2),[
92
... [1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0],
93
... [0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0],
94
... [0,0,0,0,0,1,0,1,0,0,0,1,1,1,1,1,1],
95
... [0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,1]])
96
sage: weight_dist(M)
97
[1, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 4, 0, 0, 0, 0, 0]
98
sage: M=Matrix(GF(2),[
99
... [1,0,0,1,1,1,1,0,0,1,0,0,0,0,0,0,0],
100
... [0,1,0,0,1,1,1,1,0,0,1,0,0,0,0,0,0],
101
... [0,0,1,0,0,1,1,1,1,0,0,1,0,0,0,0,0],
102
... [0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0,0],
103
... [0,0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0],
104
... [0,0,0,0,0,1,0,0,1,1,1,1,0,0,1,0,0],
105
... [0,0,0,0,0,0,1,0,0,1,1,1,1,0,0,1,0],
106
... [0,0,0,0,0,0,0,1,0,0,1,1,1,1,0,0,1]])
107
sage: weight_dist(M)
108
[1, 0, 0, 0, 0, 0, 68, 0, 85, 0, 68, 0, 34, 0, 0, 0, 0, 0]
109
110
"""
111
cdef bitset_t word
112
cdef int i,j,k, dim=M.nrows(), deg=M.ncols()
113
cdef list L
114
cdef int *LL = <int *> sage_malloc((deg+1) * sizeof(int))
115
cdef bitset_s *basis = <bitset_s *> sage_malloc(dim * sizeof(bitset_s))
116
for i from 0 <= i < dim:
117
bitset_init(&basis[i], deg)
118
bitset_zero(&basis[i])
119
for j in M.row(i).nonzero_positions():
120
bitset_set(&basis[i], j)
121
for i from 0 <= i < deg+1: LL[i] = 0
122
bitset_init(word, deg)
123
bitset_zero(word)
124
i = 0
125
j = 0
126
while 1:
127
LL[bitset_hamming_weight(word)] += 1
128
i ^= 1
129
k = 0
130
if not i:
131
while not j & (1 << k): k += 1
132
k += 1
133
if k == dim: break
134
else:
135
j ^= (1 << k)
136
bitset_xor(word, word, &basis[k])
137
bitset_free(word)
138
L = [int(LL[i]) for i from 0 <= i < deg+1]
139
for i from 0 <= i < dim:
140
bitset_free(&basis[i])
141
sage_free(LL)
142
sage_free(basis)
143
return L
144
145
def test_word_perms(t_limit=5.0):
146
"""
147
Tests the WordPermutation structs for at least t_limit seconds.
148
149
These are structures written in pure C for speed, and are tested from this
150
function, which performs the following tests:
151
152
1. Tests create_word_perm, which creates a WordPermutation from a Python
153
list L representing a permutation i --> L[i]. Takes a random word and
154
permutes it by a random list permutation, and tests that the result
155
agrees with doing it the slow way.
156
157
1b. Tests create_array_word_perm, which creates a WordPermutation from a
158
C array. Does the same as above.
159
160
2. Tests create_comp_word_perm, which creates a WordPermutation as a
161
composition of two WordPermutations. Takes a random word and
162
two random permutations, and tests that the result of permuting by the
163
composition is correct.
164
165
3. Tests create_inv_word_perm and create_id_word_perm, which create a
166
WordPermutation as the inverse and identity permutations, resp.
167
Takes a random word and a random permutation, and tests that the result
168
permuting by the permutation and its inverse in either order, and
169
permuting by the identity both return the original word.
170
171
.. NOTE::
172
173
The functions permute_word_by_wp and dealloc_word_perm are implicitly
174
involved in each of the above tests.
175
176
TESTS::
177
178
sage: from sage.coding.binary_code import test_word_perms
179
sage: test_word_perms() # long time (5s on sage.math, 2011)
180
181
"""
182
cdef WordPermutation *g, *h, *i
183
cdef codeword cw1, cw2, cw3
184
cdef int n = sizeof(codeword) << 3
185
cdef int j, *arr = <int*> sage_malloc(n * sizeof(int))
186
if arr is NULL:
187
raise MemoryError("Error allocating memory.")
188
from sage.misc.prandom import randint
189
from sage.combinat.permutation import Permutations
190
S = Permutations(range(n))
191
t = cputime()
192
while cputime(t) < t_limit:
193
word = [randint(0,1) for _ in xrange(n)]
194
cw1 = 0
195
for j from 0 <= j < n:
196
cw1 += (<codeword>word[j]) << (<codeword>j)
197
# 1. test create_word_perm
198
gg = S.random_element()
199
g = create_word_perm(gg)
200
word2 = [0]*n
201
for j from 0 <= j < n:
202
word2[gg[j]] = word[j]
203
cw2 = permute_word_by_wp(g, cw1)
204
cw3 = 0
205
for j from 0 <= j < n:
206
cw3 += (<codeword>word2[j]) << (<codeword>j)
207
if cw3 != cw2:
208
print "ERROR1"
209
dealloc_word_perm(g)
210
# 1b. test create_array_word_perm
211
gg = S.random_element()
212
for j from 0 <= j < n:
213
arr[j] = gg[j]
214
g = create_array_word_perm(arr, 0, n)
215
word2 = [0]*n
216
for j from 0 <= j < n:
217
word2[gg[j]] = word[j]
218
cw2 = permute_word_by_wp(g, cw1)
219
cw3 = 0
220
for j from 0 <= j < n:
221
cw3 += (<codeword>word2[j]) << (<codeword>j)
222
if cw3 != cw2:
223
print "ERROR1b"
224
dealloc_word_perm(g)
225
# 2. test create_comp_word_perm
226
gg = S.random_element()
227
hh = S.random_element()
228
g = create_word_perm(gg)
229
h = create_word_perm(hh)
230
i = create_comp_word_perm(g, h)
231
word2 = [0]*n
232
for j from 0 <= j < n:
233
word2[gg[hh[j]]] = word[j]
234
cw2 = permute_word_by_wp(i, cw1)
235
cw3 = 0
236
for j from 0 <= j < n:
237
cw3 += (<codeword>word2[j]) << (<codeword>j)
238
if cw3 != cw2:
239
print "ERROR2"
240
dealloc_word_perm(g)
241
dealloc_word_perm(h)
242
dealloc_word_perm(i)
243
# 3. test create_inv_word_perm and create_id_word_perm
244
gg = S.random_element()
245
g = create_word_perm(gg)
246
h = create_inv_word_perm(g)
247
i = create_id_word_perm(n)
248
cw2 = permute_word_by_wp(g, cw1)
249
cw2 = permute_word_by_wp(h, cw2)
250
if cw1 != cw2:
251
print "ERROR3a"
252
cw2 = permute_word_by_wp(h, cw1)
253
cw2 = permute_word_by_wp(g, cw2)
254
if cw1 != cw2:
255
print "ERROR3b"
256
cw2 = permute_word_by_wp(i, cw1)
257
if cw1 != cw2:
258
print "ERROR3c"
259
dealloc_word_perm(g)
260
dealloc_word_perm(h)
261
dealloc_word_perm(i)
262
sage_free(arr)
263
264
cdef WordPermutation *create_word_perm(object list_perm):
265
r"""
266
Create a word permutation from a Python list permutation L, i.e. such that
267
$i \mapsto L[i]$.
268
"""
269
cdef int i, j, parity, comb, words_per_chunk, num_chunks = 1
270
cdef codeword *images_i, image
271
cdef WordPermutation *word_perm = <WordPermutation *> sage_malloc( sizeof(WordPermutation) )
272
if word_perm is NULL:
273
raise RuntimeError("Error allocating memory.")
274
word_perm.degree = len(list_perm)
275
list_perm = copy(list_perm)
276
while num_chunks*chunk_size < word_perm.degree:
277
num_chunks += 1
278
word_perm.images = <codeword **> sage_malloc(num_chunks * sizeof(codeword *))
279
if word_perm.images is NULL:
280
sage_free(word_perm)
281
raise RuntimeError("Error allocating memory.")
282
word_perm.chunk_num = num_chunks
283
words_per_chunk = 1 << chunk_size
284
word_perm.gate = ( (<codeword>1) << chunk_size ) - 1
285
list_perm += range(len(list_perm), chunk_size*num_chunks)
286
word_perm.chunk_words = words_per_chunk
287
for i from 0 <= i < num_chunks:
288
images_i = <codeword *> sage_malloc(words_per_chunk * sizeof(codeword))
289
if images_i is NULL:
290
for j from 0 <= j < i:
291
sage_free(word_perm.images[j])
292
sage_free(word_perm.images)
293
sage_free(word_perm)
294
raise RuntimeError("Error allocating memory.")
295
word_perm.images[i] = images_i
296
for j from 0 <= j < chunk_size:
297
images_i[1 << j] = (<codeword>1) << list_perm[chunk_size*i + j]
298
image = <codeword> 0
299
parity = 0
300
comb = 0
301
while 1:
302
images_i[comb] = image
303
parity ^= 1
304
j = 0
305
if not parity:
306
while not comb & (1 << j): j += 1
307
j += 1
308
if j == chunk_size: break
309
else:
310
comb ^= (1 << j)
311
image ^= images_i[1 << j]
312
return word_perm
313
314
cdef WordPermutation *create_array_word_perm(int *array, int start, int degree):
315
"""
316
Create a word permutation of a given degree from a C array, starting at start.
317
"""
318
cdef int i, j, cslim, parity, comb, words_per_chunk, num_chunks = 1
319
cdef codeword *images_i, image
320
cdef WordPermutation *word_perm = <WordPermutation *> sage_malloc( sizeof(WordPermutation) )
321
if word_perm is NULL:
322
raise RuntimeError("Error allocating memory.")
323
word_perm.degree = degree
324
while num_chunks*chunk_size < word_perm.degree:
325
num_chunks += 1
326
word_perm.images = <codeword **> sage_malloc(num_chunks * sizeof(codeword *))
327
if word_perm.images is NULL:
328
sage_free(word_perm)
329
raise RuntimeError("Error allocating memory.")
330
word_perm.chunk_num = num_chunks
331
words_per_chunk = 1 << chunk_size
332
word_perm.gate = ( (<codeword>1) << chunk_size ) - 1
333
word_perm.chunk_words = words_per_chunk
334
for i from 0 <= i < num_chunks:
335
images_i = <codeword *> sage_malloc(words_per_chunk * sizeof(codeword))
336
if images_i is NULL:
337
for j from 0 <= j < i:
338
sage_free(word_perm.images[j])
339
sage_free(word_perm.images)
340
sage_free(word_perm)
341
raise RuntimeError("Error allocating memory.")
342
word_perm.images[i] = images_i
343
cslim = min(chunk_size, degree - i*chunk_size)
344
for j from 0 <= j < cslim:
345
images_i[1 << j] = (<codeword>1) << array[start + chunk_size*i + j]
346
image = <codeword> 0
347
parity = 0
348
comb = 0
349
while 1:
350
images_i[comb] = image
351
parity ^= 1
352
j = 0
353
if not parity:
354
while not comb & (1 << j): j += 1
355
j += 1
356
if j == chunk_size: break
357
else:
358
comb ^= (1 << j)
359
image ^= images_i[1 << j]
360
return word_perm
361
362
cdef WordPermutation *create_id_word_perm(int degree):
363
"""
364
Create the identity word permutation of degree degree.
365
"""
366
cdef int i, j, parity, comb, words_per_chunk, num_chunks = 1
367
cdef codeword *images_i, image
368
cdef WordPermutation *word_perm = <WordPermutation *> sage_malloc( sizeof(WordPermutation) )
369
if word_perm is NULL:
370
raise RuntimeError("Error allocating memory.")
371
word_perm.degree = degree
372
while num_chunks*chunk_size < degree:
373
num_chunks += 1
374
word_perm.images = <codeword **> sage_malloc(num_chunks * sizeof(codeword *))
375
if word_perm.images is NULL:
376
sage_free(word_perm)
377
raise RuntimeError("Error allocating memory.")
378
word_perm.chunk_num = num_chunks
379
words_per_chunk = 1 << chunk_size
380
word_perm.gate = ( (<codeword>1) << chunk_size ) - 1
381
word_perm.chunk_words = words_per_chunk
382
for i from 0 <= i < num_chunks:
383
images_i = <codeword *> sage_malloc(words_per_chunk * sizeof(codeword))
384
if images_i is NULL:
385
for j from 0 <= j < i:
386
sage_free(word_perm.images[j])
387
sage_free(word_perm.images)
388
sage_free(word_perm)
389
raise RuntimeError("Error allocating memory.")
390
word_perm.images[i] = images_i
391
for j from 0 <= j < chunk_size:
392
images_i[1 << j] = (<codeword>1) << (chunk_size*i + j)
393
image = <codeword> 0
394
parity = 0
395
comb = 0
396
while 1:
397
images_i[comb] = image
398
parity ^= 1
399
j = 0
400
if not parity:
401
while not comb & (1 << j): j += 1
402
j += 1
403
if j == chunk_size: break
404
else:
405
comb ^= (1 << j)
406
image ^= images_i[1 << j]
407
return word_perm
408
409
cdef WordPermutation *create_comp_word_perm(WordPermutation *g, WordPermutation *h):
410
r"""
411
Create the composition of word permutations $g \circ h$.
412
"""
413
cdef int i, j, parity, comb, words_per_chunk, num_chunks = 1
414
cdef codeword *images_i, image
415
cdef WordPermutation *word_perm = <WordPermutation *> sage_malloc( sizeof(WordPermutation) )
416
if word_perm is NULL:
417
raise RuntimeError("Error allocating memory.")
418
word_perm.degree = g.degree
419
while num_chunks*chunk_size < word_perm.degree:
420
num_chunks += 1
421
word_perm.images = <codeword **> sage_malloc(num_chunks * sizeof(codeword *))
422
if word_perm.images is NULL:
423
sage_free(word_perm)
424
raise RuntimeError("Error allocating memory.")
425
word_perm.chunk_num = num_chunks
426
words_per_chunk = 1 << chunk_size
427
word_perm.gate = ( (<codeword>1) << chunk_size ) - 1
428
word_perm.chunk_words = words_per_chunk
429
for i from 0 <= i < num_chunks:
430
images_i = <codeword *> sage_malloc(words_per_chunk * sizeof(codeword))
431
if images_i is NULL:
432
for j from 0 <= j < i:
433
sage_free(word_perm.images[j])
434
sage_free(word_perm.images)
435
sage_free(word_perm)
436
raise RuntimeError("Error allocating memory.")
437
word_perm.images[i] = images_i
438
for j from 0 <= j < chunk_size:
439
image = (<codeword>1) << (chunk_size*i + j)
440
image = permute_word_by_wp(h, image)
441
image = permute_word_by_wp(g, image)
442
images_i[1 << j] = image
443
image = <codeword> 0
444
parity = 0
445
comb = 0
446
while 1:
447
images_i[comb] = image
448
parity ^= 1
449
j = 0
450
if not parity:
451
while not comb & (1 << j): j += 1
452
j += 1
453
if j == chunk_size: break
454
else:
455
comb ^= (1 << j)
456
image ^= images_i[1 << j]
457
return word_perm
458
459
cdef WordPermutation *create_inv_word_perm(WordPermutation *g):
460
r"""
461
Create the inverse $g^{-1}$ of the word permutation of $g$.
462
"""
463
cdef int i, j, *array = <int *> sage_malloc( g.degree * sizeof(int) )
464
cdef codeword temp
465
cdef WordPermutation *w
466
for i from 0 <= i < g.degree:
467
j = 0
468
temp = permute_word_by_wp(g, (<codeword>1) << i)
469
while not ((<codeword>1) << j) & temp:
470
j += 1
471
array[j] = i
472
w = create_array_word_perm(array, 0, g.degree)
473
sage_free(array)
474
return w
475
476
cdef int dealloc_word_perm(WordPermutation *wp):
477
"""
478
Free the memory used by a word permutation.
479
"""
480
cdef int i
481
for i from 0 <= i < wp.chunk_num:
482
sage_free(wp.images[i])
483
sage_free(wp.images)
484
sage_free(wp)
485
486
cdef codeword permute_word_by_wp(WordPermutation *wp, codeword word):
487
"""
488
Return the codeword obtained by applying the permutation wp to word.
489
"""
490
cdef int num_chunks = wp.chunk_num
491
cdef int i
492
cdef codeword gate = wp.gate
493
cdef codeword image = 0
494
cdef codeword **images = wp.images
495
for i from 0 <= i < num_chunks:
496
image += images[i][(word >> i*chunk_size) & gate]
497
return image
498
499
def test_expand_to_ortho_basis(B=None):
500
"""
501
This function is written in pure C for speed, and is tested from this
502
function.
503
504
INPUT:
505
B -- a BinaryCode in standard form
506
507
OUTPUT:
508
An array of codewords which represent the expansion of a basis for $B$ to a
509
basis for $(B^\prime)^\perp$, where $B^\prime = B$ if the all-ones vector 1
510
is in $B$, otherwise $B^\prime = \text{span}(B,1)$ (note that this guarantees
511
that all the vectors in the span of the output have even weight).
512
513
TESTS:
514
sage: from sage.coding.binary_code import test_expand_to_ortho_basis, BinaryCode
515
sage: M = Matrix(GF(2), [[1,1,1,1,1,1,0,0,0,0],[0,0,1,1,1,1,1,1,1,1]])
516
sage: B = BinaryCode(M)
517
sage: B.put_in_std_form()
518
0
519
sage: test_expand_to_ortho_basis(B=B)
520
INPUT CODE:
521
Binary [10,2] linear code, generator matrix
522
[1010001111]
523
[0101111111]
524
Expanding to the basis of an orthogonal complement...
525
Basis:
526
0010000010
527
0001000010
528
0000100001
529
0000010001
530
0000001001
531
532
"""
533
cdef codeword *output
534
cdef int k=0, i
535
cdef BinaryCode C
536
if not isinstance(B, BinaryCode):
537
raise TypeError()
538
C = B
539
print "INPUT CODE:"
540
print C
541
print "Expanding to the basis of an orthogonal complement..."
542
output = expand_to_ortho_basis(C, C.ncols)
543
print "Basis:"
544
while output[k]:
545
k += 1
546
for i from 0 <= i < k:
547
print ''.join(reversed(Integer(output[i]).binary().zfill(C.ncols)))
548
sage_free(output)
549
550
cdef codeword *expand_to_ortho_basis(BinaryCode B, int n):
551
r"""
552
INPUT:
553
B -- a BinaryCode in standard form
554
n -- the degree
555
556
OUTPUT:
557
An array of codewords which represent the expansion of a basis for $B$ to a
558
basis for $(B^\prime)^\perp$, where $B^\prime = B$ if the all-ones vector 1
559
is in $B$, otherwise $B^\prime = \text{span}(B,1)$ (note that this guarantees
560
that all the vectors in the span of the output have even weight).
561
"""
562
# assumes B is already in standard form
563
cdef codeword *basis, word = 0, temp, new, pivots = 0, combo, parity
564
cdef codeword n_gate = (~<codeword>0) >> ( (sizeof(codeword)<<3) - n)
565
cdef int i, j, m, k = B.nrows, dead, d
566
cdef WordPermutation *wp
567
basis = <codeword *> sage_malloc( (n+1) * sizeof(codeword) )
568
if basis is NULL:
569
raise MemoryError()
570
for i from 0 <= i < k:
571
basis[i] = B.basis[i]
572
word ^= basis[i]
573
# If 11...1 is already a word of the code,
574
# then being orthogonal to the code guarantees
575
# being even weight. Otherwise, add this in.
576
word = (~word) & n_gate
577
if word:
578
basis[k] = word
579
temp = (<codeword>1) << k
580
i = k
581
while not word & temp:
582
temp = temp << 1
583
i += 1
584
for j from 0 <= j < k:
585
if temp & basis[j]:
586
basis[j] ^= word
587
temp += (<codeword>1 << k) - 1
588
i = k
589
word = <codeword>1 << k
590
k += 1
591
else: # NOTE THIS WILL NEVER HAPPEN AS CURRENTLY SET UP!
592
temp = (<codeword>1 << k) - 1
593
i = k
594
word = <codeword>1 << k
595
# Now:
596
# k is the length of the basis so far
597
j = k
598
while i < n:
599
# we are now looking at the ith free variable,
600
# word has a 1 in the ith place, and
601
# j is the current row we are putting in basis
602
new = 0
603
for m from 0 <= m < k:
604
if basis[m] & word:
605
new ^= basis[m]
606
basis[j] = (new & temp) + word
607
j += ((word ^ temp) >> i) & 1
608
i += 1
609
word = word << 1
610
temp = (<codeword>1 << B.nrows) - 1
611
for i from k <= i < n:
612
basis[i-k] = basis[i] ^ B.words[basis[i] & temp]
613
k = n-k
614
i = 0
615
word = (<codeword>1 << B.nrows)
616
while i < k and (word & n_gate):
617
m = i
618
while m < k and not basis[m] & word:
619
m += 1
620
if m < k:
621
pivots += word
622
if m != i:
623
new = basis[i]
624
basis[i] = basis[m]
625
basis[m] = new
626
for j from 0 <= j < i:
627
if basis[j] & word:
628
basis[j] ^= basis[i]
629
for j from i < j < k:
630
if basis[j] & word:
631
basis[j] ^= basis[i]
632
i += 1
633
word = word << 1
634
for j from i <= j < n:
635
basis[j] = 0
636
# now basis is length i
637
perm = range(B.nrows)
638
perm_c = []
639
for j from B.nrows <= j < B.ncols:
640
if (<codeword>1 << j) & pivots:
641
perm.append(j)
642
else:
643
perm_c.append(j)
644
perm.extend(perm_c)
645
perm.extend(range(B.ncols, n))
646
perm_c = [0]*n
647
for j from 0 <= j < n:
648
perm_c[perm[j]] = j
649
wp = create_word_perm(perm_c)
650
for j from 0 <= j < i:
651
basis[j] = permute_word_by_wp(wp, basis[j])
652
for j from 0 <= j < B.nrows:
653
B.basis[j] = permute_word_by_wp(wp, B.basis[j])
654
dealloc_word_perm(wp)
655
word = 0
656
parity = 0
657
combo = 0
658
while 1:
659
B.words[combo] = word
660
parity ^= 1
661
j = 0
662
if not parity:
663
while not combo & (1 << j): j += 1
664
j += 1
665
if j == B.nrows: break
666
else:
667
combo ^= (1 << j)
668
word ^= B.basis[j]
669
return basis
670
671
cdef class BinaryCode:
672
"""
673
Minimal, but optimized, binary code object.
674
675
EXAMPLE:
676
sage: import sage.coding.binary_code
677
sage: from sage.coding.binary_code import *
678
sage: M = Matrix(GF(2), [[1,1,1,1]])
679
sage: B = BinaryCode(M) # create from matrix
680
sage: C = BinaryCode(B, 60) # create using glue
681
sage: D = BinaryCode(C, 240)
682
sage: E = BinaryCode(D, 85)
683
sage: B
684
Binary [4,1] linear code, generator matrix
685
[1111]
686
sage: C
687
Binary [6,2] linear code, generator matrix
688
[111100]
689
[001111]
690
sage: D
691
Binary [8,3] linear code, generator matrix
692
[11110000]
693
[00111100]
694
[00001111]
695
sage: E
696
Binary [8,4] linear code, generator matrix
697
[11110000]
698
[00111100]
699
[00001111]
700
[10101010]
701
702
sage: M = Matrix(GF(2), [[1]*32])
703
sage: B = BinaryCode(M)
704
sage: B
705
Binary [32,1] linear code, generator matrix
706
[11111111111111111111111111111111]
707
708
"""
709
def __cinit__(self, arg1, arg2=None):
710
cdef int nrows, i, j, size
711
cdef int nwords, other_nwords, parity, combination
712
cdef codeword word, glue_word
713
cdef BinaryCode other
714
cdef codeword *self_words, *self_basis, *other_basis
715
716
self.radix = sizeof(int) << 3
717
718
if is_Matrix(arg1):
719
self.ncols = arg1.ncols()
720
self.nrows = arg1.nrows()
721
nrows = self.nrows
722
self.nwords = 1 << nrows
723
nwords = self.nwords
724
elif isinstance(arg1, BinaryCode):
725
other = <BinaryCode> arg1
726
self.nrows = other.nrows + 1
727
glue_word = <codeword> arg2
728
size = 0
729
while 0 < ((<codeword> 1) << size) <= glue_word:
730
size += 1
731
if other.ncols > size:
732
self.ncols = other.ncols
733
else:
734
self.ncols = size
735
other_nwords = other.nwords
736
self.nwords = 2 * other_nwords
737
nrows = self.nrows
738
nwords = self.nwords
739
else: raise NotImplementedError("!")
740
741
if self.nrows >= self.radix or self.ncols > self.radix:
742
raise NotImplementedError("Columns and rows are stored as ints. This code is too big.")
743
744
self.words = <codeword *> sage_malloc( nwords * sizeof(int) )
745
self.basis = <codeword *> sage_malloc( nrows * sizeof(int) )
746
if self.words is NULL or self.basis is NULL:
747
if self.words is not NULL: sage_free(self.words)
748
if self.basis is not NULL: sage_free(self.basis)
749
raise MemoryError("Memory.")
750
self_words = self.words
751
self_basis = self.basis
752
753
if is_Matrix(arg1):
754
rows = arg1.rows()
755
for i from 0 <= i < nrows:
756
word = <codeword> 0
757
for j in rows[i].nonzero_positions():
758
word += (1<<j)
759
self_basis[i] = word
760
761
word = <codeword> 0
762
parity = 0
763
combination = 0
764
while 1:
765
self_words[combination] = word
766
parity ^= 1
767
j = 0
768
if not parity:
769
while not combination & (1 << j): j += 1
770
j += 1
771
if j == nrows: break
772
else:
773
combination ^= (1 << j)
774
word ^= self_basis[j]
775
776
else: # isinstance(arg1, BinaryCode)
777
other_basis = other.basis
778
for i from 0 <= i < nrows-1:
779
self_basis[i] = other_basis[i]
780
i = nrows - 1
781
self_basis[i] = glue_word
782
783
memcpy(self_words, other.words, other_nwords*(self.radix>>3))
784
785
for combination from 0 <= combination < other_nwords:
786
self_words[combination+other_nwords] = self_words[combination] ^ glue_word
787
788
def __dealloc__(self):
789
sage_free(self.words)
790
sage_free(self.basis)
791
792
def __reduce__(self):
793
"""
794
Method for pickling and unpickling BinaryCodes.
795
796
TESTS:
797
sage: from sage.coding.binary_code import *
798
sage: M = Matrix(GF(2), [[1,1,1,1]])
799
sage: B = BinaryCode(M)
800
sage: loads(dumps(B)) == B
801
True
802
803
"""
804
return BinaryCode, (self.matrix(),)
805
806
def __cmp__(self, other):
807
"""
808
Comparison of BinaryCodes.
809
810
TESTS:
811
sage: from sage.coding.binary_code import *
812
sage: M = Matrix(GF(2), [[1,1,1,1]])
813
sage: B = BinaryCode(M)
814
sage: C = BinaryCode(B.matrix())
815
sage: B == C
816
True
817
818
"""
819
return cmp(self.matrix(), other.matrix())
820
821
def matrix(self):
822
"""
823
Returns the generator matrix of the BinaryCode, i.e. the code is the
824
rowspace of B.matrix().
825
826
EXAMPLE:
827
sage: M = Matrix(GF(2), [[1,1,1,1,0,0],[0,0,1,1,1,1]])
828
sage: from sage.coding.binary_code import *
829
sage: B = BinaryCode(M)
830
sage: B.matrix()
831
[1 1 1 1 0 0]
832
[0 0 1 1 1 1]
833
834
"""
835
cdef int i, j
836
from sage.matrix.constructor import matrix
837
from sage.rings.all import GF
838
rows = []
839
for i from 0 <= i < self.nrows:
840
row = [0]*self.ncols
841
for j from 0 <= j < self.ncols:
842
if self.basis[i] & ((<codeword>1) << j):
843
row[j] = 1
844
rows.append(row)
845
return matrix(GF(2), self.nrows, self.ncols, rows)
846
847
def print_data(self):
848
"""
849
Print all data for self.
850
851
EXAMPLES:
852
sage: import sage.coding.binary_code
853
sage: from sage.coding.binary_code import *
854
sage: M = Matrix(GF(2), [[1,1,1,1]])
855
sage: B = BinaryCode(M)
856
sage: C = BinaryCode(B, 60)
857
sage: D = BinaryCode(C, 240)
858
sage: E = BinaryCode(D, 85)
859
sage: B.print_data() # random - actually "print P.print_data()"
860
ncols: 4
861
nrows: 1
862
nwords: 2
863
radix: 32
864
basis:
865
1111
866
words:
867
0000
868
1111
869
sage: C.print_data() # random - actually "print P.print_data()"
870
ncols: 6
871
nrows: 2
872
nwords: 4
873
radix: 32
874
basis:
875
111100
876
001111
877
words:
878
000000
879
111100
880
001111
881
110011
882
sage: D.print_data() # random - actually "print P.print_data()"
883
ncols: 8
884
nrows: 3
885
nwords: 8
886
radix: 32
887
basis:
888
11110000
889
00111100
890
00001111
891
words:
892
00000000
893
11110000
894
00111100
895
11001100
896
00001111
897
11111111
898
00110011
899
11000011
900
sage: E.print_data() # random - actually "print P.print_data()"
901
ncols: 8
902
nrows: 4
903
nwords: 16
904
radix: 32
905
basis:
906
11110000
907
00111100
908
00001111
909
10101010
910
words:
911
00000000
912
11110000
913
00111100
914
11001100
915
00001111
916
11111111
917
00110011
918
11000011
919
10101010
920
01011010
921
10010110
922
01100110
923
10100101
924
01010101
925
10011001
926
01101001
927
"""
928
from sage.graphs.generic_graph_pyx import binary
929
cdef int ui
930
cdef int i
931
s = ''
932
s += "ncols:" + str(self.ncols)
933
s += "\nnrows:" + str(self.nrows)
934
s += "\nnwords:" + str(self.nwords)
935
s += "\nradix:" + str(self.radix)
936
s += "\nbasis:\n"
937
for i from 0 <= i < self.nrows:
938
b = list(binary(self.basis[i]).zfill(self.ncols))
939
b.reverse()
940
b.append('\n')
941
s += ''.join(b)
942
s += "\nwords:\n"
943
for ui from 0 <= ui < self.nwords:
944
b = list(binary(self.words[ui]).zfill(self.ncols))
945
b.reverse()
946
b.append('\n')
947
s += ''.join(b)
948
949
def __repr__(self):
950
"""
951
String representation of self.
952
953
EXAMPLE:
954
sage: import sage.coding.binary_code
955
sage: from sage.coding.binary_code import *
956
sage: M = Matrix(GF(2), [[1,1,1,1,0,0,0,0],[0,0,1,1,1,1,0,0],[0,0,0,0,1,1,1,1],[1,0,1,0,1,0,1,0]])
957
sage: B = BinaryCode(M)
958
sage: B
959
Binary [8,4] linear code, generator matrix
960
[11110000]
961
[00111100]
962
[00001111]
963
[10101010]
964
965
"""
966
cdef int i, j
967
s = 'Binary [%d,%d] linear code, generator matrix\n'%(self.ncols, self.nrows)
968
for i from 0 <= i < self.nrows:
969
s += '[' + self._word((<codeword> 1)<<i) + ']\n'
970
return s
971
972
def _word(self, coords):
973
"""
974
Considering coords as an integer in binary, think of the 0's and 1's as
975
coefficients of the basis given by self.matrix(). This function returns
976
a string representation of that word.
977
978
EXAMPLE:
979
sage: from sage.coding.binary_code import *
980
sage: M = Matrix(GF(2), [[1,1,1,1]])
981
sage: B = BinaryCode(M)
982
sage: B._word(0)
983
'0000'
984
sage: B._word(1)
985
'1111'
986
987
Note that behavior under input which does not represent a word in
988
the code is unspecified (gives nonsense).
989
990
"""
991
s = ''
992
for j from 0 <= j < self.ncols:
993
s += '%d'%self.is_one(coords,j)
994
return s
995
996
def _is_one(self, word, col):
997
"""
998
Returns the col-th letter of word, i.e. 0 or 1. Words are expressed
999
as integers, which represent linear combinations of the rows of the
1000
generator matrix of the code.
1001
1002
EXAMPLE:
1003
sage: import sage.coding.binary_code
1004
sage: from sage.coding.binary_code import *
1005
sage: M = Matrix(GF(2), [[1,1,1,1,0,0,0,0],[0,0,1,1,1,1,0,0],[0,0,0,0,1,1,1,1],[1,0,1,0,1,0,1,0]])
1006
sage: B = BinaryCode(M)
1007
sage: B
1008
Binary [8,4] linear code, generator matrix
1009
[11110000]
1010
[00111100]
1011
[00001111]
1012
[10101010]
1013
sage: B._is_one(7, 4)
1014
0
1015
sage: B._is_one(8, 4)
1016
1
1017
sage: B._is_automorphism([1,0,3,2,4,5,6,7], [0, 1, 2, 3, 4, 5, 6, 7, 9, 8, 11, 10, 13, 12, 15, 14])
1018
1
1019
1020
"""
1021
return self.is_one(word, col) != 0
1022
1023
cdef int is_one(self, int word, int column):
1024
return (self.words[word] & (<codeword> 1 << column)) >> column
1025
1026
def _is_automorphism(self, col_gamma, word_gamma):
1027
"""
1028
Check whether a given permutation is an automorphism of the code.
1029
1030
INPUT:
1031
col_gamma -- permutation sending i |--> col_gamma[i] acting
1032
on the columns.
1033
word_gamma -- permutation sending i |--> word_gamma[i] acting
1034
on the words.
1035
1036
EXAMPLE:
1037
sage: import sage.coding.binary_code
1038
sage: from sage.coding.binary_code import *
1039
sage: M = Matrix(GF(2), [[1,1,1,1,0,0,0,0],[0,0,1,1,1,1,0,0],[0,0,0,0,1,1,1,1],[1,0,1,0,1,0,1,0]])
1040
sage: B = BinaryCode(M)
1041
sage: B
1042
Binary [8,4] linear code, generator matrix
1043
[11110000]
1044
[00111100]
1045
[00001111]
1046
[10101010]
1047
sage: B._is_automorphism([1,0,3,2,4,5,6,7], [0, 1, 2, 3, 4, 5, 6, 7, 9, 8, 11, 10, 13, 12, 15, 14])
1048
1
1049
1050
"""
1051
cdef int i
1052
cdef int *_col_gamma
1053
cdef int *_word_gamma
1054
_word_gamma = <int *> sage_malloc(self.nwords * sizeof(int))
1055
_col_gamma = <int *> sage_malloc(self.ncols * sizeof(int))
1056
if _col_gamma is NULL or _word_gamma is NULL:
1057
if _word_gamma is not NULL: sage_free(_word_gamma)
1058
if _col_gamma is not NULL: sage_free(_col_gamma)
1059
raise MemoryError("Memory.")
1060
for i from 0 <= i < self.nwords:
1061
_word_gamma[i] = word_gamma[i]
1062
for i from 0 <= i < self.ncols:
1063
_col_gamma[i] = col_gamma[i]
1064
result = self.is_automorphism(_col_gamma, _word_gamma)
1065
sage_free(_col_gamma)
1066
sage_free(_word_gamma)
1067
return result
1068
1069
cdef int is_automorphism(self, int *col_gamma, int *word_gamma):
1070
cdef int i, j, self_nwords = self.nwords, self_ncols = self.ncols
1071
i = 1
1072
while i < self_nwords:
1073
for j from 0 <= j < self_ncols:
1074
if self.is_one(i, j) != self.is_one(word_gamma[i], col_gamma[j]):
1075
return 0
1076
i = i << 1
1077
return 1
1078
1079
def apply_permutation(self, labeling):
1080
"""
1081
Apply a column permutation to the code.
1082
1083
INPUT:
1084
labeling -- a list permutation of the columns
1085
1086
EXAMPLE:
1087
sage: from sage.coding.binary_code import *
1088
sage: B = BinaryCode(codes.ExtendedBinaryGolayCode().gen_mat())
1089
sage: B
1090
Binary [24,12] linear code, generator matrix
1091
[100000000000101011100011]
1092
[010000000000111110010010]
1093
[001000000000110100101011]
1094
[000100000000110001110110]
1095
[000010000000110011011001]
1096
[000001000000011001101101]
1097
[000000100000001100110111]
1098
[000000010000101101111000]
1099
[000000001000010110111100]
1100
[000000000100001011011110]
1101
[000000000010101110001101]
1102
[000000000001010111000111]
1103
sage: B.apply_permutation(range(11,-1,-1) + range(12, 24))
1104
sage: B
1105
Binary [24,12] linear code, generator matrix
1106
[000000000001101011100011]
1107
[000000000010111110010010]
1108
[000000000100110100101011]
1109
[000000001000110001110110]
1110
[000000010000110011011001]
1111
[000000100000011001101101]
1112
[000001000000001100110111]
1113
[000010000000101101111000]
1114
[000100000000010110111100]
1115
[001000000000001011011110]
1116
[010000000000101110001101]
1117
[100000000000010111000111]
1118
1119
"""
1120
# Tests for this function implicitly test _apply_permutation_to_basis
1121
# and _update_words_from_basis. These functions should not be used
1122
# individually by the user, so they remain cdef'd.
1123
self._apply_permutation_to_basis(labeling)
1124
self._update_words_from_basis()
1125
1126
cdef void _apply_permutation_to_basis(self, object labeling):
1127
cdef WordPermutation *wp
1128
cdef int i
1129
wp = create_word_perm(labeling)
1130
for i from 0 <= i < self.nrows:
1131
self.basis[i] = permute_word_by_wp(wp, self.basis[i])
1132
dealloc_word_perm(wp)
1133
1134
cdef void _update_words_from_basis(self):
1135
cdef codeword word
1136
cdef int j, parity, combination
1137
word = 0
1138
parity = 0
1139
combination = 0
1140
while 1:
1141
self.words[combination] = word
1142
parity ^= 1
1143
j = 0
1144
if not parity:
1145
while not combination & (1 << j): j += 1
1146
j += 1
1147
if j == self.nrows: break
1148
else:
1149
combination ^= (1 << j)
1150
word ^= self.basis[j]
1151
1152
1153
cpdef int put_in_std_form(self):
1154
"""
1155
Put the code in binary form, which is defined by an identity matrix on
1156
the left, augmented by a matrix of data.
1157
1158
EXAMPLE:
1159
sage: from sage.coding.binary_code import *
1160
sage: M = Matrix(GF(2), [[1,1,1,1,0,0],[0,0,1,1,1,1]])
1161
sage: B = BinaryCode(M); B
1162
Binary [6,2] linear code, generator matrix
1163
[111100]
1164
[001111]
1165
sage: B.put_in_std_form(); B
1166
0
1167
Binary [6,2] linear code, generator matrix
1168
[101011]
1169
[010111]
1170
1171
"""
1172
cdef codeword swap, current = 1, pivots = 0
1173
cdef int i, j, k, row = 0
1174
cdef object perm
1175
while row < self.nrows:
1176
i = row
1177
while i < self.nrows and not self.basis[i] & current:
1178
i += 1
1179
if i < self.nrows:
1180
pivots += current
1181
if i != row:
1182
swap = self.basis[row]
1183
self.basis[row] = self.basis[i]
1184
self.basis[i] = swap
1185
for j from 0 <= j < row:
1186
if self.basis[j] & current:
1187
self.basis[j] ^= self.basis[row]
1188
for j from row < j < self.nrows:
1189
if self.basis[j] & current:
1190
self.basis[j] ^= self.basis[row]
1191
row += 1
1192
current = current << 1
1193
perm = [0]*self.ncols
1194
j = 0
1195
k = self.nrows
1196
for i from 0 <= i < self.ncols:
1197
if ((<codeword>1) << i) & pivots:
1198
perm[i] = j
1199
j += 1
1200
else:
1201
perm[i] = k
1202
k += 1
1203
self._apply_permutation_to_basis(perm)
1204
self._update_words_from_basis()
1205
1206
cdef class OrbitPartition:
1207
"""
1208
Structure which keeps track of which vertices are equivalent
1209
under the part of the automorphism group that has already been
1210
seen, during search. Essentially a disjoint-set data structure*,
1211
which also keeps track of the minimum element and size of each
1212
cell of the partition, and the size of the partition.
1213
1214
* http://en.wikipedia.org/wiki/Disjoint-set_data_structure
1215
1216
"""
1217
def __cinit__(self, int nrows, int ncols):
1218
cdef int col
1219
cdef int nwords, word
1220
nwords = (1 << nrows)
1221
self.nwords = nwords
1222
self.ncols = ncols
1223
self.wd_parent = <int *> sage_malloc( nwords * sizeof(int) )
1224
self.wd_rank = <int *> sage_malloc( nwords * sizeof(int) )
1225
self.wd_min_cell_rep = <int *> sage_malloc( nwords * sizeof(int) )
1226
self.wd_size = <int *> sage_malloc( nwords * sizeof(int) )
1227
self.col_parent = <int *> sage_malloc( ncols * sizeof(int) )
1228
self.col_rank = <int *> sage_malloc( ncols * sizeof(int) )
1229
self.col_min_cell_rep = <int *> sage_malloc( ncols * sizeof(int) )
1230
self.col_size = <int *> sage_malloc( ncols * sizeof(int) )
1231
if self.wd_parent is NULL or self.wd_rank is NULL or self.wd_min_cell_rep is NULL \
1232
or self.wd_size is NULL or self.col_parent is NULL or self.col_rank is NULL \
1233
or self.col_min_cell_rep is NULL or self.col_size is NULL:
1234
if self.wd_parent is not NULL: sage_free(self.wd_parent)
1235
if self.wd_rank is not NULL: sage_free(self.wd_rank)
1236
if self.wd_min_cell_rep is not NULL: sage_free(self.wd_min_cell_rep)
1237
if self.wd_size is not NULL: sage_free(self.wd_size)
1238
if self.col_parent is not NULL: sage_free(self.col_parent)
1239
if self.col_rank is not NULL: sage_free(self.col_rank)
1240
if self.col_min_cell_rep is not NULL: sage_free(self.col_min_cell_rep)
1241
if self.col_size is not NULL: sage_free(self.col_size)
1242
raise MemoryError("Memory.")
1243
for word from 0 <= word < nwords:
1244
self.wd_parent[word] = word
1245
self.wd_rank[word] = 0
1246
self.wd_min_cell_rep[word] = word
1247
self.wd_size[word] = 1
1248
for col from 0 <= col < ncols:
1249
self.col_parent[col] = col
1250
self.col_rank[col] = 0
1251
self.col_min_cell_rep[col] = col
1252
self.col_size[col] = 1
1253
1254
def __dealloc__(self):
1255
sage_free(self.wd_parent)
1256
sage_free(self.wd_rank)
1257
sage_free(self.wd_min_cell_rep)
1258
sage_free(self.wd_size)
1259
sage_free(self.col_parent)
1260
sage_free(self.col_rank)
1261
sage_free(self.col_min_cell_rep)
1262
sage_free(self.col_size)
1263
1264
def __repr__(self):
1265
"""
1266
Return a string representation of the orbit partition.
1267
1268
EXAMPLE:
1269
sage: import sage.coding.binary_code
1270
sage: from sage.coding.binary_code import *
1271
sage: O = OrbitPartition(4, 8)
1272
sage: O
1273
OrbitPartition on 16 words and 8 columns. Data:
1274
Words:
1275
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
1276
Columns:
1277
0,1,2,3,4,5,6,7
1278
1279
"""
1280
cdef int i
1281
cdef int j
1282
s = 'OrbitPartition on %d words and %d columns. Data:\n'%(self.nwords, self.ncols)
1283
# s += 'Parents::\n'
1284
s += 'Words:\n'
1285
for i from 0 <= i < self.nwords:
1286
s += '%d,'%self.wd_parent[i]
1287
s = s[:-1] + '\nColumns:\n'
1288
for j from 0 <= j < self.ncols:
1289
s += '%d,'%self.col_parent[j]
1290
# s = s[:-1] + '\n'
1291
# s += 'Min Cell Reps::\n'
1292
# s += 'Words:\n'
1293
# for i from 0 <= i < self.nwords:
1294
# s += '%d,'%self.wd_min_cell_rep[i]
1295
# s = s[:-1] + '\nColumns:\n'
1296
# for j from 0 <= j < self.ncols:
1297
# s += '%d,'%self.col_min_cell_rep[j]
1298
return s[:-1]
1299
1300
def _wd_find(self, word):
1301
"""
1302
Returns the root of word.
1303
1304
EXAMPLE:
1305
sage: import sage.coding.binary_code
1306
sage: from sage.coding.binary_code import *
1307
sage: O = OrbitPartition(4, 8)
1308
sage: O
1309
OrbitPartition on 16 words and 8 columns. Data:
1310
Words:
1311
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
1312
Columns:
1313
0,1,2,3,4,5,6,7
1314
sage: O._wd_find(12)
1315
12
1316
1317
"""
1318
return self.wd_find(word)
1319
1320
cdef int wd_find(self, int word):
1321
# print 'wd_find', word
1322
if self.wd_parent[word] == word:
1323
return word
1324
else:
1325
self.wd_parent[word] = self.wd_find(self.wd_parent[word])
1326
return self.wd_parent[word]
1327
1328
def _wd_union(self, x, y):
1329
"""
1330
Join the cells containing x and y.
1331
1332
EXAMPLE:
1333
sage: import sage.coding.binary_code
1334
sage: from sage.coding.binary_code import *
1335
sage: O = OrbitPartition(4, 8)
1336
sage: O
1337
OrbitPartition on 16 words and 8 columns. Data:
1338
Words:
1339
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
1340
Columns:
1341
0,1,2,3,4,5,6,7
1342
sage: O._wd_union(1,10)
1343
sage: O
1344
OrbitPartition on 16 words and 8 columns. Data:
1345
Words:
1346
0,1,2,3,4,5,6,7,8,9,1,11,12,13,14,15
1347
Columns:
1348
0,1,2,3,4,5,6,7
1349
sage: O._wd_find(10)
1350
1
1351
1352
"""
1353
self.wd_union(x, y)
1354
1355
cdef void wd_union(self, int x, int y):
1356
# print 'wd_union', x, y
1357
cdef int x_root, y_root
1358
x_root = self.wd_find(x)
1359
y_root = self.wd_find(y)
1360
if self.wd_rank[x_root] > self.wd_rank[y_root]:
1361
self.wd_parent[y_root] = x_root
1362
self.wd_min_cell_rep[x_root] = min(self.wd_min_cell_rep[x_root],self.wd_min_cell_rep[y_root])
1363
self.wd_size[x_root] += self.wd_size[y_root]
1364
elif self.wd_rank[x_root] < self.wd_rank[y_root]:
1365
self.wd_parent[x_root] = y_root
1366
self.wd_min_cell_rep[y_root] = min(self.wd_min_cell_rep[x_root],self.wd_min_cell_rep[y_root])
1367
self.wd_size[y_root] += self.wd_size[x_root]
1368
elif x_root != y_root:
1369
self.wd_parent[y_root] = x_root
1370
self.wd_min_cell_rep[x_root] = min(self.wd_min_cell_rep[x_root],self.wd_min_cell_rep[y_root])
1371
self.wd_size[x_root] += self.wd_size[y_root]
1372
self.wd_rank[x_root] += 1
1373
1374
def _col_find(self, col):
1375
"""
1376
Returns the root of col.
1377
1378
EXAMPLE:
1379
sage: import sage.coding.binary_code
1380
sage: from sage.coding.binary_code import *
1381
sage: O = OrbitPartition(4, 8)
1382
sage: O
1383
OrbitPartition on 16 words and 8 columns. Data:
1384
Words:
1385
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
1386
Columns:
1387
0,1,2,3,4,5,6,7
1388
sage: O._col_find(6)
1389
6
1390
1391
"""
1392
return self.col_find(col)
1393
1394
cdef int col_find(self, int col):
1395
# print 'col_find', col
1396
if self.col_parent[col] == col:
1397
return col
1398
else:
1399
self.col_parent[col] = self.col_find(self.col_parent[col])
1400
return self.col_parent[col]
1401
1402
def _col_union(self, x, y):
1403
"""
1404
Join the cells containing x and y.
1405
1406
EXAMPLE:
1407
sage: import sage.coding.binary_code
1408
sage: from sage.coding.binary_code import *
1409
sage: O = OrbitPartition(4, 8)
1410
sage: O
1411
OrbitPartition on 16 words and 8 columns. Data:
1412
Words:
1413
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
1414
Columns:
1415
0,1,2,3,4,5,6,7
1416
sage: O._col_union(1,4)
1417
sage: O
1418
OrbitPartition on 16 words and 8 columns. Data:
1419
Words:
1420
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
1421
Columns:
1422
0,1,2,3,1,5,6,7
1423
sage: O._col_find(4)
1424
1
1425
1426
"""
1427
self.col_union(x, y)
1428
1429
cdef void col_union(self, int x, int y):
1430
# print 'col_union', x, y
1431
cdef int x_root, y_root
1432
x_root = self.col_find(x)
1433
y_root = self.col_find(y)
1434
if self.col_rank[x_root] > self.col_rank[y_root]:
1435
self.col_parent[y_root] = x_root
1436
self.col_min_cell_rep[x_root] = min(self.col_min_cell_rep[x_root],self.col_min_cell_rep[y_root])
1437
self.col_size[x_root] += self.col_size[y_root]
1438
elif self.col_rank[x_root] < self.col_rank[y_root]:
1439
self.col_parent[x_root] = y_root
1440
self.col_min_cell_rep[y_root] = min(self.col_min_cell_rep[x_root],self.col_min_cell_rep[y_root])
1441
self.col_size[y_root] += self.col_size[x_root]
1442
elif x_root != y_root:
1443
self.col_parent[y_root] = x_root
1444
self.col_min_cell_rep[x_root] = min(self.col_min_cell_rep[x_root],self.col_min_cell_rep[y_root])
1445
self.col_size[x_root] += self.col_size[y_root]
1446
self.col_rank[x_root] += 1
1447
1448
def _merge_perm(self, col_gamma, wd_gamma):
1449
"""
1450
Merges the cells of self under the given permutation. If gamma[a] = b,
1451
then after merge_perm, a and b will be in the same cell. Returns 0 if
1452
nothing was done, otherwise returns 1.
1453
1454
EXAMPLE:
1455
sage: import sage.coding.binary_code
1456
sage: from sage.coding.binary_code import *
1457
sage: O = OrbitPartition(4, 8)
1458
sage: O
1459
OrbitPartition on 16 words and 8 columns. Data:
1460
Words:
1461
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
1462
Columns:
1463
0,1,2,3,4,5,6,7
1464
sage: O._merge_perm([1,0,3,2,4,5,6,7], [0,1,2,3,4,5,6,7,9,8,11,10,13,12,15,14])
1465
1
1466
sage: O
1467
OrbitPartition on 16 words and 8 columns. Data:
1468
Words:
1469
0,1,2,3,4,5,6,7,8,8,10,10,12,12,14,14
1470
Columns:
1471
0,0,2,2,4,5,6,7
1472
1473
"""
1474
cdef int i
1475
cdef int *_col_gamma
1476
cdef int *_wd_gamma
1477
_wd_gamma = <int *> sage_malloc(self.nwords * sizeof(int))
1478
_col_gamma = <int *> sage_malloc(self.ncols * sizeof(int))
1479
if _col_gamma is NULL or _wd_gamma is NULL:
1480
if _wd_gamma is not NULL: sage_free(_wd_gamma)
1481
if _col_gamma is not NULL: sage_free(_col_gamma)
1482
raise MemoryError("Memory.")
1483
for i from 0 <= i < self.nwords:
1484
_wd_gamma[i] = wd_gamma[i]
1485
for i from 0 <= i < self.ncols:
1486
_col_gamma[i] = col_gamma[i]
1487
result = self.merge_perm(_col_gamma, _wd_gamma)
1488
sage_free(_col_gamma)
1489
sage_free(_wd_gamma)
1490
return result
1491
1492
cdef int merge_perm(self, int *col_gamma, int *wd_gamma):
1493
cdef int i, gamma_i_root
1494
cdef int j, gamma_j_root, return_value = 0
1495
cdef int *self_wd_parent = self.wd_parent
1496
cdef int *self_col_parent = self.col_parent
1497
# print 'merge_perm'
1498
# print 'col_gamma:', [col_gamma[i] for i from 0 <= i < self.ncols]
1499
# print 'wd_gamma:', [wd_gamma[i] for i from 0 <= i < self.nwords]
1500
for i from 0 <= i < self.nwords:
1501
gamma_i_root = self.wd_find(wd_gamma[i])
1502
if gamma_i_root != i:
1503
return_value = 1
1504
self.wd_union(i, gamma_i_root)
1505
for j from 0 <= j < self.ncols:
1506
gamma_j_root = self.col_find(col_gamma[j])
1507
if gamma_j_root != j:
1508
return_value = 1
1509
self.col_union(j, gamma_j_root)
1510
return return_value
1511
1512
cdef class PartitionStack:
1513
"""
1514
Partition stack structure for traversing the search tree during automorphism
1515
group computation.
1516
1517
"""
1518
def __cinit__(self, arg1, arg2=None):
1519
cdef int k, nwords, ncols, sizeof_int
1520
cdef PartitionStack other = None
1521
cdef int *wd_ents, *wd_lvls, *col_ents, *col_lvls
1522
cdef int *col_degs, *col_counts, *col_output, *wd_degs, *wd_counts, *wd_output
1523
sizeof_int = sizeof(int)
1524
1525
try:
1526
self.nrows = <int> arg1
1527
self.nwords = 1 << self.nrows
1528
self.ncols = <int> arg2
1529
except Exception:
1530
other = arg1
1531
self.nrows = other.nrows
1532
self.nwords = other.nwords
1533
self.ncols = other.ncols
1534
1535
self.radix = sizeof_int << 3
1536
self.flag = (1 << (self.radix-1))
1537
1538
# data
1539
self.wd_ents = <int *> sage_malloc( self.nwords * sizeof_int )
1540
self.wd_lvls = <int *> sage_malloc( self.nwords * sizeof_int )
1541
self.col_ents = <int *> sage_malloc( self.ncols * sizeof_int )
1542
self.col_lvls = <int *> sage_malloc( self.ncols * sizeof_int )
1543
1544
# scratch space
1545
self.col_degs = <int *> sage_malloc( self.ncols * sizeof_int )
1546
self.col_counts = <int *> sage_malloc( self.nwords * sizeof_int )
1547
self.col_output = <int *> sage_malloc( self.ncols * sizeof_int )
1548
self.wd_degs = <int *> sage_malloc( self.nwords * sizeof_int )
1549
self.wd_counts = <int *> sage_malloc( (self.ncols+1) * sizeof_int )
1550
self.wd_output = <int *> sage_malloc( self.nwords * sizeof_int )
1551
1552
if self.wd_ents is NULL or self.wd_lvls is NULL or self.col_ents is NULL \
1553
or self.col_lvls is NULL or self.col_degs is NULL or self.col_counts is NULL \
1554
or self.col_output is NULL or self.wd_degs is NULL or self.wd_counts is NULL \
1555
or self.wd_output is NULL:
1556
if self.wd_ents is not NULL: sage_free(self.wd_ents)
1557
if self.wd_lvls is not NULL: sage_free(self.wd_lvls)
1558
if self.col_ents is not NULL: sage_free(self.col_ents)
1559
if self.col_lvls is not NULL: sage_free(self.col_lvls)
1560
if self.col_degs is not NULL: sage_free(self.col_degs)
1561
if self.col_counts is not NULL: sage_free(self.col_counts)
1562
if self.col_output is not NULL: sage_free(self.col_output)
1563
if self.wd_degs is not NULL: sage_free(self.wd_degs)
1564
if self.wd_counts is not NULL: sage_free(self.wd_counts)
1565
if self.wd_output is not NULL: sage_free(self.wd_output)
1566
raise MemoryError("Memory.")
1567
1568
nwords = self.nwords
1569
ncols = self.ncols
1570
1571
if other:
1572
memcpy(self.wd_ents, other.wd_ents, self.nwords * sizeof_int)
1573
memcpy(self.wd_lvls, other.wd_lvls, self.nwords * sizeof_int)
1574
memcpy(self.col_ents, other.col_ents, self.ncols * sizeof_int)
1575
memcpy(self.col_lvls, other.col_lvls, self.ncols * sizeof_int)
1576
else:
1577
wd_ents = self.wd_ents
1578
wd_lvls = self.wd_lvls
1579
col_ents = self.col_ents
1580
col_lvls = self.col_lvls
1581
for k from 0 <= k < nwords-1:
1582
wd_ents[k] = k
1583
wd_lvls[k] = 2*ncols
1584
for k from 0 <= k < ncols-1:
1585
col_ents[k] = k
1586
col_lvls[k] = 2*ncols
1587
wd_ents[nwords-1] = nwords-1
1588
wd_lvls[nwords-1] = -1
1589
col_ents[ncols-1] = ncols-1
1590
col_lvls[ncols-1] = -1
1591
1592
col_degs = self.col_degs
1593
col_counts = self.col_counts
1594
col_output = self.col_output
1595
wd_degs = self.wd_degs
1596
wd_counts = self.wd_counts
1597
wd_output = self.wd_output
1598
for k from 0 <= k < ncols:
1599
col_degs[k]=0
1600
col_output[k]=0
1601
wd_counts[k]=0
1602
wd_counts[ncols]=0
1603
for k from 0 <= k < nwords:
1604
col_counts[k]=0
1605
wd_degs[k]=0
1606
wd_output[k]=0
1607
1608
def __dealloc__(self):
1609
if self.basis_locations: sage_free(self.basis_locations)
1610
sage_free(self.wd_ents)
1611
sage_free(self.wd_lvls)
1612
sage_free(self.col_ents)
1613
sage_free(self.col_lvls)
1614
sage_free(self.col_degs)
1615
sage_free(self.col_counts)
1616
sage_free(self.col_output)
1617
sage_free(self.wd_degs)
1618
sage_free(self.wd_counts)
1619
sage_free(self.wd_output)
1620
1621
def print_data(self):
1622
"""
1623
Prints all data for self.
1624
1625
EXAMPLE:
1626
sage: import sage.coding.binary_code
1627
sage: from sage.coding.binary_code import *
1628
sage: P = PartitionStack(2, 6)
1629
sage: print P.print_data()
1630
nwords:4
1631
nrows:2
1632
ncols:6
1633
radix:32
1634
wd_ents:
1635
0
1636
1
1637
2
1638
3
1639
wd_lvls:
1640
12
1641
12
1642
12
1643
-1
1644
col_ents:
1645
0
1646
1
1647
2
1648
3
1649
4
1650
5
1651
col_lvls:
1652
12
1653
12
1654
12
1655
12
1656
12
1657
-1
1658
col_degs:
1659
0
1660
0
1661
0
1662
0
1663
0
1664
0
1665
col_counts:
1666
0
1667
0
1668
0
1669
0
1670
col_output:
1671
0
1672
0
1673
0
1674
0
1675
0
1676
0
1677
wd_degs:
1678
0
1679
0
1680
0
1681
0
1682
wd_counts:
1683
0
1684
0
1685
0
1686
0
1687
0
1688
0
1689
0
1690
wd_output:
1691
0
1692
0
1693
0
1694
0
1695
1696
"""
1697
cdef int i, j
1698
s = ''
1699
s += "nwords:" + str(self.nwords) + '\n'
1700
s += "nrows:" + str(self.nrows) + '\n'
1701
s += "ncols:" + str(self.ncols) + '\n'
1702
s += "radix:" + str(self.radix) + '\n'
1703
s += "wd_ents:" + '\n'
1704
for i from 0 <= i < self.nwords:
1705
s += str(self.wd_ents[i]) + '\n'
1706
s += "wd_lvls:" + '\n'
1707
for i from 0 <= i < self.nwords:
1708
s += str(self.wd_lvls[i]) + '\n'
1709
s += "col_ents:" + '\n'
1710
for i from 0 <= i < self.ncols:
1711
s += str(self.col_ents[i]) + '\n'
1712
s += "col_lvls:" + '\n'
1713
for i from 0 <= i < self.ncols:
1714
s += str(self.col_lvls[i]) + '\n'
1715
s += "col_degs:" + '\n'
1716
for i from 0 <= i < self.ncols:
1717
s += str(self.col_degs[i]) + '\n'
1718
s += "col_counts:" + '\n'
1719
for i from 0 <= i < self.nwords:
1720
s += str(self.col_counts[i]) + '\n'
1721
s += "col_output:" + '\n'
1722
for i from 0 <= i < self.ncols:
1723
s += str(self.col_output[i]) + '\n'
1724
s += "wd_degs:" + '\n'
1725
for i from 0 <= i < self.nwords:
1726
s += str(self.wd_degs[i]) + '\n'
1727
s += "wd_counts:" + '\n'
1728
for i from 0 <= i < self.ncols + 1:
1729
s += str(self.wd_counts[i]) + '\n'
1730
s += "wd_output:" + '\n'
1731
for i from 0 <= i < self.nwords:
1732
s += str(self.wd_output[i]) + '\n'
1733
if self.basis_locations:
1734
s += "basis_locations:" + '\n'
1735
j = 1
1736
while (1 << j) < self.nwords:
1737
j += 1
1738
for i from 0 <= i < j:
1739
s += str(self.basis_locations[i]) + '\n'
1740
return s
1741
1742
def __repr__(self):
1743
"""
1744
Return a string representation of self.
1745
1746
EXAMPLE:
1747
sage: import sage.coding.binary_code
1748
sage: from sage.coding.binary_code import *
1749
sage: P = PartitionStack(2, 6)
1750
sage: P
1751
({0,1,2,3}) ({0,1,2,3,4,5})
1752
1753
"""
1754
cdef int i, j, k
1755
s = ''
1756
last = ''
1757
current = ''
1758
for k from 0 <= k < 2*self.ncols:
1759
current = self._repr_at_k(k)
1760
if current == last: break
1761
s += current
1762
last = current
1763
return s
1764
1765
def _repr_at_k(self, k):
1766
"""
1767
Gives a string representing the partition at level k:
1768
1769
EXAMPLE:
1770
sage: from sage.coding.binary_code import *
1771
sage: P = PartitionStack(2, 6); P
1772
({0,1,2,3}) ({0,1,2,3,4,5})
1773
sage: P._repr_at_k(0)
1774
'({0,1,2,3}) ({0,1,2,3,4,5})\n'
1775
1776
"""
1777
s = '({'
1778
for j from 0 <= j < self.nwords:
1779
s += str(self.wd_ents[j])
1780
if self.wd_lvls[j] <= k:
1781
s += '},{'
1782
else:
1783
s += ','
1784
s = s[:-2] + ') '
1785
s += '({'
1786
for j from 0 <= j < self.ncols:
1787
s += str(self.col_ents[j])
1788
if self.col_lvls[j] <= k:
1789
s += '},{'
1790
else:
1791
s += ','
1792
s = s[:-2] + ')\n'
1793
return s
1794
1795
def _is_discrete(self, k):
1796
"""
1797
Returns whether the partition at level k is discrete.
1798
1799
EXAMPLE:
1800
sage: import sage.coding.binary_code
1801
sage: from sage.coding.binary_code import *
1802
sage: P = PartitionStack(2, 6)
1803
sage: [P._split_vertex(i,i+1) for i in range(5)]
1804
[0, 1, 2, 3, 4]
1805
sage: P._sort_wds(0, [0,2,3,1], 5)
1806
0
1807
sage: P
1808
({0,3,1,2}) ({0,1,2,3,4,5})
1809
({0,3,1,2}) ({0},{1,2,3,4,5})
1810
({0,3,1,2}) ({0},{1},{2,3,4,5})
1811
({0,3,1,2}) ({0},{1},{2},{3,4,5})
1812
({0,3,1,2}) ({0},{1},{2},{3},{4,5})
1813
({0},{3},{1},{2}) ({0},{1},{2},{3},{4},{5})
1814
sage: P._is_discrete(4)
1815
0
1816
sage: P._is_discrete(5)
1817
1
1818
1819
"""
1820
return self.is_discrete(k)
1821
1822
cdef int is_discrete(self, int k):
1823
cdef int i, self_ncols = self.ncols, self_nwords = self.nwords
1824
cdef int *self_col_lvls = self.col_lvls
1825
cdef int *self_wd_lvls = self.wd_lvls
1826
for i from 0 <= i < self_ncols:
1827
if self_col_lvls[i] > k:
1828
return 0
1829
for i from 0 <= i < self_nwords:
1830
if self_wd_lvls[i] > k:
1831
return 0
1832
return 1
1833
1834
def _num_cells(self, k):
1835
"""
1836
Returns the number of cells in the partition at level k.
1837
1838
EXAMPLE:
1839
sage: import sage.coding.binary_code
1840
sage: from sage.coding.binary_code import *
1841
sage: P = PartitionStack(2, 6)
1842
sage: [P._split_vertex(i,i+1) for i in range(5)]
1843
[0, 1, 2, 3, 4]
1844
sage: P
1845
({0,1,2,3}) ({0,1,2,3,4,5})
1846
({0,1,2,3}) ({0},{1,2,3,4,5})
1847
({0,1,2,3}) ({0},{1},{2,3,4,5})
1848
({0,1,2,3}) ({0},{1},{2},{3,4,5})
1849
({0,1,2,3}) ({0},{1},{2},{3},{4,5})
1850
({0,1,2,3}) ({0},{1},{2},{3},{4},{5})
1851
sage: P._num_cells(3)
1852
5
1853
1854
"""
1855
return self.num_cells(k)
1856
1857
cdef int num_cells(self, int k):
1858
cdef int i, j = 0
1859
cdef int *self_wd_lvls = self.wd_lvls
1860
cdef int *self_col_lvls = self.col_lvls
1861
for i from 0 <= i < self.nwords:
1862
if self_wd_lvls[i] <= k:
1863
j += 1
1864
for i from 0 <= i < self.ncols:
1865
if self_col_lvls[i] <= k:
1866
j += 1
1867
return j
1868
1869
def _sat_225(self, k):
1870
"""
1871
Returns whether the partition at level k satisfies the hypotheses of
1872
Lemma 2.25 in Brendan McKay's Practical Graph Isomorphism paper (see
1873
sage/graphs/graph_isom.pyx.
1874
1875
EXAMPLE:
1876
sage: import sage.coding.binary_code
1877
sage: from sage.coding.binary_code import *
1878
sage: P = PartitionStack(2, 6)
1879
sage: [P._split_vertex(i,i+1) for i in range(5)]
1880
[0, 1, 2, 3, 4]
1881
sage: P._sat_225(3)
1882
0
1883
sage: P._sat_225(4)
1884
1
1885
sage: P
1886
({0,1,2,3}) ({0,1,2,3,4,5})
1887
({0,1,2,3}) ({0},{1,2,3,4,5})
1888
({0,1,2,3}) ({0},{1},{2,3,4,5})
1889
({0,1,2,3}) ({0},{1},{2},{3,4,5})
1890
({0,1,2,3}) ({0},{1},{2},{3},{4,5})
1891
({0,1,2,3}) ({0},{1},{2},{3},{4},{5})
1892
1893
"""
1894
return self.sat_225(k)
1895
1896
cdef int sat_225(self, int k):
1897
cdef int i, n = self.nwords + self.ncols, in_cell = 0
1898
cdef int nontrivial_cells = 0, total_cells = self.num_cells(k)
1899
cdef int *self_wd_lvls = self.wd_lvls
1900
cdef int *self_col_lvls = self.col_lvls
1901
if n <= total_cells + 4:
1902
return 1
1903
for i from 0 <= i < self.nwords:
1904
if self_wd_lvls[i] <= k:
1905
if in_cell:
1906
nontrivial_cells += 1
1907
in_cell = 0
1908
else:
1909
in_cell = 1
1910
in_cell = 0
1911
for i from 0 <= i < self.ncols:
1912
if self_col_lvls[i] <= k:
1913
if in_cell:
1914
nontrivial_cells += 1
1915
in_cell = 0
1916
else:
1917
in_cell = 1
1918
if n == total_cells + nontrivial_cells:
1919
return 1
1920
if n == total_cells + nontrivial_cells + 1:
1921
return 1
1922
return 0
1923
1924
# def _new_min_cell_reps(self, k): #TODO
1925
# """
1926
# Returns an integer whose bits represent which columns are minimal cell
1927
# representatives.
1928
#
1929
# EXAMPLE:
1930
# sage: import sage.coding.binary_code
1931
# sage: from sage.coding.binary_code import *
1932
# sage: P = PartitionStack(2, 6)
1933
# sage: [P._split_column(i,i+1) for i in range(5)]
1934
# [0, 1, 2, 3, 4]
1935
# sage: a = P._min_cell_reps(2)
1936
# sage: Integer(a).binary()
1937
# '111'
1938
# sage: P
1939
# ({0,1,2,3})
1940
# ({0,1,2,3})
1941
# ({0,1,2,3})
1942
# ({0,1,2,3})
1943
# ({0,1,2,3,4,5})
1944
# ({0},{1,2,3,4,5})
1945
# ({0},{1},{2,3,4,5})
1946
# ({0},{1},{2},{3,4,5})
1947
# ({0},{1},{2},{3},{4,5})
1948
# ({0},{1},{2},{3},{4},{5})
1949
#
1950
# """
1951
# return self.min_cell_reps(k)
1952
#
1953
# cdef int min_cell_reps(self, int k):
1954
# cdef int i
1955
# cdef int reps = 1
1956
# cdef int *self_col_lvls = self.col_lvls
1957
# for i from 0 < i < self.ncols:
1958
# if self_col_lvls[i-1] <= k:
1959
# reps += (1 << i)
1960
# return reps
1961
#
1962
cdef void new_min_cell_reps(self, int k, unsigned int *Omega, int start):
1963
cdef int i, j
1964
cdef int *self_col_lvls = self.col_lvls, *self_wd_lvls = self.wd_lvls
1965
cdef int *self_col_ents = self.col_ents, *self_wd_ents = self.wd_ents
1966
cdef int reps = (1 << self_col_ents[0]), length, word
1967
cdef int radix = self.radix, nwords = self.nwords, ncols = self.ncols
1968
length = 1 + nwords/radix
1969
if nwords%radix:
1970
length += 1
1971
for i from 0 <= i < length:
1972
Omega[start+i] = 0
1973
for i from 0 < i < ncols:
1974
Omega[start] += ((self_col_lvls[i-1] <= k) << self_col_ents[i])
1975
Omega[start+1] = (1 << self_wd_ents[0])
1976
for i from 0 < i < nwords:
1977
if self_wd_lvls[i-1] <= k:
1978
word = self_wd_lvls[i-1]
1979
Omega[start+1+word/radix] += (1 << word%radix)
1980
1981
# def _fixed_cols(self, mcrs, k): #TODO
1982
# """
1983
# Returns an integer whose bits represent which columns are fixed. For
1984
# efficiency, mcrs is the output of min_cell_reps.
1985
#
1986
# EXAMPLE:
1987
# sage: import sage.coding.binary_code
1988
# sage: from sage.coding.binary_code import *
1989
# sage: P = PartitionStack(2, 6)
1990
# sage: [P._split_column(i,i+1) for i in range(5)]
1991
# [0, 1, 2, 3, 4]
1992
# sage: a = P._fixed_cols(7, 2)
1993
# sage: Integer(a).binary()
1994
# '11'
1995
# sage: P
1996
# ({0,1,2,3})
1997
# ({0,1,2,3})
1998
# ({0,1,2,3})
1999
# ({0,1,2,3})
2000
# ({0,1,2,3,4,5})
2001
# ({0},{1,2,3,4,5})
2002
# ({0},{1},{2,3,4,5})
2003
# ({0},{1},{2},{3,4,5})
2004
# ({0},{1},{2},{3},{4,5})
2005
# ({0},{1},{2},{3},{4},{5})
2006
#
2007
# """
2008
# return self.fixed_cols(mcrs, k)
2009
#
2010
# cdef int fixed_cols(self, int mcrs, int k):
2011
# cdef int i
2012
# cdef int fixed = 0
2013
# cdef int *self_col_lvls = self.col_lvls
2014
# for i from 0 <= i < self.ncols:
2015
# if self_col_lvls[i] <= k:
2016
# fixed += (1 << i)
2017
# return fixed & mcrs
2018
#
2019
cdef void fixed_vertices(self, int k, unsigned int *Phi, unsigned int *Omega, int start):
2020
cdef int i, j, length, ell, fixed = 0
2021
cdef int radix = self.radix, nwords = self.nwords, ncols = self.ncols
2022
cdef int *self_col_lvls = self.col_lvls, *self_wd_lvls = self.wd_lvls
2023
cdef int *self_col_ents = self.col_ents, *self_wd_ents = self.wd_ents
2024
for i from 0 <= i < ncols:
2025
fixed += ((self_col_lvls[i] <= k) << self_col_ents[i])
2026
Phi[start] = fixed & Omega[start]
2027
# zero out the rest of Phi
2028
length = 1 + nwords/self.radix
2029
if nwords%self.radix:
2030
length += 1
2031
for i from 0 < i < length:
2032
Phi[start+i] = 0
2033
for i from 0 <= i < nwords:
2034
ell = self_wd_ents[i]
2035
Phi[start+1+ell/radix] = ((self_wd_lvls[i] <= k) << ell%radix)
2036
for i from 0 < i < length:
2037
Phi[i] &= Omega[i]
2038
2039
# def _first_smallest_nontrivial(self, k): #TODO
2040
# """
2041
# Returns an integer representing the first, smallest nontrivial cell of columns.
2042
#
2043
# EXAMPLE:
2044
# sage: import sage.coding.binary_code
2045
# sage: from sage.coding.binary_code import *
2046
# sage: P = PartitionStack(2, 6)
2047
# sage: [P._split_column(i,i+1) for i in range(5)]
2048
# [0, 1, 2, 3, 4]
2049
# sage: a = P._first_smallest_nontrivial(2)
2050
# sage: Integer(a).binary().zfill(32)
2051
# '00000000000000000000000000111100'
2052
# sage: P
2053
# ({0,1,2,3})
2054
# ({0,1,2,3})
2055
# ({0,1,2,3})
2056
# ({0,1,2,3})
2057
# ({0,1,2,3,4,5})
2058
# ({0},{1,2,3,4,5})
2059
# ({0},{1},{2,3,4,5})
2060
# ({0},{1},{2},{3,4,5})
2061
# ({0},{1},{2},{3},{4,5})
2062
# ({0},{1},{2},{3},{4},{5})
2063
#
2064
# """
2065
# return self.first_smallest_nontrivial(k)
2066
#
2067
# cdef int first_smallest_nontrivial(self, int k):
2068
# cdef int cell
2069
# cdef int i = 0, j = 0, location = 0, ncols = self.ncols
2070
# cdef int *self_col_lvls = self.col_lvls
2071
# while True:
2072
# if self_col_lvls[i] <= k:
2073
# if i != j and ncols > i - j + 1:
2074
# ncols = i - j + 1
2075
# location = j
2076
# j = i + 1
2077
# if self_col_lvls[i] == -1: break
2078
# i += 1
2079
# # location now points to the beginning of the first, smallest,
2080
# # nontrivial cell
2081
# j = location
2082
# self.v = self.col_ents[j]
2083
# while True:
2084
# if self_col_lvls[j] <= k: break
2085
# j += 1
2086
# # j now points to the last element of the cell
2087
## print "fsnt:", location, j-location+1
2088
# i = self.radix - j - 1 # the cell is represented in binary, reading from the right:
2089
# cell = (~0 << location) ^ (~0 << j+1) # <------- self.radix ----->
2090
# return cell # [0]*(radix-j-1) + [1]*(j-location+1) + [0]*location
2091
#
2092
cdef int new_first_smallest_nontrivial(self, int k, unsigned int *W, int start):
2093
cdef int ell
2094
cdef int i = 0, j = 0, location = 0, min = self.ncols, nwords = self.nwords
2095
cdef int min_is_col = 1, radix = self.radix
2096
cdef int *self_col_lvls = self.col_lvls, *self_wd_lvls = self.wd_lvls
2097
cdef int *self_col_ents = self.col_ents, *self_wd_ents = self.wd_ents
2098
while 1:
2099
if self_col_lvls[i] <= k:
2100
if i != j and min > i - j + 1:
2101
min = i - j + 1
2102
location = j
2103
j = i + 1
2104
if self_col_lvls[i] == -1: break
2105
i += 1
2106
# i = 0; j = 0
2107
# while 1:
2108
# if self_wd_lvls[i] <= k:
2109
# if i != j and min > i - j + 1:
2110
# min = i - j + 1
2111
# min_is_col = 0
2112
# location = j
2113
# j = i + 1
2114
# if self_wd_lvls[i] == -1: break
2115
# i += 1
2116
# location now points to the beginning of the first, smallest,
2117
# nontrivial cell
2118
j = location
2119
#zero out this level of W:
2120
ell = 1 + nwords/radix
2121
if nwords%radix:
2122
ell += 1
2123
for i from 0 <= i < ell:
2124
W[start+i] = 0
2125
if min_is_col:
2126
while 1:
2127
if self_col_lvls[j] <= k: break
2128
j += 1
2129
# j now points to the last element of the cell
2130
i = location
2131
while i <= j:
2132
W[start] ^= (1 << self_col_ents[i])
2133
i += 1
2134
return self_col_ents[location]
2135
else:
2136
while 1:
2137
if self_wd_lvls[j] <= k: break
2138
j += 1
2139
# j now points to the last element of the cell
2140
i = location
2141
while i <= j:
2142
ell = self_wd_ents[i]
2143
W[start+1+ell/radix] ^= (1 << ell%radix)
2144
i += 1
2145
return self_wd_ents[location] ^ self.flag
2146
2147
def _dangerous_dont_use_set_ents_lvls(self, col_ents, col_lvls, wd_ents, wd_lvls):
2148
"""
2149
For debugging only.
2150
2151
EXAMPLE:
2152
sage: import sage.coding.binary_code
2153
sage: from sage.coding.binary_code import *
2154
sage: P = PartitionStack(2, 6)
2155
sage: P
2156
({0,1,2,3}) ({0,1,2,3,4,5})
2157
sage: P._dangerous_dont_use_set_ents_lvls([99]*6, [0,3,2,3,5,-1], [4,3,5,6], [3,2,1,-1])
2158
sage: P
2159
({4,3,5,6}) ({99},{99,99,99,99,99})
2160
({4,3,5},{6}) ({99},{99,99,99,99,99})
2161
({4,3},{5},{6}) ({99},{99,99},{99,99,99})
2162
({4},{3},{5},{6}) ({99},{99},{99},{99},{99,99})
2163
2164
"""
2165
cdef int i
2166
for i from 0 <= i < len(col_ents):
2167
self.col_ents[i] = col_ents[i]
2168
for i from 0 <= i < len(col_lvls):
2169
self.col_lvls[i] = col_lvls[i]
2170
for i from 0 <= i < len(wd_ents):
2171
self.wd_ents[i] = wd_ents[i]
2172
for i from 0 <= i < len(wd_lvls):
2173
self.wd_lvls[i] = wd_lvls[i]
2174
2175
def _col_percolate(self, start, end):
2176
"""
2177
Do one round of bubble sort on ents.
2178
2179
EXAMPLE:
2180
sage: import sage.coding.binary_code
2181
sage: from sage.coding.binary_code import *
2182
sage: P = PartitionStack(2, 6)
2183
sage: P._dangerous_dont_use_set_ents_lvls(range(5,-1,-1), [1,2,2,3,3,-1], range(3,-1,-1), [1,1,2,-1])
2184
sage: P
2185
({3,2,1,0}) ({5,4,3,2,1,0})
2186
({3},{2},{1,0}) ({5},{4,3,2,1,0})
2187
({3},{2},{1},{0}) ({5},{4},{3},{2,1,0})
2188
({3},{2},{1},{0}) ({5},{4},{3},{2},{1},{0})
2189
sage: P._wd_percolate(0,3)
2190
sage: P._col_percolate(0,5)
2191
sage: P
2192
({0,3,2,1}) ({0,5,4,3,2,1})
2193
({0},{3},{2,1}) ({0},{5,4,3,2,1})
2194
({0},{3},{2},{1}) ({0},{5},{4},{3,2,1})
2195
({0},{3},{2},{1}) ({0},{5},{4},{3},{2},{1})
2196
2197
"""
2198
self.col_percolate(start, end)
2199
2200
cdef void col_percolate(self, int start, int end):
2201
cdef int i, temp
2202
cdef int *self_col_ents = self.col_ents
2203
for i from end >= i > start:
2204
if self_col_ents[i] < self_col_ents[i-1]:
2205
temp = self.col_ents[i]
2206
self_col_ents[i] = self_col_ents[i-1]
2207
self_col_ents[i-1] = temp
2208
2209
def _wd_percolate(self, start, end):
2210
"""
2211
Do one round of bubble sort on ents.
2212
2213
EXAMPLE:
2214
sage: import sage.coding.binary_code
2215
sage: from sage.coding.binary_code import *
2216
sage: P = PartitionStack(2, 6)
2217
sage: P._dangerous_dont_use_set_ents_lvls(range(5,-1,-1), [1,2,2,3,3,-1], range(3,-1,-1), [1,1,2,-1])
2218
sage: P
2219
({3,2,1,0}) ({5,4,3,2,1,0})
2220
({3},{2},{1,0}) ({5},{4,3,2,1,0})
2221
({3},{2},{1},{0}) ({5},{4},{3},{2,1,0})
2222
({3},{2},{1},{0}) ({5},{4},{3},{2},{1},{0})
2223
sage: P._wd_percolate(0,3)
2224
sage: P._col_percolate(0,5)
2225
sage: P
2226
({0,3,2,1}) ({0,5,4,3,2,1})
2227
({0},{3},{2,1}) ({0},{5,4,3,2,1})
2228
({0},{3},{2},{1}) ({0},{5},{4},{3,2,1})
2229
({0},{3},{2},{1}) ({0},{5},{4},{3},{2},{1})
2230
2231
"""
2232
self.wd_percolate(start, end)
2233
2234
cdef void wd_percolate(self, int start, int end):
2235
cdef int i, temp
2236
cdef int *self_wd_ents = self.wd_ents
2237
for i from end >= i > start:
2238
if self_wd_ents[i] < self_wd_ents[i-1]:
2239
temp = self.wd_ents[i]
2240
self_wd_ents[i] = self_wd_ents[i-1]
2241
self_wd_ents[i-1] = temp
2242
2243
# def _split_column(self, int v, int k): #TODO
2244
# """
2245
# Split column v out, placing it before the rest of the cell it was in.
2246
# Returns the location of the split column.
2247
#
2248
# EXAMPLE:
2249
# sage: import sage.coding.binary_code
2250
# sage: from sage.coding.binary_code import *
2251
# sage: P = PartitionStack(2, 6)
2252
# sage: [P._split_column(i,i+1) for i in range(5)]
2253
# [0, 1, 2, 3, 4]
2254
# sage: P
2255
# ({0,1,2,3})
2256
# ({0,1,2,3})
2257
# ({0,1,2,3})
2258
# ({0,1,2,3})
2259
# ({0,1,2,3,4,5})
2260
# ({0},{1,2,3,4,5})
2261
# ({0},{1},{2,3,4,5})
2262
# ({0},{1},{2},{3,4,5})
2263
# ({0},{1},{2},{3},{4,5})
2264
# ({0},{1},{2},{3},{4},{5})
2265
# sage: P = PartitionStack(2, 6)
2266
# sage: P._split_column(0,1)
2267
# 0
2268
# sage: P._split_column(2,2)
2269
# 1
2270
# sage: P
2271
# ({0,1,2,3})
2272
# ({0,1,2,3})
2273
# ({0,1,2,3})
2274
# ({0,1,2,3})
2275
# ({0,2,1,3,4,5})
2276
# ({0},{2,1,3,4,5})
2277
# ({0},{2},{1,3,4,5})
2278
# ({0},{2},{1,3,4,5})
2279
# ({0},{2},{1,3,4,5})
2280
# ({0},{2},{1,3,4,5})
2281
#
2282
# """
2283
# return self.split_column(v, k)
2284
#
2285
# cdef int split_column(self, int v, int k):
2286
# cdef int i = 0, j
2287
# cdef int *self_col_ents = self.col_ents
2288
# cdef int *self_col_lvls = self.col_lvls
2289
# while self_col_ents[i] != v: i += 1
2290
# j = i
2291
# while self_col_lvls[i] > k: i += 1
2292
# if j == 0 or self_col_lvls[j-1] <= k:
2293
# self.col_percolate(j+1, i)
2294
# else:
2295
# while j != 0 and self_col_lvls[j-1] > k:
2296
# self_col_ents[j] = self_col_ents[j-1]
2297
# j -= 1
2298
# self_col_ents[j] = v
2299
# self_col_lvls[j] = k
2300
# return j
2301
#
2302
2303
def _split_vertex(self, v, k):
2304
"""
2305
Split vertex v out, placing it before the rest of the cell it was in.
2306
Returns the location of the split vertex.
2307
2308
NOTE:
2309
There is a convention regarding whether a vertex is a word or a
2310
column. See the 'flag' attribute of the PartitionStack object:
2311
If vertex&flag is not zero, it is a word.
2312
2313
EXAMPLE:
2314
sage: import sage.coding.binary_code
2315
sage: from sage.coding.binary_code import *
2316
sage: P = PartitionStack(2, 6)
2317
sage: [P._split_vertex(i,i+1) for i in range(5)]
2318
[0, 1, 2, 3, 4]
2319
sage: P
2320
({0,1,2,3}) ({0,1,2,3,4,5})
2321
({0,1,2,3}) ({0},{1,2,3,4,5})
2322
({0,1,2,3}) ({0},{1},{2,3,4,5})
2323
({0,1,2,3}) ({0},{1},{2},{3,4,5})
2324
({0,1,2,3}) ({0},{1},{2},{3},{4,5})
2325
({0,1,2,3}) ({0},{1},{2},{3},{4},{5})
2326
2327
"""
2328
return self.split_vertex(v, k)
2329
2330
cdef int split_vertex(self, int v, int k):
2331
cdef int i = 0, j, flag = self.flag
2332
cdef int *ents
2333
cdef int *lvls
2334
if v & flag:
2335
ents = self.wd_ents
2336
lvls = self.wd_lvls
2337
v = v ^ flag
2338
while ents[i] != v: i += 1
2339
v = v ^ flag
2340
else:
2341
ents = self.col_ents
2342
lvls = self.col_lvls
2343
while ents[i] != v: i += 1
2344
j = i
2345
while lvls[i] > k: i += 1
2346
if j == 0 or lvls[j-1] <= k:
2347
if v & self.flag:
2348
self.wd_percolate(j+1, i)
2349
else:
2350
self.col_percolate(j+1, i)
2351
else:
2352
while j != 0 and lvls[j-1] > k:
2353
ents[j] = ents[j-1]
2354
j -= 1
2355
if v & flag:
2356
ents[j] = v ^ flag
2357
else:
2358
ents[j] = v
2359
lvls[j] = k
2360
if v & flag:
2361
return j ^ flag
2362
else:
2363
return j
2364
2365
def _col_degree(self, C, col, wd_ptr, k):
2366
"""
2367
Returns the number of words in the cell specified by wd_ptr that have a
2368
1 in the col-th column.
2369
2370
EXAMPLE:
2371
sage: import sage.coding.binary_code
2372
sage: from sage.coding.binary_code import *
2373
sage: P = PartitionStack(2, 6)
2374
sage: M = Matrix(GF(2), [[1,1,1,1,0,0],[0,0,1,1,1,1]])
2375
sage: B = BinaryCode(M)
2376
sage: B
2377
Binary [6,2] linear code, generator matrix
2378
[111100]
2379
[001111]
2380
sage: [P._split_vertex(i,i+1) for i in range(5)]
2381
[0, 1, 2, 3, 4]
2382
sage: P
2383
({0,1,2,3}) ({0,1,2,3,4,5})
2384
({0,1,2,3}) ({0},{1,2,3,4,5})
2385
({0,1,2,3}) ({0},{1},{2,3,4,5})
2386
({0,1,2,3}) ({0},{1},{2},{3,4,5})
2387
({0,1,2,3}) ({0},{1},{2},{3},{4,5})
2388
({0,1,2,3}) ({0},{1},{2},{3},{4},{5})
2389
sage: P._col_degree(B, 2, 0, 2)
2390
2
2391
2392
"""
2393
return self.col_degree(C, col, wd_ptr, k)
2394
2395
cdef int col_degree(self, BinaryCode CG, int col, int wd_ptr, int k):
2396
cdef int i = 0
2397
cdef int *self_wd_lvls = self.wd_lvls, *self_wd_ents = self.wd_ents
2398
while 1:
2399
if CG.is_one(self_wd_ents[wd_ptr], col): i += 1
2400
if self_wd_lvls[wd_ptr] > k: wd_ptr += 1
2401
else: break
2402
return i
2403
2404
def _wd_degree(self, C, wd, col_ptr, k):
2405
"""
2406
Returns the number of columns in the cell specified by col_ptr that are
2407
1 in wd.
2408
2409
EXAMPLE:
2410
sage: import sage.coding.binary_code
2411
sage: from sage.coding.binary_code import *
2412
sage: P = PartitionStack(2, 6)
2413
sage: M = Matrix(GF(2), [[1,1,1,1,0,0],[0,0,1,1,1,1]])
2414
sage: B = BinaryCode(M)
2415
sage: B
2416
Binary [6,2] linear code, generator matrix
2417
[111100]
2418
[001111]
2419
sage: [P._split_vertex(i,i+1) for i in range(5)]
2420
[0, 1, 2, 3, 4]
2421
sage: P
2422
({0,1,2,3}) ({0,1,2,3,4,5})
2423
({0,1,2,3}) ({0},{1,2,3,4,5})
2424
({0,1,2,3}) ({0},{1},{2,3,4,5})
2425
({0,1,2,3}) ({0},{1},{2},{3,4,5})
2426
({0,1,2,3}) ({0},{1},{2},{3},{4,5})
2427
({0,1,2,3}) ({0},{1},{2},{3},{4},{5})
2428
sage: P._wd_degree(B, 1, 1, 1)
2429
3
2430
2431
"""
2432
cdef int *ham_wts = hamming_weights()
2433
result = self.wd_degree(C, wd, col_ptr, k, ham_wts)
2434
sage_free(ham_wts)
2435
return result
2436
2437
cdef int wd_degree(self, BinaryCode CG, int wd, int col_ptr, int k, int *ham_wts):
2438
2439
cdef int *self_col_lvls = self.col_lvls, *self_col_ents = self.col_ents
2440
cdef int mask = (1 << self_col_ents[col_ptr])
2441
while self_col_lvls[col_ptr] > k:
2442
col_ptr += 1
2443
mask += (1 << self_col_ents[col_ptr])
2444
mask &= CG.words[wd]
2445
return ham_wts[mask & 65535] + ham_wts[(mask >> 16) & 65535]
2446
2447
def _sort_cols(self, start, degrees, k):
2448
"""
2449
Essentially a counting sort, but on only one cell of the partition.
2450
2451
INPUT:
2452
start -- location of the beginning of the cell
2453
k -- at what level of refinement the partition of interest lies
2454
degrees -- the counts to sort by
2455
2456
EXAMPLE:
2457
sage: import sage.coding.binary_code
2458
sage: from sage.coding.binary_code import *
2459
sage: P = PartitionStack(2, 6)
2460
sage: [P._split_vertex(i,i+1) for i in range(5)]
2461
[0, 1, 2, 3, 4]
2462
sage: P._sort_cols(1, [0,2,2,1,1], 1)
2463
2
2464
sage: P
2465
({0,1,2,3}) ({0,1,4,5,2,3})
2466
({0,1,2,3}) ({0},{1},{4,5},{2,3})
2467
2468
"""
2469
cdef int i
2470
for i from 0 <= i < len(degrees):
2471
self.col_degs[i] = degrees[i]
2472
return self.sort_cols(start, k)
2473
2474
cdef int sort_cols(self, int start, int k):
2475
cdef int i, j, max, max_location, self_ncols = self.ncols
2476
cdef int self_nwords = self.nwords, ii
2477
cdef int *self_col_counts = self.col_counts
2478
cdef int *self_col_lvls = self.col_lvls
2479
cdef int *self_col_degs = self.col_degs
2480
cdef int *self_col_ents = self.col_ents
2481
cdef int *self_col_output = self.col_output
2482
for ii from 0 <= ii < self_nwords:
2483
self_col_counts[ii] = 0
2484
i = 0
2485
while self_col_lvls[i+start] > k:
2486
self_col_counts[self_col_degs[i]] += 1
2487
i += 1
2488
self_col_counts[self_col_degs[i]] += 1
2489
2490
# i+start is the right endpoint of the cell now
2491
max = self_col_counts[0]
2492
max_location = 0
2493
for ii from 0 < ii < self_nwords:
2494
if self_col_counts[ii] > max:
2495
max = self_col_counts[ii]
2496
max_location = ii
2497
self_col_counts[ii] += self_col_counts[ii-1]
2498
2499
for j from i >= j >= 0:
2500
self_col_counts[self_col_degs[j]] -= 1
2501
self_col_output[self_col_counts[self_col_degs[j]]] = self_col_ents[start+j]
2502
2503
max_location = self_col_counts[max_location] + start
2504
2505
for j from 0 <= j <= i:
2506
self_col_ents[start+j] = self_col_output[j]
2507
2508
ii = 1
2509
while ii < self_nwords and self_col_counts[ii] <= i:
2510
if self_col_counts[ii] > 0:
2511
self_col_lvls[start + self_col_counts[ii] - 1] = k
2512
self.col_percolate(start + self_col_counts[ii-1], start + self_col_counts[ii] - 1)
2513
ii += 1
2514
2515
return max_location
2516
2517
def _sort_wds(self, start, degrees, k):
2518
"""
2519
Essentially a counting sort, but on only one cell of the partition.
2520
2521
INPUT:
2522
start -- location of the beginning of the cell
2523
k -- at what level of refinement the partition of interest lies
2524
degrees -- the counts to sort by
2525
2526
EXAMPLE:
2527
sage: import sage.coding.binary_code
2528
sage: from sage.coding.binary_code import *
2529
sage: P = PartitionStack(3, 6)
2530
sage: P._sort_wds(0, [0,0,3,3,3,3,2,2], 1)
2531
4
2532
sage: P
2533
({0,1,6,7,2,3,4,5}) ({0,1,2,3,4,5})
2534
({0,1},{6,7},{2,3,4,5}) ({0,1,2,3,4,5})
2535
2536
"""
2537
cdef int i
2538
for i from 0 <= i < len(degrees):
2539
self.wd_degs[i] = degrees[i]
2540
return self.sort_wds(start, k)
2541
2542
cdef int sort_wds(self, int start, int k):
2543
cdef int i, j, max, max_location, self_nwords = self.nwords
2544
cdef int ii, self_ncols = self.ncols
2545
cdef int *self_wd_counts = self.wd_counts
2546
cdef int *self_wd_lvls = self.wd_lvls
2547
cdef int *self_wd_degs = self.wd_degs
2548
cdef int *self_wd_ents = self.wd_ents
2549
cdef int *self_wd_output = self.wd_output
2550
2551
for ii from 0 <= ii < self_ncols+1:
2552
self_wd_counts[ii] = 0
2553
i = 0
2554
while self_wd_lvls[i+start] > k:
2555
self_wd_counts[self_wd_degs[i]] += 1
2556
i += 1
2557
self_wd_counts[self_wd_degs[i]] += 1
2558
2559
# i+start is the right endpoint of the cell now
2560
max = self_wd_counts[0]
2561
max_location = 0
2562
for ii from 0 < ii < self_ncols+1:
2563
if self_wd_counts[ii] > max:
2564
max = self_wd_counts[ii]
2565
max_location = ii
2566
self_wd_counts[ii] += self_wd_counts[ii-1]
2567
2568
for j from i >= j >= 0:
2569
if j > i: break # cython bug with ints...
2570
self_wd_counts[self_wd_degs[j]] -= 1
2571
self_wd_output[self_wd_counts[self_wd_degs[j]]] = self_wd_ents[start+j]
2572
2573
max_location = self_wd_counts[max_location] + start
2574
2575
for j from 0 <= j <= i:
2576
self_wd_ents[start+j] = self_wd_output[j]
2577
2578
ii = 1
2579
while ii < self_ncols+1 and self_wd_counts[ii] <= i:
2580
if self_wd_counts[ii] > 0:
2581
self_wd_lvls[start + self_wd_counts[ii] - 1] = k
2582
self.wd_percolate(start + self_wd_counts[ii-1], start + self_wd_counts[ii] - 1)
2583
ii += 1
2584
2585
return max_location
2586
2587
def _refine(self, k, alpha, CG):
2588
"""
2589
EXAMPLE::
2590
2591
sage: import sage.coding.binary_code
2592
sage: from sage.coding.binary_code import *
2593
sage: M = Matrix(GF(2), [[1,1,1,1,0,0,0,0],[0,0,1,1,1,1,0,0],[0,0,0,0,1,1,1,1],[1,0,1,0,1,0,1,0]])
2594
sage: B = BinaryCode(M)
2595
sage: P = PartitionStack(4, 8)
2596
sage: P._refine(1, [[0,0],[1,0]], B)
2597
181
2598
sage: P._split_vertex(0, 2)
2599
0
2600
sage: P._refine(2, [[0,0]], B)
2601
290
2602
sage: P._split_vertex(1, 3)
2603
1
2604
sage: P._refine(3, [[0,1]], B)
2605
463
2606
sage: P._split_vertex(2, 4)
2607
2
2608
sage: P._refine(4, [[0,2]], B)
2609
1500
2610
sage: P._split_vertex(3, 5)
2611
3
2612
sage: P._refine(5, [[0,3]], B)
2613
641
2614
sage: P._split_vertex(4, 6)
2615
4
2616
sage: P._refine(6, [[0,4]], B)
2617
1224
2618
sage: P._is_discrete(5)
2619
0
2620
sage: P._is_discrete(6)
2621
1
2622
sage: P
2623
({0,4,6,2,13,9,11,15,10,14,12,8,7,3,1,5}) ({0,1,2,3,4,7,6,5})
2624
({0},{4,6,2,13,9,11,15,10,14,12,8,7,3,1},{5}) ({0,1,2,3,4,7,6,5})
2625
({0},{4,6,2,13,9,11,15},{10,14,12,8,7,3,1},{5}) ({0},{1,2,3,4,7,6,5})
2626
({0},{4,6,2},{13,9,11,15},{10,14,12,8},{7,3,1},{5}) ({0},{1},{2,3,4,7,6,5})
2627
({0},{4},{6,2},{13,9},{11,15},{10,14},{12,8},{7,3},{1},{5}) ({0},{1},{2},{3,4,7,6,5})
2628
({0},{4},{6,2},{13,9},{11,15},{10,14},{12,8},{7,3},{1},{5}) ({0},{1},{2},{3},{4,7,6,5})
2629
({0},{4},{6},{2},{13},{9},{11},{15},{10},{14},{12},{8},{7},{3},{1},{5}) ({0},{1},{2},{3},{4},{7},{6},{5})
2630
2631
"""
2632
cdef int i, alpha_length = len(alpha)
2633
cdef int *_alpha = <int *> sage_malloc( (self.nwords + self.ncols) * sizeof(int) )
2634
cdef int *ham_wts = hamming_weights()
2635
if _alpha is NULL:
2636
raise MemoryError("Memory.")
2637
for i from 0 <= i < alpha_length:
2638
if alpha[i][0]:
2639
_alpha[i] = alpha[i][1] ^ self.flag
2640
else:
2641
_alpha[i] = alpha[i][1]
2642
result = self.refine(k, _alpha, alpha_length, CG, ham_wts)
2643
sage_free(_alpha)
2644
sage_free(ham_wts)
2645
return result
2646
2647
cdef int refine(self, int k, int *alpha, int alpha_length, BinaryCode CG, int *ham_wts):
2648
cdef int q, r, s, t, flag = self.flag, self_ncols = self.ncols
2649
cdef int t_w, self_nwords = self.nwords, invariant = 0, i, j, m = 0
2650
cdef int *self_wd_degs = self.wd_degs, *self_wd_lvls = self.wd_lvls, *self_wd_ents = self.wd_ents
2651
cdef int *self_col_degs = self.col_degs, *self_col_lvls = self.col_lvls, *self_col_ents = self.col_ents
2652
while not self.is_discrete(k) and m < alpha_length:
2653
# print "m:", m
2654
# print "alpha:", ','.join(['w'+str(alpha[i]^flag) if alpha[i]&flag else 'c'+str(alpha[i]) for i from 0 <= i < alpha_length])
2655
# print self
2656
invariant += 1
2657
j = 0
2658
if alpha[m] & flag:
2659
# print 'word'
2660
while j < self_ncols:
2661
# print 'j', j
2662
# print self
2663
i = j; s = 0
2664
invariant += 8
2665
while 1:
2666
# print 'col_i', self_col_ents[i]
2667
# print 'alpha[m]^flag', alpha[m]^flag
2668
self_col_degs[i-j] = self.col_degree(CG, self_col_ents[i], alpha[m]^flag, k)
2669
# print 'deg', self_col_degs[i-j]
2670
if s == 0 and self_col_degs[i-j] != self_col_degs[0]: s = 1
2671
i += 1
2672
if self_col_lvls[i-1] <= k: break
2673
if s:
2674
# print 's'
2675
invariant += 8
2676
t = self.sort_cols(j, k)
2677
invariant += t
2678
q = m
2679
while q < alpha_length:
2680
if alpha[q] == j:
2681
alpha[q] = t
2682
break
2683
q += 1
2684
r = j
2685
while 1:
2686
if r == j or self.col_lvls[r-1] == k:
2687
if r != t:
2688
alpha[alpha_length] = r
2689
alpha_length += 1
2690
r += 1
2691
if r >= i: break
2692
invariant += self.col_degree(CG, self_col_ents[i-1], alpha[m]^flag, k)
2693
invariant += (i-j)
2694
j = i
2695
else:
2696
# print 'col'
2697
while j < self.nwords:
2698
# print 'j', j
2699
# print self
2700
i = j; s = 0
2701
invariant += 64
2702
while 1:
2703
# print 'i', i
2704
self_wd_degs[i-j] = self.wd_degree(CG, self_wd_ents[i], alpha[m], k, ham_wts)
2705
# print 'deg', self_wd_degs[i-j]
2706
if s == 0 and self_wd_degs[i-j] != self_wd_degs[0]: s = 1
2707
i += 1
2708
if self_wd_lvls[i-1] <= k: break
2709
if s:
2710
invariant += 64
2711
t_w = self.sort_wds(j, k)
2712
invariant += t_w
2713
q = m
2714
j ^= flag
2715
while q < alpha_length:
2716
if alpha[q] == j:
2717
alpha[q] = t_w ^ flag
2718
break
2719
q += 1
2720
j ^= flag
2721
r = j
2722
while 1:
2723
if r == j or self.wd_lvls[r-1] == k:
2724
if r != t_w:
2725
alpha[alpha_length] = r^flag
2726
alpha_length += 1
2727
r += 1
2728
if r >= i: break
2729
invariant += self.wd_degree(CG, self_wd_ents[i-1], alpha[m], k, ham_wts)
2730
invariant += (i-j)
2731
j = i
2732
m += 1
2733
return invariant
2734
2735
def _clear(self, k):
2736
"""
2737
EXAMPLE:
2738
sage: import sage.coding.binary_code
2739
sage: from sage.coding.binary_code import *
2740
sage: P = PartitionStack(2, 6)
2741
sage: [P._split_vertex(i,i+1) for i in range(5)]
2742
[0, 1, 2, 3, 4]
2743
sage: P
2744
({0,1,2,3}) ({0,1,2,3,4,5})
2745
({0,1,2,3}) ({0},{1,2,3,4,5})
2746
({0,1,2,3}) ({0},{1},{2,3,4,5})
2747
({0,1,2,3}) ({0},{1},{2},{3,4,5})
2748
({0,1,2,3}) ({0},{1},{2},{3},{4,5})
2749
({0,1,2,3}) ({0},{1},{2},{3},{4},{5})
2750
sage: P._clear(2)
2751
sage: P
2752
({0,1,2,3}) ({0,1,2,3,4,5})
2753
({0,1,2,3}) ({0},{1,2,3,4,5})
2754
2755
"""
2756
self.clear(k)
2757
2758
cdef void clear(self, int k):
2759
cdef int i, j = 0, nwords = self.nwords, ncols = self.ncols
2760
cdef int *wd_lvls = self.wd_lvls, *col_lvls = self.col_lvls
2761
for i from 0 <= i < nwords:
2762
if wd_lvls[i] >= k:
2763
wd_lvls[i] += 1
2764
if wd_lvls[i] < k:
2765
self.wd_percolate(j, i)
2766
j = i + 1
2767
j = 0
2768
for i from 0 <= i < ncols:
2769
if col_lvls[i] >= k:
2770
col_lvls[i] += 1
2771
if col_lvls[i] < k:
2772
self.col_percolate(j, i)
2773
j = i + 1
2774
2775
def _cmp(self, other, C):
2776
"""
2777
EXAMPLE:
2778
sage: import sage.coding.binary_code
2779
sage: from sage.coding.binary_code import *
2780
sage: M = Matrix(GF(2), [[1,1,1,1,0,0,0,0],[0,0,1,1,1,1,0,0],[0,0,0,0,1,1,1,1],[1,0,1,0,1,0,1,0]])
2781
sage: B = BinaryCode(M)
2782
sage: P = PartitionStack(4, 8)
2783
sage: P._refine(0, [[0,0],[1,0]], B)
2784
181
2785
sage: P._split_vertex(0, 1)
2786
0
2787
sage: P._refine(1, [[0,0]], B)
2788
290
2789
sage: P._split_vertex(1, 2)
2790
1
2791
sage: P._refine(2, [[0,1]], B)
2792
463
2793
sage: P._split_vertex(2, 3)
2794
2
2795
sage: P._refine(3, [[0,2]], B)
2796
1500
2797
sage: P._split_vertex(4, 4)
2798
4
2799
sage: P._refine(4, [[0,4]], B)
2800
1224
2801
sage: P._is_discrete(4)
2802
1
2803
sage: Q = PartitionStack(P)
2804
sage: Q._clear(4)
2805
sage: Q._split_vertex(5, 4)
2806
4
2807
sage: Q._refine(4, [[0,4]], B)
2808
1224
2809
sage: Q._is_discrete(4)
2810
1
2811
sage: Q._cmp(P, B)
2812
0
2813
2814
"""
2815
return self.cmp(other, C)
2816
2817
cdef int cmp(self, PartitionStack other, BinaryCode CG):
2818
cdef int *self_wd_ents = self.wd_ents
2819
cdef codeword *CG_words = CG.words
2820
cdef int i, j, l, m, span = 1, ncols = self.ncols, nwords = self.nwords
2821
for i from 0 < i < nwords:
2822
for j from 0 <= j < ncols:
2823
l = CG.is_one(self.wd_ents[i], self.col_ents[j])
2824
m = CG.is_one(other.wd_ents[i], other.col_ents[j])
2825
if l != m:
2826
return l - m
2827
return 0
2828
2829
def print_basis(self):
2830
"""
2831
EXAMPLE:
2832
sage: import sage.coding.binary_code
2833
sage: from sage.coding.binary_code import *
2834
sage: P = PartitionStack(4, 8)
2835
sage: P._dangerous_dont_use_set_ents_lvls(range(8), range(7)+[-1], [4,7,12,11,1,9,3,0,2,5,6,8,10,13,14,15], [0]*16)
2836
sage: P
2837
({4},{7},{12},{11},{1},{9},{3},{0},{2},{5},{6},{8},{10},{13},{14},{15}) ({0},{1,2,3,4,5,6,7})
2838
({4},{7},{12},{11},{1},{9},{3},{0},{2},{5},{6},{8},{10},{13},{14},{15}) ({0},{1},{2,3,4,5,6,7})
2839
({4},{7},{12},{11},{1},{9},{3},{0},{2},{5},{6},{8},{10},{13},{14},{15}) ({0},{1},{2},{3,4,5,6,7})
2840
({4},{7},{12},{11},{1},{9},{3},{0},{2},{5},{6},{8},{10},{13},{14},{15}) ({0},{1},{2},{3},{4,5,6,7})
2841
({4},{7},{12},{11},{1},{9},{3},{0},{2},{5},{6},{8},{10},{13},{14},{15}) ({0},{1},{2},{3},{4},{5,6,7})
2842
({4},{7},{12},{11},{1},{9},{3},{0},{2},{5},{6},{8},{10},{13},{14},{15}) ({0},{1},{2},{3},{4},{5},{6,7})
2843
({4},{7},{12},{11},{1},{9},{3},{0},{2},{5},{6},{8},{10},{13},{14},{15}) ({0},{1},{2},{3},{4},{5},{6},{7})
2844
sage: P._find_basis()
2845
sage: P.print_basis()
2846
basis_locations:
2847
4
2848
8
2849
0
2850
11
2851
2852
"""
2853
cdef int i, j
2854
if self.basis_locations:
2855
print "basis_locations:"
2856
j = 1
2857
while (1 << j) < self.nwords:
2858
j += 1
2859
for i from 0 <= i < j:
2860
print self.basis_locations[i]
2861
2862
def _find_basis(self):
2863
"""
2864
EXAMPLE:
2865
sage: import sage.coding.binary_code
2866
sage: from sage.coding.binary_code import *
2867
sage: P = PartitionStack(4, 8)
2868
sage: P._dangerous_dont_use_set_ents_lvls(range(8), range(7)+[-1], [4,7,12,11,1,9,3,0,2,5,6,8,10,13,14,15], [0]*16)
2869
sage: P
2870
({4},{7},{12},{11},{1},{9},{3},{0},{2},{5},{6},{8},{10},{13},{14},{15}) ({0},{1,2,3,4,5,6,7})
2871
({4},{7},{12},{11},{1},{9},{3},{0},{2},{5},{6},{8},{10},{13},{14},{15}) ({0},{1},{2,3,4,5,6,7})
2872
({4},{7},{12},{11},{1},{9},{3},{0},{2},{5},{6},{8},{10},{13},{14},{15}) ({0},{1},{2},{3,4,5,6,7})
2873
({4},{7},{12},{11},{1},{9},{3},{0},{2},{5},{6},{8},{10},{13},{14},{15}) ({0},{1},{2},{3},{4,5,6,7})
2874
({4},{7},{12},{11},{1},{9},{3},{0},{2},{5},{6},{8},{10},{13},{14},{15}) ({0},{1},{2},{3},{4},{5,6,7})
2875
({4},{7},{12},{11},{1},{9},{3},{0},{2},{5},{6},{8},{10},{13},{14},{15}) ({0},{1},{2},{3},{4},{5},{6,7})
2876
({4},{7},{12},{11},{1},{9},{3},{0},{2},{5},{6},{8},{10},{13},{14},{15}) ({0},{1},{2},{3},{4},{5},{6},{7})
2877
sage: P._find_basis()
2878
sage: P.print_basis()
2879
basis_locations:
2880
4
2881
8
2882
0
2883
11
2884
2885
"""
2886
cdef int i
2887
cdef int *ham_wts = hamming_weights()
2888
self.find_basis(ham_wts)
2889
sage_free(ham_wts)
2890
2891
cdef int find_basis(self, int *ham_wts):
2892
cdef int i = 0, j, k, nwords = self.nwords, weight, basis_elts = 0, nrows = self.nrows
2893
cdef int *self_wd_ents = self.wd_ents
2894
if self.basis_locations is NULL:
2895
self.basis_locations = <int *> sage_malloc( 2 * nrows * sizeof(int) )
2896
if self.basis_locations is NULL:
2897
raise MemoryError("Memory.")
2898
while i < nwords:
2899
j = self_wd_ents[i]
2900
weight = ham_wts[j & 65535] + ham_wts[(j>>16) & 65535]
2901
if weight == 1:
2902
basis_elts += 1
2903
k = 0
2904
while not (1<<k) & j:
2905
k += 1
2906
self.basis_locations[k] = i
2907
if basis_elts == nrows: break
2908
i += 1
2909
for i from 0 <= i < nrows:
2910
self.basis_locations[nrows + i] = self_wd_ents[1 << i]
2911
2912
def _get_permutation(self, other):
2913
"""
2914
EXAMPLE:
2915
sage: import sage.coding.binary_code
2916
sage: from sage.coding.binary_code import *
2917
sage: M = Matrix(GF(2), [[1,1,1,1,0,0,0,0],[0,0,1,1,1,1,0,0],[0,0,0,0,1,1,1,1],[1,0,1,0,1,0,1,0]])
2918
sage: B = BinaryCode(M)
2919
sage: P = PartitionStack(4, 8)
2920
sage: P._refine(0, [[0,0],[1,0]], B)
2921
181
2922
sage: P._split_vertex(0, 1)
2923
0
2924
sage: P._refine(1, [[0,0]], B)
2925
290
2926
sage: P._split_vertex(1, 2)
2927
1
2928
sage: P._refine(2, [[0,1]], B)
2929
463
2930
sage: P._split_vertex(2, 3)
2931
2
2932
sage: P._refine(3, [[0,2]], B)
2933
1500
2934
sage: P._split_vertex(4, 4)
2935
4
2936
sage: P._refine(4, [[0,4]], B)
2937
1224
2938
sage: P._is_discrete(4)
2939
1
2940
sage: Q = PartitionStack(P)
2941
sage: Q._clear(4)
2942
sage: Q._split_vertex(5, 4)
2943
4
2944
sage: Q._refine(4, [[0,4]], B)
2945
1224
2946
sage: Q._is_discrete(4)
2947
1
2948
sage: P._get_permutation(Q)
2949
([0, 1, 2, 3, 4, 5, 6, 7, 12, 13, 14, 15, 8, 9, 10, 11], [0, 1, 2, 3, 5, 4, 7, 6])
2950
2951
"""
2952
cdef int i
2953
cdef int *word_g = <int *> sage_malloc( self.nwords * sizeof(int) )
2954
cdef int *col_g = <int *> sage_malloc( self.ncols * sizeof(int) )
2955
if word_g is NULL or col_g is NULL:
2956
if word_g is not NULL: sage_free(word_g)
2957
if col_g is not NULL: sage_free(col_g)
2958
raise MemoryError("Memory.")
2959
self.get_permutation(other, word_g, col_g)
2960
word_l = [word_g[i] for i from 0 <= i < self.nwords]
2961
col_l = [col_g[i] for i from 0 <= i < self.ncols]
2962
sage_free(word_g)
2963
sage_free(col_g)
2964
return word_l, col_l
2965
2966
cdef void get_permutation(self, PartitionStack other, int *word_gamma, int *col_gamma):
2967
cdef int i
2968
cdef int *self_wd_ents = self.wd_ents, *other_wd_ents = other.wd_ents
2969
cdef int *self_col_ents = self.col_ents, *other_col_ents = other.col_ents
2970
# word_gamma[i] := image of the ith row as linear comb of rows
2971
for i from 0 <= i < self.nwords:
2972
word_gamma[other_wd_ents[i]] = self_wd_ents[i]
2973
for i from 0 <= i < self.ncols:
2974
col_gamma[other_col_ents[i]] = self_col_ents[i]
2975
2976
cdef class BinaryCodeClassifier:
2977
2978
def __cinit__(self):
2979
self.radix = sizeof(codeword) << 3
2980
self.ham_wts = hamming_weights()
2981
self.L = 100 # memory limit for Phi and Omega- multiply by 8KB
2982
self.aut_gens_size = self.radix * 100
2983
2984
self.w_gamma_size = 1 << (self.radix/2)
2985
self.alpha_size = self.w_gamma_size + self.radix
2986
self.Phi_size = self.w_gamma_size/self.radix + 1
2987
2988
self.w_gamma = <int *> sage_malloc( self.w_gamma_size * sizeof(int) )
2989
self.alpha = <int *> sage_malloc( self.alpha_size * sizeof(int) )
2990
self.Phi = <unsigned int *> sage_malloc( self.Phi_size * (self.L+1) * sizeof(unsigned int) )
2991
self.Omega = <unsigned int *> sage_malloc( self.Phi_size * self.L * sizeof(unsigned int) )
2992
self.W = <unsigned int *> sage_malloc( self.Phi_size * self.radix * 2 * sizeof(unsigned int) )
2993
2994
self.base = <int *> sage_malloc( self.radix * sizeof(int) )
2995
self.aut_gp_gens = <int *> sage_malloc( self.aut_gens_size * sizeof(int) )
2996
self.c_gamma = <int *> sage_malloc( self.radix * sizeof(int) )
2997
self.labeling = <int *> sage_malloc( self.radix * 3 * sizeof(int) )
2998
self.Lambda1 = <int *> sage_malloc( self.radix * 2 * sizeof(int) )
2999
self.Lambda2 = <int *> sage_malloc( self.radix * 2 * sizeof(int) )
3000
self.Lambda3 = <int *> sage_malloc( self.radix * 2 * sizeof(int) )
3001
self.v = <int *> sage_malloc( self.radix * 2 * sizeof(int) )
3002
self.e = <int *> sage_malloc( self.radix * 2 * sizeof(int) )
3003
3004
if self.Phi is NULL or self.Omega is NULL or self.W is NULL or self.Lambda1 is NULL \
3005
or self.Lambda2 is NULL or self.Lambda3 is NULL or self.w_gamma is NULL \
3006
or self.c_gamma is NULL or self.alpha is NULL or self.v is NULL or self.e is NULL \
3007
or self.aut_gp_gens is NULL or self.labeling is NULL or self.base is NULL:
3008
if self.Phi is not NULL: sage_free(self.Phi)
3009
if self.Omega is not NULL: sage_free(self.Omega)
3010
if self.W is not NULL: sage_free(self.W)
3011
if self.Lambda1 is not NULL: sage_free(self.Lambda1)
3012
if self.Lambda2 is not NULL: sage_free(self.Lambda2)
3013
if self.Lambda3 is not NULL: sage_free(self.Lambda3)
3014
if self.w_gamma is not NULL: sage_free(self.w_gamma)
3015
if self.c_gamma is not NULL: sage_free(self.c_gamma)
3016
if self.alpha is not NULL: sage_free(self.alpha)
3017
if self.v is not NULL: sage_free(self.v)
3018
if self.e is not NULL: sage_free(self.e)
3019
if self.aut_gp_gens is not NULL: sage_free(self.aut_gp_gens)
3020
if self.labeling is not NULL: sage_free(self.labeling)
3021
if self.base is not NULL: sage_free(self.base)
3022
raise MemoryError("Memory.")
3023
3024
def __dealloc__(self):
3025
sage_free(self.ham_wts)
3026
sage_free(self.Phi)
3027
sage_free(self.Omega)
3028
sage_free(self.W)
3029
sage_free(self.Lambda1)
3030
sage_free(self.Lambda2)
3031
sage_free(self.Lambda3)
3032
sage_free(self.c_gamma)
3033
sage_free(self.w_gamma)
3034
sage_free(self.alpha)
3035
sage_free(self.v)
3036
sage_free(self.e)
3037
sage_free(self.aut_gp_gens)
3038
sage_free(self.labeling)
3039
sage_free(self.base)
3040
3041
cdef void record_automorphism(self, int *gamma, int ncols):
3042
cdef int i, j
3043
if self.aut_gp_index + ncols > self.aut_gens_size:
3044
self.aut_gens_size *= 2
3045
self.aut_gp_gens = <int *> sage_realloc( self.aut_gp_gens, self.aut_gens_size * sizeof(int) )
3046
if self.aut_gp_gens is NULL:
3047
raise MemoryError("Memory.")
3048
j = self.aut_gp_index
3049
for i from 0 <= i < ncols:
3050
self.aut_gp_gens[i+j] = gamma[i]
3051
self.aut_gp_index += ncols
3052
3053
def _aut_gp_and_can_label(self, CC, verbosity=0):
3054
"""
3055
Compute the automorphism group and canonical label of the code CC.
3056
3057
INPUT:
3058
CC - a BinaryCode object
3059
verbosity - a nonnegative integer
3060
3061
OUTPUT:
3062
a tuple, (gens, labeling, size, base)
3063
gens -- a list of permutations (in list form) representing generators
3064
of the permutation automorphism group of the code CC.
3065
labeling -- a permutation representing the canonical labeling of the
3066
code. mostly for internal use; entries describe the relabeling
3067
on the columns.
3068
size -- the order of the automorphism group.
3069
base -- a set of cols whose action determines the action on all cols
3070
3071
EXAMPLES:
3072
sage: import sage.coding.binary_code
3073
sage: from sage.coding.binary_code import *
3074
sage: BC = BinaryCodeClassifier()
3075
3076
sage: M = Matrix(GF(2),[
3077
... [1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0],
3078
... [0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0],
3079
... [0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1],
3080
... [0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1],
3081
... [0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1]])
3082
sage: B = BinaryCode(M)
3083
sage: gens, labeling, size, base = BC._aut_gp_and_can_label(B)
3084
sage: S = SymmetricGroup(M.ncols())
3085
sage: L = [S([x+1 for x in g]) for g in gens]
3086
sage: PermutationGroup(L).order()
3087
322560
3088
sage: size
3089
322560
3090
3091
sage: M = Matrix(GF(2),[
3092
... [1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0],
3093
... [0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0],
3094
... [0,0,0,0,0,1,0,1,0,0,0,1,1,1,1,1,1],
3095
... [0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,1]])
3096
sage: B = BinaryCode(M)
3097
sage: gens, labeling, size, base = BC._aut_gp_and_can_label(B)
3098
sage: S = SymmetricGroup(M.ncols())
3099
sage: L = [S([x+1 for x in g]) for g in gens]
3100
sage: PermutationGroup(L).order()
3101
2304
3102
sage: size
3103
2304
3104
3105
sage: M=Matrix(GF(2),[
3106
... [1,0,0,1,1,1,1,0,0,1,0,0,0,0,0,0,0],
3107
... [0,1,0,0,1,1,1,1,0,0,1,0,0,0,0,0,0],
3108
... [0,0,1,0,0,1,1,1,1,0,0,1,0,0,0,0,0],
3109
... [0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0,0],
3110
... [0,0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0],
3111
... [0,0,0,0,0,1,0,0,1,1,1,1,0,0,1,0,0],
3112
... [0,0,0,0,0,0,1,0,0,1,1,1,1,0,0,1,0],
3113
... [0,0,0,0,0,0,0,1,0,0,1,1,1,1,0,0,1]])
3114
sage: B = BinaryCode(M)
3115
sage: gens, labeling, size, base = BC._aut_gp_and_can_label(B)
3116
sage: S = SymmetricGroup(M.ncols())
3117
sage: L = [S([x+1 for x in g]) for g in gens]
3118
sage: PermutationGroup(L).order()
3119
136
3120
sage: size
3121
136
3122
3123
sage: M=Matrix(GF(2),[
3124
... [0,1,0,1,1,1,0,0,0,1,0,0,0,1,0,0,0,1,1,1,0,1],
3125
... [1,0,1,1,1,0,0,0,1,0,0,0,1,0,0,0,1,1,1,0,1,0],
3126
... [0,1,1,1,0,0,0,1,0,0,1,1,0,0,0,1,1,1,0,1,0,0],
3127
... [1,1,1,0,0,0,1,0,0,1,0,0,0,0,1,1,1,0,1,0,0,1],
3128
... [1,1,0,0,0,1,0,0,1,0,1,0,0,1,1,1,0,1,0,0,1,0],
3129
... [1,0,0,0,1,0,0,1,0,1,1,0,1,1,1,0,1,0,0,1,0,0],
3130
... [0,0,0,1,0,0,1,0,1,1,1,1,1,1,0,1,0,0,1,0,0,0],
3131
... [0,0,1,0,0,1,0,1,1,1,0,1,1,0,1,0,0,1,0,0,0,1],
3132
... [0,1,0,0,1,0,1,1,1,0,0,1,0,1,0,0,1,0,0,0,1,1],
3133
... [1,0,0,1,0,1,1,1,0,0,0,0,1,0,0,1,0,0,0,1,1,1],
3134
... [0,0,1,0,1,1,1,0,0,0,1,1,0,0,1,0,0,0,1,1,1,0]])
3135
sage: B = BinaryCode(M)
3136
sage: gens, labeling, size, base = BC._aut_gp_and_can_label(B)
3137
sage: S = SymmetricGroup(M.ncols())
3138
sage: L = [S([x+1 for x in g]) for g in gens]
3139
sage: PermutationGroup(L).order()
3140
887040
3141
sage: size
3142
887040
3143
3144
sage: B = BinaryCode(Matrix(GF(2),[[1,0,1],[0,1,1]]))
3145
sage: BC._aut_gp_and_can_label(B)
3146
([[0, 2, 1], [1, 0, 2]], [0, 1, 2], 6, [0, 1])
3147
3148
sage: B = BinaryCode(Matrix(GF(2),[[1,1,1,1]]))
3149
sage: BC._aut_gp_and_can_label(B)
3150
([[0, 1, 3, 2], [0, 2, 1, 3], [1, 0, 2, 3]], [0, 1, 2, 3], 24, [0, 1, 2])
3151
3152
sage: B = BinaryCode(Matrix(GF(2),[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]]))
3153
sage: gens, labeling, size, base = BC._aut_gp_and_can_label(B)
3154
sage: size
3155
87178291200
3156
3157
sage: M = Matrix(GF(2),[
3158
... [1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0],
3159
... [0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0],
3160
... [0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1,1,1],
3161
... [0,0,1,1,0,0,0,0,0,0,1,1,1,1,0,0,1,1],
3162
... [0,0,0,1,0,0,0,1,0,1,0,1,0,1,1,1,0,1],
3163
... [0,1,0,0,0,1,0,0,0,1,1,1,0,1,0,1,1,0]])
3164
sage: B = BinaryCode(M)
3165
sage: BC._aut_gp_and_can_label(B)[2]
3166
2160
3167
3168
sage: M = Matrix(GF(2),[
3169
... [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
3170
... [0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
3171
... [0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0],
3172
... [0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0],
3173
... [0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0],
3174
... [0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0],
3175
... [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
3176
... [1,0,1,0,1,0,1,0,1,1,0,0,0,0,0,0,1,1,0,0],
3177
... [1,1,0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,1,0,0],
3178
... [1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,0,1,0]])
3179
sage: B = BinaryCode(M)
3180
sage: BC._aut_gp_and_can_label(B)[2]
3181
294912
3182
3183
sage: M = Matrix(GF(2), [
3184
... [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
3185
... [0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
3186
... [0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0],
3187
... [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0],
3188
... [0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0],
3189
... [0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0],
3190
... [0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1],
3191
... [0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,0,0,1,1,1,0,1],
3192
... [0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,1,1,1,1,0,0,0,1]])
3193
sage: B = BinaryCode(M)
3194
sage: BC = BinaryCodeClassifier()
3195
sage: BC._aut_gp_and_can_label(B)[2]
3196
442368
3197
3198
"""
3199
cdef int i, j
3200
cdef BinaryCode C = CC
3201
self.aut_gp_and_can_label(C, verbosity)
3202
i = 0
3203
py_aut_gp_gens = []
3204
while i < self.aut_gp_index:
3205
gen = [self.aut_gp_gens[i+j] for j from 0 <= j < C.ncols]
3206
py_aut_gp_gens.append(gen)
3207
i += C.ncols
3208
py_labeling = [self.labeling[i] for i from 0 <= i < C.ncols]
3209
base = []
3210
for i from 0 <= i < self.radix:
3211
if self.base[i] == -1:
3212
break
3213
base.append(self.base[i])
3214
aut_gp_size = self.aut_gp_size
3215
return py_aut_gp_gens, py_labeling, aut_gp_size, base
3216
3217
cdef void aut_gp_and_can_label(self, BinaryCode C, int verbosity):
3218
3219
# declare variables:
3220
cdef int i, j, ii, jj, iii, jjj, iiii # local variables
3221
3222
cdef PartitionStack nu, zeta, rho # nu is the current position in the tree,
3223
# zeta the first terminal position,
3224
# and rho the best-so-far guess at canonical labeling position
3225
cdef int k = 0 # the number of partitions in nu
3226
cdef int k_rho # the number of partitions in rho
3227
cdef int *v = self.v # list of vertices determining nu
3228
cdef int h = -1 # longest common ancestor of zeta and nu: zeta[h] == nu[h], zeta[h+1] != nu[h+1]
3229
# -1 indicates that zeta is not yet defined
3230
cdef int hb # longest common ancestor of rho and nu:
3231
# rho[hb] == nu[hb], rho[hb+1] != nu[hb+1]
3232
cdef int hh = 1 # the height of the oldest ancestor of nu satisfying Lemma 2.25 in [1]:
3233
# if nu does not satisfy it at k, then hh = k
3234
cdef int ht # smallest such that all descendants of zeta[ht] are equivalent under
3235
# the portion of the automorphism group so far discovered
3236
cdef int *alpha # for storing pointers to cells of nu[k]
3237
cdef int tvc # tvc keeps track of which vertex is the first where nu and zeta differ-
3238
# zeta was defined by splitting one vertex, and nu was defined by splitting tvc
3239
3240
cdef OrbitPartition Theta # keeps track of which vertices have been discovered to be equivalent
3241
cdef unsigned int *Phi # Phi stores the fixed point sets of each automorphism
3242
cdef unsigned int *Omega # Omega stores the minimal elements of each cell of the orbit partition
3243
cdef int l = -1 # current index for storing values in Phi and Omega- we start at -1 so that when
3244
# we increment first, the first place we write to is 0.
3245
cdef unsigned int *W # for each k, W[k] is a list (as int mask) of the vertices to be searched down from
3246
# the current partition, at k. Phi and Omega are ultimately used to make the size of
3247
# W as small as possible
3248
cdef int *e # 0 or 1, whether or not we have used Omega and Phi to narrow down W[k] yet: see states 12 and 17
3249
3250
cdef int index = 0 # Define $\Gamma^{(-1)} := \text{Aut}(C)$, and
3251
# $\Gamma^{(i)} := \Gamma^{(-1)}_{v_0,...,v_i}$.
3252
# Then index = $|\Gamma^{(k-1)}|/|\Gamma^{(k)}|$ at (POINT A)
3253
# and size = $|\Gamma^{(k-1)}|$ at (POINT A) and (POINT B).
3254
3255
cdef int *Lambda = self.Lambda1 # for tracking indicator values- zf and zb are
3256
cdef int *zf__Lambda_zeta = self.Lambda2 # indicator vectors remembering Lambda[k] for
3257
cdef int *zb__Lambda_rho = self.Lambda3 # zeta and rho, respectively
3258
cdef int qzb # keeps track of Lambda[k] {>,<,=} zb[k]
3259
cdef int hzf__h_zeta # the max height for which Lambda and zf agree
3260
cdef int hzb__h_rho = -1 # the max height for which Lambda and zb agree
3261
3262
cdef int *word_gamma, *col_gamma = self.c_gamma # used for storing permutations
3263
cdef int nwords = C.nwords, ncols = C.ncols, nrows = C.nrows
3264
cdef int *ham_wts = self.ham_wts
3265
cdef int state # keeps track of position in algorithm - see sage/graphs/graph_isom.pyx, search for "STATE DIAGRAM"
3266
3267
self.aut_gp_index = 0
3268
self.aut_gp_size = Integer(1)
3269
3270
if self.w_gamma_size < nwords:
3271
while self.w_gamma_size < nwords:
3272
self.w_gamma_size *= 2
3273
self.alpha_size = self.w_gamma_size + self.radix
3274
self.Phi_size = self.w_gamma_size/self.radix + 1
3275
self.w_gamma = <int *> sage_realloc(self.w_gamma, self.w_gamma_size * sizeof(int) )
3276
self.alpha = <int *> sage_realloc(self.alpha, self.alpha_size * sizeof(int) )
3277
self.Phi = <unsigned int *> sage_realloc(self.Phi, self.Phi_size * self.L * sizeof(int) )
3278
self.Omega = <unsigned int *> sage_realloc(self.Omega, self.Phi_size * self.L * sizeof(int) )
3279
self.W = <unsigned int *> sage_realloc(self.W, self.Phi_size * self.radix * 2 * sizeof(int) )
3280
if self.w_gamma is NULL or self.alpha is NULL or self.Phi is NULL or self.Omega is NULL or self.W is NULL:
3281
if self.w_gamma is not NULL: sage_free(self.w_gamma)
3282
if self.alpha is not NULL: sage_free(self.alpha)
3283
if self.Phi is not NULL: sage_free(self.Phi)
3284
if self.Omega is not NULL: sage_free(self.Omega)
3285
if self.W is not NULL: sage_free(self.W)
3286
raise MemoryError("Memory.")
3287
for i from 0 <= i < self.Phi_size * self.L:
3288
self.Omega[i] = 0
3289
word_gamma = self.w_gamma
3290
alpha = self.alpha # think of alpha as of length exactly nwords + ncols
3291
Phi = self.Phi
3292
Omega = self.Omega
3293
W = self.W
3294
e = self.e
3295
nu = PartitionStack(nrows, ncols)
3296
Theta = OrbitPartition(nrows, ncols)
3297
3298
# trivial case
3299
if ncols == 0 or nrows == 0:
3300
raise NotImplementedError("Must supply a nontrivial code.")
3301
3302
state = 1
3303
while state != -1:
3304
if False:
3305
print '-----'
3306
print "k:", k
3307
print "h:", h
3308
if False:
3309
if k != -1:
3310
if v[k]&nu.flag:
3311
print "v[k]: word ", v[k]^nu.flag
3312
else:
3313
print "v[k]: col ", v[k]
3314
if tvc&nu.flag:
3315
print "tvc- wd", tvc^nu.flag
3316
else:
3317
print "tvc- col", tvc
3318
if W[self.Phi_size * k]:
3319
print "W[k]: cols", Integer(W[self.Phi_size * k]).binary()
3320
else:
3321
j = nwords/self.radix
3322
if nwords%self.radix:
3323
j += 1
3324
L = ''
3325
for i from 0 <= i < j:
3326
if i == j - 1:
3327
jj = nwords%self.radix
3328
if jj == 0:
3329
jj = self.radix
3330
else:
3331
jj = self.radix
3332
for ii from 0 <= ii < jj:
3333
if W[self.Phi_size * k + 1 + i] & (1 << ii):
3334
L += '1'
3335
else:
3336
L += '0'
3337
print "W[k]: words", L#[Integer(W[self.Phi_size * k + 1 + i]).binary() for i from 0 <= i < j]
3338
if False:
3339
print 'nu'
3340
print nu
3341
if tvc&nu.flag:
3342
print 'tvc is word', tvc^nu.flag
3343
else:
3344
print 'tvc is col', tvc
3345
if v[k]&nu.flag:
3346
print 'v[k] is word', v[k]^nu.flag
3347
else:
3348
print 'v[k] is col', v[k]
3349
if False:
3350
if h != -1:
3351
print 'zeta'
3352
print zeta
3353
print 'rho'
3354
print rho
3355
print "hzf:", hzf__h_zeta
3356
print "aut_gp_index", self.aut_gp_index
3357
print 'hh', hh
3358
print 'ht', ht
3359
print 'hzf__h_zeta', hzf__h_zeta
3360
print 'qzb', qzb
3361
if False:
3362
print '-----'
3363
print "state:", state
3364
3365
if state == 1: # Entry point: once only
3366
alpha[0] = 0
3367
alpha[1] = nu.flag
3368
nu.refine(k, alpha, 2, C, ham_wts)
3369
if nu.sat_225(k): hh = k
3370
if nu.is_discrete(k): state = 18; continue
3371
3372
# store the first smallest nontrivial cell in W[k], and set v[k]
3373
# equal to its minimum element
3374
v[k] = nu.new_first_smallest_nontrivial(k, W, self.Phi_size * k)
3375
3376
Lambda[k] = 0
3377
e[k] = 0
3378
state = 2
3379
3380
elif state == 2: # Move down the search tree one level by refining nu:
3381
# split out a vertex, and refine nu against it
3382
k += 1
3383
nu.clear(k)
3384
3385
alpha[0] = nu.split_vertex(v[k-1], k)
3386
Lambda[k] = nu.refine(k, alpha, 1, C, ham_wts) # store the invariant to Lambda[k]
3387
# only if this is the first time moving down the search tree:
3388
if h == -1: state = 5; continue
3389
3390
# update hzf__h_zeta
3391
if hzf__h_zeta == k-1 and Lambda[k] == zf__Lambda_zeta[k]: hzf__h_zeta = k
3392
# update qzb
3393
if qzb == 0:
3394
if zb__Lambda_rho[k] == -1 or Lambda[k] < zb__Lambda_rho[k]:
3395
qzb = -1
3396
elif Lambda[k] > zb__Lambda_rho[k]:
3397
qzb = 1
3398
else:
3399
qzb = 0
3400
# update hzb
3401
if hzb__h_rho == k-1 and qzb == 0: hzb__h_rho = k
3402
# if Lambda[k] > zb[k], then zb[k] := Lambda[k]
3403
# (zb keeps track of the indicator invariants corresponding to
3404
# rho, the closest canonical leaf so far seen- if Lambda is
3405
# bigger, then rho must be about to change
3406
if qzb > 0: zb__Lambda_rho[k] = Lambda[k]
3407
state = 3
3408
3409
elif state == 3: # attempt to rule out automorphisms while moving down the tree
3410
# if k > hzf, then we know that nu currently does not look like zeta, the first
3411
# terminal node encountered, thus there is no automorphism to discover. If qzb < 0,
3412
# i.e. Lambda[k] < zb[k], then the indicator is not maximal, and we can't reach a
3413
# canonical leaf. If neither of these is the case, then proceed to state 4.
3414
if hzf__h_zeta <= k or qzb >= 0: state = 4
3415
else: state = 6
3416
3417
elif state == 4: # at this point we have -not- ruled out the presence of automorphisms
3418
if nu.is_discrete(k): state = 7; continue # we have a terminal node, so process it
3419
3420
# otherwise, prepare to split out another column:
3421
# store the first smallest nontrivial cell in W[k], and set v[k]
3422
# equal to its minimum element
3423
v[k] = nu.new_first_smallest_nontrivial(k, W, self.Phi_size * k)
3424
if not nu.sat_225(k): hh = k + 1
3425
e[k] = 0 # see state 12 and 17
3426
state = 2 # continue down the tree
3427
3428
elif state == 5: # same as state 3, but in the case where we haven't yet defined zeta
3429
# i.e. this is our first time down the tree. Once we get to the bottom,
3430
# we will have zeta = nu = rho, so we do:
3431
zf__Lambda_zeta[k] = Lambda[k]
3432
zb__Lambda_rho[k] = Lambda[k]
3433
state = 4
3434
3435
elif state == 6: # at this stage, there is no reason to continue downward, so backtrack
3436
j = k
3437
# print 'current k', j
3438
# print 'ht', ht
3439
# print 'hzb__h_rho', hzb__h_rho
3440
# print 'hh', hh
3441
# return to the longest ancestor nu[i] of nu that could have a
3442
# descendant equivalent to zeta or could improve on rho.
3443
# All terminal nodes descending from nu[hh] are known to be
3444
# equivalent, so i < hh. Also, if i > hzb, none of the
3445
# descendants of nu[i] can improve rho, since the indicator is
3446
# off (Lambda(nu) < Lambda(rho)). If i >= ht, then no descendant
3447
# of nu[i] is equivalent to zeta (see [1, p67]).
3448
if ht-1 > hzb__h_rho:
3449
if ht-1 < hh-1:
3450
k = ht-1
3451
else:
3452
k = hh-1
3453
else:
3454
if hzb__h_rho < hh-1:
3455
k = hzb__h_rho
3456
else:
3457
k = hh-1
3458
# TODO: is the following line necessary?
3459
if k == -1: k = 0
3460
3461
if hb > k:# update hb since we are backtracking
3462
hb = k
3463
# if j == hh, then all nodes lower than our current position are equivalent, so bail out
3464
if j == hh: state = 13; continue
3465
3466
# recall hh: the height of the oldest ancestor of zeta for which Lemma 2.25 is
3467
# satisfied, which implies that all terminal nodes descended from there are equivalent.
3468
# If we are looking at such a node, then the partition at nu[hh] can be used for later
3469
# pruning, so we store its fixed set and a set of representatives of its cells.
3470
if l < self.L-1: l += 1
3471
nu.new_min_cell_reps(hh, Omega, self.Phi_size*l)
3472
nu.fixed_vertices(hh, Phi, Omega, self.Phi_size*l)
3473
3474
state = 12
3475
3476
elif state == 7: # we have just arrived at a terminal node of the search tree T(G, Pi)
3477
# if this is the first terminal node, go directly to 18, to
3478
# process zeta
3479
if h == -1: state = 18; continue
3480
3481
# hzf is the extremal height of ancestors of both nu and zeta, so if k < hzf, nu is not
3482
# equivalent to zeta, i.e. there is no automorphism to discover.
3483
if k < hzf__h_zeta: state = 8; continue
3484
3485
nu.get_permutation(zeta, word_gamma, col_gamma)
3486
# print "gamma:", str([[word_gamma[i] for i from 0 <= i < nwords], [col_gamma[i] for i from 0 <= i < ncols]]).replace(' ','')
3487
# print Theta
3488
# if C^gamma == C, the permutation is an automorphism, goto 10
3489
if C.is_automorphism(col_gamma, word_gamma):
3490
state = 10
3491
else:
3492
state = 8
3493
3494
elif state == 8: # we have just ruled out the presence of automorphism and have not yet
3495
# considered whether nu improves on rho
3496
# if qzb < 0, then rho already has larger indicator tuple
3497
if qzb < 0: state = 6; continue
3498
3499
# if Lambda[k] > zb[k] or nu is shorter than rho, then we have an improvement for rho
3500
if (qzb > 0) or (k < k_rho): state = 9; continue
3501
3502
# now Lambda[k] == zb[k] and k == k_rho, so we appeal to an enumeration:
3503
j = nu.cmp(rho, C)
3504
# if C(nu) > C(rho), we have a new label, goto 9
3505
if j > 0: state = 9; continue
3506
3507
# if C(nu) < C(rho), no new label, goto 6
3508
if j < 0: state = 6; continue
3509
3510
# if C(nu) == C(rho), get the automorphism and goto 10
3511
rho.get_permutation(nu, word_gamma, col_gamma)
3512
# print "gamma:", str([[word_gamma[i] for i from 0 <= i < nwords], [col_gamma[i] for i from 0 <= i < ncols]]).replace(' ','')
3513
# print Theta
3514
state = 10
3515
3516
elif state == 9: # nu is a better guess at the canonical label than rho
3517
rho = PartitionStack(nu)
3518
k_rho = k
3519
qzb = 0
3520
hb = k
3521
hzb__h_rho = k
3522
# set zb[k+1] = Infinity
3523
zb__Lambda_rho[k+1] = -1
3524
state = 6
3525
3526
elif state == 10: # we have an automorphism to process
3527
# increment l
3528
if l < self.L-1: l += 1
3529
# store information about the automorphism to Omega and Phi
3530
ii = self.Phi_size*l
3531
jj = 1 + nwords/self.radix
3532
# Omega[ii] = ~(~0 << ncols)
3533
for i from 0 <= i < jj:
3534
Omega[ii+i] = ~0
3535
Phi[ii+i] = 0
3536
if nwords%self.radix:
3537
jj += 1
3538
# Omega[ii+jj-1] = ~((1 << nwords%self.radix) - 1)
3539
# Omega stores the minimum cell representatives
3540
i = 0
3541
while i < ncols:
3542
j = col_gamma[i] # i is a minimum
3543
while j != i: # cell rep,
3544
Omega[ii] ^= (1<<j) # so cancel
3545
j = col_gamma[j] # cellmates
3546
i += 1
3547
while i < ncols and not Omega[ii]&(1<<i): # find minimal element
3548
i += 1 # of next cell
3549
i = 0
3550
jj = self.radix
3551
while i < nwords:
3552
j = word_gamma[i]
3553
while j != i:
3554
Omega[ii+1+j/jj] ^= (1<<(j%jj))
3555
j = word_gamma[j]
3556
i += 1
3557
while i < nwords and not Omega[ii+1+i/jj]&(1<<(i%jj)):
3558
i += 1
3559
# Phi stores the columns fixed by the automorphism
3560
for i from 0 <= i < ncols:
3561
if col_gamma[i] == i:
3562
Phi[ii] ^= (1 << i)
3563
for i from 0 <= i < nwords:
3564
if word_gamma[i] == i:
3565
Phi[ii+1+i/jj] ^= (1<<(i%jj))
3566
3567
# Now incorporate the automorphism into Theta
3568
j = Theta.merge_perm(col_gamma, word_gamma)
3569
3570
# j stores whether anything happened or not- if not, then the automorphism we have
3571
# discovered is already in the subgroup spanned by the generators we have output
3572
if not j: state = 11; continue
3573
3574
# otherwise, we have a new generator, so record it:
3575
self.record_automorphism(col_gamma, ncols)
3576
# The variable tvc was set to be the minimum element of W[k] the last time the
3577
# algorithm came up to meet zeta. At this point, we were considering the new
3578
# possibilities for descending away from zeta at this level.
3579
# if this is still a minimum cell representative of Theta, even in light
3580
# of this new automorphism, then the current branch off of zeta hasn't been
3581
# found equivalent to one already searched yet, so there may still be a
3582
# better canonical label downward.
3583
if tvc & nu.flag:
3584
i = tvc^nu.flag
3585
if Theta.wd_min_cell_rep[Theta.wd_find(i)] == i:
3586
state = 11; continue
3587
else:
3588
if Theta.col_min_cell_rep[Theta.col_find(tvc)] == tvc:
3589
state = 11; continue
3590
3591
# Otherwise, proceed to where zeta meets nu:
3592
k = h
3593
state = 13
3594
3595
elif state == 11: # We have just found a new automorphism, and deduced that there may
3596
# be a better canonical label below the current branch off of zeta. So go to where
3597
# nu meets rho
3598
k = hb
3599
state = 12
3600
3601
elif state == 12: # Coming here from either state 6 or 11, the algorithm has discovered
3602
# some new information. 11 came from 10, where a new line in Omega and
3603
# Phi was just recorded, and 6 stored information about implicit auto-
3604
# morphisms in Omega and Phi
3605
if e[k] == 1:
3606
# this means that the algorithm has come upward to this position (in state 17)
3607
# before, so we have already intersected W[k] with the bulk of Omega and Phi, but
3608
# we should still catch up with the latest ones
3609
ii = self.Phi_size*l
3610
jj = self.Phi_size*k
3611
j = 1 + nwords/self.radix
3612
if nwords%self.radix:
3613
j += 1
3614
W[jj] &= Omega[ii]
3615
for i from 0 < i < j:
3616
W[jj+i] &= Omega[ii+i]
3617
state = 13
3618
3619
elif state == 13: # hub state
3620
if k == -1: state = -1; continue # exit point
3621
3622
if k > h: state = 17; continue # we are still on the same principal branch from zeta
3623
3624
if k == h: state = 14; continue # update the stabilizer index and check for new splits,
3625
# since we have returned to a partition of zeta
3626
# otherwise k < h, hence we have just backtracked up zeta, and are one level closer to done
3627
h = k
3628
tvc = 0
3629
jj = self.Phi_size*k
3630
if W[jj]:
3631
# print 'W[jj]', W[jj]
3632
# print tvc
3633
while not (1 << tvc) & W[jj]:
3634
tvc += 1
3635
else:
3636
ii = 0
3637
while not W[jj+1+ii]:
3638
ii += 1
3639
while not W[jj+1+ii] & (1 << tvc):
3640
tvc += 1
3641
tvc = (ii*self.radix + tvc) ^ nu.flag
3642
# now tvc points to the minimal cell representative of W[k]
3643
state = 14
3644
3645
elif state == 14: # see if there are any more splits to make from this level of zeta (see state 17)
3646
# print Theta
3647
if v[k]&nu.flag == tvc&nu.flag:
3648
if tvc&nu.flag:
3649
# print 'v[k] is word', v[k]^nu.flag
3650
# print 'tvc is word', tvc^nu.flag
3651
if Theta.wd_find(v[k]^nu.flag) == Theta.wd_find(tvc^nu.flag):
3652
index += 1
3653
# print 'index', index
3654
else:
3655
# print 'v[k] is col', v[k]
3656
# print 'tvc is col', tvc
3657
if Theta.col_find(v[k]) == Theta.col_find(tvc):
3658
index += 1
3659
# print 'index', index
3660
# keep tabs on how many elements are in the same cell of Theta as tvc
3661
# find the next split
3662
jj = self.Phi_size*k
3663
if v[k]&nu.flag:
3664
ii = self.radix
3665
i = (v[k]^nu.flag) + 1
3666
while i < nwords and not (1 << i%ii) & W[jj+1+i/ii]:
3667
i += 1
3668
if i < nwords:
3669
v[k] = i^nu.flag
3670
else:
3671
# there is no new split at this level
3672
state = 16; continue
3673
# new split column better be a minimal representative in Theta, or wasted effort
3674
if Theta.wd_min_cell_rep[Theta.wd_find(i)] == i:
3675
state = 15
3676
else:
3677
state = 14
3678
else:
3679
i = v[k] + 1
3680
while i < ncols and not (1 << i) & W[jj]:
3681
i += 1
3682
if i < ncols:
3683
v[k] = i
3684
else:
3685
# there is no new split at this level
3686
state = 16; continue
3687
# new split column better be a minimal representative in Theta, or wasted effort
3688
# print 'checking whether v[k] is a minimum cell rep of theta'
3689
# print 'Theta.col_find(v[k]) = ', Theta.col_find(v[k])
3690
# print 'Theta.col_min_cell_rep(^)', Theta.col_min_cell_rep[Theta.col_find(v[k])]
3691
# print 'v[k]', v[k]
3692
if Theta.col_min_cell_rep[Theta.col_find(v[k])] == v[k]:
3693
state = 15
3694
else:
3695
state = 14
3696
3697
elif state == 15: # split out the column v[k]
3698
# hh is smallest such that nu[hh] satisfies Lemma 2.25. If it is larger than k+1,
3699
# it must be modified, since we are changing that part
3700
if k + 1 < hh:
3701
hh = k + 1
3702
# hzf is maximal such that indicators line up for nu and zeta
3703
if k < hzf__h_zeta:
3704
hzf__h_zeta = k
3705
# hzb is longest such that nu and rho have the same indicators
3706
if hzb__h_rho >= k:
3707
hzb__h_rho = k
3708
qzb = 0
3709
state = 2
3710
3711
elif state == 16: # backtrack up zeta, updating information about stabilizer vector
3712
jj = self.Phi_size*k
3713
if W[jj]:
3714
i = W[jj]
3715
j = ham_wts[i & 65535] + ham_wts[(i >> 16) & 65535]
3716
else:
3717
i = 0; j = 0
3718
ii = self.radix
3719
while i*ii < nwords:
3720
iii = W[jj+1+i]
3721
j += ham_wts[iii & 65535] + ham_wts[(iii >> 16) & 65535]
3722
i += 1
3723
if j == index and ht == k + 1: ht = k
3724
# print "POINT A, index =", index
3725
self.aut_gp_size *= index
3726
# (POINT A)
3727
index = 0
3728
k -= 1
3729
if hb > k: # update hb since we are backtracking
3730
hb = k
3731
state = 13
3732
3733
elif state == 17: # see if there are any more splits to make from this level of nu (and not zeta)
3734
3735
jjj = self.Phi_size*k
3736
if e[k] == 0: # now is the time to narrow down W[k] by Omega and Phi
3737
# intersect W[k] with each Omega[i] such that v[0]...v[k-1] is in Phi[i]
3738
jj = self.Phi_size*self.L
3739
iii = nwords/self.radix
3740
if nwords%self.radix:
3741
iii += 1
3742
for ii from 0 <= ii < iii:
3743
Phi[jj+ii] = 0
3744
for ii from 0 <= ii < k:
3745
if v[ii]&nu.flag:
3746
i = v[ii]^nu.flag
3747
Phi[jj+1+i/self.radix] ^= (1 << i%self.radix)
3748
else:
3749
Phi[jj] ^= (1 << v[ii])
3750
for i from 0 <= i <= l:
3751
ii = self.Phi_size*i
3752
iiii = 1
3753
for j from 0 <= j < iii:
3754
if Phi[ii + j] & Phi[jj + j] != Phi[jj + j]:
3755
iiii = 0
3756
break
3757
if iiii:
3758
for j from 0 <= j < iii:
3759
W[jjj + j] &= Omega[ii + j]
3760
e[k] = 1
3761
3762
# see if there is a vertex to split out
3763
if nu.flag&v[k]:
3764
i = (v[k]^nu.flag)
3765
while i < nwords:
3766
i += 1
3767
if (1 << i%self.radix) & W[jjj+1+i/self.radix]: break
3768
if i < nwords:
3769
v[k] = i^nu.flag
3770
state = 15; continue
3771
else:
3772
i = v[k]
3773
while i < ncols:
3774
i += 1
3775
if (1 << i) & W[jjj]: break
3776
if i < ncols:
3777
v[k] = i
3778
state = 15; continue
3779
3780
k -= 1
3781
state = 13
3782
3783
elif state == 18: # the first time nu becomes a discrete partition: set up zeta, our "identity" leaf
3784
# initialize counters for zeta:
3785
h = k # zeta[h] == nu[h]
3786
ht = k # nodes descended from zeta[ht] are all equivalent
3787
hzf__h_zeta = k # max such that indicators for zeta and nu agree
3788
zeta = PartitionStack(nu)
3789
for i from 0 <= i < k:
3790
self.base[i] = v[i]
3791
self.base_size = k
3792
if k != self.radix:
3793
self.base[k] = -1
3794
# (POINT B)
3795
k -= 1
3796
rho = PartitionStack(nu)
3797
# initialize counters for rho:
3798
k_rho = k+1 # number of partitions in rho
3799
hzb__h_rho = k # max such that indicators for rho and nu agree - BDM had k+1
3800
hb = k # rho[hb] == nu[hb] - BDM had k+1
3801
qzb = 0 # Lambda[k] == zb[k], so...
3802
state = 13
3803
3804
# end big while loop
3805
rho.find_basis(ham_wts)
3806
for i from 0 <= i < ncols:
3807
self.labeling[rho.col_ents[i]] = i
3808
for i from 0 <= i < 2*nrows:
3809
self.labeling[i+ncols] = rho.basis_locations[i]
3810
3811
def put_in_canonical_form(self, BinaryCode B):
3812
"""
3813
Puts the code into canonical form.
3814
3815
Canonical form is obtained by performing row reduction, permuting the
3816
pivots to the front so that the generator matrix is of the form: the
3817
identity matrix augmented to the right by arbitrary data.
3818
3819
EXAMPLE:
3820
sage: from sage.coding.binary_code import *
3821
sage: BC = BinaryCodeClassifier()
3822
sage: B = BinaryCode(codes.ExtendedBinaryGolayCode().gen_mat())
3823
sage: B.apply_permutation(range(24,-1,-1))
3824
sage: B
3825
Binary [24,12] linear code, generator matrix
3826
[011000111010100000000000]
3827
[001001001111100000000001]
3828
[011010100101100000000010]
3829
[001101110001100000000100]
3830
[010011011001100000001000]
3831
[010110110011000000010000]
3832
[011101100110000000100000]
3833
[000011110110100001000000]
3834
[000111101101000010000000]
3835
[001111011010000100000000]
3836
[010110001110101000000000]
3837
[011100011101010000000000]
3838
sage: BC.put_in_canonical_form(B)
3839
sage: B
3840
Binary [24,12] linear code, generator matrix
3841
[100000000000001100111001]
3842
[010000000000001010001111]
3843
[001000000000001111010010]
3844
[000100000000010110101010]
3845
[000010000000010110010101]
3846
[000001000000010001101101]
3847
[000000100000011000110110]
3848
[000000010000011111001001]
3849
[000000001000010101110011]
3850
[000000000100010011011110]
3851
[000000000010001011110101]
3852
[000000000001001101101110]
3853
3854
"""
3855
aut_gp_gens, labeling, size, base = self._aut_gp_and_can_label(B)
3856
B._apply_permutation_to_basis(labeling)
3857
B.put_in_std_form()
3858
3859
def generate_children(self, BinaryCode B, int n, int d=2):
3860
"""
3861
Use canonical augmentation to generate children of the code B.
3862
3863
INPUT:
3864
B -- a BinaryCode
3865
n -- limit on the degree of the code
3866
d -- test whether new vector has weight divisible by d. If d==4, this
3867
ensures that all doubly-even canonically augmented children are
3868
generated.
3869
3870
EXAMPLE:
3871
sage: from sage.coding.binary_code import *
3872
sage: BC = BinaryCodeClassifier()
3873
sage: B = BinaryCode(Matrix(GF(2), [[1,1,1,1]]))
3874
sage: BC.generate_children(B, 6, 4)
3875
[
3876
[1 1 1 1 0 0]
3877
[0 1 0 1 1 1]
3878
]
3879
3880
NOTE:
3881
The function self_orthogonal_binary_codes makes heavy use of this
3882
function.
3883
3884
MORE EXAMPLES:
3885
sage: soc_iter = self_orthogonal_binary_codes(12, 6, 4)
3886
sage: L = list(soc_iter)
3887
sage: for n in range(0, 13):
3888
... s = 'n=%2d : '%n
3889
... for k in range(1,7):
3890
... s += '%3d '%len([C for C in L if C.length() == n and C.dimension() == k])
3891
... print s
3892
n= 0 : 0 0 0 0 0 0
3893
n= 1 : 0 0 0 0 0 0
3894
n= 2 : 0 0 0 0 0 0
3895
n= 3 : 0 0 0 0 0 0
3896
n= 4 : 1 0 0 0 0 0
3897
n= 5 : 0 0 0 0 0 0
3898
n= 6 : 0 1 0 0 0 0
3899
n= 7 : 0 0 1 0 0 0
3900
n= 8 : 1 1 1 1 0 0
3901
n= 9 : 0 0 0 0 0 0
3902
n=10 : 0 1 1 1 0 0
3903
n=11 : 0 0 1 1 0 0
3904
n=12 : 1 2 3 4 2 0
3905
3906
"""
3907
cdef BinaryCode m
3908
cdef codeword *ortho_basis, *B_can_lab, current, swap
3909
cdef codeword word, temp, gate, nonzero_gate, orbit, bwd, k_gate
3910
cdef codeword *temp_basis, *orbit_checks, orb_chx_size, orb_chx_shift, radix_gate
3911
cdef WordPermutation *gwp, *hwp, *can_lab, *can_lab_inv
3912
cdef WordPermutation **parent_generators
3913
cdef BinaryCode B_aug
3914
cdef int i, ii, j, jj, ij, k = 0, parity, combo, num_gens
3915
cdef int base_size, *multimod2_index, row
3916
cdef int *ham_wts = self.ham_wts
3917
cdef int *num_inner_gens, *num_outer_gens, *v, log_2_radix
3918
cdef bint bingo, bingo2, bingo3
3919
3920
B.put_in_std_form()
3921
ortho_basis = expand_to_ortho_basis(B, n) # modifies B!
3922
3923
# print 'parent:'
3924
# print B
3925
aut_gp_gens, labeling, size, base = self._aut_gp_and_can_label(B)
3926
B_can_lab = <codeword *> sage_malloc(B.nrows * sizeof(codeword))
3927
can_lab = create_word_perm(labeling[:B.ncols])
3928
if B_can_lab is NULL or can_lab is NULL:
3929
sage_free(ortho_basis)
3930
if B_can_lab is not NULL:
3931
sage_free(B_can_lab)
3932
if can_lab is not NULL:
3933
sage_free(can_lab)
3934
raise MemoryError()
3935
for i from 0 <= i < B.nrows:
3936
B_can_lab[i] = permute_word_by_wp(can_lab, B.basis[i])
3937
dealloc_word_perm(can_lab)
3938
row = 0
3939
current = 1
3940
while row < B.nrows:
3941
i = row
3942
while i < B.nrows and not B_can_lab[i] & current:
3943
i += 1
3944
if i < B.nrows:
3945
if i != row:
3946
swap = B_can_lab[row]
3947
B_can_lab[row] = B_can_lab[i]
3948
B_can_lab[i] = swap
3949
for j from 0 <= j < row:
3950
if B_can_lab[j] & current:
3951
B_can_lab[j] ^= B_can_lab[row]
3952
for j from row < j < B.nrows:
3953
if B_can_lab[j] & current:
3954
B_can_lab[j] ^= B_can_lab[row]
3955
row += 1
3956
current = current << 1
3957
num_gens = len(aut_gp_gens)
3958
base_size = len(base)
3959
3960
# print 'gens:'
3961
# for g in aut_gp_gens:
3962
# print g
3963
3964
parent_generators = <WordPermutation **> sage_malloc( len(aut_gp_gens) * sizeof(WordPermutation*) )
3965
temp_basis = <codeword *> sage_malloc( self.radix * sizeof(codeword) )
3966
3967
output = []
3968
3969
3970
for i from 0 <= i < len(aut_gp_gens):
3971
parent_generators[i] = create_word_perm(aut_gp_gens[i] + range(B.ncols, n))
3972
3973
word = 0
3974
while ortho_basis[k] & (((<codeword>1) << B.ncols) - 1):
3975
k += 1
3976
j = k
3977
while ortho_basis[j]:
3978
word ^= ortho_basis[j]
3979
j += 1
3980
3981
# print "ortho_basis:"
3982
# for i from 0 <= i < k:
3983
# print ''.join(reversed(Integer(ortho_basis[i]).binary().zfill(n)))
3984
# print '-'
3985
# for i from k <= i < j:
3986
# print ''.join(reversed(Integer(ortho_basis[i]).binary().zfill(n)))
3987
# print 'word:'
3988
# print ''.join(reversed(Integer(word).binary().zfill(n)))
3989
3990
log_2_radix = 0
3991
while ((<codeword>1) << log_2_radix) < self.radix:
3992
log_2_radix += 1
3993
# now we assume (<codeword>1 << log_2_radix) == self.radix
3994
if k < log_2_radix:
3995
orb_chx_size = 0
3996
else:
3997
orb_chx_size = k - log_2_radix
3998
orbit_checks = <codeword *> sage_malloc( ((<codeword>1) << orb_chx_size) * sizeof(codeword) )
3999
if orbit_checks is NULL:
4000
raise MemoryError()
4001
for temp from 0 <= temp < ((<codeword>1) << orb_chx_size):
4002
orbit_checks[temp] = 0
4003
4004
4005
combo = 0
4006
parity = 0
4007
gate = (<codeword>1 << B.nrows) - 1
4008
k_gate = (<codeword>1 << k) - 1
4009
nonzero_gate = ( (<codeword>1 << (n-B.ncols)) - 1 ) << B.ncols
4010
radix_gate = (((<codeword>1) << log_2_radix) - 1)
4011
# print 'gate:', ''.join(reversed(Integer(gate).binary().zfill(n)))
4012
# print 'gate:', ''.join(reversed(Integer(nonzero_gate).binary().zfill(n)))
4013
while 1:
4014
# print ' while 1'
4015
# print ' ' + ''.join(reversed(Integer(word).binary().zfill(n)))
4016
if nonzero_gate & word == nonzero_gate and \
4017
(ham_wts[word & 65535] + ham_wts[(word >> 16) & 65535])%d == 0:
4018
# print ''.join(reversed(Integer(word).binary().zfill(n)))
4019
temp = (word >> B.nrows) & ((<codeword>1 << k) - 1)
4020
# print "if not orbit_checks[temp >> log_2_radix] & ((<codeword>1) << (temp & radix_gate)):"
4021
# print temp >> log_2_radix
4022
# print temp & radix_gate
4023
if not orbit_checks[temp >> log_2_radix] & ((<codeword>1) << (temp & radix_gate)):
4024
B_aug = BinaryCode(B, word)
4025
# print 'child:'
4026
# print B_aug
4027
# print 'canonically labeling child'
4028
aug_aut_gp_gens, aug_labeling, aug_size, aug_base = self._aut_gp_and_can_label(B_aug)
4029
# print 'done canonically labeling child'
4030
# check if (B, B_aug) ~ (m(B_aug), B_aug)
4031
4032
can_lab = create_word_perm(aug_labeling[:n])
4033
# print 'relabeling:'
4034
# print [self.labeling[j] for j from 0 <= j < n]
4035
can_lab_inv = create_inv_word_perm(can_lab)
4036
for j from 0 <= j < B_aug.nrows:
4037
temp_basis[j] = permute_word_by_wp(can_lab, B_aug.basis[j])
4038
# print 'temp_basis:'
4039
# for j from 0 <= j < B_aug.nrows:
4040
# print ''.join(reversed(Integer(temp_basis[j]).binary().zfill(n)))
4041
4042
# row reduce to get canonical label
4043
i = 0
4044
j = 0
4045
while j < B_aug.nrows:
4046
ii = j
4047
while ii < B_aug.nrows and not temp_basis[ii] & (<codeword>1 << i):
4048
ii += 1
4049
if ii != B_aug.nrows:
4050
if ii != j:
4051
swap = temp_basis[ii]
4052
temp_basis[ii] = temp_basis[j]
4053
temp_basis[j] = swap
4054
for jj from 0 <= jj < j:
4055
if temp_basis[jj] & (<codeword>1 << i):
4056
temp_basis[jj] ^= temp_basis[j]
4057
for jj from j < jj < B_aug.nrows:
4058
if temp_basis[jj] & (<codeword>1 << i):
4059
temp_basis[jj] ^= temp_basis[j]
4060
j += 1
4061
i += 1
4062
# done row reduction
4063
4064
# print 'temp_basis:'
4065
for j from 0 <= j < B.nrows:
4066
temp_basis[j] = permute_word_by_wp(can_lab_inv, temp_basis[j])
4067
# print ''.join(reversed(Integer(temp_basis[j]).binary().zfill(n)))
4068
from sage.matrix.constructor import matrix
4069
from sage.rings.all import ZZ
4070
from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroupElement
4071
from sage.interfaces.gap import gap
4072
rs = []
4073
for i from 0 <= i < B.nrows:
4074
r = []
4075
for j from 0 <= j < n:
4076
r.append((((<codeword>1)<<j)&temp_basis[i])>>j)
4077
rs.append(r)
4078
m = BinaryCode(matrix(ZZ, rs))
4079
# print 'm:'
4080
# print m
4081
m_aut_gp_gens, m_labeling, m_size, m_base = self._aut_gp_and_can_label(m)
4082
from sage.rings.arith import factorial
4083
if True:#size*factorial(n-B.ncols) == m_size:
4084
# print 'in if'
4085
# print 'm_aut_gp_gens:', m_aut_gp_gens
4086
if len(m_aut_gp_gens) == 0:
4087
aut_m = PermutationGroup([()])
4088
else:
4089
aut_m = PermutationGroup([PermutationGroupElement([a+1 for a in g]) for g in m_aut_gp_gens])
4090
# print 'aut_m:', aut_m
4091
# print 'aug_aut_gp_gens:', aug_aut_gp_gens
4092
if len(aug_aut_gp_gens) == 0:
4093
aut_B_aug = PermutationGroup([()])
4094
else:
4095
aut_B_aug = PermutationGroup([PermutationGroupElement([a+1 for a in g]) for g in aug_aut_gp_gens])
4096
# print 'aut_B_aug:', aut_B_aug
4097
H = aut_m._gap_(gap).Intersection2(aut_B_aug._gap_(gap))
4098
# print 'H:', H
4099
rt_transversal = list(gap('List(RightTransversal( %s,%s ));'\
4100
%(str(aut_B_aug.__interface[gap]),str(H))))
4101
# print 'rt_transversal:', rt_transversal
4102
rt_transversal = [PermutationGroupElement(g) for g in rt_transversal if str(g) != '()']
4103
rt_transversal = [[a-1 for a in g.domain()] for g in rt_transversal]
4104
rt_transversal = [g + range(len(g), n) for g in rt_transversal]
4105
rt_transversal.append(range(n))
4106
# print 'rt_transversal:', rt_transversal
4107
bingo2 = 0
4108
for coset_rep in rt_transversal:
4109
# print 'coset_rep:'
4110
# print coset_rep
4111
hwp = create_word_perm(coset_rep)
4112
#hwp = create_inv_word_perm(gwp) # since we want a left transversal
4113
#dealloc_word_perm(gwp)
4114
bingo2 = 1
4115
for j from 0 <= j < B.nrows:
4116
temp = permute_word_by_wp(hwp, temp_basis[j])
4117
if temp != B.words[temp & gate]:
4118
bingo2 = 0
4119
dealloc_word_perm(hwp)
4120
break
4121
if bingo2:
4122
dealloc_word_perm(hwp)
4123
break
4124
if bingo2:
4125
from sage.matrix.constructor import Matrix
4126
from sage.rings.all import GF
4127
M = Matrix(GF(2), B_aug.nrows, B_aug.ncols)
4128
for i from 0 <= i < B_aug.ncols:
4129
for j from 0 <= j < B_aug.nrows:
4130
M[j,i] = B_aug.is_one(1 << j, i)
4131
output.append(M)
4132
# print "ACCEPT"
4133
dealloc_word_perm(can_lab)
4134
dealloc_word_perm(can_lab_inv)
4135
#...
4136
# print ' orbit_checks:'
4137
# for temp from 0 <= temp < ((<codeword>1) << orb_chx_size):
4138
# print ' ' + ''.join(reversed(Integer(orbit_checks[temp]).binary().zfill(n)))
4139
orbits = [word]
4140
j = 0
4141
while j < len(orbits):
4142
for i from 0 <= i < len(aut_gp_gens):
4143
# print ' i', i
4144
temp = <codeword> orbits[j]
4145
temp = permute_word_by_wp(parent_generators[i], temp)
4146
# print ' temp:', ''.join(reversed(Integer(temp).binary().zfill(n)))
4147
temp ^= B.words[temp & gate]
4148
# print ' temp:', ''.join(reversed(Integer(temp).binary().zfill(n)))
4149
if temp not in orbits:
4150
orbits.append(temp)
4151
j += 1
4152
for temp in orbits:
4153
temp = (temp >> B.nrows) & k_gate
4154
# print ' temp:', temp
4155
# print ' ', temp >> log_2_radix
4156
# print ' ', ((<codeword>1) << (temp & radix_gate))
4157
orbit_checks[temp >> log_2_radix] |= ((<codeword>1) << (temp & radix_gate))
4158
4159
4160
parity ^= 1
4161
i = 0
4162
if not parity:
4163
while not combo & (1 << i): i += 1
4164
i += 1
4165
if i == k: break
4166
else:
4167
combo ^= (1 << i)
4168
word ^= ortho_basis[i]
4169
4170
for i from 0 <= i < len(aut_gp_gens):
4171
dealloc_word_perm(parent_generators[i])
4172
sage_free(B_can_lab)
4173
sage_free(parent_generators)
4174
sage_free(orbit_checks)
4175
sage_free(ortho_basis)
4176
sage_free(temp_basis)
4177
return output
4178
4179
4180
4181
4182