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

Feat:minors algorithm for the MREstimator #120

Merged
merged 5 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .support_minors import SupportMinors
from .kernel_search import KernelSearch
from .big_k import BigK
from .minors import Minors
165 changes: 165 additions & 0 deletions cryptographic_estimators/MREstimator/MRAlgorithms/minors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# ****************************************************************************
# Copyright 2023 Technology Innovation Institute
#
# 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
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# ****************************************************************************

from ...MREstimator.mr_algorithm import MRAlgorithm
from ...MREstimator.mr_problem import MRProblem
from ...base_algorithm import optimal_parameter
from math import log2, ceil
from math import comb as binomial
from .mr_helper import minors_polynomial
from ..mr_constants import MR_NUMBER_OF_KERNEL_VECTORS_TO_GUESS, MR_NUMBER_OF_COEFFICIENTS_TO_GUESS


class Minors(MRAlgorithm):
"""
Construct an instance of Minors estimator


INPUT:

- ``problem`` -- an instance of the MRProblem class
- ``w`` -- linear algebra constant (default: 3)
- ``theta`` -- exponent of the conversion factor (default: 2.81)

EXAMPLES::

sage: from cryptographic_estimators.MREstimator.MRAlgorithms.minors import Minors
sage: from cryptographic_estimators.MREstimator.mr_problem import MRProblem
sage: E = Minors(MRProblem(q=7, m=9, n=10, k=15, r=4))
sage: E
Minors estimator for the MinRank problem with (q, m, n, k, r) = (7, 9, 10, 15, 4)
"""

def __init__(self, problem: MRProblem, **kwargs):

super(Minors, self).__init__(problem, **kwargs)

q, m, n, k, r = self.problem.get_parameters()
self.set_parameter_ranges('a', 0, min(n - r, ceil(k / m)))
self.set_parameter_ranges('lv', 0, r)
self._name = "Minors"

@optimal_parameter
def a(self):
"""
Return the optimal `a`, i.e. no. of vectors to guess in the kernel of the low-rank matrix

EXAMPLES::

sage: from cryptographic_estimators.MREstimator.MRAlgorithms.minors import Minors
sage: from cryptographic_estimators.MREstimator.mr_problem import MRProblem
sage: ME = Minors(MRProblem(q=7, m=9, n=10, k=15, r=4))
sage: ME.a()
2

TESTS::

sage: from cryptographic_estimators.MREstimator.MRAlgorithms.minors import Minors
sage: from cryptographic_estimators.MREstimator.mr_problem import MRProblem
sage: ME = Minors(MRProblem(q=16, m=15, n=15, k=78, r=6))
sage: ME.a()
5
"""
return self._get_optimal_parameter(MR_NUMBER_OF_KERNEL_VECTORS_TO_GUESS)

@optimal_parameter
def lv(self):
"""
Return the optimal `lv`, i.e. no. of entries to guess in the solution

EXAMPLES::

sage: from cryptographic_estimators.MREstimator.MRAlgorithms.minors import Minors
sage: from cryptographic_estimators.MREstimator.mr_problem import MRProblem
sage: ME = Minors(MRProblem(q=7, m=9, n=10, k=15, r=4))
sage: ME.lv()
0

TESTS::

sage: from cryptographic_estimators.MREstimator.MRAlgorithms.minors import Minors
sage: from cryptographic_estimators.MREstimator.mr_problem import MRProblem
sage: ME = Minors(MRProblem(q=16, m=15, n=15, k=78, r=6))
sage: ME.lv()
0
"""
return self._get_optimal_parameter(MR_NUMBER_OF_COEFFICIENTS_TO_GUESS)

def _ME_time_memory_complexity_helper_(self, m: int, n_reduced: int, k_reduced: int, r: int, time_mem: str):
out = 0
poly = minors_polynomial(m, n_reduced, k_reduced, r)
D = poly.degree()
if k_reduced > 0:
if time_mem == "time":
w = self._w
out = w * log2(binomial(k_reduced + D, D))
elif time_mem == "memory":
out = 2 * log2(binomial(k_reduced + D, D))
return out


