Skip to content

Commit 02d409e

Browse files
committed
Trac #40513, #40514: Allow zero code and add intersection method
1 parent 858268b commit 02d409e

File tree

2 files changed

+113
-49
lines changed

2 files changed

+113
-49
lines changed

src/sage/coding/abstract_code.py

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ def __init__(self, length, default_encoder_name=None,
215215
INPUT:
216216
217217
- ``length`` -- the length of ``self`` (a Python int or a Sage Integer,
218-
must be > 0)
218+
must be >= 0)
219219
220220
- ``default_encoder_name`` -- (default: ``None``) the name of
221221
the default encoder of ``self``
@@ -281,19 +281,24 @@ def __init__(self, length, default_encoder_name=None,
281281
...
282282
ValueError: length must be a Python int or a Sage Integer
283283
284-
If the length of the code is not a nonzero positive integer
285-
(See :issue:`21326`), it will raise an exception::
284+
If the length of the code is negative, it will raise an exception::
286285
287-
sage: C = MyCodeFamily(0)
286+
sage: C = MyCodeFamily(-1)
288287
Traceback (most recent call last):
289288
...
290-
ValueError: length must be a nonzero positive integer
289+
ValueError: length must be a non-negative integer
290+
291+
Codes of length 0 are allowed::
292+
293+
sage: C = MyCodeFamily(0)
294+
sage: C.length()
295+
0
291296
"""
292297

293298
if not isinstance(length, (int, Integer)):
294299
raise ValueError("length must be a Python int or a Sage Integer")
295-
if length <= 0:
296-
raise ValueError("length must be a nonzero positive integer")
300+
if length < 0:
301+
raise ValueError("length must be a non-negative integer")
297302

298303
self._length = length
299304
self._metric = metric
@@ -417,7 +422,7 @@ def __call__(self, m):
417422
sage: C = LinearCode(G)
418423
sage: word = vector((0, 1, 1, 0))
419424
sage: C(word)
420-
(1, 1, 0, 0, 1, 1, 0)
425+
(0, 1, 1, 0, 0, 1, 1)
421426
422427
sage: c = C.random_element()
423428
sage: C(c) == c
@@ -723,14 +728,14 @@ def decode_to_message(self, word, decoder_name=None, *args, **kwargs):
723728
sage: C = LinearCode(G)
724729
sage: word = vector(GF(2), (1, 1, 0, 0, 1, 1, 0))
725730
sage: C.decode_to_message(word)
726-
(0, 1, 1, 0)
731+
(1, 1, 0, 0)
727732
728733
It is possible to manually choose the decoder amongst the list of the available ones::
729734
730735
sage: sorted(C.decoders_available())
731736
['InformationSet', 'NearestNeighbor', 'Syndrome']
732737
sage: C.decode_to_message(word, 'NearestNeighbor')
733-
(0, 1, 1, 0)
738+
(1, 1, 0, 0)
734739
"""
735740
return self.unencode(self.decode_to_code(word, decoder_name, *args, **kwargs), **kwargs)
736741

@@ -882,17 +887,17 @@ def encode(self, word, encoder_name=None, *args, **kwargs):
882887
sage: C = LinearCode(G)
883888
sage: word = vector((0, 1, 1, 0))
884889
sage: C.encode(word)
885-
(1, 1, 0, 0, 1, 1, 0)
890+
(0, 1, 1, 0, 0, 1, 1)
886891
sage: C(word)
887-
(1, 1, 0, 0, 1, 1, 0)
892+
(0, 1, 1, 0, 0, 1, 1)
888893
889894
It is possible to manually choose the encoder amongst the list of the available ones::
890895
891896
sage: sorted(C.encoders_available())
892897
['GeneratorMatrix', 'Systematic']
893898
sage: word = vector((0, 1, 1, 0))
894899
sage: C.encode(word, 'GeneratorMatrix')
895-
(1, 1, 0, 0, 1, 1, 0)
900+
(0, 1, 1, 0, 0, 1, 1)
896901
"""
897902
E = self.encoder(encoder_name, *args, **kwargs)
898903
return E.encode(word)
@@ -1057,7 +1062,7 @@ def unencode(self, c, encoder_name=None, nocheck=False, **kwargs):
10571062
sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0], [1,0,0,1,1,0,0],
10581063
....: [0,1,0,1,0,1,0], [1,1,0,1,0,0,1]])
10591064
sage: C = LinearCode(G)
1060-
sage: c = vector(GF(2), (1, 1, 0, 0, 1, 1, 0))
1065+
sage: c = vector(GF(2), (0, 1, 1, 0, 0, 1, 1))
10611066
sage: C.unencode(c)
10621067
(0, 1, 1, 0)
10631068
"""

src/sage/coding/linear_code.py

Lines changed: 94 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,17 @@ class should inherit from this class. Also ``AbstractLinearCode`` should never
8080
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]])
8181
sage: C = LinearCode(G)
8282
sage: C.basis()
83-
[(1, 1, 1, 0, 0, 0, 0),
84-
(1, 0, 0, 1, 1, 0, 0),
85-
(0, 1, 0, 1, 0, 1, 0),
86-
(1, 1, 0, 1, 0, 0, 1)]
83+
[(1, 0, 0, 0, 0, 1, 1),
84+
(0, 1, 0, 0, 1, 0, 1),
85+
(0, 0, 1, 0, 1, 1, 0),
86+
(0, 0, 0, 1, 1, 1, 1)]
8787
sage: c = C.basis()[1]
8888
sage: c in C
8989
True
9090
sage: c.nonzero_positions()
91-
[0, 3, 4]
91+
[1, 4, 6]
9292
sage: c.support()
93-
[0, 3, 4]
93+
[1, 4, 6]
9494
sage: c.parent()
9595
Vector space of dimension 7 over Finite Field of size 2
9696
@@ -1441,7 +1441,7 @@ def _minimum_weight_codeword(self, algorithm=None):
14411441
sage: G = Matrix(GF(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]])
14421442
sage: C = LinearCode(G)
14431443
sage: C._minimum_weight_codeword() # needs sage.libs.gap
1444-
(0, 1, 0, 1, 0, 1, 0)
1444+
(0, 0, 1, 0, 1, 1, 0)
14451445
14461446
TESTS:
14471447
@@ -2314,35 +2314,43 @@ def __init__(self, generator, d=None):
23142314
23152315
sage: G = matrix(GF(2), [[0,0,0]])
23162316
sage: C = LinearCode(G)
2317-
Traceback (most recent call last):
2318-
...
2319-
ValueError: this linear code contains no nonzero vector
2317+
sage: C
2318+
[3, 0] linear code over GF(2)
2319+
sage: C.dimension()
2320+
0
23202321
"""
23212322

2322-
base_ring = generator.base_ring()
2323+
from sage.matrix.constructor import matrix
2324+
2325+
if isinstance(generator, AbstractLinearCode):
2326+
generator = generator.generator_matrix()
2327+
2328+
if hasattr(generator, 'basis'):
2329+
if generator.dimension() == 0:
2330+
G = matrix(generator.base_ring(), 0, generator.ambient_space().dimension())
2331+
else:
2332+
G = matrix(generator.basis())
2333+
else:
2334+
G = generator
2335+
2336+
base_ring = G.base_ring()
23232337
if not base_ring.is_field():
23242338
raise ValueError("'generator' must be defined on a field (not a ring)")
23252339

2326-
try:
2327-
basis = None
2328-
if hasattr(generator, "nrows"): # generator matrix case
2329-
if generator.rank() < generator.nrows():
2330-
basis = generator.row_space().basis()
2331-
else:
2332-
basis = generator.basis() # vector space etc. case
2333-
if basis is not None:
2334-
from sage.matrix.constructor import matrix
2335-
generator = matrix(base_ring, basis)
2336-
if generator.nrows() == 0:
2337-
raise ValueError("this linear code contains no nonzero vector")
2338-
except AttributeError:
2339-
# Assume input is an AbstractLinearCode, extract its generator matrix
2340-
generator = generator.generator_matrix()
2340+
if G.is_zero() or G.nrows() == 0:
2341+
super().__init__(base_ring, G.ncols(), "GeneratorMatrix", "Syndrome")
2342+
self._dimension = 0
2343+
self._generator_matrix = matrix(base_ring, 0, self.length())
2344+
self._minimum_distance = d
2345+
return
23412346

2342-
super().__init__(base_ring, generator.ncols(),
2347+
G_echelon = G.echelon_form()
2348+
generator_matrix = G_echelon.matrix_from_rows([i for i, r in enumerate(G_echelon) if not r.is_zero()])
2349+
2350+
super().__init__(base_ring, generator_matrix.ncols(),
23432351
"GeneratorMatrix", "Syndrome")
2344-
self._generator_matrix = generator
2345-
self._dimension = generator.rank()
2352+
self._generator_matrix = generator_matrix
2353+
self._dimension = self._generator_matrix.rank()
23462354
self._minimum_distance = d
23472355

23482356
def __hash__(self):
@@ -2382,7 +2390,58 @@ def _latex_(self):
23822390
"""
23832391
return "[%s, %s]\\textnormal{ Linear code over }%s"\
23842392
% (self.length(), self.dimension(), self.base_ring()._latex_())
2393+
2394+
def intersection(self, other):
2395+
"""
2396+
Return the intersection of this linear code with another.
2397+
2398+
The intersection of two linear codes C1 and C2 is the set of all
2399+
codewords that are in both C1 and C2. It is also a linear code.
2400+
2401+
This is computed using the identity:
2402+
C1 ∩ C2 = (C1_dual + C2_dual)_dual
2403+
2404+
INPUT:
2405+
- ``other`` -- a linear code.
2406+
2407+
OUTPUT:
2408+
- a linear code, the intersection of self and other.
2409+
2410+
EXAMPLES:
2411+
sage: F = GF(2)
2412+
sage: G1 = matrix(F, [[1,1,0,0], [0,0,1,1]])
2413+
sage: C1 = LinearCode(G1)
2414+
sage: G2 = matrix(F, [[1,0,1,0], [0,1,0,1]])
2415+
sage: C2 = LinearCode(G2)
2416+
sage: C_int = C1.intersection(C2)
2417+
sage: C_int
2418+
[4, 1] linear code over GF(2)
2419+
sage: c = vector(F, (1,1,1,1))
2420+
sage: c in C_int
2421+
True
2422+
2423+
# Test intersection with the zero code
2424+
sage: C_zero = LinearCode(matrix(F, [[0,0,0,0]]))
2425+
sage: C1.intersection(C_zero)
2426+
[4, 0] linear code over GF(2)
2427+
"""
2428+
if not isinstance(other, AbstractLinearCode):
2429+
raise TypeError("Intersection is only defined between linear codes.")
2430+
if self.base_field() != other.base_field():
2431+
raise TypeError("Codes must be over the same base field.")
2432+
if self.length() != other.length():
2433+
raise ValueError("Codes must have the same length.")
2434+
2435+
C1_dual = self.dual_code()
2436+
C2_dual = other.dual_code()
2437+
2438+
G1d = C1_dual.generator_matrix()
2439+
G2d = C2_dual.generator_matrix()
2440+
G_sum = G1d.stack(G2d)
2441+
sum_of_duals = LinearCode(G_sum)
23852442

