Skip to content

Chapter 7: Lattices for programmers

A lattice is the set of integer combinations of a basis. Fix nn linearly independent vectors b1,,bnb_1, \ldots, b_n in Rn\mathbb{R}^n and the lattice L(B)L(B) is the set of all sums c1b1++cnbnc_1 b_1 + \cdots + c_n b_n with integer coefficients ciZc_i \in \mathbb{Z}. The basis BB is a presentation of the lattice; the lattice is the object the presentation names. In dimension at least two, the same lattice has infinitely many different bases; in dimension one, aZa\mathbb{Z} admits only aa and a-a as bases.

Every lattice-based scheme in Part II rests on a claim about the lattice as an object. The shortest nonzero vector is hard to find, a noisy version of the closest vector is hard to find, or the lattice carries enough structure that a fast transform is available. The geometric quantities in this chapter, determinant, dual, successive minima, SVP, and CVP, are properties of the lattice rather than of a particular basis (Micciancio & Goldwasser, 2002). Cryptographic instances still come with representations: a public basis may be hard to use, a trapdoor basis may make sampling or decoding easy, and the ring or module representations of Chapter 9 expose the fast arithmetic used by schemes such as ML-KEM. Chapter 7 builds the algebra that makes the basis-invariant side precise: bases over Z\mathbb{Z}, change of basis, the determinant, the dual lattice, and Minkowski’s bound on how short the shortest vector can be.

Consider the 2D lattice LR2L \subset \mathbb{R}^2 with basis vectors b1=(3,1)b_1 = (3, 1) and b2=(1,2)b_2 = (1, 2). Writing these as the columns of a 2-by-2 integer matrix gives the basis

B1=(3112).B_1 = \begin{pmatrix} 3 & 1 \\ 1 & 2 \end{pmatrix}.

Every point in L(B1)L(B_1) is of the form B1xB_1 x for some integer vector xZ2x \in \mathbb{Z}^2. The integer combinations (3,1)(3, 1), (1,2)(1, 2), (4,3)=b1+b2(4, 3) = b_1 + b_2, and (2,1)=b1b2(2, -1) = b_1 - b_2 all sit in LL. The vector (1,0)(1, 0) does not: solving B1x=(1,0)B_1 x = (1, 0) gives x1=2/5x_1 = 2/5 and x2=1/5x_2 = -1/5, which are not integers.

Now fix a second basis

B2=(3413)B_2 = \begin{pmatrix} 3 & 4 \\ 1 & 3 \end{pmatrix}

with columns b1=(3,1)b'_1 = (3, 1) and b2=(4,3)b'_2 = (4, 3). The claim is that L(B1)L(B_1) and L(B2)L(B_2) are the same lattice. The quick argument is that (4,3)=1b1+1b2(4, 3) = 1 \cdot b_1 + 1 \cdot b_2 sits in L(B1)L(B_1), and (1,2)=1b21b1(1, 2) = 1 \cdot b'_2 - 1 \cdot b'_1 sits in L(B2)L(B_2). Every basis vector on each side is an integer combination of the other side’s basis vectors, so the two lattices share their generating sets and are equal.

The reader can check this in Python:

import numpy as np
# A lattice in Z^2, stored as its basis B with columns b_1, b_2.
B1 = np.array([[3, 1], [1, 2]], dtype=np.int64)
B2 = np.array([[3, 4], [1, 3]], dtype=np.int64)
def lattice_point(B, x):
return tuple(int(a) for a in B @ np.asarray(x, dtype=np.int64))
print("b_1 =", lattice_point(B1, [1, 0]))
# ==> b_1 = (3, 1)
print("b_2 =", lattice_point(B1, [0, 1]))
# ==> b_2 = (1, 2)
print("b'_2 =", lattice_point(B2, [0, 1]))
# ==> b'_2 = (4, 3)
print("b_1 + b_2 (in L(B1)) =", lattice_point(B1, [1, 1]))
# ==> b_1 + b_2 (in L(B1)) = (4, 3)

