Skip to content

Commit

Permalink
avoid all LLL calls in postprocessing
Browse files Browse the repository at this point in the history
  • Loading branch information
malb committed Jul 12, 2017
1 parent 4bcaadd commit 99bf78c
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 22 deletions.
62 changes: 42 additions & 20 deletions src/fpylll/algorithms/bkz.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,31 +192,53 @@ def svp_postprocessing(self, kappa, block_size, solution, tracer):
if solution is None:
return True

nonzero_vectors = len([x for x in solution if x])
if nonzero_vectors == 1:
first_nonzero_vector = None
for i in range(block_size):
if abs(solution[i]) == 1:
first_nonzero_vector = i
break
j_nz = None

self.M.move_row(kappa + first_nonzero_vector, kappa)
with tracer.context("lll"):
self.lll_obj.size_reduction(kappa, kappa + first_nonzero_vector + 1)
for i in range(block_size):
if abs(solution[i]) == 1:
j_nz = i
break

else:
d = self.M.d
self.M.create_row()
if len([x for x in solution if x]) == 1:
self.M.move_row(kappa + j_nz, kappa)

with self.M.row_ops(d, d+1):
elif j_nz is not None:
with self.M.row_ops(kappa + j_nz, kappa + j_nz + 1):
for i in range(block_size):
self.M.row_addmul(d, kappa + i, solution[i])
if solution[i] and i != j_nz:
self.M.row_addmul(kappa + j_nz, kappa + i, solution[j_nz] * solution[i])

self.M.move_row(kappa + j_nz, kappa)

self.M.move_row(d, kappa)
with tracer.context("lll"):
self.lll_obj(kappa, kappa, kappa + block_size + 1)
self.M.move_row(kappa + block_size, d)
self.M.remove_last_row()
else:
solution = list(solution)

for i in range(block_size):
if solution[i] < 0:
solution[i] = -solution[i]
self.M.negate_row(kappa + i)

with self.M.row_ops(kappa, kappa + block_size):
offset = 1
while offset < block_size:
k = block_size - 1
while k - offset >= 0:
if solution[k] or solution[k - offset]:
if solution[k] < solution[k - offset]:
solution[k], solution[k - offset] = solution[k - offset], solution[k]
self.M.swap_rows(kappa + k - offset, kappa + k)

while solution[k - offset]:
while solution[k - offset] <= solution[k]:
solution[k] = solution[k] - solution[k - offset]
self.M.row_addmul(kappa + k - offset, kappa + k, 1)

solution[k], solution[k - offset] = solution[k - offset], solution[k]
self.M.swap_rows(kappa + k - offset, kappa + k)
k -= 2 * offset
offset *= 2

self.M.move_row(kappa + block_size - 1, kappa)

return False

Expand Down
2 changes: 1 addition & 1 deletion src/fpylll/fplll/fplll.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ cdef extern from "fplll/gso.h" namespace "fplll":
void discover_all_rows() nogil
void set_r(int i, int j, FT& f) nogil
void move_row(int oldR, int newR) nogil
void swap_rows(int row1, int row2)
void row_swap(int row1, int row2)

void row_addmul(int i, int j, const FT& x) nogil
void row_addmul_we(int i, int j, const FT& x, long expoAdd) nogil
Expand Down
41 changes: 41 additions & 0 deletions src/fpylll/fplll/gso.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,47 @@ cdef class MatGSO:

raise RuntimeError("MatGSO object '%s' has no core."%self)

def swap_rows(self, int i, int j):
"""
Swap rows ``i`` and ``j``.
:param int i: row index
:param int j: row index
"""
preprocess_indices(i, j, self.d, self.d)
if self._type == gso_mpz_d:
return self._core.mpz_d.row_swap(i, j)
IF HAVE_LONG_DOUBLE:
if self._type == gso_mpz_ld:
return self._core.mpz_ld.row_swap(i, j)
if self._type == gso_mpz_dpe:
return self._core.mpz_dpe.row_swap(i, j)
IF HAVE_QD:
if self._type == gso_mpz_dd:
return self._core.mpz_dd.row_swap(i, j)
if self._type == gso_mpz_qd:
return self._core.mpz_qd.row_swap(i, j)
if self._type == gso_mpz_mpfr:
return self._core.mpz_mpfr.row_swap(i, j)

if self._type == gso_long_d:
return self._core.long_d.row_swap(i, j)
IF HAVE_LONG_DOUBLE:
if self._type == gso_long_ld:
return self._core.long_ld.row_swap(i, j)
if self._type == gso_long_dpe:
return self._core.long_dpe.row_swap(i, j)
IF HAVE_QD:
if self._type == gso_long_dd:
return self._core.long_dd.row_swap(i, j)
if self._type == gso_long_qd:
return self._core.long_qd.row_swap(i, j)
if self._type == gso_long_mpfr:
return self._core.long_mpfr.row_swap(i, j)

raise RuntimeError("MatGSO object '%s' has no core."%self)

def negate_row(self, int i):
"""Set `b_i` to `-b_i`.
Expand Down
27 changes: 26 additions & 1 deletion tests/test_bkz_python.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
"""
from copy import copy

from fpylll import IntegerMatrix
from fpylll import IntegerMatrix, LLL
from fpylll.algorithms.simple_bkz import BKZReduction as SimpleBKZ
from fpylll.algorithms.simple_dbkz import DBKZReduction as SimpleDualBKZ
from fpylll.algorithms.bkz import BKZReduction as BKZ
from fpylll.algorithms.bkz2 import BKZReduction as BKZ2
from fpylll.algorithms.bkz_stats import BKZTreeTracer

from fpylll import BKZ as fplll_bkz
from fpylll.util import set_random_seed

Expand Down Expand Up @@ -46,3 +48,26 @@ def test_bkz_call(block_size=10):
A = make_integer_matrix(n)
B = copy(A)
cls(B)(params=params)


def test_bkz_postprocessing():
A = IntegerMatrix.random(20, "qary", bits=20, k=10, int_type="long")
LLL.reduction(A)

bkz = BKZ(A)
bkz.M.update_gso()
tracer = BKZTreeTracer(bkz)

solution = (2, 2, 0, 3, 4, 5, 7)

v = A.multiply_left(solution, 3)
bkz.svp_postprocessing(3, len(solution), solution, tracer)
w = tuple(A[3])
assert v == w

solution = (2, 1, 0, 3, 4, 5, 7)

v = A.multiply_left(solution, 3)
bkz.svp_postprocessing(3, len(solution), solution, tracer)
w = tuple(A[3])
assert v == w

0 comments on commit 99bf78c

Please sign in to comment.