2443+
return sum_of_duals.dual_code()
2444+
23862445
def generator_matrix(self, encoder_name=None, **kwargs):
23872446
r"""
23882447
Return a generator matrix of ``self``.
@@ -2401,8 +2460,8 @@ def generator_matrix(self, encoder_name=None, **kwargs):
24012460
sage: G = matrix(GF(3),2,[1,-1,1,-1,1,1])
24022461
sage: code = LinearCode(G)
24032462
sage: code.generator_matrix()
2404-
[1 2 1]
2405-
[2 1 1]
2463+
[1 2 0]
2464+
[0 0 1]
24062465
"""
24072466
if encoder_name is None or encoder_name == 'GeneratorMatrix':
24082467
g = self._generator_matrix
@@ -2493,10 +2552,10 @@ def generator_matrix(self):
24932552
sage: C = LinearCode(G)
24942553
sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C)
24952554
sage: E.generator_matrix()
2496-
[1 1 1 0 0 0 0]
2497-
[1 0 0 1 1 0 0]
2498-
[0 1 0 1 0 1 0]
2499-
[1 1 0 1 0 0 1]
2555+
[1 0 0 0 0 1 1]
2556+
[0 1 0 0 1 0 1]
2557+
[0 0 1 0 1 1 0]
2558+
[0 0 0 1 1 1 1]
25002559
"""
25012560
g = self.code().generator_matrix()
25022561
g.set_immutable()

0 commit comments

Comments
 (0)