Skip to content
Merged
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
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)
Copy link
Contributor

@GiacomoPope GiacomoPope Aug 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's the IntegerMatrix.from_matrix() method, could you not do

gmat = fpyll.IntegerMatrix.from_matrix(gram)
gso = fpylll.GSO.Mat(gmat, gram=True)

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))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's also a to_matrix() method, so something like:

Mu = 1 + matrix(gso.to_matrix())

might work

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't work for me https://sagecell.sagemath.org/?q=yduvls (10.3)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh for the MatGSO type it's the property .gso.G

Mu = 1 + matrix(gso.G)

https://sagecell.sagemath.org/?q=qifgtc

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahha yes. I think this should be added before it's merged, seems quicker :-)

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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for length,comb in combs:
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