Every Python block this chapter prints is also a standalone file in the companion repository, under chapter-code/ch07/, one file per block. Appendix C covers the clone and the environment they run on.

The two bases generate the same set. The next section names the algebraic structure underlying this equality. The matrix UU with B2=B1UB_2 = B_1 U is an integer matrix with determinant ±1\pm 1, called a unimodular matrix, and any two bases of the same lattice are related by some such UU.

Basis over Z\mathbb{Z}

Section titled “Basis over Z”

A basis of a full-rank lattice LRnL \subset \mathbb{R}^n is a set of nn linearly independent vectors b1,,bnb_1, \ldots, b_n in Rn\mathbb{R}^n such that every element of LL is a unique integer combination of the bib_i. The skeleton of this definition matches Chapter 2’s basis over a field Fp\mathbb{F}_p: nn independent vectors that span every element of the structure exactly once. The important difference is that the coefficients come from Z\mathbb{Z}, not from a field (Micciancio & Goldwasser, 2002).

Z\mathbb{Z} is a commutative ring with identity but not a field, so “basis over Z\mathbb{Z}” means something strictly more restricted than “basis over Q\mathbb{Q} or R\mathbb{R}”. The restriction shows up as soon as the reader tries to rescale a basis vector. Over R\mathbb{R}, any nonzero multiple of b1b_1 generates the same line; over Z\mathbb{Z}, only ±b1\pm b_1 does. The vector 2b12 b_1 generates the strict sublattice of even-coefficient combinations and has index 2 in the original lattice (Micciancio & Goldwasser, 2002). This is the narrower meaning of basis that Chapter 2 forward-pointed to.

An nn-by-nn integer matrix UU is unimodular when its determinant is ±1\pm 1. Equivalently, UU is unimodular when both UU and U1U^{-1} are integer matrices, which follows from the adjugate formula U1=adj(U)/det(U)U^{-1} = \mathrm{adj}(U) / \det(U) applied to an integer UU (Micciancio & Goldwasser, 2002). The group of unimodular matrices is denoted GLn(Z)\mathrm{GL}_n(\mathbb{Z}).

The central theorem of this section is that two bases generate the same lattice exactly when they are related by a unimodular change of basis.

Theorem (basis equivalence). Let B1,B2Rn×nB_1, B_2 \in \mathbb{R}^{n \times n} be two full-rank matrices. Then L(B1)=L(B2)L(B_1) = L(B_2) if and only if there exists a unimodular matrix UGLn(Z)U \in \mathrm{GL}_n(\mathbb{Z}) with B2=B1UB_2 = B_1 U (Micciancio & Goldwasser, 2002).

The proof fits in two paragraphs. Suppose first that B2=B1UB_2 = B_1 U for some unimodular UU. Every column of B2B_2 is then an integer combination of the columns of B1B_1 (the coefficients are the columns of UU, which are integers), so L(B2)L(B1)L(B_2) \subseteq L(B_1). The reverse inclusion follows by writing B1=B2U1B_1 = B_2 U^{-1} and observing that U1U^{-1} is also an integer matrix because UU is unimodular.

Conversely, suppose L(B1)=L(B2)L(B_1) = L(B_2). Every column of B2B_2 sits in L(B1)L(B_1), so there is an integer matrix UU with B2=B1UB_2 = B_1 U. Running the same argument with the roles of B1B_1 and B2B_2 swapped produces an integer matrix VV with B1=B2VB_1 = B_2 V, hence B1=B1UVB_1 = B_1 U V and UV=IU V = I. Taking determinants of UV=IU V = I gives detUdetV=1\det U \cdot \det V = 1. Both factors are integers, so each is ±1\pm 1. So UU is unimodular.

Concretely, the change-of-basis matrix for the two bases above is

U=B11B2=(1101),U = B_1^{-1} B_2 = \begin{pmatrix} 1 & 1 \\ 0 & 1 \end{pmatrix},

