Skip to content

Commit

Permalink
refactor: sage docstrings migration to python SDFq
Browse files Browse the repository at this point in the history
Applied changes discussed at
#169
to SDFq estimator.
  • Loading branch information
Dioprz committed Sep 18, 2024
1 parent 68d1aec commit 7b24489
Show file tree
Hide file tree
Showing 8 changed files with 248 additions and 287 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from ...SDFqEstimator.sdfq_algorithm import SDFqAlgorithm
from ...SDFqEstimator.sdfq_problem import SDFqProblem
from ...SDFqEstimator.sdfq_helper import binom, log2, min_max, inf
from ...SDFqEstimator.sdfq_helper import binom, log2
from ...base_algorithm import optimal_parameter
from ..sdfq_constants import *
from types import SimpleNamespace
Expand All @@ -26,27 +26,29 @@
class LeeBrickell(SDFqAlgorithm):
def __init__(self, problem: SDFqProblem, **kwargs):
"""
Construct an instance of Lee-Brickells's estimator [LB88]_
expected weight distribution::
+--------------------------------+-------------------------------+
| <----------+ n - k +---------> | <----------+ k +------------> |
| w-p | p |
+--------------------------------+-------------------------------+
INPUT:
- ``problem`` -- SDProblem object including all necessary parameters
Construct an instance of Lee-Brickell's estimator [LB88].
TESTS::
sage: from cryptographic_estimators.SDFqEstimator.SDFqAlgorithms import LeeBrickell
sage: from cryptographic_estimators.SDFqEstimator import SDFqProblem
sage: LeeBrickell(SDFqProblem(n=961,k=771,w=48,q=31)).time_complexity()
The expected weight distribution is as follows:
+--------------------------------+-------------------------------+
| <----------+ n - k +---------> | <----------+ k +------------> |
| w-p | p |
+--------------------------------+-------------------------------+
Args:
problem (SDFqProblem): An SDProblem object including all necessary parameters.
Tests:
>>> from cryptographic_estimators.SDFqEstimator.SDFqAlgorithms import LeeBrickell
>>> from cryptographic_estimators.SDFqEstimator import SDFqProblem
>>> LeeBrickell(SDFqProblem(n=961,k=771,w=48,q=31)).time_complexity()
140.31928490910389
EXAMPLES::
sage: from cryptographic_estimators.SDFqEstimator.SDFqAlgorithms import LeeBrickell
sage: from cryptographic_estimators.SDFqEstimator import SDFqProblem
sage: LeeBrickell(SDFqProblem(n=100,k=50,w=10,q=5))
Examples:
>>> from cryptographic_estimators.SDFqEstimator.SDFqAlgorithms import LeeBrickell
>>> from cryptographic_estimators.SDFqEstimator import SDFqProblem
>>> LeeBrickell(SDFqProblem(n=100,k=50,w=10,q=5))
Lee-Brickell estimator for syndrome decoding problem with (n,k,w) = (100,50,10) over Finite Field of size 5
"""
self._name = "LeeBrickell"
super(LeeBrickell, self).__init__(problem, **kwargs)
Expand All @@ -57,21 +59,26 @@ def __init__(self, problem: SDFqProblem, **kwargs):
@optimal_parameter
def p(self):
"""
Return the optimal parameter $p$ used in the algorithm optimization
EXAMPLES::
sage: from cryptographic_estimators.SDFqEstimator.SDFqAlgorithms import LeeBrickell
sage: from cryptographic_estimators.SDFqEstimator import SDFqProblem
sage: A = LeeBrickell(SDFqProblem(n=100,k=50,w=10,q=5))
sage: A.p()
Returns the optimal parameter p used in the algorithm optimization.
Examples:
>>> from cryptographic_estimators.SDFqEstimator.SDFqAlgorithms import LeeBrickell
>>> from cryptographic_estimators.SDFqEstimator import SDFqProblem
>>> A = LeeBrickell(SDFqProblem(n=100,k=50,w=10,q=5))
>>> A.p()
2
"""
return self._get_optimal_parameter("p")

