Skip to content

Commit

Permalink
sagemathgh-38492: expose fplll enumeration routines in IntegralLattice
Browse files Browse the repository at this point in the history
This is useful for situations where we'd like to iterate over short-ish
or close-ish vectors without necessarily requiring *the*
shortest/closest solution, and without knowing a priori how many vectors
we need to try. Among other things, it shows up in some algorithms for
solving (quaternion) norm equations.

URL: sagemath#38492
Reported by: Lorenz Panny
Reviewer(s): Giacomo Pope
  • Loading branch information
Release Manager committed Aug 9, 2024
2 parents 8587c13 + f2c4e22 commit b178c10
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 3 deletions.
4 changes: 2 additions & 2 deletions build/pkgs/configure/checksums.ini
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
tarball=configure-VERSION.tar.gz
sha1=259cd70efeb5bfc37ab38d67a9d84e8fc368e603
sha256=fe3d338452726766f95ba9df3325d8cd03548c0e45d2c909006b395ad31b8326
sha1=e7adc4b553577aa2ab6ab579a41457f37565b0ac
sha256=95e81e424da6a8abc43d74cf7270e27ff870ed753678c7c85434d7d2fa0ba6b0
2 changes: 1 addition & 1 deletion build/pkgs/configure/package-version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
79b13118ab80f055d3054745134cd02bb2660127
6e2716e9dcd0b6c06ffd847af708d29b0a63d6dd
100 changes: 100 additions & 0 deletions src/sage/modules/free_quadratic_module_integer_symmetric.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
- Simon Brandhorst (2017-09): First created
- Paolo Menegatti (2018-03): Added IntegralLatticeDirectSum, IntegralLatticeGluing
- Lorenz Panny (2024): enumeration routines for short and close vectors
"""

# ****************************************************************************
Expand Down Expand Up @@ -1527,6 +1528,105 @@ def short_vectors(self, n, **kwargs):
short = q.short_vector_list_up_to_length(n, *kwargs)
return [[self(v * self.basis_matrix()) for v in L] for L in short]

def _fplll_enumerate(self, target=None):
r"""
Internal helper method to invoke the fplll enumeration routines.
EXAMPLES::
sage: L = IntegralLattice('A4')
sage: t = vector([1.2, -3/11, 5.5, -9.1])
sage: short = L.enumerate_short_vectors() # implicit doctest
sage: [next(short) for _ in range(10)]
[(0, 0, 0, 1), (0, 0, 1, 1), (0, 1, 1, 1), (1, 1, 1, 1), (0, 0, 1, 0),
(1, 1, 1, 0), (0, 1, 1, 0), (0, 1, 0, 0), (1, 1, 0, 0), (1, 0, 0, 0)]
sage: close = L.enumerate_close_vectors(t) # implicit doctest
sage: [next(close) for _ in range(10)]
[(1, 0, 6, -9), (1, -1, 5, -9), (2, 0, 6, -9), (1, 0, 5, -9), (1, -1, 5, -10),
(2, 1, 6, -9), (1, 0, 5, -10), (2, 0, 5, -9), (1, 0, 6, -8), (1, -1, 6, -9)]
"""
L = self.LLL()
dim = L.dimension()
gram = L.gram_matrix()
basis = L.basis_matrix()

import fpylll
gmat = fpylll.IntegerMatrix(dim, dim)
for i in range(dim):
for j in range(dim):
gmat[i,j] = gram[i,j]
gso = fpylll.GSO.Mat(gmat, gram=True)
ok = gso.update_gso()
assert ok

coord = None
if target is not None:
coord = basis.solve_left(target)
Mu = 1 + matrix([gso.get_mu(i,j) for j in range(dim)] for i in range(dim))
coord *= Mu

count = 8
bound = gso.get_r(dim-1, dim-1)
seen = set()
while True:
enum = fpylll.Enumeration(gso, count, fpylll.EvaluatorStrategy.BEST_N_SOLUTIONS)
try:
combs = enum.enumerate(0, dim, bound, 0, coord)
except fpylll.EnumerationError:
combs = []
if len(combs) < count:
bound *= 2
continue
for length,comb in combs:
vec = sum(ZZ(c)*b for c,b in zip(comb,basis))
if tuple(vec) not in seen:
yield vec
seen.add(tuple(vec))
count *= 2

def enumerate_short_vectors(self):
r"""
Return an iterator over all the vectors in this lattice (modulo sign),
starting from shorter vectors.
.. WARNING::
The returned vectors are not necessarily ordered strictly
by length.
EXAMPLES::
sage: L = IntegralLattice(4, [[1,2,3,4], [7,7,8,8], [1,-1,1,0]])
sage: short = L.enumerate_short_vectors()
sage: [next(short) for _ in range(20)]
[(1, -1, 1, 0), (2, -2, 2, 0), (3, -3, 3, 0), (0, 3, 2, 4), (1, 2, 3, 4),
(4, 4, 1, 0), (3, 2, -2, -4), (3, 5, 0, 0), (4, 1, -1, -4), (-1, 4, 1, 4),
(2, 1, 4, 4), (5, 3, 2, 0), (2, 3, -3, -4), (2, 6, -1, 0), (5, 0, 0, -4),
(-2, 5, 0, 4), (4, -4, 4, 0), (6, 2, 3, 0), (1, 4, -4, -4), (3, 0, 5, 4)]
"""
yield from self._fplll_enumerate()

def enumerate_close_vectors(self, target):
r"""
Return an iterator over all the vectors in this lattice, starting
from vectors relatively close to the given ``target`` vector.
.. WARNING::
The returned vectors are not necessarily ordered strictly
by their distance to the target.
EXAMPLES::
sage: L = IntegralLattice(4, [[1,2,3,4], [7,7,8,8], [1,-1,1,0]])
sage: t = vector([1/2, -133/7, 123.44, -11])
sage: close = L.enumerate_close_vectors(t)
sage: [next(close) for _ in range(10)]
[(1, -18, 123, 148), (2, -19, 124, 148), (0, -17, 122, 148), (3, -20, 125, 148), (-1, -16, 121, 148),
(-2, -20, 125, 152), (-2, -23, 123, 148), (4, -21, 126, 148), (-3, -22, 122, 148), (-3, -19, 124, 152)]
"""
yield from self._fplll_enumerate(target)

def twist(self, s, discard_basis=False):
r"""
Return the lattice with inner product matrix scaled by ``s``.
Expand Down

0 comments on commit b178c10

Please sign in to comment.