with detU=1\det U = 1. So UU is unimodular and the claim that B1B_1 and B2B_2 generate the same lattice is formally correct.

The determinant and the fundamental parallelepiped

Section titled “The determinant and the fundamental parallelepiped”

The determinant of a full-rank lattice LL is defined by det(L)=detB\det(L) = |\det B| for any basis BB of LL. Basis-invariance follows from the equivalence theorem: if B2=B1UB_2 = B_1 U with unimodular UU, then detB2=detB1detU=±detB1\det B_2 = \det B_1 \cdot \det U = \pm \det B_1, and taking absolute values gives detB2=detB1|\det B_2| = |\det B_1|. So det(L)\det(L) is a property of LL alone, not of the choice of basis (Micciancio & Goldwasser, 2002).

The determinant has a geometric interpretation. The fundamental parallelepiped of a basis BB is the half-open region

P(B)={Bx:0xi<1 for all i}.\mathcal{P}(B) = \{B x : 0 \leq x_i < 1 \text{ for all } i\}.

Its nn-dimensional volume is [0,1]ndetBdx=detB\int_{[0,1]^n} |\det B| \, dx = |\det B| by the Jacobian of the linear map xBxx \mapsto B x, so det(L)\det(L) is the volume of the fundamental parallelepiped of any basis (Micciancio & Goldwasser, 2002). An equivalent way to think about this: det(L)\det(L) is the amount of Rn\mathbb{R}^n-volume per lattice point. A lattice with a small determinant is dense, and a lattice with a large determinant is sparse.

The running example has detB1=3211=5\det B_1 = 3 \cdot 2 - 1 \cdot 1 = 5, so det(L)=5\det(L) = 5. Every fundamental parallelepiped of LL has area 55, regardless of which basis the reader uses to draw it.

Figure 7.1 shows LL with b1b_1 and b2b_2 drawn as arrows and the fundamental parallelepiped shaded.

A 2D lattice with basis vectors and fundamental parallelepiped A two-dimensional integer lattice is drawn with the origin at the center of the figure. Lattice points appear as small light circles at several integer combinations of the two basis vectors b1 equal to (3, 1) and b2 equal to (1, 2). Two arrows from the origin show the basis vectors. A shaded parallelogram with vertices at the origin, b1, b1 plus b2 equal to (4, 3), and b2 is the fundamental parallelepiped of the basis; its area is 5 and no lattice point lies strictly inside it. The four shortest nonzero lattice vectors are plus or minus (1, 2) and plus or minus (2, negative 1), all of length square root of 5. Faint axis lines mark the x and y axes as a reference grid. b_1 = (3, 1) b_2 = (1, 2) Lattice L with basis (b_1, b_2) and shaded fundamental parallelepiped of area 5.
Figure 7.1. The 2D lattice LL with basis b1=(3,1)b_1 = (3, 1) and b2=(1,2)b_2 = (1, 2). Lattice points are integer combinations of the basis. The shaded parallelogram is the fundamental parallelepiped P(B)\mathcal{P}(B) of area det(L)=5\det(L) = 5. Half-open translates of P(B)\mathcal{P}(B) by lattice vectors tile the plane, and the tiling has exactly one lattice point per tile.

The interior of the fundamental parallelepiped contains no lattice point. Half-open translates of P(B)\mathcal{P}(B) by lattice vectors tile the plane with exactly one lattice point per tile. Closing the parallelogram on every side would double-count boundary points.

Given a full-rank lattice LRnL \subset \mathbb{R}^n, the dual lattice is

L={yRn:y,xZ for all xL}.L^* = \{y \in \mathbb{R}^n : \langle y, x \rangle \in \mathbb{Z} \text{ for all } x \in L\}.

