Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge of previous 'rank metric channel' enhancement #39117

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
245 changes: 236 additions & 9 deletions src/sage/coding/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,18 @@
transmitted message
- :class:`ErrorErasureChannel`, which creates a specific number of errors and a
specific number of erasures in each transmitted message
- :class:`StaticRankErrorChannel`, which creates an error of specific rank in each
transmitted message. This is the rank analogue of :class:`StaticErrorRateChannel`

AUTHORS:

- David Lucas (2015): initial version
- Maxime Bombar (2021): Rank metric channel
"""

# ****************************************************************************
# Copyright (C) 2015 David Lucas <[email protected]>
# Copyright (C) 2021 Maxime Bombar <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand All @@ -49,15 +57,17 @@
# ****************************************************************************
from copy import copy

from sage.structure.sage_object import SageObject
from sage.rings.integer import Integer
from sage.rings.finite_rings.finite_field_constructor import GF
from sage.categories.cartesian_product import cartesian_product
from sage.categories.fields import Fields
from sage.functions.other import binomial
from sage.matrix.constructor import matrix
from sage.misc.abstract_method import abstract_method
from sage.misc.prandom import randint, random, sample
from sage.modules.free_module import VectorSpace, span
from sage.modules.free_module_element import vector
from sage.misc.abstract_method import abstract_method
from sage.categories.cartesian_product import cartesian_product
from sage.modules.free_module import VectorSpace
from sage.arith.misc import binomial
from sage.rings.finite_rings.finite_field_constructor import GF
from sage.rings.integer import Integer
from sage.structure.sage_object import SageObject


def random_error_vector(n, F, error_positions):
Expand Down Expand Up @@ -550,8 +560,8 @@

def transmit_unsafe(self, message):
r"""
Return ``message`` with as many errors as ``self._number_errors`` in it,
and as many erasures as ``self._number_erasures`` in it.
Return ``message`` with as many errors as ``self._number_errors``
in it, and as many erasures as ``self._number_erasures`` in it.

If ``self._number_errors`` was passed as a tuple for the number of errors, it will
pick a random integer between the bounds of the tuple and use it as the number of errors.
Expand Down Expand Up @@ -816,3 +826,220 @@
"""
return sum(self.probability_of_exactly_t_errors(i)
for i in range(t+1))


class StaticRankErrorChannel(Channel):
r"""
Channel which adds an error of static rank to each message it transmits.

The input space and the output space of this channel are the same.

INPUT:

- ``space`` -- the space of both input and output

- ``rank_errors`` -- the rank of the error added to each transmitted message
It can be either an integer of a tuple. If a tuple is passed as
argument, the rank of the error will be a random integer between the
two bounds of the tuple.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

message. It can be

It can be either an integer or a tuple?

- ``relative_field`` -- The field to which the extension is relative.
If not given, it will default to the prime_subfield of the ambient space
base_field.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

``prime_subfield`` or just the prime subfield?

``base_field`` or just the base field?

EXAMPLES:

We construct a StaticRankErrorChannel which adds error of rank 2
to any transmitted message::

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:class:`StaticRankErrorChannel`?

sage: n_err = 2
sage: Chan = channels.StaticRankErrorChannel(GF(256)^6, n_err)
sage: Chan
Channel creating error of rank 2 over Finite Field of size 2,
of input and output space Vector space of dimension 6
over Finite Field in z8 of size 2^8

We can also pass a tuple for the number of errors::

sage: n_err = (1, 4)
sage: Chan = channels.StaticRankErrorChannel(GF(256)^6, n_err)
sage: Chan
Channel creating error of rank between 1 and 4 over Finite Field
of size 2, of input and output space Vector space of dimension 6
over Finite Field in z8 of size 2^8
"""

def __init__(self, space, rank_errors, relative_field=None):
r"""
TESTS:

If the number of errors exceeds the dimension of the input space,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about

r"""
Initialize.

TESTS:

?

or the field extension degree, then an error is raised::

sage: n_err = 7
sage: channels.StaticRankErrorChannel(GF(256)^6, n_err)
Traceback (most recent call last):
...
ValueError: There might be errors of rank larger than the dimension of the input space.
sage: channels.StaticRankErrorChannel(GF(64)^8, n_err)
Traceback (most recent call last):
...
ValueError: There might be errors of rank larger than the field extension degree.

If ``relative_field`` is specified and is not a subfield of the base field,
it will return an error::

sage: n_err = 2
sage: Chan = channels.StaticRankErrorChannel(GF(16)^6, n_err, GF(8))
Traceback (most recent call last):
...
ValueError: Finite Field in z4 of size 2^4 is not an extension of
Finite Field in z3 of size 2^3

If ``relative_field`` is specified and is not a field,
it will return an error::

sage: n_err = 2
sage: Chan = channels.StaticRankErrorChannel(GF(16)^6, n_err, GF(4)^2)
Traceback (most recent call last):
...
ValueError: relative_field must be a Field and
Vector space of dimension 2 over Finite Field in z2
of size 2^2 is not.
"""
if isinstance(rank_errors, (Integer, int)):
rank_errors = (rank_errors, rank_errors)
if not isinstance(rank_errors, (tuple, list)):
raise ValueError("rank_errors must be a tuple, a list, an Integer or a Python int")