def _are_parameters_invalid(self, parameters: dict):
"""
return if the parameter set `parameters` is invalid
Checks if the given parameter set is invalid.
Args:
parameters (dict): The parameter set to be checked.
Returns:
bool: True if the parameter set is invalid, False otherwise.
"""
n, k, w, _ = self.problem.get_parameters()
par = SimpleNamespace(**parameters)
Expand All @@ -81,27 +88,30 @@ def _are_parameters_invalid(self, parameters: dict):

def _time_and_memory_complexity(self, parameters: dict, verbose_information=None):
"""
Return time complexity of Lee-Brickell's algorithm over Fq, q > 2 for
given set of parameters
NOTE: this optimization assumes that the algorithm is executed on the generator matrix
INPUT:
- ``parameters`` -- dictionary including parameters
- ``verbose_information`` -- if set to a dictionary `permutations`,
`gauß` and `list` will be returned.
EXAMPLES::
sage: from cryptographic_estimators.SDFqEstimator.SDFqAlgorithms import LeeBrickell
sage: from cryptographic_estimators.SDFqEstimator import SDFqProblem
sage: A = LeeBrickell(SDFqProblem(n=100,k=50,q=3,w=10))
sage: A.p()
Return the time complexity of Lee-Brickell's algorithm over Fq, where q > 2,
for the given set of parameters.
Note:
This optimization assumes that the algorithm is executed on the generator matrix.
Args:
parameters (dict): A dictionary of parameters.
verbose_information (dict, optional): If set to a dictionary, `permutations`,
`gauß`, and `list` will be returned.
Examples:
>>> from cryptographic_estimators.SDFqEstimator.SDFqAlgorithms import LeeBrickell
>>> from cryptographic_estimators.SDFqEstimator import SDFqProblem
>>> A = LeeBrickell(SDFqProblem(n=100,k=50,q=3,w=10))
>>> A.p()
2
"""
n, k, w, q = self.problem.get_parameters()
par = SimpleNamespace(**parameters)

solutions = self.problem.nsolutions
enum = binom(k, par.p) * (q-1)**(max(0, par.p-self.is_syndrome_zero))
# TODO: Remove?
# Beullens code uses (contrary to his paper)
#enum = k**par.p * q**(max(0, par.p-self.is_syndrome_zero))
memory = log2(k * n)
Expand Down
39 changes: 19 additions & 20 deletions cryptographic_estimators/SDFqEstimator/SDFqAlgorithms/prange.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,35 +23,34 @@

