Skip to content

Commit

Permalink
Merge pull request #275 from ludopulles/master
Browse files Browse the repository at this point in the history
Change choice of q in qary-lattice generation
  • Loading branch information
malb authored Jun 15, 2024
2 parents 2d66121 + 38fb8ed commit d03bc38
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 69 deletions.
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,16 @@ target/

/.vagrant/
/Vagrantfile

# Ignore any build files
/src/fpylll/*.so
/src/fpylll/*/*.so

/src/fpylll/config.pxi
/.ipynb_checkpoints/*.ipynb
/MANIFEST
/valgrind-python.supp
/.pytest_cache/
/activate
/fpylll-env
/fpylll-fplll
/fpylll-fplll
24 changes: 12 additions & 12 deletions docs/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,10 @@ To compute the LLL reduced matrix of :math:`{\bf A}`
>>> A_original = copy(A)
>>> A_lll = LLL.reduction(A)
>>> print(A_lll)
[ -1 9 -5 -3 ]
[ 12 -2 7 -17 ]
[ -18 3 16 -1 ]
[ 4 17 20 12 ]
[ -3 21 -15 -23 ]
[ -10 70 -50 111 ]
[ -94 95 93 30 ]
[ 238 23 64 -52 ]

To test if a matrix is LLL-reduced

Expand All @@ -162,10 +162,10 @@ For the BKZ reduction of :math:`{\bf A}` with blocksize say 3 (without pruning),
>>> A.randomize("qary", bits=10, k=3)
>>> A_bkz = BKZ.reduction(A, BKZ.Param(block_size))
>>> print(A_bkz)
[ -1 9 -5 -3 ]
[ 12 -2 7 -17 ]
[ -18 3 16 -1 ]
[ 4 17 20 12 ]
[ -3 21 -15 -23 ]
[ -10 70 -50 111 ]
[ -94 95 93 30 ]
[ 238 23 64 -52 ]

If we want to use pruning we can use the default pruning of fplll [GNR10]_.

Expand All @@ -188,19 +188,19 @@ use it from the GSO tool detailed above
>>> _ = M.update_gso()
>>> w = M.babai([1, 17, -3, -75, 102])
>>> A.multiply_left(w)
(-4, 16, -5, -78, 97)
(-6, 12, -19, -71, 98)

To compute the norm of a shortest vector of the lattice generated by the rows of the matrix :math:`{\bf A}` we use the ``shortest_vector`` method of the SVP class, and measure the first row of the resulting matrix :math:`{\bf A}`

::

>>> from fpylll import SVP
>>> SVP.shortest_vector(A)
(2, -2, 7, 4, -1)
(-3, 0, 21, -15, -23)
>>> print(A[0])
(2, -2, 7, 4, -1)
(-3, 0, 21, -15, -23)
>>> A[0].norm()
8.602325267042627
34.698703...

For the Closest Vector Problem, fplll (and so fpylll) uses enumeration::

Expand Down
2 changes: 1 addition & 1 deletion src/fpylll/fplll/enumeration.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ cdef class Enumeration:
>>> enum = Enumeration(M, strategy=EvaluatorStrategy.BEST_N_SOLUTIONS, sub_solutions=True)
>>> _ = enum.enumerate(0, 30, 0.999*M.get_r(0, 0), 0, pruning=pruning.coefficients)
>>> [int(round(a)) for a,b in enum.sub_solutions[:5]]
[5569754193, 5556022462, 5083806188, 5022873440, 4260865083]
[13018980230, 12980748618, 12469398480, 10737191842, 10723577014]
"""
cdef list sub_solutions = []
Expand Down
10 changes: 5 additions & 5 deletions src/fpylll/fplll/fplll.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -388,11 +388,11 @@ cdef extern from "fplll/nr/matrix.h" namespace "fplll":
void gen_intrel(int bits) nogil
void gen_simdioph(int bits, int bits2) nogil
void gen_uniform(int bits) nogil
void gen_ntrulike(int bits) nogil
void gen_ntrulike_withq(int q) nogil
void gen_ntrulike2(int bits) nogil
void gen_ntrulike2_withq(int q) nogil
void gen_qary_withq(int k, int q) nogil
void gen_ntrulike(const Z_NR[T] &q) nogil
void gen_ntrulike_bits(int bits) nogil
void gen_ntrulike2(const Z_NR[T] &q) nogil
void gen_ntrulike2_bits(int bits) nogil
void gen_qary(int k, const Z_NR[T] &q) nogil
void gen_qary_prime(int k, int bits) nogil
void gen_trg(double alpha) nogil

Expand Down
22 changes: 11 additions & 11 deletions src/fpylll/fplll/gso.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -458,19 +458,19 @@ cdef class MatGSO:
>>> M = GSO.Mat(A, flags=GSO.INT_GRAM); _ = M.update_gso()
>>> G = M.G
>>> print(G)
[ 2176 0 0 0 0 0 0 0 0 0 ]
[ 1818 4659 0 0 0 0 0 0 0 0 ]
[ 2695 5709 7416 0 0 0 0 0 0 0 ]
[ 2889 5221 7077 7399 0 0 0 0 0 0 ]
[ 2746 3508 4717 4772 4618 0 0 0 0 0 ]
[ 2332 1590 2279 2332 2597 2809 0 0 0 0 ]
[ 265 1749 2491 2438 0 0 2809 0 0 0 ]
[ 159 265 212 1219 318 0 0 2809 0 0 ]
[ 742 636 1537 2067 1802 0 0 0 2809 0 ]
[ 159 2650 2650 1908 1696 0 0 0 0 2809 ]
[ 822597 0 0 0 0 0 0 0 0 0 ]
[ 357490 391403 0 0 0 0 0 0 0 0 ]
[ 474377 396238 635122 0 0 0 0 0 0 0 ]
[ 503594 382116 396118 448816 0 0 0 0 0 0 ]
[ 555245 288280 463386 393750 495338 0 0 0 0 0 ]
[ 313028 6756 146380 121045 286004 316969 0 0 0 0 ]
[ 2815 136246 304583 104718 163270 0 316969 0 0 0 ]
[ 215629 24209 28150 106407 90080 0 0 316969 0 0 ]
[ 287693 210562 276996 164396 139061 0 0 0 316969 0 ]
[ 182975 246031 97962 279811 145254 0 0 0 0 316969 ]
>>> A[0].norm()**2
2176.0
822597.000...
>>> M = GSO.Mat(G, gram=True); _ = M.update_gso()
>>> G == M.G
Expand Down
29 changes: 19 additions & 10 deletions src/fpylll/fplll/integer_matrix.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,9 @@ cdef class IntegerMatrix:
:seealso: :func:`~IntegerMatrix.random`
"""
cdef Z_NR[mpz_t] t_mpz
cdef Z_NR[long] t_long

if algorithm == "intrel":
bits = int(kwds["bits"])
sig_on()
Expand Down Expand Up @@ -1018,19 +1021,21 @@ cdef class IntegerMatrix:
q = int(kwds["q"])
sig_on()
if self._type == ZT_MPZ:
self._core.mpz.gen_ntrulike_withq(q)
assign_Z_NR_mpz(t_mpz, q)
self._core.mpz.gen_ntrulike(t_mpz)
elif self._type == ZT_LONG:
self._core.long.gen_ntrulike_withq(q)
t_long = <long> q
self._core.long.gen_ntrulike(t_long)
else:
raise RuntimeError("Integer type '%s' not understood."%self._type)
sig_off()
elif "bits" in kwds:
bits = int(kwds["bits"])
sig_on()
if self._type == ZT_MPZ:
self._core.mpz.gen_ntrulike(bits)
self._core.mpz.gen_ntrulike_bits(bits)
elif self._type == ZT_LONG:
self._core.long.gen_ntrulike(bits)
self._core.long.gen_ntrulike_bits(bits)
else:
raise RuntimeError("Integer type '%s' not understood."%self._type)
sig_off()
Expand All @@ -1042,19 +1047,21 @@ cdef class IntegerMatrix:
q = int(kwds["q"])
sig_on()
if self._type == ZT_MPZ:
self._core.mpz.gen_ntrulike2_withq(q)
assign_Z_NR_mpz(t_mpz, q)
self._core.mpz.gen_ntrulike2(t_mpz)
elif self._type == ZT_LONG:
self._core.long.gen_ntrulike2_withq(q)
t_long = <long> q
self._core.long.gen_ntrulike2(t_long)
else:
raise RuntimeError("Integer type '%s' not understood."%self._type)
sig_off()
elif "bits" in kwds:
bits = int(kwds["bits"])
sig_on()
if self._type == ZT_MPZ:
self._core.mpz.gen_ntrulike2(bits)
self._core.mpz.gen_ntrulike2_bits(bits)
elif self._type == ZT_LONG:
self._core.long.gen_ntrulike2(bits)
self._core.long.gen_ntrulike2_bits(bits)
else:
raise RuntimeError("Integer type '%s' not understood."%self._type)
sig_off()
Expand All @@ -1067,9 +1074,11 @@ cdef class IntegerMatrix:
q = int(kwds["q"])
sig_on()
if self._type == ZT_MPZ:
self._core.mpz.gen_qary_withq(k, q)
assign_Z_NR_mpz(t_mpz, q)
self._core.mpz.gen_qary(k, t_mpz)
elif self._type == ZT_LONG:
self._core.long.gen_qary_withq(k, q)
t_long = <long> q
self._core.long.gen_qary(k, t_long)
else:
raise RuntimeError("Integer type '%s' not understood."%self._type)
sig_off()
Expand Down
6 changes: 4 additions & 2 deletions src/fpylll/fplll/pruner.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ EXAMPLE::
>>> radius = sum([m.get_r(0, 0) for m in M])/len(M)
>>> pr = Pruning.run(radius, 10000, [m.r() for m in M], 0.4)
>>> print(pr) # doctest: +ELLIPSIS
PruningParams<7.797437, (1.00,...,0.80), 0.6594>
PruningParams<1.397930, (1.00,...,0.43), 0.4055>
>>> print(Pruning.run(M[0].get_r(0, 0), 2**20, [m.r() for m in M], 0.9, pruning=pr))
PruningParams<1.001130, (1.00,...,0.98), 0.9410>
PruningParams<1.437235, (1.00,...,0.98), 0.9410>
.. moduleauthor:: Martin R. Albrecht <[email protected]>
Expand Down
18 changes: 9 additions & 9 deletions src/fpylll/tools/bkz_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ def simulate(r, param):
>>> M = GSO.Mat(A)
>>> from fpylll.tools.bkz_simulator import simulate
>>> _ = simulate(M, BKZ.Param(block_size=40, max_loops=4, flags=BKZ.VERBOSE))
{"i": 0, "r_0": 2^33.3, "r_0/gh": 6.110565, "rhf": 1.018340, "/": -0.07013, "hv/hv": 2.424131}
{"i": 1, "r_0": 2^32.7, "r_0/gh": 4.018330, "rhf": 1.016208, "/": -0.06161, "hv/hv": 2.156298}
{"i": 2, "r_0": 2^32.3, "r_0/gh": 2.973172, "rhf": 1.014679, "/": -0.05745, "hv/hv": 2.047014}
{"i": 3, "r_0": 2^32.1, "r_0/gh": 2.583479, "rhf": 1.013966, "/": -0.05560, "hv/hv": 2.000296}
{"i": 0, "r_0": 2^34.7, "r_0/gh": 5.547855, "rhf": 1.017849, "/": -0.06928, "hv/hv": 2.406481}
{"i": 1, "r_0": 2^34.2, "r_0/gh": 3.894188, "rhf": 1.016049, "/": -0.06136, "hv/hv": 2.150078}
{"i": 2, "r_0": 2^33.8, "r_0/gh": 2.949459, "rhf": 1.014638, "/": -0.05735, "hv/hv": 2.044402}
{"i": 3, "r_0": 2^33.6, "r_0/gh": 2.574565, "rhf": 1.013949, "/": -0.05556, "hv/hv": 1.999163}
"""

r = _extract_log_norms(r)
Expand Down Expand Up @@ -190,10 +190,10 @@ def simulate_prob(r, param, prng_seed=0xdeadbeef):
>>> M = GSO.Mat(A)
>>> from fpylll.tools.bkz_simulator import simulate_prob
>>> _ = simulate_prob(M, BKZ.Param(block_size=40, max_loops=4, flags=BKZ.VERBOSE))
{"i": 0, "r_0": 2^33.1, "r_0/gh": 5.193166, "rhf": 1.017512, "/": -0.07022, "hv/hv": 2.428125}
{"i": 1, "r_0": 2^32.7, "r_0/gh": 3.997766, "rhf": 1.016182, "/": -0.06214, "hv/hv": 2.168460}
{"i": 2, "r_0": 2^32.3, "r_0/gh": 3.020156, "rhf": 1.014759, "/": -0.05808, "hv/hv": 2.059562}
{"i": 3, "r_0": 2^32.2, "r_0/gh": 2.783102, "rhf": 1.014344, "/": -0.05603, "hv/hv": 2.013191}
{"i": 0, "r_0": 2^34.5, "r_0/gh": 4.714937, "rhf": 1.017021, "/": -0.06936, "hv/hv": 2.410445}
{"i": 1, "r_0": 2^34.2, "r_0/gh": 3.874259, "rhf": 1.016023, "/": -0.06189, "hv/hv": 2.162205}
{"i": 2, "r_0": 2^33.8, "r_0/gh": 2.996068, "rhf": 1.014718, "/": -0.05798, "hv/hv": 2.056934}
{"i": 3, "r_0": 2^33.7, "r_0/gh": 2.773499, "rhf": 1.014326, "/": -0.05598, "hv/hv": 2.012050}
"""

if param.block_size <= 2:
Expand Down Expand Up @@ -301,7 +301,7 @@ def averaged_simulate_prob(L, param, tries=10):
>>> from fpylll.tools.bkz_simulator import averaged_simulate_prob
>>> _ = averaged_simulate_prob(M, BKZ.Param(block_size=40, max_loops=4))
>>> print(_[0][:3])
[4663149828.487..., 4267813469.1884..., 4273411937.5775...]
[13371442256.252..., 12239031147.433..., 12256303707.863...]
"""
if tries < 1:
raise ValueError("Need to average over positive number of tries.")
Expand Down
8 changes: 4 additions & 4 deletions src/fpylll/tools/quality.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ def get_current_slope(r, start_row=0, stop_row=-1):
>>> M = GSO.Mat(A); _ = M.update_gso()
>>> from fpylll.tools.quality import get_current_slope
>>> M.get_current_slope(0, 100) # doctest: +ELLIPSIS
-0.085500625...
-0.083173398...
>>> get_current_slope(M.r(), 0, 100) # doctest: +ELLIPSIS
-0.085500625...
-0.083173398...
"""
x = [log(r[i]) for i in range(start_row, stop_row)]
Expand Down Expand Up @@ -69,9 +69,9 @@ def basis_quality(M):
>>> from fpylll.tools.quality import basis_quality
>>> from fpylll.tools.bkz_stats import pretty_dict
>>> str(pretty_dict(basis_quality(M)))
'{"r_0": 2^34.0, "r_0/gh": 9.389811, "rhf": 1.020530, "/": -0.08550, "hv/hv": 2.940943}'
'{"r_0": 2^35.3, "r_0/gh": 8.564671, "rhf": 1.020061, "/": -0.08317, "hv/hv": 2.832300}'
>>> str(pretty_dict(basis_quality(M.r())))
'{"r_0": 2^34.0, "r_0/gh": 9.389811, "rhf": 1.020530, "/": -0.08550, "hv/hv": 2.940943}'
'{"r_0": 2^35.3, "r_0/gh": 8.564671, "rhf": 1.020061, "/": -0.08317, "hv/hv": 2.832300}'
"""

Expand Down
23 changes: 9 additions & 14 deletions tests/test_gso.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

def make_integer_matrix(m, n, int_type="mpz"):
A = IntegerMatrix(m, n, int_type=int_type)
A.randomize("qary", k=m//2, bits=m)
A.randomize("qary", k=m//2, bits=max(1, m))
return A


Expand Down Expand Up @@ -94,9 +94,9 @@ def test_gso_update_gso():
g00.append(M.get_gram(0, 0))

for i in range(1, len(r00)):
assert r00[0] == pytest.approx(r00[i], rel=EPSILON)
assert re00[0] == pytest.approx(re00[i], rel=EPSILON)
assert g00[0] == pytest.approx(g00[i], rel=EPSILON)
assert r00[0] == pytest.approx(r00[i], rel=EPSILON)
assert re00[0] == pytest.approx(re00[i], rel=EPSILON)
assert g00[0] == pytest.approx(g00[i], rel=EPSILON)


def test_gso_babai():
Expand Down Expand Up @@ -145,11 +145,10 @@ def test_gso_coherence_gram_matrix():
Test if the GSO is coherent if it is given a matrix A or its associated
Gram matrix A*A^T
"""

EPSILON = 0.0001

for m, n in dimensions:
for int_type in int_types:
for int_type in int_types:
for m, n in dimensions:
# long is not tested for high dimensions because of integer overflow
if m > 20 and int_type == "long":
continue
Expand All @@ -171,12 +170,8 @@ def test_gso_coherence_gram_matrix():

# Check if computations coincide
for i in range(m):
M_A.get_r(i, i) == pytest.approx(M_G.get_r(i, j), rel=EPSILON)
assert M_A.get_r(i, i) == pytest.approx(M_G.get_r(i, i), rel=EPSILON)

for j in range(i):
assert (
M_A.get_r(i, j) == pytest.approx(M_G.get_r(i, j), rel=EPSILON)
)
assert (
M_A.get_mu(i, j) == pytest.approx(M_G.get_mu(i, j), rel=EPSILON)
)
assert M_A.get_r(i, j) == pytest.approx(M_G.get_r(i, j), rel=EPSILON)
assert M_A.get_mu(i, j) == pytest.approx(M_G.get_mu(i, j), rel=EPSILON)
8 changes: 8 additions & 0 deletions tests/test_random.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-

from cysignals.signals import SignalError
from pytest import raises

from fpylll import IntegerMatrix, FPLLL


Expand All @@ -9,6 +12,11 @@ def make_integer_matrix(m, n, int_type="mpz"):
return A


def test_zero_bits():
with raises(SignalError):
IntegerMatrix.random(10, "qary", k=5, bits=0)


def test_randomize():
FPLLL.set_random_seed(1337)
A0 = make_integer_matrix(20, 20)
Expand Down

0 comments on commit d03bc38

Please sign in to comment.