Check warning on line 913 in src/sage/coding/channel.py

View check run for this annotation

Codecov / codecov/patch

src/sage/coding/channel.py#L913

Added line #L913 was not covered by tests
super(StaticRankErrorChannel, self).__init__(space, space)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ValueError("rank_errors must be a tuple, a list, or an integer")

?

if rank_errors[1] > space.dimension():
raise ValueError("There might be errors of rank larger than the dimension of the input space.")
self._rank_errors = rank_errors
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ValueError("there might be errors of rank larger than the dimension of the input space")

?

self._base_field = space.base_field()
if not relative_field:
self._relative_field = self._base_field.prime_subfield()
else:
if relative_field not in Fields():
raise ValueError("relative_field must be a Field and %s is not." % relative_field)
if not relative_field.is_subring(self._base_field):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must be a field

?

%s is not"

?

raise ValueError("%s is not an extension of %s" % (self._base_field, relative_field))
self._relative_field = relative_field
V, vec_to_field, field_to_vec = self._base_field.vector_space(self._relative_field)
self._column_space = V
self._extension_degree = V.dimension()
if rank_errors[1] > self._extension_degree:
raise ValueError("There might be errors of rank larger than the field extension degree.")
self._map_to_field = vec_to_field

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ValueError("there might be errors of rank larger than the field extension degree")

or

ValueError("there could be errors of rank larger than the field extension degree")

?

def _repr_(self):
r"""
Return a string representation of ``self``.

EXAMPLES::

sage: n_err = 2
sage: Chan = channels.StaticRankErrorChannel(GF(256)^6, n_err)
sage: Chan
Channel creating error of rank 2 over Finite Field of size 2,
of input and output space Vector space of dimension 6
over Finite Field in z8 of size 2^8
"""
no_err = self.rank_errors()
return "Channel creating error of rank %s over %s, of input and output space %s"\
% (format_interval(no_err), self._relative_field, self.input_space())

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

%s" \

def _latex_(self):
r"""
Return a latex representation of ``self``.

EXAMPLES::

sage: n_err = 2
sage: Chan = channels.StaticRankErrorChannel(GF(256)^6, n_err)
sage: latex(Chan)
\textnormal{Channel creating error of rank 2 over Finite Field
of size 2, of input and output space Vector space of dimension 6
over Finite Field in z8 of size 2^8}
"""
no_err = self.rank_errors()
return "\\textnormal{Channel creating error of rank %s over %s, of input and output space %s}"\
% (format_interval(no_err), self._relative_field, self.input_space())

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a space before \

def transmit_unsafe(self, message):
r"""
Return ``message`` with as many errors as ``self._rank_errors`` in it.

If ``self._rank_errors`` was passed as a tuple for the number of errors, it will
pick a random integer between the bounds of the tuple and use it as the number of errors.

This method does not check if ``message`` belongs to the input space of``self``.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

of ``self``

INPUT:

- ``message`` -- a vector

OUTPUT:

- a vector of the output space

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OUTPUT: a vector of the output space

EXAMPLES::

sage: F = GF(16)^6
sage: n_err = 2
sage: Chan = channels.StaticRankErrorChannel(F, n_err, GF(4))
sage: set_random_seed(10)
sage: msg = F.random_element()
sage: msg
(z4 + 1, z4, z4^3 + z4 + 1, z4^3 + z4^2 + z4 + 1, z4^2, z4^2)
sage: c = Chan.transmit_unsafe(msg)

TESTS::

sage: from sage.coding.linear_rank_metric import rank_distance
sage: rank_distance(msg, c, GF(4))
2
"""
rank_errors = randint(*self.rank_errors())
Fq = self._relative_field
V = self._column_space
n = self.input_space().dimension()
good = False
while not good:
basis = [V.random_element() for i in range(rank_errors)]
R = span(basis)
err = [R.random_element() for i in range(n)]
M = matrix(Fq, err)
good = (M.rank() == rank_errors)
e = vector(map(self._map_to_field, M.rows()))
w = message + e
return w

def rank_errors(self):
r"""
Return the number of rank errors created by ``self``.

EXAMPLES::

sage: n_err = 3
sage: Chan = channels.StaticRankErrorChannel(GF(256)^6, n_err)
sage: Chan.rank_errors()
(3, 3)
"""
return self._rank_errors