class Prange(SDFqAlgorithm):
def __init__(self, problem: SDFqProblem, **kwargs):
"""
Construct an instance of Prange's estimator [Pra62]_
expected weight distribution::
+--------------------------------+-------------------------------+
| <----------+ n - k +---------> | <----------+ k +------------> |
| w | 0 |
+--------------------------------+-------------------------------+
"""Construct an instance of Prange's estimator [Pra62].
INPUT:
- ``problem`` -- SDProblem object including all necessary parameters
The expected weight distribution is as follows:
EXAMPLES::
sage: from cryptographic_estimators.SDFqEstimator.SDFqAlgorithms import Prange
sage: from cryptographic_estimators.SDFqEstimator import SDFqProblem
sage: Prange(SDFqProblem(n=100,k=50,w=10,q=3))
Prange estimator for syndrome decoding problem with (n,k,w) = (100,50,10) over Finite Field of size 3
+--------------------------------+-------------------------------+
| <----------+ n - k +---------> | <----------+ k +------------> |
| w | 0 |
+--------------------------------+-------------------------------+
Args:
problem (SDFqProblem): SDProblem object including all necessary parameters.
Examples:
>>> from cryptographic_estimators.SDFqEstimator.SDFqAlgorithms import Prange
>>> from cryptographic_estimators.SDFqEstimator import SDFqProblem
>>> Prange(SDFqProblem(n=100,k=50,w=10,q=3))
Prange estimator for syndrome decoding problem with (n,k,w) = (100,50,10) over Finite Field of size 3
"""
self._name = "Prange"
super(Prange, self).__init__(problem, **kwargs)

def _time_and_memory_complexity(self, parameters: dict, verbose_information=None):
"""
Return time complexity of Prange's algorithm for given set of parameters
INPUT:
- ``parameters`` -- dictionary including parameters
- ``verbose_information`` -- if set to a dictionary `permutations` and `gauß` will be returned.
Returns the time complexity of Prange's algorithm for the given set of parameters.
Args:
parameters (dict): A dictionary including the parameters.
verbose_information (dict, optional): If set, 'permutations' and 'gauß' will be returned.
"""

n, k, w, _ = self.problem.get_parameters()
Expand Down
89 changes: 41 additions & 48 deletions cryptographic_estimators/SDFqEstimator/SDFqAlgorithms/stern.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,23 @@

class Stern(SDFqAlgorithm):
def __init__(self, problem: SDFqProblem, **kwargs):
"""
Construct an instance of Stern's estimator [Pet11]_, [Ste88]_, [BLP08]_.
Expected weight distribution::
+-------------------------+---------+-------------+-------------+
| <----+ n - k - l +----> |<-- l -->|<--+ k/2 +-->|<--+ k/2 +-->|
| w - 2p | 0 | p | p |
+-------------------------+---------+-------------+-------------+
"""Construct an instance of Stern's estimator [Pet11]_, [Ste88]_, [BLP08]_.
INPUT:
Expected weight distribution:
- ``problem`` -- SDProblem object including all necessary parameters
+-------------------------+---------+-------------+-------------+
| <----+ n - k - l +----> |<-- l -->|<--+ k/2 +-->|<--+ k/2 +-->|
| w - 2p | 0 | p | p |
+-------------------------+---------+-------------+-------------+
EXAMPLES::
Args:
problem (SDFqProblem): SDProblem object including all necessary parameters.
sage: from cryptographic_estimators.SDFqEstimator.SDFqAlgorithms import Stern
sage: from cryptographic_estimators.SDFqEstimator import SDFqProblem
sage: Stern(SDFqProblem(n=100,k=50,w=10,q=3))
Examples:
>>> from cryptographic_estimators.SDFqEstimator.SDFqAlgorithms import Stern
>>> from cryptographic_estimators.SDFqEstimator import SDFqProblem
>>> Stern(SDFqProblem(n=100,k=50,w=10,q=3))
Stern estimator for syndrome decoding problem with (n,k,w) = (100,50,10) over Finite Field of size 3
"""
self._name = "Stern"
super(Stern, self).__init__(problem, **kwargs)
Expand All @@ -56,40 +52,40 @@ def __init__(self, problem: SDFqProblem, **kwargs):
@optimal_parameter
def l(self):
"""
Return the optimal parameter $l$ used in the algorithm optimization
EXAMPLES::
Return the optimal parameter `l` used in the algorithm optimization.
sage: from cryptographic_estimators.SDFqEstimator.SDFqAlgorithms import Stern
sage: from cryptographic_estimators.SDFqEstimator import SDFqProblem
sage: A = Stern(SDFqProblem(n=100,k=50,w=10,q=3))
sage: A.l()
Examples:
>>> from cryptographic_estimators.SDFqEstimator.SDFqAlgorithms import Stern
>>> from cryptographic_estimators.SDFqEstimator import SDFqProblem
>>> A = Stern(SDFqProblem(n=100,k=50,w=10,q=3))
>>> A.l()
7
"""

return self._get_optimal_parameter("l")

@optimal_parameter
def p(self):
"""
Return the optimal parameter $p$ used in the algorithm optimization
EXAMPLES::
sage: from cryptographic_estimators.SDFqEstimator.SDFqAlgorithms import Stern
sage: from cryptographic_estimators.SDFqEstimator import SDFqProblem
sage: A = Stern(SDFqProblem(n=100,k=50,w=10,q=3))
sage: A.p()
Returns the optimal parameter `p` used in the algorithm optimization.
Examples:
>>> from cryptographic_estimators.SDFqEstimator.SDFqAlgorithms import Stern
>>> from cryptographic_estimators.SDFqEstimator import SDFqProblem
>>> A = Stern(SDFqProblem(n=100,k=50,w=10,q=3))
>>> A.p()
2
"""

return self._get_optimal_parameter("p")