For a lattice with basis BB, the dual is itself a full-rank lattice, and a basis for it is given by BT=(B1)TB^{-T} = (B^{-1})^T (Micciancio & Goldwasser, 2002). The derivation is two lines. A vector yy sits in LL^* exactly when y,Bk=(BTy)Tk\langle y, B k \rangle = (B^T y)^T k is an integer for every kZnk \in \mathbb{Z}^n. Evaluating the condition at k=e1,,enk = e_1, \ldots, e_n shows that every entry of BTyB^T y is an integer, so BTyZnB^T y \in \mathbb{Z}^n, which is equivalent to y=BTzy = B^{-T} z for z=BTyZnz = B^T y \in \mathbb{Z}^n.

Two facts about the dual matter in the rest of Part II. First, the dual-of-dual identity (BT)T=B(B^{-T})^{-T} = B gives L=LL^{**} = L. The double dual returns the original lattice. Second, when BB is an integer matrix, the dual basis BTB^{-T} is generally rational rather than integer, so LL^* need not sit inside Zn\mathbb{Z}^n even when LZnL \subseteq \mathbb{Z}^n. The special case L=ZnL = \mathbb{Z}^n is self-dual, and every other full-rank LZnL \subseteq \mathbb{Z}^n acquires fractional dual vectors: LZnL \subseteq \mathbb{Z}^n forces ZnL\mathbb{Z}^n \subseteq L^*, while det(L)=1/det(L)<1\det(L^*) = 1/\det(L) < 1 rules out LZnL^* \subseteq \mathbb{Z}^n. The dual lives in the same real span as LL but at a different scale. Chapter 9 uses the dual lattice to state the Ring-LWE and Module-LWE hardness assumptions; Chapter 7 supplies the definition, the computational identity, and nothing more. Pontryagin duality and the algebraic dual Hom(L,Z)\mathrm{Hom}(L, \mathbb{Z}) are out of scope.

The successive minima of a full-rank lattice LL are a sequence of real numbers

λ1(L)λ2(L)λn(L).\lambda_1(L) \leq \lambda_2(L) \leq \cdots \leq \lambda_n(L).

The value λi(L)\lambda_i(L) is the smallest real rr such that LL contains ii linearly independent vectors of Euclidean norm at most rr. So λ1(L)\lambda_1(L) is the length of the shortest nonzero vector in LL, and λn(L)\lambda_n(L) is the smallest radius that captures nn linearly independent lattice vectors (Micciancio & Goldwasser, 2002).

Minkowski’s theorem (first bound). For every full-rank lattice LRnL \subset \mathbb{R}^n,

λ1(L)ndet(L)1/n.\lambda_1(L) \leq \sqrt{n} \cdot \det(L)^{1/n}.

This is the form of Minkowski’s bound cited throughout the rest of this book (Micciancio & Goldwasser, 2002). The proof applies Minkowski’s convex body theorem to a suitably scaled centrally symmetric convex body, such as a cube, and then converts the resulting coordinate bound into the Euclidean norm bound. The full argument is out of scope here. Micciancio and Goldwasser give it in full (Micciancio & Goldwasser, 2002). What matters downstream is the algorithmic gap the bound leaves open, which Chapter 13 takes up.

Minkowski’s bound is an existence statement: it guarantees that a short vector is present in LL, but it does not identify which one. Finding the shortest vector, or finding a vector within a factor γ\gamma of λ1(L)\lambda_1(L), is the shortest vector problem and its approximate variant, stated later in this chapter.

The four shortest nonzero lattice vectors of the running example are ±b2=±(1,2)\pm b_2 = \pm(1, 2) and ±(2,1)=±(b1b2)\pm(2, -1) = \pm(b_1 - b_2), all of length 5\sqrt{5}, and no lattice vector is shorter. The argument is a finite case check on the Gram matrix

B1TB1=(10555),B_1^T B_1 = \begin{pmatrix} 10 & 5 \\ 5 & 5 \end{pmatrix},