def number_errors(self):
r"""
Return the number of errors created by ``self``.

.. NOTE::

This function is here to ease the life of people coming from the Hamming world.

EXAMPLES::

sage: n_err = 3
sage: Chan = channels.StaticRankErrorChannel(GF(256)^6, n_err)
sage: Chan.number_errors()
(3, 3)
"""
return self._rank_errors
5 changes: 4 additions & 1 deletion src/sage/coding/channels_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
#*****************************************************************************

from sage.misc.lazy_import import lazy_import as _lazy_import

_lazy_import('sage.coding.channel', ['ErrorErasureChannel',
'QarySymmetricChannel',
'StaticErrorRateChannel'])
'StaticErrorRateChannel',
'StaticRankErrorChannel',
])
42 changes: 20 additions & 22 deletions src/sage/coding/gabidulin_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -938,18 +938,19 @@ def _decode_to_code_and_message(self, r):

EXAMPLES::

sage: Fqm = GF(2^9)
sage: Fq = GF(2^3)
sage: C = codes.GabidulinCode(Fqm, 2, 2, Fq)
sage: Fqm = GF(5^12)
sage: Fq = GF(5)
sage: C = codes.GabidulinCode(Fqm, 11, 4, Fq)
sage: D = codes.decoders.GabidulinGaoDecoder(C)
sage: E = codes.encoders.GabidulinPolynomialEvaluationEncoder(C)
sage: S.<x> = Fqm['x', C.twisting_homomorphism()]
sage: z9 = Fqm.gen()
sage: p = (z9^6 + z9^4)*x + z9^2 + z9
sage: p = x**3 - x + 3
sage: codeword_vector = E.encode(p, "vector")
sage: r = D.decode_to_message(codeword_vector) #indirect doctest
sage: r
(z9^6 + z9^4)*x + z9^2 + z9
sage: Chan = channels.StaticRankErrorChannel(C.ambient_space(), 3)
sage: y = Chan(codeword_vector)
sage: c, m = D._decode_to_code_and_message(y)
sage: c == codeword_vector and m == p
True
"""
C = self.code()
length = len(r)
Expand All @@ -960,7 +961,6 @@ def _decode_to_code_and_message(self, r):
return r, self.connected_encoder().unencode_nocheck(r)

points = [(eval_pts[i], r[i]) for i in range(len(eval_pts))]
# R = S.lagrange_polynomial(eval_pts, list(r))
R = S.lagrange_polynomial(points)
r_out, u_out = self._partial_xgcd(S.minimal_vanishing_polynomial(eval_pts),
R, (C.length() + C.dimension()) // 2)
Expand Down Expand Up @@ -993,15 +993,12 @@ def decode_to_code(self, r):
sage: D = codes.decoders.GabidulinGaoDecoder(C)
sage: E = codes.encoders.GabidulinPolynomialEvaluationEncoder(C)
sage: S.<x> = Fqm['x', C.twisting_homomorphism()]
sage: z20 = Fqm.gen()
sage: p = x
sage: codeword_vector = E.encode(p, "vector")
sage: codeword_vector
(1, z20^3, z20^6, z20^9, z20^12)
sage: l = list(codeword_vector)
sage: l[0] = l[1] #make an error
sage: D.decode_to_code(vector(l))
(1, z20^3, z20^6, z20^9, z20^12)
sage: Chan = channels.StaticRankErrorChannel(C.ambient_space(), 1)
sage: y = Chan(codeword_vector)
sage: D.decode_to_code(y) == codeword_vector
True
"""
return self._decode_to_code_and_message(r)[0]

Expand All @@ -1018,18 +1015,19 @@ def decode_to_message(self, r):

EXAMPLES::

sage: Fqm = GF(2^9)
sage: Fqm = GF(2^18)
sage: Fq = GF(2^3)
sage: C = codes.GabidulinCode(Fqm, 2, 2, Fq)
sage: C = codes.GabidulinCode(Fqm, 6, 2, Fq)
sage: D = codes.decoders.GabidulinGaoDecoder(C)
sage: E = codes.encoders.GabidulinPolynomialEvaluationEncoder(C)
sage: S.<x> = Fqm['x', C.twisting_homomorphism()]
sage: z9 = Fqm.gen()
sage: p = (z9^6 + z9^4)*x + z9^2 + z9
sage: p = x + 1
sage: codeword_vector = E.encode(p, "vector")
sage: r = D.decode_to_message(codeword_vector)
sage: Chan = channels.StaticRankErrorChannel(C.ambient_space(), 2)
sage: y = Chan(codeword_vector)
sage: r = D.decode_to_message(y)
sage: r
(z9^6 + z9^4)*x + z9^2 + z9
x + 1
"""
return self._decode_to_code_and_message(r)[1]

Expand Down
Loading