def _are_parameters_invalid(self, parameters: dict):
"""
Return if the parameter set `parameters` is invalid
Args:
parameters (dict): The parameter set to be checked for validity.
Returns:
bool: True if the parameter set is invalid, False otherwise.
"""
n, k, w, _ = self.problem.get_parameters()
par = SimpleNamespace(**parameters)
Expand All @@ -100,8 +96,10 @@ def _are_parameters_invalid(self, parameters: dict):

def _valid_choices(self):
"""
Generator which yields on each call a new set of valid parameters based on the `_parameter_ranges` and already
set parameters in `_optimal_parameters`
Yields a new set of valid parameters based on the `_parameter_ranges` and already set parameters in `_optimal_parameters`.
Yields:
set: A new set of valid parameters.
"""
new_ranges = self._fix_ranges_for_already_set_parameters()

Expand All @@ -117,18 +115,13 @@ def _valid_choices(self):
yield indices

def _time_and_memory_complexity(self, parameters: dict, verbose_information=None):
"""
Return time complexity of Sterns's algorithm over Fq for given set of
parameters. Code originaly from
- https://github.com/secomms/pkpattack/blob/main/cost_isd.sage
which was adapted from:
- https://github.com/christianepetersisdfq/blob/master/isdfq.gp
INPUT:
- ``parameters`` -- dictionary including parameters
- ``verbose_information`` -- if set to a dictionary `permutations`,
`gauß` and `list` will be returned.
"""Return time complexity of Sterns's algorithm over Fq for given set of parameters.
Code originally from https://github.com/secomms/pkpattack/blob/main/cost_isd.sage, which was adapted from https://github.com/christianepetersisdfq/blob/master/isdfq.gp.
Args:
parameters (dict): Dictionary including parameters.
verbose_information (dict, optional): If set to a dictionary, 'permutations', 'gauß', and 'list' will be returned.
"""
n, k, w, q = self.problem.get_parameters()
par = SimpleNamespace(**parameters)
Expand Down
37 changes: 16 additions & 21 deletions cryptographic_estimators/SDFqEstimator/sdfq_algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,17 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# ****************************************************************************

from ..helper import ComplexityType
from ..base_algorithm import BaseAlgorithm, optimal_parameter
from ..base_algorithm import BaseAlgorithm
from .sdfq_problem import SDFqProblem
from .sdfq_helper import _optimize_m4ri
from math import inf, log2


class SDFqAlgorithm(BaseAlgorithm):
"""
Base class for Syndrome Decoding over FQ algorithms complexity estimator
Base class for Syndrome Decoding over FQ algorithms complexity estimator.
INPUT:
- ``problem`` -- SDFqProblem object including all necessary parameters
- ``hmp`` -- Indicates if Hashmap is used for list matching, if false sorting is used (default: true)
Args:
problem (SDFqProblem): An SDFqProblem object including all necessary parameters.
hmp (bool, optional): Indicates if a hashmap is used for list matching. If False, sorting is used instead. Defaults to True.
"""

def __init__(self, problem: SDFqProblem, **kwargs):
Expand All @@ -38,8 +34,7 @@ def __init__(self, problem: SDFqProblem, **kwargs):
self._adjust_radius = kwargs.get("adjust_radius", 10)

def _time_and_memory_complexity(self, parameters: dict, verbose_information=None):
"""
Computes time and memory complexity for given parameters
"""Computes time and memory complexity for given parameters.
"""
raise NotImplementedError

Expand All @@ -50,16 +45,16 @@ def _compute_memory_complexity(self, parameters: dict):
return self._time_and_memory_complexity(parameters)[1]

def _get_verbose_information(self):
"""
returns a dictionary containing
{
CONSTRAINTS,
PERMUTATIONS,
TREE,
GAUSS,
REPRESENTATIONS,
LISTS
}
"""Returns a dictionary containing information about the object.
Returns:
dict: A dictionary with the following keys:
- CONSTRAINTS
- PERMUTATIONS
- TREE
- GAUSS
- REPRESENTATIONS
- LISTS
"""
verb = dict()
_ = self._time_and_memory_complexity(self.optimal_parameters(), verbose_information=verb)
Expand Down
Loading

0 comments on commit 7b24489

Please sign in to comment.