which gives ib1+jb22=10i2+10ij+5j2\|i\,b_1 + j\,b_2\|^2 = 10 i^2 + 10 i j + 5 j^2. The nine values over i,j1|i|, |j| \leq 1 attain minimum nonzero value 55 exactly at (i,j){(0,±1),±(1,1)}(i, j) \in \{(0, \pm 1), \pm(1, -1)\}. Whenever i2|i| \geq 2 or j2|j| \geq 2, the form is at least 1010, so no lattice vector is shorter than 5\sqrt{5}. This gives λ1(L)=5\lambda_1(L) = \sqrt{5}, comfortably below Minkowski’s bound 103.162\sqrt{10} \approx 3.162 (Micciancio & Goldwasser, 2002).

The full implementation with a pytest suite lives at solutions/ch07-lattices/. The snippets below show the core idea of each operation in numpy.

Start with the lattice itself. A full-rank lattice in dimension nn is stored as an n×nn \times n integer basis matrix whose columns are the basis vectors, and a lattice point is just the matrix-vector product BxB x for some integer vector xx:

import numpy as np
# The basis B_1 from the start of the chapter, columns are b_1 and b_2.
B1 = np.array([[3, 1], [1, 2]], dtype=np.int64)
def lattice_point(B, x):
return tuple(int(a) for a in B @ np.asarray(x, dtype=np.int64))
# A handful of lattice points in L(B_1).
print(lattice_point(B1, [ 0, 0]))
# ==> (0, 0)
print(lattice_point(B1, [ 1, 1]))
# ==> (4, 3)
print(lattice_point(B1, [ 2, -1]))
# ==> (5, 0)
print(lattice_point(B1, [-1, 2]))
# ==> (-1, 3)

The change-of-basis check reduces to solving the integer linear system B2=B1UB_2 = B_1 U and testing whether UU is unimodular:

import numpy as np
B1 = np.array([[3, 1], [1, 2]], dtype=np.int64)
B2 = np.array([[3, 4], [1, 3]], dtype=np.int64)
# Solve B_1 U = B_2 for U over the rationals; unimodular iff integer and det +/-1.
U = np.linalg.solve(B1.astype(float), B2.astype(float))
U_int = np.round(U).astype(np.int64)
print("U =", U_int.tolist())
# ==> U = [[1, 1], [0, 1]]
print("U is integer?", bool(np.allclose(U, U_int)))
# ==> U is integer? True
print("det U =", int(round(np.linalg.det(U_int.astype(float)))))
# ==> det U = 1

The determinant of the lattice is the absolute value of the determinant of any basis, and the equivalence theorem guarantees it is invariant under unimodular change of basis:

import numpy as np
B1 = np.array([[3, 1], [1, 2]], dtype=np.int64)
U = np.array([[1, 1], [0, 1]], dtype=np.int64)
B2 = B1 @ U
det_B1 = abs(int(round(np.linalg.det(B1.astype(float)))))
det_B2 = abs(int(round(np.linalg.det(B2.astype(float)))))
print("|det B1| =", det_B1)
# ==> |det B1| = 5
print("|det B2| =", det_B2)
# ==> |det B2| = 5

The dual basis is BTB^{-T}, computed via numpy.linalg.inv and a transpose:

import numpy as np
B = np.array([[3, 1], [1, 2]], dtype=float)
D = np.linalg.inv(B).T
# The dual basis is rational; displaying 5 * D shows it as a small integer matrix.
print("5 * D =", np.round(5 * D).astype(np.int64).tolist())
# ==> 5 * D = [[2, -1], [-1, 3]]
# B^T @ D should equal I: every (dual, primal) pair has the right inner product.
identity = B.T @ D
print("B^T @ D rounded =", np.round(identity, 9).tolist())
# ==> B^T @ D rounded = [[1.0, 0.0], [0.0, 1.0]]

The dual-of-dual identity (BT)T=B(B^{-T})^{-T} = B is worth seeing explicitly:

import numpy as np
B = np.array([[3, 1], [1, 2]], dtype=float)
D = np.linalg.inv(B).T
DD = np.linalg.inv(D).T
print("dual-of-dual equals B?", bool(np.allclose(DD, B)))
# ==> dual-of-dual equals B? True