def _compute_time_complexity(self, parameters: dict):
"""
Return the time complexity of the algorithm for a given set of parameters

INPUT:

- ``parameters`` -- dictionary including the parameters

TESTS::

sage: from cryptographic_estimators.MREstimator.MRAlgorithms.minors import Minors
sage: from cryptographic_estimators.MREstimator.mr_problem import MRProblem
sage: ME = Minors(MRProblem(q=16, m=15, n=15, k=78, r=6))
sage: ME.time_complexity()
143.1769522683363
"""
a = parameters[MR_NUMBER_OF_KERNEL_VECTORS_TO_GUESS]
lv = parameters[MR_NUMBER_OF_COEFFICIENTS_TO_GUESS]
q, m, _, k, r = self.problem.get_parameters()
_, _, n_reduced, k_reduced, _ = self.get_problem_parameters_reduced(a, lv)
time = self.hybridization_factor(a, lv)
time_complexity = self._ME_time_memory_complexity_helper_(m, n_reduced, k_reduced, r, "time")
reduction_cost = self.cost_reduction(a)
time += max(time_complexity, reduction_cost)
if abs(time_complexity - reduction_cost) < 0:
time += 1
return time

def _compute_memory_complexity(self, parameters: dict):
"""
Return the memory complexity of the algorithm for a given set of parameters

INPUT:

- ``parameters`` -- dictionary including the parameters

TESTS::

sage: from cryptographic_estimators.MREstimator.MRAlgorithms.minors import Minors
sage: from cryptographic_estimators.MREstimator.mr_problem import MRProblem
sage: ME = Minors(MRProblem(q=16, m=15, n=15, k=78, r=6))
sage: ME.memory_complexity()
14.784634845557521
"""

a = parameters[MR_NUMBER_OF_KERNEL_VECTORS_TO_GUESS]
lv = parameters[MR_NUMBER_OF_COEFFICIENTS_TO_GUESS]
q, m, n, k, r = self.problem.get_parameters()
_, _, n_reduced, k_reduced, _ = self.get_problem_parameters_reduced(a, lv)
memory = self._ME_time_memory_complexity_helper_(m, n_reduced, k_reduced, r, "memory")
return memory
62 changes: 62 additions & 0 deletions cryptographic_estimators/MREstimator/MRAlgorithms/mr_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# ****************************************************************************
# Copyright 2023 Technology Innovation Institute
#
# 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
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# ****************************************************************************


from sage.all import QQ
from sage.rings.power_series_ring import PowerSeriesRing
from itertools import product
from math import comb as binomial
from sage.matrix.special import zero_matrix
from math import log2

def _binomial_mult(n, m, i, j, l):
return binomial(m - i, l) * binomial(n - j, l)

def entry_i_j_of_A(n, m, t, i, j):
limit = max(m - i, n - j)
return sum([_binomial_mult(n, m, i, j, l) * t ** l for l in range(limit + 1)])

def matrix_A(m, n, r, PR):
A = zero_matrix(PR, r, r)
t = PR.gen()
square_r = range(1, r + 1)
for i, j in product(square_r, square_r):
entry_i_j = entry_i_j_of_A(n, m, t, i, j)
A.add_to_entry(i-1, j-1, entry_i_j)
return A

def deteterminant_of_A(m, n, r, t):
A = matrix_A(m, n, r, t)
return A.determinant()

def minors_series(m, n, k, r):
PR = PowerSeriesRing(QQ, 't', default_prec=max(n, m) + 2)
t = PR.gen()
exp = (m - r) * (n - r) - (k + 1)
series = (1 - t) ** exp * deteterminant_of_A(m, n, r, PR)/(t ** binomial(r, 2))
return series

def minors_polynomial(m, n_reduced, k_reduced, r):
poly = 0
series = minors_series(m, n_reduced, k_reduced, r)
t = series.parent().gen()
for D in range(series.degree()):
poly += series[D] * t ** D
if series[D + 1] <= 0:
break
return poly

