Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/coding/linear_code.py
4034 views
1
# -*- coding: utf-8 -*-
2
r"""
3
Linear Codes
4
5
VERSION: 1.2
6
7
Let `F` be a finite field. Here, we will denote the finite field with `q`
8
elements by `\GF{q}`. A subspace of `F^n` (with the standard basis) is
9
called a linear code of length `n`. If its dimension is denoted `k` then we
10
typically store a basis of `C` as a `k \times n` matrix, with rows the basis
11
vectors. It is called the generator matrix of `C`. The rows of the parity
12
check matrix of `C` are a basis for the code,
13
14
.. math::
15
16
C^* = \{ v \in GF(q)^n\ |\ v\cdot c = 0,\ for \ all\ c \in C \},
17
18
called the dual space of `C`.
19
20
If `F=\GF{2}` then `C` is called a binary code. If `F = \GF{q}` then `C` is
21
called a `q`-ary code. The elements of a code `C` are called codewords.
22
23
The symmetric group `S_n` acts on `F^n` by permuting coordinates. If an
24
element `p \in S_n` sends a code `C` of length `n` to itself (in other words,
25
every codeword of `C` is sent to some other codeword of `C`) then `p` is
26
called a permutation automorphism of `C`. The (permutation) automorphism
27
group is denoted `Aut(C)`.
28
29
This file contains
30
31
#. LinearCode class definition; LinearCodeFromVectorspace conversion function,
32
33
#. The spectrum (weight distribution), covering_radius, minimum distance
34
programs (calling Steve Linton's or CJ Tjhal's C programs),
35
characteristic_function, and several implementations of the Duursma zeta
36
function (sd_zeta_polynomial, zeta_polynomial, zeta_function,
37
chinen_polynomial, for example),
38
39
#. interface with best_known_linear_code_www (interface with codetables.de
40
since A. Brouwer's online tables have been disabled),
41
bounds_minimum_distance which call tables in GUAVA (updated May 2006)
42
created by Cen Tjhai instead of the online internet tables,
43
44
#. gen_mat, gen_mat_systematic, information_set, list, check_mat, decode,
45
dual_code, extended_code, shortened, punctured, genus, binomial_moment,
46
and divisor methods for LinearCode,
47
48
#. Boolean-valued functions such as "==", is_self_dual, is_self_orthogonal,
49
is_subcode, is_permutation_automorphism, is_permutation_equivalent (which
50
interfaces with Robert Miller's partition refinement code),
51
52
#. permutation methods: is_permutation_automorphism,
53
permutation_automorphism_group, permuted_code, standard_form,
54
module_composition_factors,
55
56
#. design-theoretic methods: assmus_mattson_designs (implementing
57
Assmus-Mattson Theorem),
58
59
#. code constructions, such as HammingCode and ToricCode, are in a separate
60
``code_constructions.py`` module; in the separate ``guava.py`` module, you
61
will find constructions, such as RandomLinearCodeGuava and
62
BinaryReedMullerCode, wrapped from the corresponding GUAVA codes.
63
64
EXAMPLES::
65
66
sage: MS = MatrixSpace(GF(2),4,7)
67
sage: G = MS([[1,1,1,0,0,0,0], [1,0,0,1,1,0,0], [0,1,0,1,0,1,0], [1,1,0,1,0,0,1]])
68
sage: C = LinearCode(G)
69
sage: C.basis()
70
[(1, 1, 1, 0, 0, 0, 0),
71
(1, 0, 0, 1, 1, 0, 0),
72
(0, 1, 0, 1, 0, 1, 0),
73
(1, 1, 0, 1, 0, 0, 1)]
74
sage: c = C.basis()[1]
75
sage: c in C
76
True
77
sage: c.nonzero_positions()
78
[0, 3, 4]
79
sage: c.support()
80
[0, 3, 4]
81
sage: c.parent()
82
Vector space of dimension 7 over Finite Field of size 2
83
84
To be added:
85
86
#. More wrappers
87
88
#. GRS codes and special decoders.
89
90
#. `P^1` Goppa codes and group actions on `P^1` RR space codes.
91
92
REFERENCES:
93
94
- [HP] W. C. Huffman and V. Pless, Fundamentals of error-correcting codes,
95
Cambridge Univ. Press, 2003.
96
97
- [Gu] GUAVA manual, http://www.gap-system.org/Packages/guava.html
98
99
AUTHORS:
100
101
- David Joyner (2005-11-22, 2006-12-03): initial version
102
103
- William Stein (2006-01-23): Inclusion in Sage
104
105
- David Joyner (2006-01-30, 2006-04): small fixes
106
107
- David Joyner (2006-07): added documentation, group-theoretical methods,
108
ToricCode
109
110
- David Joyner (2006-08): hopeful latex fixes to documentation, added list and
111
__iter__ methods to LinearCode and examples, added hamming_weight function,
112
fixed random method to return a vector, TrivialCode, fixed subtle bug in
113
dual_code, added galois_closure method, fixed mysterious bug in
114
permutation_automorphism_group (GAP was over-using "G" somehow?)
115
116
- David Joyner (2006-08): hopeful latex fixes to documentation, added
117
CyclicCode, best_known_linear_code, bounds_minimum_distance,
118
assmus_mattson_designs (implementing Assmus-Mattson Theorem).
119
120
- David Joyner (2006-09): modified decode syntax, fixed bug in
121
is_galois_closed, added LinearCode_from_vectorspace, extended_code,
122
zeta_function
123
124
- Nick Alexander (2006-12-10): factor GUAVA code to guava.py
125
126
- David Joyner (2007-05): added methods punctured, shortened, divisor,
127
characteristic_polynomial, binomial_moment, support for
128
LinearCode. Completely rewritten zeta_function (old version is now
129
zeta_function2) and a new function, LinearCodeFromVectorSpace.
130
131
- David Joyner (2007-11): added zeta_polynomial, weight_enumerator,
132
chinen_polynomial; improved best_known_code; made some pythonic revisions;
133
added is_equivalent (for binary codes)
134
135
- David Joyner (2008-01): fixed bug in decode reported by Harald Schilly,
136
(with Mike Hansen) added some doctests.
137
138
- David Joyner (2008-02): translated standard_form, dual_code to Python.
139
140
- David Joyner (2008-03): translated punctured, shortened, extended_code,
141
random (and renamed random to random_element), deleted zeta_function2,
142
zeta_function3, added wrapper automorphism_group_binary_code to Robert
143
Miller's code), added direct_sum_code, is_subcode, is_self_dual,
144
is_self_orthogonal, redundancy_matrix, did some alphabetical reorganizing
145
to make the file more readable. Fixed a bug in permutation_automorphism_group
146
which caused it to crash.
147
148
- David Joyner (2008-03): fixed bugs in spectrum and zeta_polynomial, which
149
misbehaved over non-prime base rings.
150
151
- David Joyner (2008-10): use CJ Tjhal's MinimumWeight if char = 2 or 3 for
152
min_dist; add is_permutation_equivalent and improve
153
permutation_automorphism_group using an interface with Robert Miller's code;
154
added interface with Leon's code for the spectrum method.
155
156
- David Joyner (2009-02): added native decoding methods (see module_decoder.py)
157
158
- David Joyner (2009-05): removed dependence on Guava, allowing it to be an
159
option. Fixed errors in some docstrings.
160
161
- Kwankyu Lee (2010-01): added methods gen_mat_systematic, information_set, and
162
magma interface for linear codes.
163
164
- Niles Johnson (2010-08): Trac #3893: ``random_element()`` should pass on ``*args`` and ``**kwds``.
165
166
TESTS::
167
168
sage: MS = MatrixSpace(GF(2),4,7)
169
sage: G = MS([[1,1,1,0,0,0,0], [1,0,0,1,1,0,0], [0,1,0,1,0,1,0], [1,1,0,1,0,0,1]])
170
sage: C = LinearCode(G)
171
sage: C == loads(dumps(C))
172
True
173
"""
174
175
#******************************************************************************
176
# Copyright (C) 2005 David Joyner <[email protected]>
177
# 2006 William Stein <[email protected]>
178
#
179
# Distributed under the terms of the GNU General Public License (GPL),
180
# version 2 or later (at your preference).
181
#
182
# http://www.gnu.org/licenses/
183
#******************************************************************************
184
185
import urllib
186
import sage.modules.free_module as fm
187
import sage.modules.module as module
188
from sage.interfaces.all import gap
189
from sage.rings.finite_rings.constructor import FiniteField as GF
190
from sage.groups.perm_gps.permgroup import PermutationGroup
191
from sage.matrix.matrix_space import MatrixSpace
192
from sage.matrix.constructor import Matrix
193
from sage.modules.free_module_element import vector
194
from sage.rings.arith import GCD, rising_factorial, binomial
195
from sage.groups.all import SymmetricGroup
196
from sage.misc.misc import prod
197
from sage.misc.functional import log, is_even
198
from sage.rings.rational_field import QQ
199
from sage.structure.parent_gens import ParentWithGens
200
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
201
from sage.rings.fraction_field import FractionField
202
from sage.rings.integer_ring import IntegerRing
203
from sage.rings.integer import Integer
204
from sage.combinat.set_partition import SetPartitions
205
from sage.misc.randstate import current_randstate
206
from sage.misc.decorators import rename_keyword
207
208
ZZ = IntegerRing()
209
VectorSpace = fm.VectorSpace
210
211
####################### coding theory functions ###############################
212
213
def hamming_weight(v):
214
r"""
215
Returns the Hamming weight of the vector ``v``, which is the number of
216
non-zero entries.
217
218
INPUT:
219
220
- ``v`` - Vector
221
222
OUTPUT:
223
224
- Integer, the Hamming weight of ``v``
225
226
EXAMPLES::
227
228
sage: hamming_weight(vector(GF(2),[0,0,1]))
229
1
230
sage: hamming_weight(vector(GF(2),[0,0,0]))
231
0
232
"""
233
return len(v.nonzero_positions())
234
235
def code2leon(C):
236
r"""
237
Writes a file in Sage's temp directory representing the code C, returning
238
the absolute path to the file. This is the Sage translation of the
239
GuavaToLeon command in Guava's codefun.gi file.
240
241
INPUT:
242
243
- ``C`` - a linear code (over GF(p), p < 11)
244
245
OUTPUT:
246
247
- Absolute path to the file written
248
249
EXAMPLES::
250
251
sage: C = HammingCode(3,GF(2)); C
252
Linear code of length 7, dimension 4 over Finite Field of size 2
253
sage: file_loc = sage.coding.linear_code.code2leon(C)
254
sage: f = open(file_loc); print f.read()
255
LIBRARY code;
256
code=seq(2,4,7,seq(
257
1,0,0,0,0,1,1,
258
0,1,0,0,1,0,1,
259
0,0,1,0,1,1,0,
260
0,0,0,1,1,1,1
261
));
262
FINISH;
263
sage: f.close()
264
265
"""
266
from sage.misc.misc import tmp_filename
267
F = C.base_ring()
268
p = F.order() # must be prime and <11
269
s = "LIBRARY code;\n"+"code=seq(%s,%s,%s,seq(\n"%(p,C.dimension(),C.length())
270
Gr = [str(r)[1:-1].replace(" ","") for r in C.gen_mat().rows()]
271
s += ",\n".join(Gr) + "\n));\nFINISH;"
272
file_loc = tmp_filename()
273
f = open(file_loc,"w")
274
f.write(s)
275
f.close()
276
return file_loc
277
278
def wtdist_gap(Gmat, n, F):
279
r"""
280
INPUT:
281
282
- ``Gmat`` - String representing a GAP generator matrix G of a linear code
283
284
- ``n`` - Integer greater than 1, representing the number of columns of G
285
(i.e., the length of the linear code)
286
287
- ``F`` - Finite field (in Sage), base field the code
288
289
OUTPUT:
290
291
- Spectrum of the associated code
292
293
EXAMPLES::
294
295
sage: Gstr = 'Z(2)*[[1,1,1,0,0,0,0], [1,0,0,1,1,0,0], [0,1,0,1,0,1,0], [1,1,0,1,0,0,1]]'
296
sage: F = GF(2)
297
sage: sage.coding.linear_code.wtdist_gap(Gstr, 7, F)
298
[1, 0, 0, 7, 7, 0, 0, 1]
299
300
Here ``Gstr`` is a generator matrix of the Hamming [7,4,3] binary code.
301
302
ALGORITHM:
303
304
Uses C programs written by Steve Linton in the kernel of GAP, so is fairly
305
fast.
306
307
AUTHORS:
308
309
- David Joyner (2005-11)
310
"""
311
G = gap(Gmat)
312
q = F.order()
313
k = gap(F)
314
#C = G.GeneratorMatCode(k)
315
#n = int(C.WordLength())
316
z = 'Z(%s)*%s'%(q, [0]*n) # GAP zero vector as a string
317
_ = gap.eval("w:=DistancesDistributionMatFFEVecFFE("+Gmat+", GF("+str(q)+"),"+z+")")
318
# for some reason, this commented code doesn't work:
319
#dist0 = gap("DistancesDistributionMatFFEVecFFE("+Gmat+", GF("+str(q)+"),"+z+")")
320
#v0 = dist0._matrix_(F)
321
#print dist0,v0
322
#d = G.DistancesDistributionMatFFEVecFFE(k, z)
323
v = [eval(gap.eval("w["+str(i)+"]")) for i in range(1,n+2)] # because GAP returns vectors in compressed form
324
return v
325
326
@rename_keyword(deprecated='Sage version 4.6', method="algorithm")
327
def min_wt_vec_gap(Gmat, n, k, F, algorithm=None):
328
r"""
329
Returns a minimum weight vector of the code generated by ``Gmat``.
330
331
Uses C programs written by Steve Linton in the kernel of GAP, so is fairly
332
fast. The option ``algorithm="guava"`` requires Guava. The default algorithm
333
requires GAP but not Guava.
334
335
INPUT:
336
337
- ``Gmat`` - String representing a GAP generator matrix G of a linear code
338
- n - Length of the code generated by G
339
- k - Dimension of the code generated by G
340
- F - Base field
341
342
OUTPUT:
343
344
- Minimum weight vector of the code generated by ``Gmat``
345
346
REMARKS:
347
348
- The code in the default case allows one (for free) to also compute the
349
message vector `m` such that `m\*G = v`, and the (minimum) distance, as
350
a triple. however, this output is not implemented.
351
- The binary case can presumably be done much faster using Robert Miller's
352
code (see the docstring for the spectrum method). This is also not (yet)
353
implemented.
354
355
EXAMPLES::
356
357
sage: Gstr = "Z(2)*[[1,1,1,0,0,0,0], [1,0,0,1,1,0,0], [0,1,0,1,0,1,0], [1,1,0,1,0,0,1]]"
358
sage: sage.coding.linear_code.min_wt_vec_gap(Gstr,7,4,GF(2))
359
(0, 1, 0, 1, 0, 1, 0)
360
361
This output is different but still a minimum weight vector:
362
363
sage: sage.coding.linear_code.min_wt_vec_gap(Gstr,7,4,GF(2),algorithm="guava") # requires optional GAP package Guava
364
(0, 0, 1, 0, 1, 1, 0)
365
366
Here ``Gstr`` is a generator matrix of the Hamming [7,4,3] binary code.
367
368
AUTHORS:
369
370
- David Joyner (11-2005)
371
"""
372
current_randstate().set_seed_gap()
373
if algorithm=="guava":
374
from sage.interfaces.gap import gfq_gap_to_sage
375
gap.eval("G:="+Gmat)
376
C = gap(Gmat).GeneratorMatCode(F)
377
#n = int(C.length())
378
cg = C.MinimumDistanceCodeword()
379
c = [gfq_gap_to_sage(cg[j],F) for j in range(1,n+1)]
380
V = VectorSpace(F,n)
381
return V(c)
382
qstr = str(F.order())
383
zerovec = [0 for i in range(n)]
384
zerovecstr = "Z("+qstr+")*"+str(zerovec)
385
all = []
386
for i in range(1,k+1):
387
P = gap.eval("P:=AClosestVectorCombinationsMatFFEVecFFECoords("+Gmat+", GF("+qstr+"),"+zerovecstr+","+str(i)+","+str(0)+"); d:=WeightVecFFE(P[1])")
388
v = gap("[List(P[1], i->i)]")
389
m = gap("[List(P[2], i->i)]")
390
dist = gap.eval("d")
391
#print v,m,dist
392
#print [gap.eval("v["+str(i+1)+"]") for i in range(n)]
393
all.append([v._matrix_(F),m._matrix_(F),int(dist)])
394
ans = all[0]
395
for x in all:
396
if x[2]<ans[2] and x[2]>0:
397
ans = x
398
#print ans[0], ans[0].parent()
399
return vector(F,[x for x in ans[0].rows()[0]]) # ugly 1xn matrix->vector coercion!
400
401
def best_known_linear_code(n, k, F):
402
r"""
403
Returns the best known (as of 11 May 2006) linear code of length ``n``,
404
dimension ``k`` over field ``F``. The function uses the tables described
405
in ``bounds_minimum_distance`` to construct this code.
406
407
This does not require an internet connection.
408
409
EXAMPLES::
410
411
sage: best_known_linear_code(10,5,GF(2)) # long time and requires optional GAP package Guava
412
Linear code of length 10, dimension 5 over Finite Field of size 2
413
sage: gap.eval("C:=BestKnownLinearCode(10,5,GF(2))") # long time and requires optional GAP package Guava
414
'a linear [10,5,4]2..4 shortened code'
415
416
This means that best possible binary linear code of length 10 and
417
dimension 5 is a code with minimum distance 4 and covering radius
418
somewhere between 2 and 4. Use ``minimum_distance_why(10,5,GF(2))`` or
419
``print bounds_minimum_distance(10,5,GF(2))`` for further details.
420
"""
421
q = F.order()
422
C = gap("BestKnownLinearCode(%s,%s,GF(%s))"%(n,k,q))
423
G = C.GeneratorMat()
424
k = G.Length()
425
n = G[1].Length()
426
Gs = G._matrix_(F)
427
MS = MatrixSpace(F,k,n)
428
return LinearCode(MS(Gs))
429
#return gap.eval("BestKnownLinearCode(%s,%s,GF(%s))"%(n,k,q))
430
431
def best_known_linear_code_www(n, k, F, verbose=False):
432
r"""
433
Explains the construction of the best known linear code over GF(q) with
434
length n and dimension k, courtesy of the www page
435
http://www.codetables.de/.
436
437
INPUT:
438
439
- ``n`` - Integer, the length of the code
440
441
- ``k`` - Integer, the dimension of the code
442
443
- ``F`` - Finite field, of order 2, 3, 4, 5, 7, 8, or 9
444
445
- ``verbose`` - Bool (default: ``False``)
446
447
OUTPUT:
448
449
450
- Text about why the bounds are as given
451
452
EXAMPLES::
453
454
sage: L = best_known_linear_code_www(72, 36, GF(2)) # requires internet, optional
455
sage: print L # requires internet, optional
456
Construction of a linear code
457
[72,36,15] over GF(2):
458
[1]: [73, 36, 16] Cyclic Linear Code over GF(2)
459
CyclicCode of length 73 with generating polynomial x^37 + x^36 + x^34 +
460
x^33 + x^32 + x^27 + x^25 + x^24 + x^22 + x^21 + x^19 + x^18 + x^15 + x^11 +
461
x^10 + x^8 + x^7 + x^5 + x^3 + 1
462
[2]: [72, 36, 15] Linear Code over GF(2)
463
Puncturing of [1] at 1
464
last modified: 2002-03-20
465
466
This function raises an ``IOError`` if an error occurs downloading data or
467
parsing it. It raises a ``ValueError`` if the ``q`` input is invalid.
468
469
AUTHORS:
470
471
- Steven Sivek (2005-11-14)
472
- David Joyner (2008-03)
473
"""
474
q = F.order()
475
if not q in [2, 3, 4, 5, 7, 8, 9]:
476
raise ValueError, "q (=%s) must be in [2,3,4,5,7,8,9]"%q
477
n = int(n)
478
k = int(k)
479
480
param = ("?q=%s&n=%s&k=%s"%(q,n,k)).replace('L','')
481
482
url = "http://iaks-www.ira.uka.de/home/grassl/codetables/BKLC/BKLC.php"+param
483
#url = "http://homepages.cwi.nl/htbin/aeb/lincodbd/"+param
484
if verbose:
485
print "Looking up the bounds at %s"%url
486
f = urllib.urlopen(url)
487
s = f.read()
488
f.close()
489
#print s
490
491
i = s.find("<PRE>")
492
j = s.find("</PRE>")
493
if i == -1 or j == -1:
494
raise IOError, "Error parsing data (missing pre tags)."
495
text = s[i+5:j].strip()
496
return text
497
498
def bounds_minimum_distance(n, k, F):
499
r"""
500
Calculates a lower and upper bound for the minimum distance of an optimal
501
linear code with word length ``n`` and dimension ``k`` over the field
502
``F``.
503
504
The function returns a record with the two bounds and an explanation for
505
each bound. The function Display can be used to show the explanations.
506
507
The values for the lower and upper bound are obtained from a table
508
constructed by Cen Tjhai for GUAVA, derived from the table of
509
Brouwer. (See http://www.win.tue.nl/ aeb/voorlincod.html or use the
510
Sage function ``minimum_distance_why`` for the most recent data.)
511
These tables contain lower and upper bounds for `q=2` (when ``n <= 257``),
512
`q=3` (when ``n <= 243``), `q=4` (``n <= 256``). (Current as of
513
11 May 2006.) For codes over other fields and for larger word lengths,
514
trivial bounds are used.
515
516
This does not require an internet connection. The format of the output is
517
a little non-intuitive. Try ``bounds_minimum_distance(10,5,GF(2))`` for
518
an example.
519
520
This function requires optional GAP package (Guava).
521
522
EXAMPLES::
523
524
sage: print bounds_minimum_distance(10,5,GF(2)) # optional (requires Guava)
525
rec(
526
n := 10,
527
k := 5,
528
q := 2,
529
references := rec(
530
),
531
construction :=
532
[ <Operation "ShortenedCode">, [ [ <Operation "UUVCode">, [ [
533
<Operation "DualCode">,
534
[ [ <Operation "RepetitionCode">, [ 8, 2 ] ] ] ],
535
[ <Operation "UUVCode">,
536
[ [ <Operation "DualCode">, [ [ <Operation "RepetitionCode">, [ 4, 2 ] ] ] ], [ <Operation "RepetitionCode">, [ 4, 2 ] ] ] ] ] ],
537
[ 1, 2, 3, 4, 5, 6 ] ] ],
538
lowerBound := 4,
539
lowerBoundExplanation :=
540
[ "Lb(10,5)=4, by shortening of:", "Lb(16,11)=4, by the u|u+v construction applied to C1 [8,7,2] and C2 [8,4,4]: ",
541
"Lb(8,7)=2, dual of the repetition code",
542
"Lb(8,4)=4, by the u|u+v construction applied to C1 [4,3,2] and C2 [4,1,4]: ", "Lb(4,3)=2, dual of the repetition code", "Lb(4,1)=4, repetition code"
543
],
544
upperBound := 4,
545
upperBoundExplanation := [ "Ub(10,5)=4, by the Griesmer bound" ] )
546
"""
547
q = F.order()
548
gap.eval("data := BoundsMinimumDistance(%s,%s,GF(%s))"%(n,k,q))
549
Ldata = gap.eval("Display(data)")
550
return Ldata
551
552
def self_orthogonal_binary_codes(n, k, b=2, parent=None, BC=None, equal=False,
553
in_test=None):
554
"""
555
Returns a Python iterator which generates a complete set of
556
representatives of all permutation equivalence classes of
557
self-orthogonal binary linear codes of length in ``[1..n]`` and
558
dimension in ``[1..k]``.
559
560
INPUT:
561
562
- ``n`` - Integer, maximal length
563
564
- ``k`` - Integer, maximal dimension
565
566
- ``b`` - Integer, requires that the generators all have weight divisible
567
by ``b`` (if ``b=2``, all self-orthogonal codes are generated, and if
568
``b=4``, all doubly even codes are generated). Must be an even positive
569
integer.
570
571
- ``parent`` - Used in recursion (default: ``None``)
572
573
- ``BC`` - Used in recursion (default: ``None``)
574
575
- ``equal`` - If ``True`` generates only [n, k] codes (default: ``False``)
576
577
- ``in_test`` - Used in recursion (default: ``None``)
578
579
EXAMPLES:
580
581
Generate all self-orthogonal codes of length up to 7 and dimension up
582
to 3::
583
584
sage: for B in self_orthogonal_binary_codes(7,3):
585
... print B
586
...
587
Linear code of length 2, dimension 1 over Finite Field of size 2
588
Linear code of length 4, dimension 2 over Finite Field of size 2
589
Linear code of length 6, dimension 3 over Finite Field of size 2
590
Linear code of length 4, dimension 1 over Finite Field of size 2
591
Linear code of length 6, dimension 2 over Finite Field of size 2
592
Linear code of length 6, dimension 2 over Finite Field of size 2
593
Linear code of length 7, dimension 3 over Finite Field of size 2
594
Linear code of length 6, dimension 1 over Finite Field of size 2
595
596
Generate all doubly-even codes of length up to 7 and dimension up
597
to 3::
598
599
sage: for B in self_orthogonal_binary_codes(7,3,4):
600
... print B; print B.gen_mat()
601
...
602
Linear code of length 4, dimension 1 over Finite Field of size 2
603
[1 1 1 1]
604
Linear code of length 6, dimension 2 over Finite Field of size 2
605
[1 1 1 1 0 0]
606
[0 1 0 1 1 1]
607
Linear code of length 7, dimension 3 over Finite Field of size 2
608
[1 0 1 1 0 1 0]
609
[0 1 0 1 1 1 0]
610
[0 0 1 0 1 1 1]
611
612
Generate all doubly-even codes of length up to 7 and dimension up
613
to 2::
614
615
sage: for B in self_orthogonal_binary_codes(7,2,4):
616
... print B; print B.gen_mat()
617
Linear code of length 4, dimension 1 over Finite Field of size 2
618
[1 1 1 1]
619
Linear code of length 6, dimension 2 over Finite Field of size 2
620
[1 1 1 1 0 0]
621
[0 1 0 1 1 1]
622
623
Generate all self-orthogonal codes of length equal to 8 and
624
dimension equal to 4::
625
626
sage: for B in self_orthogonal_binary_codes(8, 4, equal=True):
627
... print B; print B.gen_mat()
628
Linear code of length 8, dimension 4 over Finite Field of size 2
629
[1 0 0 1 0 0 0 0]
630
[0 1 0 0 1 0 0 0]
631
[0 0 1 0 0 1 0 0]
632
[0 0 0 0 0 0 1 1]
633
Linear code of length 8, dimension 4 over Finite Field of size 2
634
[1 0 0 1 1 0 1 0]
635
[0 1 0 1 1 1 0 0]
636
[0 0 1 0 1 1 1 0]
637
[0 0 0 1 0 1 1 1]
638
639
Since all the codes will be self-orthogonal, b must be divisible by
640
2::
641
642
sage: list(self_orthogonal_binary_codes(8, 4, 1, equal=True))
643
Traceback (most recent call last):
644
...
645
ValueError: b (1) must be a positive even integer.
646
"""
647
d=int(b)
648
if d!=b or d%2==1 or d <= 0:
649
raise ValueError("b (%s) must be a positive even integer."%b)
650
from binary_code import BinaryCode, BinaryCodeClassifier
651
if k < 1 or n < 2:
652
return
653
if equal:
654
in_test = lambda M : (M.ncols() - M.nrows()) <= (n-k)
655
out_test = lambda C : (C.dimension() == k) and (C.length() == n)
656
else:
657
in_test = lambda M : True
658
out_test = lambda C : True
659
if BC is None:
660
BC = BinaryCodeClassifier()
661
if parent is None:
662
for j in xrange(d, n+1, d):
663
M = Matrix(GF(2), [[1]*j])
664
if in_test(M):
665
for N in self_orthogonal_binary_codes(n, k, d, M, BC, in_test=in_test):
666
if out_test(N): yield N
667
else:
668
C = LinearCode(parent)
669
if out_test(C): yield C
670
if k == parent.nrows():
671
return
672
for nn in xrange(parent.ncols()+1, n+1):
673
if in_test(parent):
674
for child in BC.generate_children(BinaryCode(parent), nn, d):
675
for N in self_orthogonal_binary_codes(n, k, d, child, BC, in_test=in_test):
676
if out_test(N): yield N
677
678
############################ linear codes python class ########################
679
680
class LinearCode(module.Module_old):
681
r"""
682
A class for linear codes over a finite field or finite ring. Each instance
683
is a linear code determined by a generator matrix `G` (i.e., a
684
`k \times n` matrix of (full) rank `k`, `k \leq n` over a finite field `F`.
685
686
INPUT:
687
688
- ``G`` - a generator matrix over `F`. (``G`` can be defined over a
689
finite ring but the matrices over that ring must have certain
690
attributes, such as ``rank``.)
691
692
OUTPUT:
693
694
The linear code of length `n` over `F` having `G` as a generator matrix.
695
696
EXAMPLES::
697
698
sage: MS = MatrixSpace(GF(2),4,7)
699
sage: G = MS([[1,1,1,0,0,0,0], [1,0,0,1,1,0,0], [0,1,0,1,0,1,0], [1,1,0,1,0,0,1]])
700
sage: C = LinearCode(G)
701
sage: C
702
Linear code of length 7, dimension 4 over Finite Field of size 2
703
sage: C.base_ring()
704
Finite Field of size 2
705
sage: C.dimension()
706
4
707
sage: C.length()
708
7
709
sage: C.minimum_distance()
710
3
711
sage: C.spectrum()
712
[1, 0, 0, 7, 7, 0, 0, 1]
713
sage: C.weight_distribution()
714
[1, 0, 0, 7, 7, 0, 0, 1]
715
sage: MS = MatrixSpace(GF(5),4,7)
716
sage: G = MS([[1,1,1,0,0,0,0], [1,0,0,1,1,0,0], [0,1,0,1,0,1,0], [1,1,0,1,0,0,1]])
717
sage: C = LinearCode(G)
718
sage: C
719
Linear code of length 7, dimension 4 over Finite Field of size 5
720
721
AUTHORS:
722
723
- David Joyner (11-2005)
724
"""
725
# sage: C.minimum_distance_upper_bound() # optional (net connection)
726
# 3
727
# sage: C.minimum_distance_why() # optional (net connection)
728
# Ub(7,4) = 3 follows by the Griesmer bound.
729
def __init__(self, gen_mat):
730
r"""
731
See the docstring for :meth:`LinearCode`.
732
733
EXAMPLES::
734
735
sage: MS = MatrixSpace(GF(2),4,7)
736
sage: G = MS([[1,1,1,0,0,0,0], [1,0,0,1,1,0,0], [0,1,0,1,0,1,0], [1,1,0,1,0,0,1]])
737
sage: C = LinearCode(G) # indirect doctest
738
sage: C
739
Linear code of length 7, dimension 4 over Finite Field of size 2
740
"""
741
base_ring = gen_mat[0,0].parent()
742
ParentWithGens.__init__(self, base_ring)
743
self.__gens = gen_mat.rows()
744
self.__gen_mat = gen_mat
745
self.__length = len(gen_mat.row(0))
746
self.__dim = gen_mat.rank()
747
748
def _repr_(self):
749
r"""
750
See the docstring for :meth:`LinearCode`.
751
752
EXAMPLES::
753
754
sage: MS = MatrixSpace(GF(2),4,7)
755
sage: G = MS([[1,1,1,0,0,0,0], [1,0,0,1,1,0,0], [0,1,0,1,0,1,0], [1,1,0,1,0,0,1]])
756
sage: C = LinearCode(G)
757
sage: C # indirect doctest
758
Linear code of length 7, dimension 4 over Finite Field of size 2
759
"""
760
return "Linear code of length %s, dimension %s over %s"%(self.length(), self.dimension(), self.base_ring())
761
762
def automorphism_group_binary_code(self):
763
r"""
764
This function is deprecated. Use permutation_automorphism_group instead.
765
766
This only applies to linear binary codes and returns its (permutation)
767
automorphism group. In other words, if the code `C` has length `n`
768
then it returns the subgroup of the symmetric group `S_n`:
769
770
.. math::
771
772
\{ g \in S_n\ |\ g(c) \in C, \forall c\in C\},
773
774
where `S_n` acts on `GF(2)^n` by permuting coordinates.
775
776
EXAMPLES::
777
778
sage: C = HammingCode(3,GF(2))
779
sage: G = C.automorphism_group_binary_code(); G
780
doctest:...: DeprecationWarning: This function is deprecated...
781
Permutation Group with generators [(4,5)(6,7), (4,6)(5,7), (2,3)(6,7), (2,4)(3,5), (1,2)(5,6)]
782
sage: G.order()
783
168
784
"""
785
from sage.misc.misc import deprecation
786
deprecation("This function is deprecated. Call the method"
787
+" permutation_automorphism_group instead.")
788
#deprecated 4.7
789
C = self
790
F = C.base_ring()
791
if F!=GF(2):
792
raise NotImplementedError, "Only implemented for binary codes."
793
return self.permutation_automorphism_group()
794
795
def __iter__(self):
796
"""
797
Return an iterator over the elements of this linear code.
798
799
EXAMPLES::
800
801
sage: C = HammingCode(3,GF(2))
802
sage: [list(c) for c in C if hamming_weight(c) < 4]
803
[[0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 1, 1], [0, 1, 0, 0, 1, 0, 1], [0, 0, 1, 0, 1, 1, 0], [1, 1, 1, 0, 0, 0, 0], [1, 0, 0, 1, 1, 0, 0], [0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 1, 0, 0, 1]]
804
"""
805
n = self.length()
806
k = self.dimension()
807
F = self.base_ring()
808
Cs,p = self.standard_form()
809
Gs = Cs.gen_mat()
810
V = VectorSpace(F,k)
811
MS = MatrixSpace(F,n,n)
812
perm_mat = MS(p.matrix().rows())**(-1)
813
for v in V:
814
yield (v*Gs)*perm_mat
815
816
def ambient_space(self):
817
r"""
818
Returns the ambient vector space of `self`.
819
820
EXAMPLES::
821
822
sage: C = HammingCode(3,GF(2))
823
sage: C.ambient_space()
824
Vector space of dimension 7 over Finite Field of size 2
825
"""
826
return VectorSpace(self.base_ring(),self.__length)
827
828
def assmus_mattson_designs(self, t, mode=None):
829
r"""
830
Assmus and Mattson Theorem (section 8.4, page 303 of [HP]): Let
831
`A_0, A_1, ..., A_n` be the weights of the codewords in a binary
832
linear `[n , k, d]` code `C`, and let `A_0^*, A_1^*, ..., A_n^*` be
833
the weights of the codewords in its dual `[n, n-k, d^*]` code `C^*`.
834
Fix a `t`, `0<t<d`, and let
835
836
.. math::
837
838
s = |\{ i\ |\ A_i^* \not= 0, 0< i \leq n-t\}|.
839
840
Assume `s\leq d-t`.
841
842
1. If `A_i\not= 0` and `d\leq i\leq n`
843
then `C_i = \{ c \in C\ |\ wt(c) = i\}` holds a simple t-design.
844
845
2. If `A_i^*\not= 0` and `d*\leq i\leq n-t` then
846
`C_i^* = \{ c \in C^*\ |\ wt(c) = i\}` holds a simple t-design.
847
848
A block design is a pair `(X,B)`, where `X` is a non-empty finite set
849
of `v>0` elements called points, and `B` is a non-empty finite
850
multiset of size b whose elements are called blocks, such that each
851
block is a non-empty finite multiset of `k` points. `A` design without
852
repeated blocks is called a simple block design. If every subset of
853
points of size `t` is contained in exactly `\lambda` blocks the block
854
design is called a `t-(v,k,\lambda)` design (or simply a `t`-design
855
when the parameters are not specified). When `\lambda=1` then the
856
block design is called a `S(t,k,v)` Steiner system.
857
858
In the Assmus and Mattson Theorem (1), `X` is the set `\{1,2,...,n\}`
859
of coordinate locations and `B = \{supp(c)\ |\ c \in C_i\}` is the set
860
of supports of the codewords of `C` of weight `i`. Therefore, the
861
parameters of the `t`-design for `C_i` are
862
863
::
864
865
t = given
866
v = n
867
k = i (k not to be confused with dim(C))
868
b = Ai
869
lambda = b*binomial(k,t)/binomial(v,t) (by Theorem 8.1.6,
870
p 294, in [HP])
871
872
Setting the ``mode="verbose"`` option prints out the values of the
873
parameters.
874
875
The first example below means that the binary [24,12,8]-code C has
876
the property that the (support of the) codewords of weight 8 (resp.,
877
12, 16) form a 5-design. Similarly for its dual code `C^*` (of course
878
`C=C^*` in this case, so this info is extraneous). The test fails to
879
produce 6-designs (ie, the hypotheses of the theorem fail to hold,
880
not that the 6-designs definitely don't exist). The command
881
assmus_mattson_designs(C,5,mode="verbose") returns the same value
882
but prints out more detailed information.
883
884
The second example below illustrates the blocks of the 5-(24, 8, 1)
885
design (i.e., the S(5,8,24) Steiner system).
886
887
EXAMPLES::
888
889
sage: C = ExtendedBinaryGolayCode() # example 1
890
sage: C.assmus_mattson_designs(5)
891
['weights from C: ',
892
[8, 12, 16, 24],
893
'designs from C: ',
894
[[5, (24, 8, 1)], [5, (24, 12, 48)], [5, (24, 16, 78)], [5, (24, 24, 1)]],
895
'weights from C*: ',
896
[8, 12, 16],
897
'designs from C*: ',
898
[[5, (24, 8, 1)], [5, (24, 12, 48)], [5, (24, 16, 78)]]]
899
sage: C.assmus_mattson_designs(6)
900
0
901
sage: X = range(24) # example 2
902
sage: blocks = [c.support() for c in C if hamming_weight(c)==8]; len(blocks) # long time computation
903
759
904
905
REFERENCE:
906
907
- [HP] W. C. Huffman and V. Pless, Fundamentals of ECC,
908
Cambridge Univ. Press, 2003.
909
"""
910
C = self
911
ans = []
912
G = C.gen_mat()
913
n = len(G.columns())
914
Cp = C.dual_code()
915
wts = C.spectrum()
916
d = min([i for i in range(1,len(wts)) if wts[i]!=0])
917
if t>=d:
918
return 0
919
nonzerowts = [i for i in range(len(wts)) if wts[i]!=0 and i<=n and i>=d]
920
#print d,t,len(nonzerowts)
921
if mode=="verbose":
922
#print "\n"
923
for w in nonzerowts:
924
print "The weight ",w," codewords of C form a t-(v,k,lambda) design, where"
925
print " t = ",t," , v = ",n," , k = ",w," , lambda = ",wts[w]*binomial(w,t)/binomial(n,t)#,"\n"
926
print " There are ",wts[w]," blocks of this design."
927
wtsp = Cp.spectrum()
928
dp = min([i for i in range(1,len(wtsp)) if wtsp[i]!=0])
929
nonzerowtsp = [i for i in range(len(wtsp)) if wtsp[i]!=0 and i<=n-t and i>=dp]
930
s = len([i for i in range(1,n) if wtsp[i]!=0 and i<=n-t and i>0])
931
if mode=="verbose":
932
#print "\n"
933
for w in nonzerowtsp:
934
print "The weight ",w," codewords of C* form a t-(v,k,lambda) design, where"
935
print " t = ",t," , v = ",n," , k = ",w," , lambda = ",wtsp[w]*binomial(w,t)/binomial(n,t)#,"\n"
936
print " There are ",wts[w]," blocks of this design."
937
if s<=d-t:
938
des = [[t,(n,w,wts[w]*binomial(w,t)/binomial(n,t))] for w in nonzerowts]
939
ans = ans + ["weights from C: ",nonzerowts,"designs from C: ",des]
940
desp = [[t,(n,w,wtsp[w]*binomial(w,t)/binomial(n,t))] for w in nonzerowtsp]
941
ans = ans + ["weights from C*: ",nonzerowtsp,"designs from C*: ",desp]
942
return ans
943
return 0
944
945
def basis(self):
946
r"""
947
Returns a basis of `self`.
948
949
EXAMPLES::
950
951
sage: C = HammingCode(3, GF(2))
952
sage: C.basis()
953
[(1, 0, 0, 0, 0, 1, 1), (0, 1, 0, 0, 1, 0, 1), (0, 0, 1, 0, 1, 1, 0), (0, 0, 0, 1, 1, 1, 1)]
954
"""
955
return self.__gens
956
957
# S. Pancratz, 19 Jan 2010: In the doctests below, I removed the example
958
# ``C.binomial_moment(3)``, which was also marked as ``#long``. This way,
959
# we shorten the doctests time while still maintaining a zero and a
960
# non-zero example.
961
def binomial_moment(self, i):
962
r"""
963
Returns the i-th binomial moment of the `[n,k,d]_q`-code `C`:
964
965
.. math::
966
967
B_i(C) = \sum_{S, |S|=i} \frac{q^{k_S}-1}{q-1}
968
969
where `k_S` is the dimension of the shortened code `C_{J-S}`,
970
`J=[1,2,...,n]`. (The normalized binomial moment is
971
`b_i(C) = \binom(n,d+i)^{-1}B_{d+i}(C)`.) In other words, `C_{J-S}`
972
is isomorphic to the subcode of C of codewords supported on S.
973
974
EXAMPLES::
975
976
sage: C = HammingCode(3,GF(2))
977
sage: C.binomial_moment(2)
978
0
979
sage: C.binomial_moment(4) # long time
980
35
981
982
.. warning::
983
984
This is slow.
985
986
REFERENCE:
987
988
- I. Duursma, "Combinatorics of the two-variable zeta function",
989
Finite fields and applications, 109-136, Lecture Notes in
990
Comput. Sci., 2948, Springer, Berlin, 2004.
991
"""
992
n = self.length()
993
k = self.dimension()
994
d = self.minimum_distance()
995
F = self.base_ring()
996
q = F.order()
997
J = range(1,n+1)
998
Cp = self.dual_code()
999
dp = Cp.minimum_distance()
1000
if i<d:
1001
return 0
1002
if i>n-dp and i<=n:
1003
return binomial(n,i)*(q**(i+k-n) -1)/(q-1)
1004
P = SetPartitions(J,2).list()
1005
b = QQ(0)
1006
for p in P:
1007
p = list(p)
1008
S = p[0]
1009
if len(S)==n-i:
1010
C_S = self.shortened(S)
1011
k_S = C_S.dimension()
1012
b = b + (q**(k_S) -1)/(q-1)
1013
return b
1014
1015
def __contains__(self,v):
1016
r"""
1017
Returns True if `v` can be coerced into `self`. Otherwise, returns False.
1018
1019
EXAMPLES::
1020
1021
sage: C = HammingCode(3,GF(2))
1022
sage: vector((1, 0, 0, 0, 0, 1, 1)) in C # indirect doctest
1023
True
1024
sage: vector((1, 0, 0, 0, 2, 1, 1)) in C # indirect doctest
1025
True
1026
sage: vector((1, 0, 0, 0, 0, 1/2, 1)) in C # indirect doctest
1027
False
1028
"""
1029
A = self.ambient_space()
1030
C = A.subspace(self.gens())
1031
return C.__contains__(v)
1032
1033
def characteristic(self):
1034
r"""
1035
Returns the characteristic of the base ring of `self`.
1036
1037
EXAMPLES::
1038
1039
sage: C = HammingCode(3,GF(2))
1040
sage: C.characteristic()
1041
2
1042
"""
1043
return (self.base_ring()).characteristic()
1044
1045
def characteristic_polynomial(self):
1046
r"""
1047
Returns the characteristic polynomial of a linear code, as defined in
1048
van Lint's text [vL].
1049
1050
EXAMPLES::
1051
1052
sage: C = ExtendedBinaryGolayCode()
1053
sage: C.characteristic_polynomial()
1054
-4/3*x^3 + 64*x^2 - 2816/3*x + 4096
1055
1056
REFERENCES:
1057
1058
- van Lint, Introduction to coding theory, 3rd ed., Springer-Verlag
1059
GTM, 86, 1999.
1060
"""
1061
R = PolynomialRing(QQ,"x")
1062
x = R.gen()
1063
C = self
1064
Cd = C.dual_code()
1065
Sd = Cd.support()
1066
k = C.dimension()
1067
n = C.length()
1068
q = (C.base_ring()).order()
1069
return q**(n-k)*prod([1-x/j for j in Sd if j>0])
1070
1071
def chinen_polynomial(self):
1072
"""
1073
Returns the Chinen zeta polynomial of the code.
1074
1075
EXAMPLES::
1076
1077
sage: C = HammingCode(3,GF(2))
1078
sage: C.chinen_polynomial() # long time
1079
1/5*(2*sqrt(2)*t^3 + 2*sqrt(2)*t^2 + 2*t^2 + sqrt(2)*t + 2*t + 1)/(sqrt(2) + 1)
1080
sage: C = TernaryGolayCode()
1081
sage: C.chinen_polynomial() # long time
1082
1/7*(3*sqrt(3)*t^3 + 3*sqrt(3)*t^2 + 3*t^2 + sqrt(3)*t + 3*t + 1)/(sqrt(3) + 1)
1083
1084
This last output agrees with the corresponding example given in
1085
Chinen's paper below.
1086
1087
REFERENCES:
1088
1089
- Chinen, K. "An abundance of invariant polynomials satisfying the
1090
Riemann hypothesis", April 2007 preprint.
1091
"""
1092
from sage.functions.all import sqrt
1093
C = self
1094
n = C.length()
1095
RT = PolynomialRing(QQ,2,"Ts")
1096
T,s = FractionField(RT).gens()
1097
t = PolynomialRing(QQ,"t").gen()
1098
Cd = C.dual_code()
1099
k = C.dimension()
1100
q = (C.base_ring()).characteristic()
1101
d = C.minimum_distance()
1102
dperp = Cd.minimum_distance()
1103
if dperp > d:
1104
P = RT(C.zeta_polynomial())
1105
# Sage does not find dealing with sqrt(int) *as an algebraic object*
1106
# an easy thing to do. Some tricky gymnastics are used to
1107
# make Sage deal with objects over QQ(sqrt(q)) nicely.
1108
if is_even(n):
1109
Pd = q**(k-n/2)*RT(Cd.zeta_polynomial())*T**(dperp - d)
1110
if not(is_even(n)):
1111
Pd = s*q**(k-(n+1)/2)*RT(Cd.zeta_polynomial())*T**(dperp - d)
1112
CP = P+Pd
1113
f = CP/CP(1,s)
1114
return f(t,sqrt(q))
1115
if dperp < d:
1116
P = RT(C.zeta_polynomial())*T**(d - dperp)
1117
if is_even(n):
1118
Pd = q**(k-n/2)*RT(Cd.zeta_polynomial())
1119
if not(is_even(n)):
1120
Pd = s*q**(k-(n+1)/2)*RT(Cd.zeta_polynomial())
1121
CP = P+Pd
1122
f = CP/CP(1,s)
1123
return f(t,sqrt(q))
1124
if dperp == d:
1125
P = RT(C.zeta_polynomial())
1126
if is_even(n):
1127
Pd = q**(k-n/2)*RT(Cd.zeta_polynomial())
1128
if not(is_even(n)):
1129
Pd = s*q**(k-(n+1)/2)*RT(Cd.zeta_polynomial())
1130
CP = P+Pd
1131
f = CP/CP(1,s)
1132
return f(t,sqrt(q))
1133
1134
def __cmp__(self, right):
1135
r"""
1136
Returns True if the generator matrices of `self` and `right` are
1137
equal.
1138
1139
EXAMPLES::
1140
1141
sage: C = HammingCode(3,GF(2))
1142
sage: MS = MatrixSpace(GF(2),4,7)
1143
sage: G = MS([1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1])
1144
sage: G
1145
[1 0 0 0 0 1 1]
1146
[0 1 0 0 1 0 1]
1147
[0 0 1 0 1 1 0]
1148
[0 0 0 1 1 1 1]
1149
sage: D = LinearCode(G)
1150
sage: C == D
1151
True
1152
1153
sage: Cperp = C.dual_code()
1154
sage: Cperpperp = Cperp.dual_code()
1155
sage: C == Cperpperp
1156
True
1157
"""
1158
if not isinstance(right, LinearCode):
1159
return cmp(type(self), type(right))
1160
return cmp(self.__gen_mat, right.__gen_mat)
1161
1162
def check_mat(self):
1163
r"""
1164
Returns the check matrix of ``self``.
1165
1166
EXAMPLES::
1167
1168
sage: C = HammingCode(3,GF(2))
1169
sage: Cperp = C.dual_code()
1170
sage: C; Cperp
1171
Linear code of length 7, dimension 4 over Finite Field of size 2
1172
Linear code of length 7, dimension 3 over Finite Field of size 2
1173
sage: C.gen_mat()
1174
[1 0 0 0 0 1 1]
1175
[0 1 0 0 1 0 1]
1176
[0 0 1 0 1 1 0]
1177
[0 0 0 1 1 1 1]
1178
sage: C.check_mat()
1179
[1 0 1 0 1 0 1]
1180
[0 1 1 0 0 1 1]
1181
[0 0 0 1 1 1 1]
1182
sage: Cperp.check_mat()
1183
[1 0 0 0 0 1 1]
1184
[0 1 0 0 1 0 1]
1185
[0 0 1 0 1 1 0]
1186
[0 0 0 1 1 1 1]
1187
sage: Cperp.gen_mat()
1188
[1 0 1 0 1 0 1]
1189
[0 1 1 0 0 1 1]
1190
[0 0 0 1 1 1 1]
1191
"""
1192
Cperp = self.dual_code()
1193
return Cperp.gen_mat()
1194
1195
def covering_radius(self):
1196
r"""
1197
Wraps Guava's ``CoveringRadius`` command.
1198
1199
The covering radius of a linear code `C` is the smallest number `r`
1200
with the property that each element `v` of the ambient vector space
1201
of `C` has at most a distance `r` to the code `C`. So for each
1202
vector `v` there must be an element `c` of `C` with `d(v,c) \leq r`.
1203
A binary linear code with reasonable small covering radius is often
1204
referred to as a covering code.
1205
1206
For example, if `C` is a perfect code, the covering radius is equal
1207
to `t`, the number of errors the code can correct, where `d = 2t+1`,
1208
with `d` the minimum distance of `C`.
1209
1210
EXAMPLES::
1211
1212
sage: C = HammingCode(5,GF(2))
1213
sage: C.covering_radius() # requires optional GAP package Guava
1214
1
1215
"""
1216
F = self.base_ring()
1217
G = self.gen_mat()
1218
gapG = gap(G)
1219
C = gapG.GeneratorMatCode(gap(F))
1220
r = C.CoveringRadius()
1221
try:
1222
return ZZ(r)
1223
except:
1224
raise ValueError("Sorry, the covering radius of this code cannot be computed by Guava.")
1225
1226
@rename_keyword(deprecated='Sage version 4.6', method="algorithm")
1227
def decode(self, right, algorithm="syndrome"):
1228
r"""
1229
Decodes the received vector ``right`` to an element `c` in this code.
1230
1231
Optional algorithms are "guava", "nearest neighbor" or "syndrome". The
1232
``algorithm="guava"`` wraps GUAVA's ``Decodeword``. Hamming codes have
1233
a special decoding algorithm; otherwise, ``"syndrome"`` decoding is
1234
used.
1235
1236
INPUT:
1237
1238
- ``right`` - Vector of length the length of this code
1239
- ``algorithm`` - Algorithm to use, one of ``"syndrome"``, ``"nearest
1240
neighbor"``, and ``"guava"`` (default: ``"syndrome"``)
1241
1242
OUTPUT:
1243
1244
- The codeword in this code closest to ``right``.
1245
1246
EXAMPLES::
1247
1248
sage: C = HammingCode(3,GF(2))
1249
sage: MS = MatrixSpace(GF(2),1,7)
1250
sage: F = GF(2); a = F.gen()
1251
sage: v1 = [a,a,F(0),a,a,F(0),a]
1252
sage: C.decode(v1)
1253
(1, 1, 0, 1, 0, 0, 1)
1254
sage: C.decode(v1,algorithm="nearest neighbor")
1255
(1, 1, 0, 1, 0, 0, 1)
1256
sage: C.decode(v1,algorithm="guava") # requires optional GAP package Guava
1257
(1, 1, 0, 1, 0, 0, 1)
1258
sage: v2 = matrix([[a,a,F(0),a,a,F(0),a]])
1259
sage: C.decode(v2)
1260
(1, 1, 0, 1, 0, 0, 1)
1261
sage: v3 = vector([a,a,F(0),a,a,F(0),a])
1262
sage: c = C.decode(v3); c
1263
(1, 1, 0, 1, 0, 0, 1)
1264
sage: c in C
1265
True
1266
sage: C = HammingCode(2,GF(5))
1267
sage: v = vector(GF(5),[1,0,0,2,1,0])
1268
sage: C.decode(v)
1269
(1, 0, 0, 2, 2, 0)
1270
sage: F = GF(4,"a")
1271
sage: C = HammingCode(2,F)
1272
sage: v = vector(F, [1,0,0,a,1])
1273
sage: C.decode(v)
1274
(1, 0, 1, 1, 1)
1275
sage: C.decode(v, algorithm="nearest neighbor")
1276
(1, 0, 1, 1, 1)
1277
sage: C.decode(v, algorithm="guava") # requires optional GAP package Guava
1278
(1, 0, 1, 1, 1)
1279
1280
Does not work for very long codes since the syndrome table grows too
1281
large.
1282
"""
1283
from sage.interfaces.gap import gfq_gap_to_sage
1284
from decoder import decode
1285
if algorithm=="syndrome" or algorithm=="nearest neighbor":
1286
return decode(self,right)
1287
if not(algorithm in ["syndrome", "nearest neighbor","guava"]):
1288
raise NotImplementedError, "Only 'syndrome','nearest neighbor','guava' are implemented."
1289
F = self.base_ring()
1290
q = F.order()
1291
G = self.gen_mat()
1292
n = len(G.columns())
1293
Gstr = str(gap(G))
1294
if not(type(right) == list):
1295
v = right.list()
1296
else:
1297
v = right
1298
vstr = str(gap(v))
1299
if vstr[:3] == '[ [':
1300
vstr = vstr[1:-1] # added by William Stein so const.tex works 2006-10-01
1301
gap.eval("C:=GeneratorMatCode("+Gstr+",GF("+str(q)+"))")
1302
gap.eval("c:=VectorCodeword(Decodeword( C, Codeword( "+vstr+" )))") # v->vstr, 8-27-2006
1303
ans = [gfq_gap_to_sage(gap.eval("c["+str(i)+"]"),F) for i in range(1,n+1)]
1304
V = VectorSpace(F,n)
1305
return V(ans)
1306
1307
def divisor(self):
1308
r"""
1309
Returns the divisor of a code, which is the smallest integer `d_0 > 0`
1310
such that each `A_i > 0` iff `i` is divisible by `d_0`.
1311
1312
EXAMPLES::
1313
1314
sage: C = ExtendedBinaryGolayCode()
1315
sage: C.divisor() # Type II self-dual
1316
4
1317
sage: C = QuadraticResidueCodeEvenPair(17,GF(2))[0]
1318
sage: C.divisor()
1319
2
1320
"""
1321
C = self
1322
A = C.spectrum()
1323
n = C.length()
1324
V = VectorSpace(QQ,n+1)
1325
S = V(A).nonzero_positions()
1326
S0 = [S[i] for i in range(1,len(S))]
1327
#print S0
1328
if len(S)>1: return GCD(S0)
1329
return 1
1330
1331
def dual_code(self):
1332
r"""
1333
This computes the dual code `Cd` of the code `C`,
1334
1335
.. math::
1336
1337
Cd = \{ v \in V\ |\ v\cdot c = 0,\ \forall c \in C \}.
1338
1339
Does not call GAP.
1340
1341
EXAMPLES::
1342
1343
sage: C = HammingCode(3,GF(2))
1344
sage: C.dual_code()
1345
Linear code of length 7, dimension 3 over Finite Field of size 2
1346
sage: C = HammingCode(3,GF(4,'a'))
1347
sage: C.dual_code()
1348
Linear code of length 21, dimension 3 over Finite Field in a of size 2^2
1349
"""
1350
G = self.gen_mat()
1351
H = G.transpose().kernel()
1352
V = H.ambient_vector_space()
1353
Cd = LinearCodeFromVectorSpace(V.span(H))
1354
return Cd
1355
#another way:
1356
#Gsf, p = standard_form(G)
1357
#k = len(G.rows())
1358
#n = len(G.columns())
1359
#MS = G.parent()
1360
#sG = G.matrix_from_columns(range(k,n))
1361
#Inmk = MatrixSpace(F,n-k,n-k).identity_matrix()
1362
#H = Inmk.augment(sG.transpose())
1363
#return LinearCode(H)
1364
1365
def dimension(self):
1366
r"""
1367
Returns the dimension of this code.
1368
1369
EXAMPLES::
1370
1371
sage: G = matrix(GF(2),[[1,0,0],[1,1,0]])
1372
sage: C = LinearCode(G)
1373
sage: C.dimension()
1374
2
1375
"""
1376
return self.__dim
1377
1378
def direct_sum(self, other):
1379
"""
1380
Returns the code given by the direct sum of the codes ``self`` and
1381
``other``, which must be linear codes defined over the same base ring.
1382
1383
EXAMPLES::
1384
1385
sage: C1 = HammingCode(3,GF(2))
1386
sage: C2 = C1.direct_sum(C1); C2
1387
Linear code of length 14, dimension 8 over Finite Field of size 2
1388
sage: C3 = C1.direct_sum(C2); C3
1389
Linear code of length 21, dimension 12 over Finite Field of size 2
1390
"""
1391
C1 = self; C2 = other
1392
G1 = C1.gen_mat()
1393
G2 = C2.gen_mat()
1394
F = C1.base_ring()
1395
n1 = len(G1.columns())
1396
k1 = len(G1.rows())
1397
n2 = len(G2.columns())
1398
k2 = len(G2.rows())
1399
MS1 = MatrixSpace(F,k2,n1)
1400
MS2 = MatrixSpace(F,k1,n2)
1401
Z1 = MS1(0)
1402
Z2 = MS2(0)
1403
top = G1.augment(Z2)
1404
bottom = Z1.augment(G2)
1405
G = top.stack(bottom)
1406
return LinearCode(G)
1407
1408
def __eq__(self, right):
1409
"""
1410
Checks if ``self`` is equal to ``right``.
1411
1412
EXAMPLES::
1413
1414
sage: C1 = HammingCode(3,GF(2))
1415
sage: C2 = HammingCode(3,GF(2))
1416
sage: C1 == C2
1417
True
1418
sage: C2 = C1.extended_code()
1419
sage: C3 = C2.punctured([7])
1420
sage: C1 == C3
1421
True
1422
"""
1423
slength = self.length()
1424
rlength = right.length()
1425
sdim = self.dimension()
1426
rdim = right.dimension()
1427
sF = self.base_ring()
1428
rF = right.base_ring()
1429
if slength != rlength:
1430
return False
1431
if sdim != rdim:
1432
return False
1433
if sF != rF:
1434
return False
1435
sbasis = self.gens()
1436
rbasis = right.gens()
1437
scheck = self.check_mat()
1438
rcheck = right.check_mat()
1439
for c in sbasis:
1440
if rcheck*c:
1441
return False
1442
for c in rbasis:
1443
if scheck*c:
1444
return False
1445
return True
1446
1447
def extended_code(self):
1448
r"""
1449
If ``self`` is a linear code of length `n` defined over `F` then this
1450
returns the code of length `n+1` where the last digit `c_n` satisfies
1451
the check condition `c_0+...+c_n=0`. If ``self`` is an `[n,k,d]`
1452
binary code then the extended code `C^{\vee}` is an `[n+1,k,d^{\vee}]`
1453
code, where `d^=d` (if d is even) and `d^{\vee}=d+1` (if `d` is odd).
1454
1455
EXAMPLES::
1456
1457
sage: C = HammingCode(3,GF(4,'a'))
1458
sage: C
1459
Linear code of length 21, dimension 18 over Finite Field in a of size 2^2
1460
sage: Cx = C.extended_code()
1461
sage: Cx
1462
Linear code of length 22, dimension 18 over Finite Field in a of size 2^2
1463
"""
1464
G = self.gen_mat()
1465
F = self.base_ring()
1466
k = len(G.rows())
1467
MS1 = MatrixSpace(F,k,1)
1468
ck_sums = [-sum(G.rows()[i]) for i in range(k)]
1469
last_col = MS1(ck_sums)
1470
Gx = G.augment(last_col)
1471
return LinearCode(Gx)
1472
1473
def galois_closure(self, F0):
1474
r"""
1475
If ``self`` is a linear code defined over `F` and `F_0` is a subfield
1476
with Galois group `G = Gal(F/F_0)` then this returns the `G`-module
1477
`C^-` containing `C`.
1478
1479
EXAMPLES::
1480
1481
sage: C = HammingCode(3,GF(4,'a'))
1482
sage: Cc = C.galois_closure(GF(2))
1483
sage: C; Cc
1484
Linear code of length 21, dimension 18 over Finite Field in a of size 2^2
1485
Linear code of length 21, dimension 20 over Finite Field in a of size 2^2
1486
sage: c = C.basis()[2]
1487
sage: V = VectorSpace(GF(4,'a'),21)
1488
sage: c2 = V([x^2 for x in c.list()])
1489
sage: c2 in C
1490
False
1491
sage: c2 in Cc
1492
True
1493
"""
1494
G = self.gen_mat()
1495
F = self.base_ring()
1496
q = F.order()
1497
q0 = F0.order()
1498
a = log(q,q0) # test if F/F0 is a field extension
1499
if not isinstance(a, Integer):
1500
raise ValueError,"Base field must be an extension of given field %s"%F0
1501
n = len(G.columns())
1502
k = len(G.rows())
1503
G0 = [[x**q0 for x in g.list()] for g in G.rows()]
1504
G1 = [[x for x in g.list()] for g in G.rows()]
1505
G2 = G0+G1
1506
MS = MatrixSpace(F,2*k,n)
1507
G3 = MS(G2)
1508
r = G3.rank()
1509
MS = MatrixSpace(F,r,n)
1510
Grref = G3.echelon_form()
1511
G = MS([Grref.row(i) for i in range(r)])
1512
return LinearCode(G)
1513
1514
def gen_mat(self):
1515
r"""
1516
Return a generator matrix of this code.
1517
1518
EXAMPLES::
1519
1520
sage: C1 = HammingCode(3,GF(2))
1521
sage: C1.gen_mat()
1522
[1 0 0 0 0 1 1]
1523
[0 1 0 0 1 0 1]
1524
[0 0 1 0 1 1 0]
1525
[0 0 0 1 1 1 1]
1526
sage: C2 = HammingCode(2,GF(4,"a"))
1527
sage: C2.gen_mat()
1528
[ 1 0 0 a + 1 a]
1529
[ 0 1 0 1 1]
1530
[ 0 0 1 a a + 1]
1531
"""
1532
return self.__gen_mat
1533
1534
def gen_mat_systematic(self):
1535
"""
1536
Return a systematic generator matrix of the code.
1537
1538
A generator matrix of a code is called systematic if it contains
1539
a set of columns forming an identity matrix.
1540
1541
EXAMPLES::
1542
1543
sage: G = matrix(GF(3),2,[1,-1,1,-1,1,1])
1544
sage: code = LinearCode(G)
1545
sage: code.gen_mat()
1546
[1 2 1]
1547
[2 1 1]
1548
sage: code.gen_mat_systematic()
1549
[1 2 0]
1550
[0 0 1]
1551
"""
1552
return self.__gen_mat.echelon_form()
1553
1554
def gens(self):
1555
r"""
1556
Returns the generators of this code as a list of vectors.
1557
1558
EXAMPLES::
1559
1560
sage: C = HammingCode(3,GF(2))
1561
sage: C.gens()
1562
[(1, 0, 0, 0, 0, 1, 1), (0, 1, 0, 0, 1, 0, 1), (0, 0, 1, 0, 1, 1, 0), (0, 0, 0, 1, 1, 1, 1)]
1563
"""
1564
return self.__gens
1565
1566
def genus(self):
1567
r"""
1568
Returns the "Duursma genus" of the code, `\gamma_C = n+1-k-d`.
1569
1570
EXAMPLES::
1571
1572
sage: C1 = HammingCode(3,GF(2)); C1
1573
Linear code of length 7, dimension 4 over Finite Field of size 2
1574
sage: C1.genus()
1575
1
1576
sage: C2 = HammingCode(2,GF(4,"a")); C2
1577
Linear code of length 5, dimension 3 over Finite Field in a of size 2^2
1578
sage: C2.genus()
1579
0
1580
1581
Since all Hamming codes have minimum distance 3, these computations
1582
agree with the definition, `n+1-k-d`.
1583
"""
1584
d = self.minimum_distance()
1585
n = self.length()
1586
k = self.dimension()
1587
gammaC = n+1-k-d
1588
return gammaC
1589
1590
def information_set(self):
1591
"""
1592
Return an information set of the code.
1593
1594
A set of column positions of a generator matrix of a code
1595
is called an information set if the corresponding columns
1596
form a square matrix of full rank.
1597
1598
OUTPUT:
1599
1600
- Information set of a systematic generator matrix of the code.
1601
1602
EXAMPLES::
1603
1604
sage: G = matrix(GF(3),2,[1,-1,0,-1,1,1])
1605
sage: code = LinearCode(G)
1606
sage: code.gen_mat_systematic()
1607
[1 2 0]
1608
[0 0 1]
1609
sage: code.information_set()
1610
(0, 2)
1611
"""
1612
return self.__gen_mat.transpose().pivot_rows()
1613
1614
def is_permutation_automorphism(self,g):
1615
r"""
1616
Returns `1` if `g` is an element of `S_n` (`n` = length of self) and
1617
if `g` is an automorphism of self.
1618
1619
EXAMPLES::
1620
1621
sage: C = HammingCode(3,GF(3))
1622
sage: g = SymmetricGroup(13).random_element()
1623
sage: C.is_permutation_automorphism(g)
1624
0
1625
sage: MS = MatrixSpace(GF(2),4,8)
1626
sage: G = MS([[1,0,0,0,1,1,1,0],[0,1,1,1,0,0,0,0],[0,0,0,0,0,0,0,1],[0,0,0,0,0,1,0,0]])
1627
sage: C = LinearCode(G)
1628
sage: S8 = SymmetricGroup(8)
1629
sage: g = S8("(2,3)")
1630
sage: C.is_permutation_automorphism(g)
1631
1
1632
sage: g = S8("(1,2,3,4)")
1633
sage: C.is_permutation_automorphism(g)
1634
0
1635
"""
1636
basis = self.gen_mat().rows()
1637
H = self.check_mat()
1638
V = H.column_space()
1639
HGm = H*g.matrix()
1640
# raise TypeError, (type(H), type(V), type(basis[0]), type(Gmc))
1641
for c in basis:
1642
if HGm*c != V(0):
1643
return False
1644
return True
1645
1646
@rename_keyword(deprecated='Sage version 4.6', method="algorithm")
1647
def is_permutation_equivalent(self,other,algorithm=None):
1648
"""
1649
Returns ``True`` if ``self`` and ``other`` are permutation equivalent
1650
codes and ``False`` otherwise.
1651
1652
The ``algorithm="verbose"`` option also returns a permutation (if
1653
``True``) sending ``self`` to ``other``.
1654
1655
Uses Robert Miller's double coset partition refinement work.
1656
1657
EXAMPLES::
1658
1659
sage: P.<x> = PolynomialRing(GF(2),"x")
1660
sage: g = x^3+x+1
1661
sage: C1 = CyclicCodeFromGeneratingPolynomial(7,g); C1
1662
Linear code of length 7, dimension 4 over Finite Field of size 2
1663
sage: C2 = HammingCode(3,GF(2)); C2
1664
Linear code of length 7, dimension 4 over Finite Field of size 2
1665
sage: C1.is_permutation_equivalent(C2)
1666
True
1667
sage: C1.is_permutation_equivalent(C2,algorithm="verbose")
1668
(True, (3,4)(5,7,6))
1669
sage: C1 = RandomLinearCode(10,5,GF(2))
1670
sage: C2 = RandomLinearCode(10,5,GF(3))
1671
sage: C1.is_permutation_equivalent(C2)
1672
False
1673
"""
1674
from sage.groups.perm_gps.partn_ref.refinement_binary import NonlinearBinaryCodeStruct
1675
F = self.base_ring()
1676
F_o = other.base_ring()
1677
q = F.order()
1678
G = self.gen_mat()
1679
n = self.length()
1680
n_o = other.length()
1681
if F != F_o or n != n_o:
1682
return False
1683
k = len(G.rows())
1684
MS = MatrixSpace(F,q**k,n)
1685
CW1 = MS(self.list())
1686
CW2 = MS(other.list())
1687
B1 = NonlinearBinaryCodeStruct(CW1)
1688
B2 = NonlinearBinaryCodeStruct(CW2)
1689
ans = B1.is_isomorphic(B2)
1690
if ans!=False:
1691
if algorithm=="verbose":
1692
Sn = SymmetricGroup(n)
1693
return True, Sn([i+1 for i in ans])**(-1)
1694
return True
1695
return False
1696
1697
def is_self_dual(self):
1698
"""
1699
Returns ``True`` if the code is self-dual (in the usual Hamming inner
1700
product) and ``False`` otherwise.
1701
1702
EXAMPLES::
1703
1704
sage: C = ExtendedBinaryGolayCode()
1705
sage: C.is_self_dual()
1706
True
1707
sage: C = HammingCode(3,GF(2))
1708
sage: C.is_self_dual()
1709
False
1710
"""
1711
return self == self.dual_code()
1712
1713
1714
def is_self_orthogonal(self):
1715
"""
1716
Returns ``True`` if this code is self-orthogonal and ``False``
1717
otherwise.
1718
1719
A code is self-orthogonal if it is a subcode of its dual.
1720
1721
EXAMPLES::
1722
1723
sage: C = ExtendedBinaryGolayCode()
1724
sage: C.is_self_orthogonal()
1725
True
1726
sage: C = HammingCode(3,GF(2))
1727
sage: C.is_self_orthogonal()
1728
False
1729
sage: C = QuasiQuadraticResidueCode(11) # requires optional GAP package Guava
1730
sage: C.is_self_orthogonal() # requires optional GAP package Guava
1731
True
1732
"""
1733
return self.is_subcode(self.dual_code())
1734
1735
def is_galois_closed(self):
1736
r"""
1737
Checks if ``self`` is equal to its Galois closure.
1738
1739
EXAMPLES::
1740
1741
sage: C = HammingCode(3,GF(4,"a"))
1742
sage: C.is_galois_closed()
1743
False
1744
"""
1745
F = self.base_ring()
1746
p = F.characteristic()
1747
return self == self.galois_closure(GF(p))
1748
1749
def is_subcode(self, other):
1750
"""
1751
Returns ``True`` if ``self`` is a subcode of ``other``.
1752
1753
EXAMPLES::
1754
1755
sage: C1 = HammingCode(3,GF(2))
1756
sage: G1 = C1.gen_mat()
1757
sage: G2 = G1.matrix_from_rows([0,1,2])
1758
sage: C2 = LinearCode(G2)
1759
sage: C2.is_subcode(C1)
1760
True
1761
sage: C1.is_subcode(C2)
1762
False
1763
sage: C3 = C1.extended_code()
1764
sage: C1.is_subcode(C3)
1765
False
1766
sage: C4 = C1.punctured([1])
1767
sage: C4.is_subcode(C1)
1768
False
1769
sage: C5 = C1.shortened([1])
1770
sage: C5.is_subcode(C1)
1771
False
1772
sage: C1 = HammingCode(3,GF(9,"z"))
1773
sage: G1 = C1.gen_mat()
1774
sage: G2 = G1.matrix_from_rows([0,1,2])
1775
sage: C2 = LinearCode(G2)
1776
sage: C2.is_subcode(C1)
1777
True
1778
"""
1779
G = self.gen_mat()
1780
for r in G.rows():
1781
if not(r in other):
1782
return False
1783
return True
1784
1785
def length(self):
1786
r"""
1787
Returns the length of this code.
1788
1789
EXAMPLES::
1790
1791
sage: C = HammingCode(3,GF(2))
1792
sage: C.length()
1793
7
1794
"""
1795
return self.__length
1796
1797
def list(self):
1798
r"""
1799
Return a list of all elements of this linear code.
1800
1801
EXAMPLES::
1802
1803
sage: C = HammingCode(3,GF(2))
1804
sage: Clist = C.list()
1805
sage: Clist[5]; Clist[5] in C
1806
(1, 0, 1, 0, 1, 0, 1)
1807
True
1808
"""
1809
return self.gen_mat().row_space().list()
1810
1811
def _magma_init_(self, magma):
1812
r"""
1813
Retun a string representation in Magma of this linear code.
1814
1815
EXAMPLES::
1816
1817
sage: C = HammingCode(3,GF(2))
1818
sage: Cm = magma(C) # optional - magma, indirect doctest
1819
sage: Cm.MinimumWeight() # optional - magma
1820
3
1821
1822
"""
1823
G = magma(self.gen_mat())._ref()
1824
s = 'LinearCode(%s)' % G
1825
return s
1826
1827
@rename_keyword(deprecated='Sage version 4.6', method="algorithm")
1828
def minimum_distance(self, algorithm=None):
1829
r"""
1830
Returns the minimum distance of this linear code.
1831
1832
By default, this uses a GAP kernel function (in C and not part of
1833
Guava) written by Steve Linton. If ``algorithm="guava"`` is set and
1834
`q` is 2 or 3 then this uses a very fast program written in C written
1835
by CJ Tjhal. (This is much faster, except in some small examples.)
1836
1837
Raises a ``ValueError`` in case there is no non-zero vector in this
1838
linear code.
1839
1840
INPUT:
1841
1842
- ``algorithm`` - Method to be used, ``None`` or ``"guava"``
1843
(default: ``None``)
1844
1845
OUTPUT:
1846
1847
- Integer, minimum distance of this code
1848
1849
EXAMPLES::
1850
1851
sage: MS = MatrixSpace(GF(3),4,7)
1852
sage: G = MS([[1,1,1,0,0,0,0], [1,0,0,1,1,0,0], [0,1,0,1,0,1,0], [1,1,0,1,0,0,1]])
1853
sage: C = LinearCode(G)
1854
sage: C.minimum_distance()
1855
3
1856
sage: C.minimum_distance(algorithm="guava") # requires optional GAP package Guava
1857
3
1858
sage: C = HammingCode(2,GF(4,"a")); C
1859
Linear code of length 5, dimension 3 over Finite Field in a of size 2^2
1860
sage: C.minimum_distance()
1861
3
1862
1863
This shows that trac ticket #6486 has been resolved::
1864
1865
sage: G = matrix(GF(2),[[0,0,0]])
1866
sage: C = LinearCode(G)
1867
sage: C.minimum_distance()
1868
Traceback (most recent call last):
1869
...
1870
ValueError: this linear code contains no non-zero vector
1871
"""
1872
# Special code to handle the case where there is no non-zero vector.
1873
if self.dimension() == 0:
1874
raise ValueError, "this linear code contains no non-zero vector"
1875
1876
#sage: C.minimum_distance_upper_bound() # optional (net connection)
1877
#5
1878
# sage: C.minimum_distance_why() # optional (net connection)
1879
# Ub(10,5) = 5 follows by the Griesmer bound.
1880
F = self.base_ring()
1881
q = F.order()
1882
G = self.gen_mat()
1883
n = self.length()
1884
k = self.dimension()
1885
gapG = gap(G)
1886
if (q == 2 or q == 3) and algorithm=="guava":
1887
C = gapG.GeneratorMatCode(gap(F))
1888
d = C.MinimumWeight()
1889
#print "Running Guava's MinimumWeight ...\n"
1890
return ZZ(d)
1891
Gstr = "%s*Z(%s)^0"%(gapG, q)
1892
return hamming_weight(min_wt_vec_gap(Gstr,n,k,F))
1893
1894
def module_composition_factors(self, gp):
1895
r"""
1896
Prints the GAP record of the Meataxe composition factors module in
1897
Meataxe notation. This uses GAP but not Guava.
1898
1899
EXAMPLES::
1900
1901
sage: MS = MatrixSpace(GF(2),4,8)
1902
sage: G = MS([[1,0,0,0,1,1,1,0],[0,1,1,1,0,0,0,0],[0,0,0,0,0,0,0,1],[0,0,0,0,0,1,0,0]])
1903
sage: C = LinearCode(G)
1904
sage: gp = C.permutation_automorphism_group()
1905
1906
Now type "C.module_composition_factors(gp)" to get the record printed.
1907
"""
1908
F = self.base_ring()
1909
q = F.order()
1910
gens = gp.gens()
1911
G = self.gen_mat()
1912
n = len(G.columns())
1913
MS = MatrixSpace(F,n,n)
1914
mats = [] # initializing list of mats by which the gens act on self
1915
Fn = VectorSpace(F, n)
1916
W = Fn.subspace_with_basis(G.rows()) # this is self
1917
for g in gens:
1918
p = MS(g.matrix())
1919
m = [W.coordinate_vector(r*p) for r in G.rows()]
1920
mats.append(m)
1921
mats_str = str(gap([[list(r) for r in m] for m in mats]))
1922
gap.eval("M:=GModuleByMats("+mats_str+", GF("+str(q)+"))")
1923
print gap("MTX.CompositionFactors( M )")
1924
1925
@rename_keyword(deprecated='Sage version 4.6', method="algorithm")
1926
def permutation_automorphism_group(self, algorithm="partition"):
1927
r"""
1928
If `C` is an `[n,k,d]` code over `F`, this function computes the
1929
subgroup `Aut(C) \subset S_n` of all permutation automorphisms of `C`.
1930
The binary case always uses the (default) partition refinement
1931
algorithm of Robert Miller.
1932
1933
Note that if the base ring of `C` is `GF(2)` then this is the full
1934
automorphism group.
1935
1936
INPUT:
1937
1938
- ``algorithm`` - If ``"gap"`` then GAP's MatrixAutomorphism function
1939
(written by Thomas Breuer) is used. The implementation combines an
1940
idea of mine with an improvement suggested by Cary Huffman. If
1941
``"gap+verbose"`` then code-theoretic data is printed out at
1942
several stages of the computation. If ``"partition"`` then the
1943
(default) partition refinement algorithm of Robert Miller is used.
1944
1945
OUTPUT:
1946
1947
- Permutation automorphism group
1948
1949
EXAMPLES::
1950
1951
sage: MS = MatrixSpace(GF(2),4,8)
1952
sage: G = MS([[1,0,0,0,1,1,1,0],[0,1,1,1,0,0,0,0],[0,0,0,0,0,0,0,1],[0,0,0,0,0,1,0,0]])
1953
sage: C = LinearCode(G)
1954
sage: C
1955
Linear code of length 8, dimension 4 over Finite Field of size 2
1956
sage: G = C.permutation_automorphism_group()
1957
sage: G.order()
1958
144
1959
1960
A less easy example involves showing that the permutation
1961
automorphism group of the extended ternary Golay code is the
1962
Mathieu group `M_{11}`.
1963
1964
::
1965
1966
sage: C = ExtendedTernaryGolayCode()
1967
sage: M11 = MathieuGroup(11)
1968
sage: M11.order()
1969
7920
1970
sage: G = C.permutation_automorphism_group() # long time (6s on sage.math, 2011)
1971
sage: G.is_isomorphic(M11) # long time
1972
True
1973
1974
Other examples::
1975
1976
sage: C = ExtendedBinaryGolayCode()
1977
sage: G = C.permutation_automorphism_group()
1978
sage: G.order()
1979
244823040
1980
sage: C = HammingCode(5, GF(2))
1981
sage: G = C.permutation_automorphism_group()
1982
sage: G.order()
1983
9999360
1984
sage: C = HammingCode(2,GF(3)); C
1985
Linear code of length 4, dimension 2 over Finite Field of size 3
1986
sage: C.permutation_automorphism_group(algorithm="partition")
1987
Permutation Group with generators [(1,3,4)]
1988
sage: C = HammingCode(2,GF(4,"z")); C
1989
Linear code of length 5, dimension 3 over Finite Field in z of size 2^2
1990
sage: C.permutation_automorphism_group(algorithm="partition")
1991
Permutation Group with generators [(1,3)(4,5), (1,4)(3,5)]
1992
sage: C.permutation_automorphism_group(algorithm="gap") # requires optional GAP package Guava
1993
Permutation Group with generators [(1,3)(4,5), (1,4)(3,5)]
1994
sage: C = TernaryGolayCode()
1995
sage: C.permutation_automorphism_group(algorithm="gap") # requires optional GAP package Guava
1996
Permutation Group with generators [(3,4)(5,7)(6,9)(8,11), (3,5,8)(4,11,7)(6,9,10), (2,3)(4,6)(5,8)(7,10), (1,2)(4,11)(5,8)(9,10)]
1997
1998
However, the option ``algorithm="gap+verbose"``, will print out::
1999
2000
Minimum distance: 5 Weight distribution: [1, 0, 0, 0, 0, 132, 132,
2001
0, 330, 110, 0, 24]
2002
2003
Using the 132 codewords of weight 5 Supergroup size: 39916800
2004
2005
in addition to the output of
2006
``C.permutation_automorphism_group(algorithm="gap")``.
2007
"""
2008
F = self.base_ring()
2009
q = F.order()
2010
G = self.gen_mat() if 2*self.dimension() <= self.length() else self.dual_code().gen_mat()
2011
n = len(G.columns())
2012
k = len(G.rows())
2013
if "gap" in algorithm:
2014
wts = self.spectrum() # bottleneck 1
2015
nonzerowts = [i for i in range(len(wts)) if wts[i]!=0]
2016
Sn = SymmetricGroup(n)
2017
Gp = gap("SymmetricGroup(%s)"%n) # initializing G in gap
2018
Gstr = str(gap(G))
2019
gap.eval("C:=GeneratorMatCode("+Gstr+",GF("+str(q)+"))")
2020
gap.eval("eltsC:=Elements(C)")
2021
if algorithm=="gap+verbose":
2022
print "\n Minimum distance: %s \n Weight distribution: \n %s"%(nonzerowts[1],wts)
2023
stop = 0 # only stop if all gens are autos
2024
for i in range(1,len(nonzerowts)):
2025
if stop == 1:
2026
break
2027
wt = nonzerowts[i]
2028
if algorithm=="gap+verbose":
2029
size = Gp.Size()
2030
print "\n Using the %s codewords of weight %s \n Supergroup size: \n %s\n "%(wts[wt],wt,size)
2031
gap.eval("Cwt:=Filtered(eltsC,c->WeightCodeword(c)=%s)"%wt) # bottleneck 2 (repeated
2032
gap.eval("matCwt:=List(Cwt,c->VectorCodeword(c))") # for each i until stop = 1)
2033
A = gap("MatrixAutomorphisms(matCwt)")
2034
#print "A = ",A, "\n Gp = ", Gp, "\n strGp = ", str(Gp)
2035
G2 = gap("Intersection2(%s,%s)"%(str(A).replace("\n",""),str(Gp).replace("\n",""))) # bottleneck 3
2036
Gp = G2
2037
if Gp.Size()==1:
2038
return PermutationGroup([()])
2039
autgp_gens = Gp.GeneratorsOfGroup()
2040
gens = [Sn(str(x).replace("\n","")) for x in autgp_gens]
2041
stop = 1 # get ready to stop
2042
for x in gens: # if one of these gens is not an auto then don't stop
2043
if not(self.is_permutation_automorphism(x)):
2044
stop = 0
2045
break
2046
G = PermutationGroup(gens)
2047
return G
2048
if algorithm=="partition":
2049
if q == 2:
2050
from sage.groups.perm_gps.partn_ref.refinement_binary import LinearBinaryCodeStruct
2051
B = LinearBinaryCodeStruct(G)
2052
autgp = B.automorphism_group()
2053
L = [[j+1 for j in gen] for gen in autgp[0]]
2054
AutGp = PermutationGroup(L)
2055
else:
2056
from sage.groups.perm_gps.partn_ref.refinement_matrices import MatrixStruct
2057
from sage.matrix.constructor import matrix
2058
weights = {}
2059
for c in self:
2060
wt = hamming_weight(c)
2061
if wt not in weights:
2062
weights[wt] = [c]
2063
else:
2064
weights[wt].append(c)
2065
weights.pop(0)
2066
AutGps = []
2067
for wt, words in weights.iteritems():
2068
M = MatrixStruct(matrix(words))
2069
autgp = M.automorphism_group()
2070
L = [[j+1 for j in gen] for gen in autgp[0]]
2071
G = PermutationGroup(L)
2072
AutGps.append(G)
2073
if len(AutGps) > 0:
2074
AutGp = AutGps[0]
2075
for G in AutGps[1:]:
2076
AutGp = AutGp.intersection(G)
2077
else:
2078
return PermutationGroup([])
2079
return AutGp
2080
raise NotImplementedError("The only algorithms implemented currently are 'gap', 'gap+verbose', and 'partition'.")
2081
2082
def permuted_code(self, p):
2083
r"""
2084
Returns the permuted code, which is equivalent to ``self`` via the
2085
column permutation ``p``.
2086
2087
EXAMPLES::
2088
2089
sage: C = HammingCode(3,GF(2))
2090
sage: G = C.permutation_automorphism_group(); G
2091
Permutation Group with generators [(4,5)(6,7), (4,6)(5,7), (2,3)(6,7), (2,4)(3,5), (1,2)(5,6)]
2092
sage: g = G("(2,3)(6,7)")
2093
sage: Cg = C.permuted_code(g)
2094
sage: Cg
2095
Linear code of length 7, dimension 4 over Finite Field of size 2
2096
sage: C == Cg
2097
True
2098
"""
2099
F = self.base_ring()
2100
G = self.gen_mat()
2101
n = len(G.columns())
2102
MS = MatrixSpace(F,n,n)
2103
Gp = G*MS(p.matrix().rows())
2104
return LinearCode(Gp)
2105
2106
def punctured(self, L):
2107
r"""
2108
Returns the code punctured at the positions `L`,
2109
`L \subset \{1,2,...,n\}`. If this code `C` is of length `n` in
2110
GF(q) then the code `C^L` obtained from `C` by puncturing at the
2111
positions in `L` is the code of length `n-L` consisting of codewords
2112
of `C` which have their `i-th` coordinate deleted if `i \in L` and
2113
left alone if `i\notin L`:
2114
2115
.. math::
2116
2117
C^L = \{(c_{i_1},...,c_{i_N})\ |\ (c_1,...,c_n)\in C\},
2118
2119
where `\{1,2,...,n\}-T = \{i_1,...,i_N\}`. In particular, if `L=\{j\}`
2120
then `C^L` is simply the code obtainen from `C` by deleting the `j-th`
2121
coordinate of each codeword. The code `C^L` is called the punctured
2122
code at `L`. The dimension of `C^L` can decrease if `|L|>d-1`.
2123
2124
INPUT:
2125
2126
- ``L`` - Subset of `\{1,...,n\}`, where `n` is the length of ``self``
2127
2128
OUTPUT:
2129
2130
- Linear code, the punctured code described above
2131
2132
EXAMPLES::
2133
2134
sage: C = HammingCode(3,GF(2))
2135
sage: C.punctured([1,2])
2136
Linear code of length 5, dimension 4 over Finite Field of size 2
2137
"""
2138
G = self.gen_mat()
2139
GL = G.matrix_from_columns([i for i in range(G.ncols()) if i not in L])
2140
return LinearCode(GL.row_space().basis_matrix())
2141
2142
def random_element(self, *args, **kwds):
2143
"""
2144
Returns a random codeword; passes other positional and keyword
2145
arguments to ``random_element()`` method of vector space.
2146
2147
OUTPUT:
2148
2149
- Random element of the vector space of this code
2150
2151
EXAMPLES::
2152
2153
sage: C = HammingCode(3,GF(4,'a'))
2154
sage: C.random_element() # random test
2155
(1, 0, 0, a + 1, 1, a, a, a + 1, a + 1, 1, 1, 0, a + 1, a, 0, a, a, 0, a, a, 1)
2156
2157
Passes extra positional or keyword arguments through::
2158
2159
sage: C.random_element(prob=.5, distribution='1/n') # random test
2160
(1, 0, a, 0, 0, 0, 0, a + 1, 0, 0, 0, 0, 0, 0, 0, 0, a + 1, a + 1, 1, 0, 0)
2161
"""
2162
V = self.ambient_space()
2163
S = V.subspace(self.basis())
2164
return S.random_element(*args, **kwds)
2165
2166
def redundancy_matrix(C):
2167
"""
2168
If C is a linear [n,k,d] code then this function returns a
2169
`k \times (n-k)` matrix A such that G = (I,A) generates a code (in
2170
standard form) equivalent to C. If C is already in standard form and
2171
G = (I,A) is its generator matrix then this function simply returns
2172
that A.
2173
2174
OUTPUT:
2175
2176
- Matrix, the redundancy matrix
2177
2178
EXAMPLES::
2179
2180
sage: C = HammingCode(3,GF(2))
2181
sage: C.gen_mat()
2182
[1 0 0 0 0 1 1]
2183
[0 1 0 0 1 0 1]
2184
[0 0 1 0 1 1 0]
2185
[0 0 0 1 1 1 1]
2186
sage: C.redundancy_matrix()
2187
[0 1 1]
2188
[1 0 1]
2189
[1 1 0]
2190
[1 1 1]
2191
sage: C.standard_form()[0].gen_mat()
2192
[1 0 0 0 0 1 1]
2193
[0 1 0 0 1 0 1]
2194
[0 0 1 0 1 1 0]
2195
[0 0 0 1 1 1 1]
2196
sage: C = HammingCode(2,GF(3))
2197
sage: C.gen_mat()
2198
[1 0 1 1]
2199
[0 1 1 2]
2200
sage: C.redundancy_matrix()
2201
[1 1]
2202
[1 2]
2203
"""
2204
n = C.length()
2205
k = C.dimension()
2206
C1 = C.standard_form()[0]
2207
G1 = C1.gen_mat()
2208
return G1.matrix_from_columns(range(k,n))
2209
2210
def sd_duursma_data(C, i):
2211
r"""
2212
Returns the Duursma data `v` and `m` of this formally s.d. code `C`
2213
and the type number `i` in (1,2,3,4). Does *not* check if this code
2214
is actually sd.
2215
2216
INPUT:
2217
2218
- ``i`` - Type number
2219
2220
OUTPUT:
2221
2222
- Pair ``(v, m)`` as in Duursma [D]_
2223
2224
REFERENCES:
2225
2226
.. [D] I. Duursma, "Extremal weight enumerators and ultraspherical
2227
polynomials"
2228
2229
EXAMPLES::
2230
2231
sage: MS = MatrixSpace(GF(2),2,4)
2232
sage: G = MS([1,1,0,0,0,0,1,1])
2233
sage: C = LinearCode(G)
2234
sage: C == C.dual_code() # checks that C is self dual
2235
True
2236
sage: for i in [1,2,3,4]: print C.sd_duursma_data(i)
2237
[2, -1]
2238
[2, -3]
2239
[2, -2]
2240
[2, -1]
2241
"""
2242
n = C.length()
2243
d = C.minimum_distance()
2244
if i == 1:
2245
v = (n-4*d)/2 + 4
2246
m = d-3
2247
if i == 2:
2248
v = (n-6*d)/8 + 3
2249
m = d-5
2250
if i == 3:
2251
v = (n-4*d)/4 + 3
2252
m = d-4
2253
if i == 4:
2254
v = (n-3*d)/2 + 3
2255
m = d-3
2256
return [v,m]
2257
2258
def sd_duursma_q(C,i,d0):
2259
r"""
2260
INPUT:
2261
2262
- ``C`` - sd code; does *not* check if `C` is actually an sd code
2263
- ``i`` - Type number, one of 1,2,3,4
2264
- ``d0`` - Divisor, the smallest integer such that each `A_i > 0` iff
2265
`i` is divisible by `d0`
2266
2267
OUTPUT:
2268
2269
- Coefficients `q_0, q_1, ...` of `q(T)` as in Duursma [D]_
2270
2271
REFERENCES:
2272
2273
- [D] - I. Duursma, "Extremal weight enumerators and ultraspherical
2274
polynomials"
2275
2276
EXAMPLES::
2277
2278
sage: C1 = HammingCode(3,GF(2))
2279
sage: C2 = C1.extended_code(); C2
2280
Linear code of length 8, dimension 4 over Finite Field of size 2
2281
sage: C2.is_self_dual()
2282
True
2283
sage: C2.sd_duursma_q(1,1)
2284
2/5*T^2 + 2/5*T + 1/5
2285
sage: C2.sd_duursma_q(3,1)
2286
3/5*T^4 + 1/5*T^3 + 1/15*T^2 + 1/15*T + 1/15
2287
"""
2288
q = (C.base_ring()).order()
2289
n = C.length()
2290
d = C.minimum_distance()
2291
d0 = C.divisor()
2292
if i==1 or i==2:
2293
if d>d0:
2294
c0 = QQ((n-d)*rising_factorial(d-d0,d0+1)*C.spectrum()[d])/rising_factorial(n-d0-1,d0+2)
2295
else:
2296
c0 = QQ((n-d)*C.spectrum()[d])/rising_factorial(n-d0-1,d0+2)
2297
if i==3 or i==4:
2298
if d>d0:
2299
c0 = rising_factorial(d-d0,d0+1)*C.spectrum()[d]/((q-1)*rising_factorial(n-d0,d0+1))
2300
else:
2301
c0 = C.spectrum()[d]/((q-1)*rising_factorial(n-d0,d0+1))
2302
v = ZZ(C.sd_duursma_data(i)[0])
2303
m = ZZ(C.sd_duursma_data(i)[1])
2304
if m<0 or v<0:
2305
raise ValueError("This case not implemented.")
2306
PR = PolynomialRing(QQ,"T")
2307
T = PR.gen()
2308
if i == 1:
2309
coefs = PR(c0*(1+3*T+2*T**2)**m*(2*T**2+2*T+1)**v).list()
2310
qc = [coefs[j]/binomial(4*m+2*v,m+j) for j in range(2*m+2*v+1)]
2311
q = PR(qc)
2312
if i == 2:
2313
F = ((T+1)**8+14*T**4*(T+1)**4+T**8)**v
2314
coefs = (c0*(1+T)**m*(1+4*T+6*T**2+4*T**3)**m*F).coeffs()
2315
qc = [coefs[j]/binomial(6*m+8*v,m+j) for j in range(4*m+8*v+1)]
2316
q = PR(qc)
2317
if i == 3:
2318
F = (3*T**2+4*T+1)**v*(1+3*T**2)**v
2319
# Note that: (3*T**2+4*T+1)(1+3*T**2)=(T+1)**4+8*T**3*(T+1)
2320
coefs = (c0*(1+3*T+3*T**2)**m*F).coeffs()
2321
qc = [coefs[j]/binomial(4*m+4*v,m+j) for j in range(2*m+4*v+1)]
2322
q = PR(qc)
2323
if i == 4:
2324
coefs = (c0*(1+2*T)**m*(4*T**2+2*T+1)**v).coeffs()
2325
qc = [coefs[j]/binomial(3*m+2*v,m+j) for j in range(m+2*v+1)]
2326
q = PR(qc)
2327
return q/q(1)
2328
2329
def sd_zeta_polynomial(C, typ=1):
2330
r"""
2331
Returns the Duursma zeta function of a self-dual code using the
2332
construction in [D]_.
2333
2334
INPUT:
2335
2336
- ``typ`` - Integer, type of this s.d. code; one of 1,2,3, or
2337
4 (default: 1)
2338
2339
OUTPUT:
2340
2341
- Polynomial
2342
2343
EXAMPLES::
2344
2345
sage: C1 = HammingCode(3,GF(2))
2346
sage: C2 = C1.extended_code(); C2
2347
Linear code of length 8, dimension 4 over Finite Field of size 2
2348
sage: C2.is_self_dual()
2349
True
2350
sage: C2.sd_zeta_polynomial()
2351
2/5*T^2 + 2/5*T + 1/5
2352
sage: C2.zeta_polynomial()
2353
2/5*T^2 + 2/5*T + 1/5
2354
sage: P = C2.sd_zeta_polynomial(); P(1)
2355
1
2356
sage: F.<z> = GF(4,"z")
2357
sage: MS = MatrixSpace(F, 3, 6)
2358
sage: G = MS([[1,0,0,1,z,z],[0,1,0,z,1,z],[0,0,1,z,z,1]])
2359
sage: C = LinearCode(G) # the "hexacode"
2360
sage: C.sd_zeta_polynomial(4)
2361
1
2362
2363
It is a general fact about Duursma zeta polynomials that `P(1) = 1`.
2364
2365
REFERENCES:
2366
2367
- [D] I. Duursma, "Extremal weight enumerators and ultraspherical
2368
polynomials"
2369
"""
2370
d0 = C.divisor()
2371
P = C.sd_duursma_q(typ,d0)
2372
PR = P.parent()
2373
T = FractionField(PR).gen()
2374
if typ == 1:
2375
P0 = P
2376
if typ == 2:
2377
P0 = P/(1-2*T+2*T**2)
2378
if typ == 3:
2379
P0 = P/(1+3*T**2)
2380
if typ == 4:
2381
P0 = P/(1+2*T)
2382
return P0/P0(1)
2383
2384
def shortened(self, L):
2385
r"""
2386
Returns the code shortened at the positions ``L``, where
2387
`L \subset \{1,2,...,n\}`.
2388
2389
Consider the subcode `C(L)` consisting of all codewords `c\in C` which
2390
satisfy `c_i=0` for all `i\in L`. The punctured code `C(L)^L` is
2391
called the shortened code on `L` and is denoted `C_L`. The code
2392
constructed is actually only isomorphic to the shortened code defined
2393
in this way.
2394
2395
By Theorem 1.5.7 in [HP], `C_L` is `((C^\perp)^L)^\perp`. This is used
2396
in the construction below.
2397
2398
INPUT:
2399
2400
- ``L`` - Subset of `\{1,...,n\}`, where `n` is the length of this code
2401
2402
OUTPUT:
2403
2404
- Linear code, the shortened code described above
2405
2406
EXAMPLES::
2407
2408
sage: C = HammingCode(3,GF(2))
2409
sage: C.shortened([1,2])
2410
Linear code of length 5, dimension 2 over Finite Field of size 2
2411
"""
2412
Cd = self.dual_code()
2413
Cdp = Cd.punctured(L)
2414
Cdpd = Cdp.dual_code()
2415
Gs = Cdpd.gen_mat()
2416
return LinearCode(Gs)
2417
2418
@rename_keyword(deprecated='Sage version 4.6', method="algorithm")
2419
def spectrum(self, algorithm=None):
2420
r"""
2421
Returns the spectrum of ``self`` as a list.
2422
2423
The default algorithm uses a GAP kernel function (in C) written by
2424
Steve Linton.
2425
2426
INPUT:
2427
2428
- ``algorithm`` - ``None``, ``"gap"``, ``"leon"``, or ``"binary"``;
2429
defaults to ``"gap"`` except in the binary case. If ``"gap"`` then
2430
uses the GAP function, if ``"leon"`` then uses Jeffrey Leon's
2431
software via Guava, and if ``"binary"`` then uses Sage native Cython
2432
code
2433
2434
- List, the spectrum
2435
2436
The optional algorithm (``"leon"``) may create a stack smashing error
2437
and a traceback but should return the correct answer. It appears to run
2438
much faster than the GAP algorithm in some small examples and much
2439
slower than the GAP algorithm in other larger examples.
2440
2441
EXAMPLES::
2442
2443
sage: MS = MatrixSpace(GF(2),4,7)
2444
sage: G = MS([[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]])
2445
sage: C = LinearCode(G)
2446
sage: C.spectrum()
2447
[1, 0, 0, 7, 7, 0, 0, 1]
2448
sage: F.<z> = GF(2^2,"z")
2449
sage: C = HammingCode(2, F); C
2450
Linear code of length 5, dimension 3 over Finite Field in z of size 2^2
2451
sage: C.spectrum()
2452
[1, 0, 0, 30, 15, 18]
2453
sage: C = HammingCode(3,GF(2)); C
2454
Linear code of length 7, dimension 4 over Finite Field of size 2
2455
sage: C.spectrum(algorithm="leon") # requires optional GAP package Guava
2456
[1, 0, 0, 7, 7, 0, 0, 1]
2457
sage: C.spectrum(algorithm="gap")
2458
[1, 0, 0, 7, 7, 0, 0, 1]
2459
sage: C.spectrum(algorithm="binary")
2460
[1, 0, 0, 7, 7, 0, 0, 1]
2461
sage: C = HammingCode(3,GF(3)); C
2462
Linear code of length 13, dimension 10 over Finite Field of size 3
2463
sage: C.spectrum() == C.spectrum(algorithm="leon") # requires optional GAP package Guava
2464
True
2465
sage: C = HammingCode(2,GF(5)); C
2466
Linear code of length 6, dimension 4 over Finite Field of size 5
2467
sage: C.spectrum() == C.spectrum(algorithm="leon") # requires optional GAP package Guava
2468
True
2469
sage: C = HammingCode(2,GF(7)); C
2470
Linear code of length 8, dimension 6 over Finite Field of size 7
2471
sage: C.spectrum() == C.spectrum(algorithm="leon") # requires optional GAP package Guava
2472
True
2473
2474
"""
2475
if algorithm is None:
2476
if self.base_ring().order() == 2:
2477
algorithm = "binary"
2478
else:
2479
algorithm = "gap"
2480
n = self.length()
2481
F = self.base_ring()
2482
G = self.gen_mat()
2483
if algorithm=="gap":
2484
Gstr = G._gap_init_()
2485
spec = wtdist_gap(Gstr,n,F)
2486
return spec
2487
elif algorithm=="binary":
2488
from sage.coding.binary_code import weight_dist
2489
return weight_dist(self.gen_mat())
2490
elif algorithm=="leon":
2491
if not(F.order() in [2,3,5,7]):
2492
raise NotImplementedError("The algorithm 'leon' is only implemented for q = 2,3,5,7.")
2493
# The GAP command DirectoriesPackageLibrary tells the location of the latest
2494
# version of the Guava libraries, so gives us the location of the Guava binaries too.
2495
guava_bin_dir = gap.eval('DirectoriesPackageLibrary( "guava" )')
2496
guava_bin_dir = guava_bin_dir[1:-1].strip()[5:-6] + 'bin/'
2497
input = code2leon(self)
2498
from sage.misc.misc import tmp_filename
2499
output = tmp_filename()
2500
import os
2501
status = os.system(guava_bin_dir + "wtdist " + input + "::code > " + output)
2502
if status != 0:
2503
raise RuntimeError("Problem calling Leon's wtdist program. Install gap_packages*.spkg and run './configure ../..; make'.")
2504
f = open(output); lines = f.readlines(); f.close()
2505
wts = [0]*(n+1)
2506
s = 0
2507
for L in lines:
2508
L = L.strip()
2509
if len(L) > 0:
2510
o = ord(L[0])
2511
if o >= 48 and o <= 57:
2512
wt, num = L.split()
2513
wts[eval(wt)] = eval(num)
2514
return wts
2515
else:
2516
raise NotImplementedError("The only algorithms implemented currently are 'gap', 'leon' and 'binary'.")
2517
2518
def standard_form(self):
2519
r"""
2520
Returns the standard form of this linear code.
2521
2522
An `[n,k]` linear code with generator matrix `G` in standard form is
2523
the row-reduced echelon form of `G` is `(I,A)`, where `I` denotes the
2524
`k \times k` identity matrix and `A` is a `k \times (n-k)` block. This
2525
method returns a pair `(C,p)` where `C` is a code permutation
2526
equivalent to ``self`` and `p` in `S_n`, with `n` the length of `C`,
2527
is the permutation sending ``self`` to `C`. This does not call GAP.
2528
2529
Thanks to Frank Luebeck for (the GAP version of) this code.
2530
2531
EXAMPLES::
2532
2533
sage: C = HammingCode(3,GF(2))
2534
sage: C.gen_mat()
2535
[1 0 0 0 0 1 1]
2536
[0 1 0 0 1 0 1]
2537
[0 0 1 0 1 1 0]
2538
[0 0 0 1 1 1 1]
2539
sage: Cs,p = C.standard_form()
2540
sage: p
2541
()
2542
sage: MS = MatrixSpace(GF(3),3,7)
2543
sage: G = MS([[1,0,0,0,1,1,0],[0,1,0,1,0,1,0],[0,0,0,0,0,0,1]])
2544
sage: C = LinearCode(G)
2545
sage: Cs, p = C.standard_form()
2546
sage: p
2547
(3,7)
2548
sage: Cs.gen_mat()
2549
[1 0 0 0 1 1 0]
2550
[0 1 0 1 0 1 0]
2551
[0 0 1 0 0 0 0]
2552
"""
2553
from sage.coding.code_constructions import permutation_action as perm_action
2554
mat = self.gen_mat()
2555
MS = mat.parent()
2556
A = []
2557
k = len(mat.rows())
2558
M = mat.echelon_form()
2559
d = len(mat.columns())
2560
G = SymmetricGroup(d)
2561
perm = G([()])
2562
for i in range(1,k+1):
2563
r = M.rows()[i-1]
2564
j = r.nonzero_positions()[0]
2565
if (j < d and i <> j+1):
2566
perm = perm *G([(i,j+1)])
2567
if perm <> G([()]):
2568
for i in range(k):
2569
r = M.rows()[i]
2570
A.append(perm_action(perm,r))
2571
if perm == G([()]):
2572
A = M
2573
return LinearCode(MS(A)), perm
2574
2575
def support(self):
2576
r"""
2577
Returns the set of indices `j` where `A_j` is nonzero, where
2578
spectrum(self) = `[A_0,A_1,...,A_n]`.
2579
2580
OUTPUT:
2581
2582
- List of integers
2583
2584
EXAMPLES::
2585
2586
sage: C = HammingCode(3,GF(2))
2587
sage: C.spectrum()
2588
[1, 0, 0, 7, 7, 0, 0, 1]
2589
sage: C.support()
2590
[0, 3, 4, 7]
2591
"""
2592
n = self.length()
2593
F = self.base_ring()
2594
V = VectorSpace(F,n+1)
2595
return V(self.spectrum()).support()
2596
2597
def weight_enumerator(self, names="xy"):
2598
"""
2599
Returns the weight enumerator of the code.
2600
2601
INPUT:
2602
2603
- ``names`` - String of length 2, containing two variable names
2604
(default: ``"xy"``)
2605
2606
OUTPUT:
2607
2608
- Polynomial over `\QQ`
2609
2610
EXAMPLES::
2611
2612
sage: C = HammingCode(3,GF(2))
2613
sage: C.weight_enumerator()
2614
x^7 + 7*x^4*y^3 + 7*x^3*y^4 + y^7
2615
sage: C.weight_enumerator(names="st")
2616
s^7 + 7*s^4*t^3 + 7*s^3*t^4 + t^7
2617
"""
2618
spec = self.spectrum()
2619
n = self.length()
2620
R = PolynomialRing(QQ,2,names)
2621
x,y = R.gens()
2622
we = sum([spec[i]*x**(n-i)*y**i for i in range(n+1)])
2623
return we
2624
2625
def zeta_polynomial(self, name="T"):
2626
r"""
2627
Returns the Duursma zeta polynomial of this code.
2628
2629
Assumes that the minimum distances of this code and its dual are
2630
greater than 1. Prints a warning to ``stdout`` otherwise.
2631
2632
INPUT:
2633
2634
- ``name`` - String, variable name (default: ``"T"``)
2635
2636
OUTPUT:
2637
2638
- Polynomial over `\QQ`
2639
2640
EXAMPLES::
2641
2642
sage: C = HammingCode(3,GF(2))
2643
sage: C.zeta_polynomial()
2644
2/5*T^2 + 2/5*T + 1/5
2645
sage: C = best_known_linear_code(6,3,GF(2)) # requires optional GAP package Guava
2646
sage: C.minimum_distance() # requires optional GAP package Guava
2647
3
2648
sage: C.zeta_polynomial() # requires optional GAP package Guava
2649
2/5*T^2 + 2/5*T + 1/5
2650
sage: C = HammingCode(4,GF(2))
2651
sage: C.zeta_polynomial()
2652
16/429*T^6 + 16/143*T^5 + 80/429*T^4 + 32/143*T^3 + 30/143*T^2 + 2/13*T + 1/13
2653
sage: F.<z> = GF(4,"z")
2654
sage: MS = MatrixSpace(F, 3, 6)
2655
sage: G = MS([[1,0,0,1,z,z],[0,1,0,z,1,z],[0,0,1,z,z,1]])
2656
sage: C = LinearCode(G) # the "hexacode"
2657
sage: C.zeta_polynomial()
2658
1
2659
2660
REFERENCES:
2661
2662
- I. Duursma, "From weight enumerators to zeta functions", in
2663
Discrete Applied Mathematics, vol. 111, no. 1-2, pp. 55-73, 2001.
2664
"""
2665
n = self.length()
2666
q = (self.base_ring()).order()
2667
d = self.minimum_distance()
2668
dperp = (self.dual_code()).minimum_distance()
2669
if d == 1 or dperp == 1:
2670
print "\n WARNING: There is no guarantee this function works when the minimum distance"
2671
print " of the code or of the dual code equals 1.\n"
2672
RT = PolynomialRing(QQ,"%s"%name)
2673
R = PolynomialRing(QQ,3,"xy%s"%name)
2674
x,y,T = R.gens()
2675
we = self.weight_enumerator()
2676
A = R(we)
2677
B = A(x+y,y,T)-(x+y)**n
2678
Bs = B.coefficients()
2679
b = [Bs[i]/binomial(n,i+d) for i in range(len(Bs))]
2680
r = n-d-dperp+2
2681
#print B,Bs,b,r
2682
P_coeffs = []
2683
for i in range(len(b)):
2684
if i == 0:
2685
P_coeffs.append(b[0])
2686
if i == 1:
2687
P_coeffs.append(b[1] - (q+1)*b[0])
2688
if i>1:
2689
P_coeffs.append(b[i] - (q+1)*b[i-1] + q*b[i-2])
2690
#print P_coeffs
2691
P = sum([P_coeffs[i]*T**i for i in range(r+1)])
2692
return RT(P)/RT(P)(1)
2693
2694
def zeta_function(self, name="T"):
2695
r"""
2696
Returns the Duursma zeta function of the code.
2697
2698
INPUT:
2699
2700
- ``name`` - String, variable name (default: ``"T"``)
2701
2702
OUTPUT:
2703
2704
- Element of `\QQ(T)`
2705
2706
EXAMPLES::
2707
2708
sage: C = HammingCode(3,GF(2))
2709
sage: C.zeta_function()
2710
(2/5*T^2 + 2/5*T + 1/5)/(2*T^2 - 3*T + 1)
2711
"""
2712
P = self.zeta_polynomial()
2713
q = (self.base_ring()).characteristic()
2714
RT = PolynomialRing(QQ,"%s"%name)
2715
T = RT.gen()
2716
return P/((1-T)*(1-q*T))
2717
2718
weight_distribution = spectrum
2719
2720
def LinearCodeFromVectorSpace(self):
2721
"""
2722
Simply converts a vector subspace `V` of `GF(q)^n` into a `LinearCode`.
2723
2724
EXAMPLES::
2725
2726
sage: V = VectorSpace(GF(2), 8)
2727
sage: L = V.subspace([[1,1,1,1,0,0,0,0],[0,0,0,0,1,1,1,1]])
2728
sage: C = LinearCodeFromVectorSpace(L)
2729
sage: C.gen_mat()
2730
[1 1 1 1 0 0 0 0]
2731
[0 0 0 0 1 1 1 1]
2732
sage: C.minimum_distance()
2733
4
2734
"""
2735
F = self.base_ring()
2736
B = self.basis()
2737
n = len(B[0].list())
2738
k = len(B)
2739
MS = MatrixSpace(F,k,n)
2740
G = MS([B[i].list() for i in range(k)])
2741
return LinearCode(G)
2742
2743
2744