Finally, Minkowski’s bound for the running example is 251/23.16\sqrt{2} \cdot 5^{1/2} \approx 3.16. An exhaustive search over small integer coefficients finds an explicit lattice vector under the bound (in fact, well under it):

import math
import numpy as np
B = np.array([[3, 1], [1, 2]], dtype=np.int64)
n = B.shape[1]
det_L = abs(int(round(np.linalg.det(B.astype(float)))))
bound = math.sqrt(n) * (det_L ** (1.0 / n))
print("det(L) =", det_L)
# ==> det(L) = 5
print("Minkowski bound =", round(bound, 4))
# ==> Minkowski bound = 3.1623
best_len = float("inf")
best_coef = None
for i in range(-3, 4):
for j in range(-3, 4):
if (i, j) == (0, 0):
continue
v = B @ np.array([i, j], dtype=np.int64)
length = float(np.linalg.norm(v))
if length < best_len:
best_len = length
best_coef = (i, j)
v = B @ np.array(best_coef, dtype=np.int64)
print("shortest coefficients:", best_coef)
# ==> shortest coefficients: (-1, 1)
print("shortest vector:", tuple(int(x) for x in v))
# ==> shortest vector: (-2, 1)
print("length =", round(best_len, 4))
# ==> length = 2.2361

The shortest vector has length 52.236\sqrt{5} \approx 2.236, safely below the Minkowski bound of 103.162\sqrt{10} \approx 3.162. The chapter’s pytest suite at tests/ch07/ runs the same checks on larger 2D and 3D examples and confirms the bound holds on every one of them.

The two algorithmic problems Part II cares about are stated directly in terms of the successive minima. The first asks for a shortest vector, and the second asks for a lattice vector closest to a target point.

Shortest vector problem (SVP). The input is a basis BB of a full-rank lattice LRnL \subset \mathbb{R}^n. The output is a nonzero vector vLv \in L with v2=λ1(L)\|v\|_2 = \lambda_1(L) (Micciancio & Goldwasser, 2002).

Closest vector problem (CVP). The input is a basis BB of a full-rank lattice LRnL \subset \mathbb{R}^n together with a target point tRnt \in \mathbb{R}^n. The output is a vector vLv \in L minimizing vt2\|v - t\|_2 (Micciancio & Goldwasser, 2002).

Both problems have approximate and gap variants. Fix a slack factor γ1\gamma \geq 1.

  • The approximate shortest vector problem SVPγ\mathrm{SVP}_\gamma asks for a nonzero vLv \in L with v2γλ1(L)\|v\|_2 \leq \gamma \lambda_1(L) (Micciancio & Goldwasser, 2002).
  • The gap shortest vector problem GapSVPγ\mathrm{GapSVP}_\gamma is the promise decision problem: given BB and a threshold dd, decide whether λ1(L)d\lambda_1(L) \leq d or λ1(L)>γd\lambda_1(L) > \gamma d, promised one case holds (Micciancio & Goldwasser, 2002).
  • The approximate closest vector problem CVPγ\mathrm{CVP}_\gamma asks for vLv \in L with vt2γdist(t,L)\|v - t\|_2 \leq \gamma \cdot \mathrm{dist}(t, L).
  • The gap closest vector problem GapCVPγ\mathrm{GapCVP}_\gamma is the corresponding promise decision problem: decide whether dist(t,L)d\mathrm{dist}(t, L) \leq d or dist(t,L)>γd\mathrm{dist}(t, L) > \gamma d.
  • The shortest independent vectors problem SIVPγ\mathrm{SIVP}_\gamma asks for nn linearly independent lattice vectors of maximum norm at most γλn(L)\gamma \cdot \lambda_n(L) (Micciancio & Regev, 2009, sec. 2).

