Skip to content

Commit b178c10

Browse files
author
Release Manager
committed
gh-38492: expose fplll enumeration routines in IntegralLattice
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: #38492 Reported by: Lorenz Panny Reviewer(s): Giacomo Pope
2 parents 8587c13 + f2c4e22 commit b178c10

File tree

3 files changed

+103
-3
lines changed

3 files changed

+103
-3
lines changed

build/pkgs/configure/checksums.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
tarball=configure-VERSION.tar.gz
2-
sha1=259cd70efeb5bfc37ab38d67a9d84e8fc368e603
3-
sha256=fe3d338452726766f95ba9df3325d8cd03548c0e45d2c909006b395ad31b8326
2+
sha1=e7adc4b553577aa2ab6ab579a41457f37565b0ac
3+
sha256=95e81e424da6a8abc43d74cf7270e27ff870ed753678c7c85434d7d2fa0ba6b0
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
79b13118ab80f055d3054745134cd02bb2660127
1+
6e2716e9dcd0b6c06ffd847af708d29b0a63d6dd

src/sage/modules/free_quadratic_module_integer_symmetric.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
4040
- Simon Brandhorst (2017-09): First created
4141
- Paolo Menegatti (2018-03): Added IntegralLatticeDirectSum, IntegralLatticeGluing
42+
- Lorenz Panny (2024): enumeration routines for short and close vectors
4243
"""
4344

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

1531+
def _fplll_enumerate(self, target=None):
1532+
r"""
1533+
Internal helper method to invoke the fplll enumeration routines.
1534+
1535+
EXAMPLES::
1536+
1537+
sage: L = IntegralLattice('A4')
1538+
sage: t = vector([1.2, -3/11, 5.5, -9.1])
1539+
sage: short = L.enumerate_short_vectors() # implicit doctest
1540+
sage: [next(short) for _ in range(10)]
1541+
[(0, 0, 0, 1), (0, 0, 1, 1), (0, 1, 1, 1), (1, 1, 1, 1), (0, 0, 1, 0),
1542+
(1, 1, 1, 0), (0, 1, 1, 0), (0, 1, 0, 0), (1, 1, 0, 0), (1, 0, 0, 0)]
1543+
sage: close = L.enumerate_close_vectors(t) # implicit doctest
1544+
sage: [next(close) for _ in range(10)]
1545+
[(1, 0, 6, -9), (1, -1, 5, -9), (2, 0, 6, -9), (1, 0, 5, -9), (1, -1, 5, -10),
1546+
(2, 1, 6, -9), (1, 0, 5, -10), (2, 0, 5, -9), (1, 0, 6, -8), (1, -1, 6, -9)]
1547+
"""
1548+
L = self.LLL()
1549+
dim = L.dimension()
1550+
gram = L.gram_matrix()
1551+
basis = L.basis_matrix()
1552+
1553+
import fpylll
1554+
gmat = fpylll.IntegerMatrix(dim, dim)
1555+
for i in range(dim):
1556+
for j in range(dim):
1557+
gmat[i,j] = gram[i,j]
1558+
gso = fpylll.GSO.Mat(gmat, gram=True)
1559+
ok = gso.update_gso()
1560+
assert ok
1561+
1562+
coord = None
1563+
if target is not None:
1564+
coord = basis.solve_left(target)
1565+
Mu = 1 + matrix([gso.get_mu(i,j) for j in range(dim)] for i in range(dim))
1566+
coord *= Mu
1567+
1568+
count = 8
1569+
bound = gso.get_r(dim-1, dim-1)
1570+
seen = set()
1571+
while True:
1572+
enum = fpylll.Enumeration(gso, count, fpylll.EvaluatorStrategy.BEST_N_SOLUTIONS)
1573+
try:
1574+
combs = enum.enumerate(0, dim, bound, 0, coord)
1575+
except fpylll.EnumerationError:
1576+
combs = []
1577+
if len(combs) < count:
1578+
bound *= 2
1579+
continue
1580+
for length,comb in combs:
1581+
vec = sum(ZZ(c)*b for c,b in zip(comb,basis))
1582+
if tuple(vec) not in seen:
1583+
yield vec
1584+
seen.add(tuple(vec))
1585+
count *= 2
1586+
1587+
def enumerate_short_vectors(self):
1588+
r"""
1589+
Return an iterator over all the vectors in this lattice (modulo sign),
1590+
starting from shorter vectors.
1591+
1592+
.. WARNING::
1593+
1594+
The returned vectors are not necessarily ordered strictly
1595+
by length.
1596+
1597+
EXAMPLES::
1598+
1599+
sage: L = IntegralLattice(4, [[1,2,3,4], [7,7,8,8], [1,-1,1,0]])
1600+
sage: short = L.enumerate_short_vectors()
1601+
sage: [next(short) for _ in range(20)]
1602+
[(1, -1, 1, 0), (2, -2, 2, 0), (3, -3, 3, 0), (0, 3, 2, 4), (1, 2, 3, 4),
1603+
(4, 4, 1, 0), (3, 2, -2, -4), (3, 5, 0, 0), (4, 1, -1, -4), (-1, 4, 1, 4),
1604+
(2, 1, 4, 4), (5, 3, 2, 0), (2, 3, -3, -4), (2, 6, -1, 0), (5, 0, 0, -4),
1605+
(-2, 5, 0, 4), (4, -4, 4, 0), (6, 2, 3, 0), (1, 4, -4, -4), (3, 0, 5, 4)]
1606+
"""
1607+
yield from self._fplll_enumerate()
1608+
1609+
def enumerate_close_vectors(self, target):
1610+
r"""
1611+
Return an iterator over all the vectors in this lattice, starting
1612+
from vectors relatively close to the given ``target`` vector.
1613+
1614+
.. WARNING::
1615+
1616+
The returned vectors are not necessarily ordered strictly
1617+
by their distance to the target.
1618+
1619+
EXAMPLES::
1620+
1621+
sage: L = IntegralLattice(4, [[1,2,3,4], [7,7,8,8], [1,-1,1,0]])
1622+
sage: t = vector([1/2, -133/7, 123.44, -11])
1623+
sage: close = L.enumerate_close_vectors(t)
1624+
sage: [next(close) for _ in range(10)]
1625+
[(1, -18, 123, 148), (2, -19, 124, 148), (0, -17, 122, 148), (3, -20, 125, 148), (-1, -16, 121, 148),
1626+
(-2, -20, 125, 152), (-2, -23, 123, 148), (4, -21, 126, 148), (-3, -22, 122, 148), (-3, -19, 124, 152)]
1627+
"""
1628+
yield from self._fplll_enumerate(target)
1629+
15301630
def twist(self, s, discard_basis=False):
15311631
r"""
15321632
Return the lattice with inner product matrix scaled by ``s``.

0 commit comments

Comments
 (0)