13 changes: 13 additions & 0 deletions cryptographic_estimators/MREstimator/mr_estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def table(self, show_quantum_complexity=0, show_tilde_o_time=0,
| SupportMinors | 38.8 | 13.0 | {'a': 1, 'lv': 0, 'b': 1, 'nprime': 8, 'variant': 'block_wiedemann'} |
| KernelSearch | 33.2 | 11.5 | {'a': 1, 'lv': 0} |
| BigK | 132.5 | 13.1 | {'a': 0, 'lv': 0} |
| Minors | 37.2 | 1.6 | {'a': 2, 'lv': 0} |
+---------------+-------+--------+----------------------------------------------------------------------+

TESTS:
Expand All @@ -91,8 +92,10 @@ def table(self, show_quantum_complexity=0, show_tilde_o_time=0,
| SupportMinors | 144.0 | 11.8 | {'a': 5, 'lv': 0, 'b': 1, 'nprime': 8, 'variant': 'block_wiedemann'} |
| KernelSearch | 147.7 | 14.3 | {'a': 4, 'lv': 3} |
| BigK | 154.7 | 13.8 | {'a': 5, 'lv': 3} |
| Minors | 143.2 | 14.8 | {'a': 5, 'lv': 0} |
+---------------+-------+--------+----------------------------------------------------------------------+


sage: MRE = MREstimator(q=16, m=16, n=16, k=142, r=4)
sage: MRE.table(show_all_parameters=1)
+---------------+---------------------------------------------------------------------------------------+
Expand All @@ -103,8 +106,10 @@ def table(self, show_quantum_complexity=0, show_tilde_o_time=0,
| SupportMinors | 165.9 | 18.0 | {'a': 8, 'lv': 0, 'b': 2, 'nprime': 8, 'variant': 'block_wiedemann'} |
| KernelSearch | 159.4 | 13.8 | {'a': 8, 'lv': 0} |
| BigK | 230.8 | 17.2 | {'a': 0, 'lv': 0} |
| Minors | 162.5 | 33.0 | {'a': 7, 'lv': 0} |
+---------------+-------+--------+----------------------------------------------------------------------+


sage: MRE = MREstimator(q=16, m=19, n=19, k=109, r=8)
sage: MRE.table(show_all_parameters=1)
+---------------+----------------------------------------------------------------------------------------+
Expand All @@ -115,8 +120,10 @@ def table(self, show_quantum_complexity=0, show_tilde_o_time=0,
| SupportMinors | 209.6 | 23.5 | {'a': 5, 'lv': 0, 'b': 2, 'nprime': 14, 'variant': 'block_wiedemann'} |
| KernelSearch | 207.4 | 14.9 | {'a': 5, 'lv': 0} |
| BigK | 431.1 | 17.4 | {'a': 0, 'lv': 0} |
| Minors | 211.5 | 55.0 | {'a': 4, 'lv': 0} |
+---------------+-------+--------+-----------------------------------------------------------------------+


sage: MRE = MREstimator(q=16, m=19, n=19, k=167, r=6)
sage: MRE.table(show_all_parameters=1)
+---------------+----------------------------------------------------------------------------------------+
Expand All @@ -127,8 +134,10 @@ def table(self, show_quantum_complexity=0, show_tilde_o_time=0,
| SupportMinors | 236.3 | 20.9 | {'a': 8, 'lv': 0, 'b': 2, 'nprime': 11, 'variant': 'block_wiedemann'} |
| KernelSearch | 231.7 | 14.6 | {'a': 8, 'lv': 0} |
| BigK | 351.8 | 17.9 | {'a': 0, 'lv': 0} |
| Minors | 237.6 | 45.7 | {'a': 7, 'lv': 0} |
+---------------+-------+--------+-----------------------------------------------------------------------+


sage: MRE = MREstimator(q=16, m=21, n=21, k=189, r=7)
sage: MRE.table(show_all_parameters=1)
+---------------+----------------------------------------------------------------------------------------+
Expand All @@ -139,8 +148,10 @@ def table(self, show_quantum_complexity=0, show_tilde_o_time=0,
| SupportMinors | 274.5 | 51.0 | {'a': 6, 'lv': 0, 'b': 8, 'nprime': 15, 'variant': 'block_wiedemann'} |
| KernelSearch | 269.2 | 15.5 | {'a': 8, 'lv': 0} |
| BigK | 452.6 | 18.4 | {'a': 0, 'lv': 0} |
| Minors | 278.7 | 2.0 | {'a': 9, 'lv': 0} |
+---------------+-------+--------+-----------------------------------------------------------------------+


sage: MRE = MREstimator(q=16, m=22, n=22, k=254, r=6)
sage: MRE.table(show_all_parameters=1)
+---------------+-----------------------------------------------------------------------------------------+
Expand All @@ -151,8 +162,10 @@ def table(self, show_quantum_complexity=0, show_tilde_o_time=0,
| SupportMinors | 301.2 | 17.6 | {'a': 11, 'lv': 0, 'b': 1, 'nprime': 11, 'variant': 'block_wiedemann'} |
| KernelSearch | 302.8 | 14.5 | {'a': 11, 'lv': 0} |
| BigK | 425.4 | 18.9 | {'a': 0, 'lv': 0} |
| Minors | 307.1 | 60.1 | {'a': 9, 'lv': 0} |
+---------------+-------+--------+------------------------------------------------------------------------+


"""
super(MREstimator, self).table(show_quantum_complexity=show_quantum_complexity,
show_tilde_o_time=show_tilde_o_time,
Expand Down