These approximate and gap problems are the language used in worst-case-to-average-case reductions. Regev’s 2009 result says, roughly, that efficient algorithms for suitable LWE instances would yield quantum algorithms for worst-case lattice problems such as GapSVP\mathrm{GapSVP} and SIVP\mathrm{SIVP}, at an approximation factor of O~(n/α)\tilde{O}(n/\alpha), which grows with the dimension nn and with the reciprocal of the LWE noise rate α\alpha. The exact statement depends on the modulus and error rate. Chapter 8 states the parameterized version rather than treating γ\gamma as an unspecified constant (Regev, 2009).

Minkowski’s bound is an upper bound on λ1\lambda_1, which is the output norm of SVP. The bound shows that a short vector exists; it does not show how to find one. Closing the gap between “a short vector exists by Minkowski” and “here is an algorithm that produces one” is what lattice cryptanalysis is about. If an efficient algorithm closed that gap for the approximation factors and structured instances used in deployed schemes, the standard hardness assumptions behind Part II would be seriously undermined. Chapter 13 walks BKZ and its derivatives, explains how concrete attack estimates depend on a block-size parameter, and shows how ML-KEM’s parameters are evaluated against those estimates (Albrecht et al., 2024; Avanzi et al., 2021; National Institute of Standards and Technology, 2024).

One observation closes the section. SVP in dimension two is easy. A handful of integer combinations of the basis vectors can be checked by brute force, and a two-dimensional reduction algorithm (Lagrange’s method, a predecessor of the Lenstra-Lenstra-Lovász (LLL) algorithm) finds the shortest vector in polynomial time. ML-KEM-512 fixes a degree-nn polynomial ring with n=256n = 256 and a module of rank k=2k = 2, giving kn=512k \cdot n = 512 coefficients over Zq\mathbb{Z}_q with q=3329q = 3329 (National Institute of Standards and Technology, 2024). The concrete attacks are not simply “SVP in dimension 512”. They reduce the Module-LWE instance to qq-ary lattice problems whose attack dimensions and costs depend on the chosen primal, dual, or hybrid attack model. Chapter 13 explains how BKZ-style estimates turn those structured Module-LWE instances into concrete security estimates.

Lattices, bases, determinants, and dual lattices are the ambient objects every subsequent Part II chapter rests on.

ChapterWhat it adds to the objects built here
8Learning with errors (LWE), a computational assumption tied to qq-ary lattices
9Ring-LWE and Module-LWE: from plain integer lattices to structured modules
10Regev encryption, the cleanest lattice public-key encryption scheme
11ML-KEM, from Module-LWE plus the Fujisaki-Okamoto transform of Chapter 5
12ML-DSA, which adds Module-SIS to Module-LWE and signs by Fiat-Shamir with aborts
13Lattice cryptanalysis: BKZ, reduction, and the estimates that set ML-KEM’s parameters

Two of those rows are easy to misread. Read LWE as noisy linear algebra modulo qq. Geometrically, a batch of LWE samples is a bounded-distance-decoding or noisy-CVP-style instance: the observed vector sits close to a structured qq-ary lattice point, and the adversary must either recover the secret (search LWE) or tell the noisy samples from uniform (decisional LWE) (Regev, 2009). The ring Rq=Zq[x]/(xn+1)R_q = \mathbb{Z}_q[x]/(x^n + 1) that Chapter 9 works over is not itself a lattice; it is the modular arithmetic layer used to describe Ring-LWE and Module-LWE instances whose underlying hardness is analyzed through structured qq-ary lattices.

The dual lattice earns its keep once the original lattice acquires ring structure. In the Ring-LWE and Module-LWE setting, the dual-lattice story is usually formulated before reduction modulo qq, in the number-field and ring-of-integers setting: there the dual of the ring of integers is a fractional ideal related to the inverse different. Chapter 9 picks that up where it states the Ring-LWE hardness reduction, whose Gaussian widths and dual ideals are stated in the canonical embedding rather than in the coefficient representation concrete schemes compute with modulo qq. The general definition from the algebra section above is what Chapter 9 draws on.

