diff --git a/src/sage/coding/abstract_code.py b/src/sage/coding/abstract_code.py index 3d1fe305dbc..9f422cfb360 100644 --- a/src/sage/coding/abstract_code.py +++ b/src/sage/coding/abstract_code.py @@ -215,7 +215,7 @@ def __init__(self, length, default_encoder_name=None, INPUT: - ``length`` -- the length of ``self`` (a Python int or a Sage Integer, - must be > 0) + must be >= 0) - ``default_encoder_name`` -- (default: ``None``) the name of the default encoder of ``self`` @@ -281,19 +281,24 @@ def __init__(self, length, default_encoder_name=None, ... ValueError: length must be a Python int or a Sage Integer - If the length of the code is not a nonzero positive integer - (See :issue:`21326`), it will raise an exception:: + If the length of the code is negative, it will raise an exception:: - sage: C = MyCodeFamily(0) + sage: C = MyCodeFamily(-1) Traceback (most recent call last): ... - ValueError: length must be a nonzero positive integer + ValueError: length must be a non-negative integer + + Codes of length 0 are allowed:: + + sage: C = MyCodeFamily(0) + sage: C.length() + 0 """ if not isinstance(length, (int, Integer)): raise ValueError("length must be a Python int or a Sage Integer") - if length <= 0: - raise ValueError("length must be a nonzero positive integer") + if length < 0: + raise ValueError("length must be a non-negative integer") self._length = length self._metric = metric @@ -417,7 +422,7 @@ def __call__(self, m): sage: C = LinearCode(G) sage: word = vector((0, 1, 1, 0)) sage: C(word) - (1, 1, 0, 0, 1, 1, 0) + (0, 1, 1, 0, 0, 1, 1) sage: c = C.random_element() sage: C(c) == c @@ -723,14 +728,14 @@ def decode_to_message(self, word, decoder_name=None, *args, **kwargs): sage: C = LinearCode(G) sage: word = vector(GF(2), (1, 1, 0, 0, 1, 1, 0)) sage: C.decode_to_message(word) - (0, 1, 1, 0) + (1, 1, 0, 0) It is possible to manually choose the decoder amongst the list of the available ones:: sage: sorted(C.decoders_available()) ['InformationSet', 'NearestNeighbor', 'Syndrome'] sage: C.decode_to_message(word, 'NearestNeighbor') - (0, 1, 1, 0) + (1, 1, 0, 0) """ return self.unencode(self.decode_to_code(word, decoder_name, *args, **kwargs), **kwargs) @@ -882,9 +887,9 @@ def encode(self, word, encoder_name=None, *args, **kwargs): sage: C = LinearCode(G) sage: word = vector((0, 1, 1, 0)) sage: C.encode(word) - (1, 1, 0, 0, 1, 1, 0) + (0, 1, 1, 0, 0, 1, 1) sage: C(word) - (1, 1, 0, 0, 1, 1, 0) + (0, 1, 1, 0, 0, 1, 1) It is possible to manually choose the encoder amongst the list of the available ones:: @@ -892,7 +897,7 @@ def encode(self, word, encoder_name=None, *args, **kwargs): ['GeneratorMatrix', 'Systematic'] sage: word = vector((0, 1, 1, 0)) sage: C.encode(word, 'GeneratorMatrix') - (1, 1, 0, 0, 1, 1, 0) + (0, 1, 1, 0, 0, 1, 1) """ E = self.encoder(encoder_name, *args, **kwargs) return E.encode(word) @@ -1057,7 +1062,7 @@ def unencode(self, c, encoder_name=None, nocheck=False, **kwargs): 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]]) sage: C = LinearCode(G) - sage: c = vector(GF(2), (1, 1, 0, 0, 1, 1, 0)) + sage: c = vector(GF(2), (0, 1, 1, 0, 0, 1, 1)) sage: C.unencode(c) (0, 1, 1, 0) """ diff --git a/src/sage/coding/databases.py b/src/sage/coding/databases.py index c3e76a206f1..992280eef93 100644 --- a/src/sage/coding/databases.py +++ b/src/sage/coding/databases.py @@ -232,10 +232,10 @@ def self_orthogonal_binary_codes(n, k, b=2, parent=None, BC=None, equal=False, [4, 1] linear code over GF(2) [1 1 1 1] [6, 2] linear code over GF(2) - [1 1 1 1 0 0] + [1 0 1 0 1 1] [0 1 0 1 1 1] [7, 3] linear code over GF(2) - [1 0 1 1 0 1 0] + [1 0 0 1 1 0 1] [0 1 0 1 1 1 0] [0 0 1 0 1 1 1] @@ -248,7 +248,7 @@ def self_orthogonal_binary_codes(n, k, b=2, parent=None, BC=None, equal=False, [4, 1] linear code over GF(2) [1 1 1 1] [6, 2] linear code over GF(2) - [1 1 1 1 0 0] + [1 0 1 0 1 1] [0 1 0 1 1 1] Generate all self-orthogonal codes of length equal to 8 and @@ -263,8 +263,8 @@ def self_orthogonal_binary_codes(n, k, b=2, parent=None, BC=None, equal=False, [0 0 1 0 0 1 0 0] [0 0 0 0 0 0 1 1] [8, 4] linear code over GF(2) - [1 0 0 1 1 0 1 0] - [0 1 0 1 1 1 0 0] + [1 0 0 0 1 1 0 1] + [0 1 0 0 1 0 1 1] [0 0 1 0 1 1 1 0] [0 0 0 1 0 1 1 1] diff --git a/src/sage/coding/decoder.py b/src/sage/coding/decoder.py index 05089583389..cbb17dd8598 100644 --- a/src/sage/coding/decoder.py +++ b/src/sage/coding/decoder.py @@ -293,7 +293,7 @@ def decode_to_message(self, r): sage: w_err = word + vector(GF(2), (1, 0, 0, 0, 0, 0, 0)) sage: D = C.decoder() sage: D.decode_to_message(w_err) - (0, 1, 1, 0) + (1, 1, 0, 0) """ self.defaulting_decode_to_message = True return self.code().unencode(self.decode_to_code(r)) diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index 459d6c82ebc..9243966452a 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -149,7 +149,7 @@ def encode(self, word): sage: word = vector(GF(2), (0, 1, 1, 0)) sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E.encode(word) - (1, 1, 0, 0, 1, 1, 0) + (0, 1, 1, 0, 0, 1, 1) If ``word`` is not in the message space of ``self``, it will return an exception:: @@ -183,7 +183,7 @@ def __call__(self, m): sage: word = vector(GF(2), (0, 1, 1, 0)) sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E(word) - (1, 1, 0, 0, 1, 1, 0) + (0, 1, 1, 0, 0, 1, 1) sage: F = GF(11) sage: Fx. = F[] @@ -224,7 +224,7 @@ def unencode(self, c, nocheck=False): True sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E.unencode(c) - (0, 1, 1, 0) + (1, 1, 0, 0) TESTS: @@ -241,11 +241,10 @@ def unencode(self, c, nocheck=False): Note that since :issue:`21326`, codes cannot be of length zero:: - sage: G = Matrix(GF(17), []) + sage: G = matrix(GF(2), 0, 0) sage: C = LinearCode(G) - Traceback (most recent call last): - ... - ValueError: length must be a nonzero positive integer + sage: C + [0, 0] linear code over GF(2) """ if not nocheck and c not in self.code(): raise EncodingError("Given word is not in the code") @@ -268,10 +267,10 @@ def _unencoder_matrix(self): sage: E = C.encoder() sage: E._unencoder_matrix() ( - [0 0 1 1] - [0 1 0 1] - [1 1 1 0] - [0 1 1 1], (0, 1, 2, 3) + [1 0 0 0] + [0 1 0 0] + [0 0 1 0] + [0 0 0 1], (0, 1, 2, 3) ) """ info_set = self.code().information_set() @@ -306,7 +305,7 @@ def unencode_nocheck(self, c): True sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E.unencode_nocheck(c) - (0, 1, 1, 0) + (1, 1, 0, 0) Taking a vector that does not belong to ``C`` will not raise an error but probably just give a non-sensical result:: @@ -316,7 +315,7 @@ def unencode_nocheck(self, c): False sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E.unencode_nocheck(c) - (0, 1, 1, 0) + (1, 1, 0, 0) sage: m = vector(GF(2), (0, 1, 1, 0)) sage: c1 = E.encode(m) sage: c == c1 @@ -375,10 +374,10 @@ def generator_matrix(self): sage: C = LinearCode(G) sage: E = C.encoder() sage: E.generator_matrix() - [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] + [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] """ diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index f8f6326a730..a6e14139c23 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -80,17 +80,17 @@ class should inherit from this class. Also ``AbstractLinearCode`` should never 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]]) sage: C = LinearCode(G) sage: C.basis() - [(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)] + [(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)] sage: c = C.basis()[1] sage: c in C True sage: c.nonzero_positions() - [0, 3, 4] + [1, 4, 6] sage: c.support() - [0, 3, 4] + [1, 4, 6] sage: c.parent() Vector space of dimension 7 over Finite Field of size 2 @@ -1441,7 +1441,7 @@ def _minimum_weight_codeword(self, algorithm=None): 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]]) sage: C = LinearCode(G) sage: C._minimum_weight_codeword() # needs sage.libs.gap - (0, 1, 0, 1, 0, 1, 0) + (0, 0, 1, 0, 1, 1, 0) TESTS: @@ -2310,39 +2310,56 @@ def __init__(self, generator, d=None): sage: C = LinearCode(VS); C [3, 1] linear code over GF(2) - Forbid the zero vector space (see :issue:`17452` and :issue:`6486`):: + Constructing a zero code is allowed:: sage: G = matrix(GF(2), [[0,0,0]]) sage: C = LinearCode(G) - Traceback (most recent call last): - ... - ValueError: this linear code contains no nonzero vector + sage: C + [3, 0] linear code over GF(2) + sage: C.dimension() + 0 + sage: C_dual = C.dual_code() + sage: C_dual + [3, 3] linear code over GF(2) + sage: C_dual.dimension() == C_dual.length() + True + sage: C.encode(C.message_space().zero()) + (0, 0, 0) + sage: C.decode_to_code(vector(GF(2), [1,0,1])) + (0, 0, 0) """ - base_ring = generator.base_ring() + from sage.matrix.constructor import matrix + + if isinstance(generator, AbstractLinearCode): + generator = generator.generator_matrix() + + if hasattr(generator, 'basis'): + if generator.dimension() == 0: + G = matrix(generator.base_ring(), 0, generator.ambient_space().dimension()) + else: + G = matrix(generator.basis()) + else: + G = generator + + base_ring = G.base_ring() if not base_ring.is_field(): raise ValueError("'generator' must be defined on a field (not a ring)") - try: - basis = None - if hasattr(generator, "nrows"): # generator matrix case - if generator.rank() < generator.nrows(): - basis = generator.row_space().basis() - else: - basis = generator.basis() # vector space etc. case - if basis is not None: - from sage.matrix.constructor import matrix - generator = matrix(base_ring, basis) - if generator.nrows() == 0: - raise ValueError("this linear code contains no nonzero vector") - except AttributeError: - # Assume input is an AbstractLinearCode, extract its generator matrix - generator = generator.generator_matrix() + if G.is_zero() or G.nrows() == 0: + super().__init__(base_ring, G.ncols(), "GeneratorMatrix", "Syndrome") + self._dimension = 0 + self._generator_matrix = matrix(base_ring, 0, self.length()) + self._minimum_distance = d + return - super().__init__(base_ring, generator.ncols(), + G_echelon = G.echelon_form() + generator_matrix = G_echelon.matrix_from_rows([i for i, r in enumerate(G_echelon) if not r.is_zero()]) + + super().__init__(base_ring, generator_matrix.ncols(), "GeneratorMatrix", "Syndrome") - self._generator_matrix = generator - self._dimension = generator.rank() + self._generator_matrix = generator_matrix + self._dimension = self._generator_matrix.rank() self._minimum_distance = d def __hash__(self): @@ -2382,13 +2399,65 @@ def _latex_(self): """ return "[%s, %s]\\textnormal{ Linear code over }%s"\ % (self.length(), self.dimension(), self.base_ring()._latex_()) + + def intersection(self, other): + """ + Return the intersection of this linear code with another. + + The intersection of two linear codes C1 and C2 is the set of all + codewords that are in both C1 and C2. It is also a linear code. + + This is computed using the identity: + C1 ∩ C2 = (C1_dual + C2_dual)_dual + INPUT: + - ``other`` -- a linear code. + + OUTPUT: + - a linear code, the intersection of self and other. + + .. EXAMPLES:: + + sage: F = GF(2) + sage: G1 = matrix(F, [[1,1,0,0], [0,0,1,1]]) + sage: C1 = LinearCode(G1) + sage: G2 = matrix(F, [[1,0,1,0], [0,1,0,1]]) + sage: C2 = LinearCode(G2) + sage: C_int = C1.intersection(C2) + sage: C_int + [4, 1] linear code over GF(2) + sage: c = vector(F, (1,1,1,1)) + sage: c in C_int + True + + # Test intersection with the zero code:: + + sage: C_zero = LinearCode(matrix(F, [[0,0,0,0]])) + sage: C1.intersection(C_zero) + [4, 0] linear code over GF(2) + """ + if not isinstance(other, AbstractLinearCode): + raise TypeError("Intersection is only defined between linear codes.") + if self.base_field() != other.base_field(): + raise TypeError("Codes must be over the same base field.") + if self.length() != other.length(): + raise ValueError("Codes must have the same length.") + + C1_dual = self.dual_code() + C2_dual = other.dual_code() + + G1d = C1_dual.generator_matrix() + G2d = C2_dual.generator_matrix() + G_sum = G1d.stack(G2d) + sum_of_duals = LinearCode(G_sum) + + return sum_of_duals.dual_code() + def generator_matrix(self, encoder_name=None, **kwargs): r""" Return a generator matrix of ``self``. INPUT: - - ``encoder_name`` -- (default: ``None``) name of the encoder which will be used to compute the generator matrix. ``self._generator_matrix`` will be returned if default value is kept. @@ -2401,8 +2470,8 @@ def generator_matrix(self, encoder_name=None, **kwargs): sage: G = matrix(GF(3),2,[1,-1,1,-1,1,1]) sage: code = LinearCode(G) sage: code.generator_matrix() - [1 2 1] - [2 1 1] + [1 2 0] + [0 0 1] """ if encoder_name is None or encoder_name == 'GeneratorMatrix': g = self._generator_matrix @@ -2493,10 +2562,10 @@ def generator_matrix(self): sage: C = LinearCode(G) sage: E = codes.encoders.LinearCodeGeneratorMatrixEncoder(C) sage: E.generator_matrix() - [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] + [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] """ g = self.code().generator_matrix() g.set_immutable() @@ -3063,4 +3132,4 @@ def decoding_radius(self): LinearCode._registered_encoders["GeneratorMatrix"] = LinearCodeGeneratorMatrixEncoder LinearCodeSyndromeDecoder._decoder_type = {"hard-decision", "dynamic"} -LinearCodeNearestNeighborDecoder._decoder_type = {"hard-decision", "always-succeed", "complete"} +LinearCodeNearestNeighborDecoder._decoder_type = {"hard-decision", "always-succeed", "complete"} \ No newline at end of file diff --git a/src/sage/coding/linear_code_no_metric.py b/src/sage/coding/linear_code_no_metric.py index 0845e19dee8..12a9f26b43c 100644 --- a/src/sage/coding/linear_code_no_metric.py +++ b/src/sage/coding/linear_code_no_metric.py @@ -238,8 +238,8 @@ def generator_matrix(self, encoder_name=None, **kwargs): sage: G = matrix(GF(3), 2, [1,-1,1,-1,1,1]) sage: code = LinearCode(G) sage: code.generator_matrix() - [1 2 1] - [2 1 1] + [1 2 0] + [0 0 1] """ E = self.encoder(encoder_name, **kwargs) return E.generator_matrix() @@ -528,8 +528,8 @@ def systematic_generator_matrix(self, systematic_positions=None): ....: [ 2, 1, 1, 1]]) sage: C = LinearCode(G) sage: C.generator_matrix() - [1 2 1 0] - [2 1 1 1] + [1 2 0 1] + [0 0 1 2] sage: C.systematic_generator_matrix() [1 2 0 1] [0 0 1 2] @@ -762,7 +762,7 @@ def __getitem__(self, i): sage: G = Matrix(GF(3), [[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]]) sage: C = LinearCode(G) sage: C[24] - (2, 2, 0, 1, 2, 2, 0) + (0, 2, 2, 0, 0, 2, 1) sage: C[24] == C.list()[24] True