Coverage for src/pqlattice/lattice/_svp.py: 100%
58 statements
« prev ^ index » next coverage.py v7.11.0, created at 2026-01-12 21:36 +0100
« prev ^ index » next coverage.py v7.11.0, created at 2026-01-12 21:36 +0100
1from fractions import Fraction
3from .._utils import as_integer, as_rational
4from ..typing import SquareMatrix, Vector, validate_aliases
5from ._gso import gso
6from ._lll import lll
9@validate_aliases
10def schnorr_euchner_svp(mu: SquareMatrix, B: Vector) -> Vector:
11 n = len(B)
12 best_norm: Fraction = B[0]
13 best_coeffs: Vector = as_integer([1] + [0] * (n - 1))
15 r: Vector = as_rational([0] * (n + 1))
16 c: Vector = as_rational([0] * n)
17 x: Vector = as_integer([0] * n)
18 v: Vector = as_rational([0] * n)
20 last_move = [0] * n
21 k = n - 1
23 r[n] = Fraction(0)
24 v[n - 1] = Fraction(0)
25 c[n - 1] = Fraction(0)
26 x[n - 1] = 0
27 last_move[n - 1] = 0
29 while True:
30 y: Fraction = x[k] - c[k]
31 current_norm: Fraction = r[k + 1] + (y * y * B[k])
32 # print(f"{k=}: {current_norm=:.5f}, {best_norm=:.5f}")
33 if current_norm < best_norm:
34 if k == 0:
35 if current_norm > 0:
36 best_norm = current_norm
37 best_coeffs = x.copy()
39 k += 1
40 last_move[k] += 1
42 step_sign = -1 if (last_move[k] % 2 == 0) else 1
43 step_mag = (last_move[k] + 1) // 2
44 x[k] = round(c[k]) + (step_sign * step_mag)
46 else:
47 k -= 1
48 center_val = Fraction(0)
49 for j in range(k + 1, n):
50 center_val += mu[j][k] * x[j]
52 c[k] = -center_val
53 x[k] = round(c[k])
54 last_move[k] = 0
55 r[k + 1] = current_norm
56 else:
57 k += 1
58 if k == n:
59 break
61 last_move[k] += 1
62 step_sign = -1 if (last_move[k] % 2 == 0) else 1
63 step_mag = (last_move[k] + 1) // 2
65 x[k] = round(c[k]) + (step_sign * step_mag)
67 return best_coeffs
70@validate_aliases
71def shortest_vector(lattice_basis: SquareMatrix) -> Vector:
72 """_summary_
74 Parameters
75 ----------
76 lattice_basis : SquareMatrix
77 _description_
79 Returns
80 -------
81 Vector
82 _description_
83 """
84 B = lll(lattice_basis)
85 B_star, U = gso(B)
86 B_norms2 = as_rational([sum(x * x for x in v) for v in B_star])
87 mu = U.T
88 coeffs = schnorr_euchner_svp(mu, B_norms2)
89 return coeffs @ B