Chapter 8 is the immediate next step: it turns the objects built here into the LWE hardness assumption.

Exercise 1. Verify a unimodular change of basis. Take B1B_1 and B2B_2 from the start of the chapter. Compute U=B11B2U = B_1^{-1} B_2 by hand, check that every entry is an integer, and compute detU\det U. Then construct UU a second way: express each column of B2B_2 as an integer combination of the columns of B1B_1 directly, and collect the coefficients into a matrix. The two constructions must agree.

Exercise 2. Dual-of-dual on a 3D lattice. Fix the basis

B=(201130014).B = \begin{pmatrix} 2 & 0 & 1 \\ 1 & 3 & 0 \\ 0 & 1 & 4 \end{pmatrix}.

Compute the dual basis D=BTD = B^{-T} in Python using numpy.linalg.inv and a transpose. Compute the dual of DD in the same way, call it D(2)D^{(2)}, and verify that D(2)D^{(2)} equals BB up to floating-point error. Explain in one sentence why the entries of DD are in general rational rather than integer.

Exercise 3. Minkowski’s bound on a diagonal lattice. Take B=diag(2,3,5,7)B = \mathrm{diag}(2, 3, 5, 7) as a basis of a full-rank lattice LZ4L \subset \mathbb{Z}^4. Compute det(L)\det(L) and Minkowski’s bound 4det(L)1/4\sqrt{4} \cdot \det(L)^{1/4} in Python. Then find an explicit nonzero lattice vector whose Euclidean norm is below the bound, and report its integer coefficients in the given basis.

Exercise 4. State SVP and CVP from memory. Without looking at the section above, write the shortest vector problem and the closest vector problem with their inputs and outputs. Then state the approximate versions SVPγ\mathrm{SVP}_\gamma and CVPγ\mathrm{CVP}_\gamma, and the promise-decision versions GapSVPγ\mathrm{GapSVP}_\gamma and GapCVPγ\mathrm{GapCVP}_\gamma. Check your statements against the definitions in Micciancio-Goldwasser 2002 (Micciancio & Goldwasser, 2002) and Regev 2009 (Regev, 2009), and note any differences.

Worked solutions and editorial notes for these exercises are in Appendix D, Chapter 7. A separate track, for rebuilding rather than reading: the package exercises/ch07-lattices has every function the chapter teaches replaced by a stub. Run PQC_IMPL=exercises pytest tests/ch07 to grade your version against the suite that proves the reference one.

Albrecht, M. R., et al. (2024). Lattice Estimator. Open-source tool, github.com/malb/lattice-estimator. https://github.com/malb/lattice-estimator
Avanzi, R., Bos, J., Ducas, L., Kiltz, E., Lepoint, T., Lyubashevsky, V., Schanck, J. M., Schwabe, P., Seiler, G., & Stehlé, D. (2021). CRYSTALS-Kyber Algorithm Specifications and Supporting Documentation (Version 3.02). NIST Post-Quantum Cryptography Project, Round 3 submission package. https://pq-crystals.org/kyber/data/kyber-specification-round3-20210804.pdf
Micciancio, D., & Goldwasser, S. (2002). Complexity of Lattice Problems: A Cryptographic Perspective (Vol. 671). Kluwer Academic Publishers. https://link.springer.com/book/10.1007/978-1-4615-0897-7
Micciancio, D., & Regev, O. (2009). Lattice-based cryptography. In D. J. Bernstein, J. Buchmann, & E. Dahmen (Eds.), Post-Quantum Cryptography (pp. 147–191). Springer. https://doi.org/10.1007/978-3-540-88702-7_5
National Institute of Standards and Technology. (2024). FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard. Federal Information Processing Standards Publication. https://doi.org/10.6028/NIST.FIPS.203
Regev, O. (2009). On lattices, learning with errors, random linear codes, and cryptography. Journal of the ACM, 56(6), 34:1-34:40. https://doi.org/10.1145/1568318.1568324

Last updated: