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