Skip to content

Algebraic Geometry - Part-II

Table of Contents

1. Ideals and Ideal Membership

Definition (Ideal): A subset \(I \subseteq \mathbb{K}[x_1,\dots,x_n]\) is an ideal if

  • \(0 \in I\) and \(f, g \in I \Rightarrow f + g \in I\) (closed under addition)
  • \(f \in I,\ h \in \mathbb{K}[x] \Rightarrow h \cdot f \in I\) (absorbs multiplication)

The ideal generated by \(f_1, \dots, f_s\) is

\[ \langle f_1, \dots, f_s \rangle = \left\{ \sum_{i=1}^{s} h_i f_i \ :\ h_i \in \mathbb{K}[x] \right\} \]

Hilbert's Basis Theorem guarantees every ideal of \(\mathbb{K}[x_1,\dots,x_n]\) has a finite generating set.

from src.alggeom.polynomial import Variable, Polynomial, parse_polynomial_string
from src.alggeom.ideal import Ideal

x, y = Variable("x"), Variable("y")
v = lambda name: Polynomial.variable(Variable(name))
one = Polynomial.constant

f1 = v('x') * v('y') - one(1)     # xy - 1
f2 = v('x')**2 - v('y')           # x^2 - y

I = Ideal([f1, f2])
print(I)                          # Ideal(2 generators)

The central computational question is ideal membership: given \(f\), decide whether \(f \in I\). Since \(f \in I\) iff the remainder of \(f\) modulo a Groebner basis of \(I\) is zero (see Section 5), contains answers it exactly:

print(I.contains(v('y')**2 - v('x')))   # True   <- reduces to 0 modulo the basis
print(I.contains(v('x') - v('y')))      # False  <- nonzero remainder

2. Ideal Arithmetic: Sum, Product, Intersection

Given ideals \(I = \langle F \rangle\) and \(J = \langle G \rangle\):

Operation Definition Generators
Sum\(I + J\) \(\{\, f + g : f \in I, g \in J \,\}\) \(F \cup G\)
Product\(IJ\) \(\langle\, fg : f \in I, g \in J \,\rangle\) \(\{\, fg : f \in F, g \in G \,\}\)
Intersection\(I \cap J\) \(\{\, f : f \in I \text{ and } f \in J \,\}\) needs elimination theory
Ix, Iy = Ideal([v('x')]), Ideal([v('y')])

print([str(g) for g in Ix.sum(Iy).generators])       # ['x', 'y']
print([str(g) for g in Ix.product(Iy).generators])   # ['x * y']

from src.alggeom.groebnerbasis import ideal_intersection
print([str(g) for g in ideal_intersection([v('x')], [v('y')], ['x', 'y'])])
# ['x * y']    <- <x> n <y> = <xy>

Intersection is computed by the classic trick: introduce a new variable \(t\) and eliminate it from

\[ t \cdot I + (1 - t) \cdot J \ \subseteq\ \mathbb{K}[x_1,\dots,x_n, t] \]

The polynomials of a lex Groebner basis that do not contain \(t\) generate exactly \(I \cap J\).

3. Leading Term Ideals

Fix a monomial order. For an ideal \(I\):

\[ \langle \text{LT}(I) \rangle = \langle\, \text{LT}(f) : f \in I \,\rangle \]

This is a monomial ideal - generated by monomials instead of arbitrary polynomials - and its combinatorics encode the geometry of \(V(I)\) (dimension, degree, Hilbert functions).

The generators of \(I\) alone usually do not generate \(\langle \text{LT}(I)\rangle\): reducing one polynomial can produce new leading terms. This is what makes Groebner bases necessary.

4. S-Polynomials

Definition (S-polynomial): For nonzero \(f, g\) with leading monomials \(\text{LM}(f)\), \(\text{LM}(g)\), let \(L = \text{lcm}(\text{LM}(f), \text{LM}(g))\). Then

\[ S(f, g) = \frac{L}{\text{LM}(f)} \cdot \frac{1}{\text{LC}(f)} f \;-\; \frac{L}{\text{LM}(g)} \cdot \frac{1}{\text{LC}(g)} g \]

\(S(f,g)\) is constructed so its leading terms cancel. It measures whether the leading terms of \(f\) and \(g\) "interact": if some combination of \(f\) and \(g\) hides a leading term not divisible by either \(\text{LM}(f)\) or \(\text{LM}(g)\), the S-polynomial exposes it.

5. Buchberger's Algorithm and Groebner Bases

Definition (Groebner basis): A finite set \(G \subseteq I\) is a Groebner basis of \(I\) if the leading terms of \(G\) generate the leading term ideal:

\[ \langle \text{LT}(G) \rangle = \langle \text{LT}(I) \rangle \]

Equivalently: every \(f \in I\) reduces to \(0\) modulo \(G\).

Buchberger's algorithm starts from the generators and repeatedly:

  1. Picks a pair \((g_i, g_j)\)
  2. Computes \(S(g_i, g_j)\)
  3. Reduces it modulo the current basis
  4. If the remainder is nonzero, appends it as a new generator

until all S-polynomials reduce to zero. Two classical criteria avoid useless pairs:

  • Product criterion - if \(\text{LM}(g_i)\) and \(\text{LM}(g_j)\) share no variable, \(S(g_i,g_j)\) always reduces to zero
  • Chain criterion - if some third \(\text{LM}(g_k)\) divides \(\text{lcm}(\text{LM}(g_i), \text{LM}(g_j))\), the pair is redundant
from src.alggeom.groebnerbasis import GroebnerBasis, compute_groebner_basis

gb = GroebnerBasis([f1, f2])          # grevlex by default
print([str(g) for g in gb.groebner_basis])
# ['x * y - 1.0000', 'x^2 - y', 'y^2 - x']

print(gb.is_groebner_basis())         # True  (all S-polys reduce to 0)

mem, remainder = gb.ideal_membership(v('y')**2 - v('x'))
print(mem)                            # True

A basis is called reduced when no generator's leading monomial is divisible by another's and every generator is fully reduced by the others - this form is unique for a given ideal and order.

6. Elimination Theory

Elimination Theorem: Let \(G\) be a Groebner basis of \(I \subseteq \mathbb{K}[x_1,\dots,x_n]\) with respect to lex order \(x_1 > x_2 > \dots > x_n\). Then

\[ G \cap \mathbb{K}[x_{k+1}, \dots, x_n] \]

is a Groebner basis of the \(k\)-th elimination ideal \(I_k = I \cap \mathbb{K}[x_{k+1},\dots,x_n]\).

# I = <x - 1, x + y - 2>; eliminate x
G_lex = compute_groebner_basis(
    [v('x') - one(1), v('x') + v('y') - one(2)],
    'lex'
)
print([str(g) for g in G_lex])
# ['x - 1.0000', '-y + 1.0000']     <- second poly involves only y

Elimination converts solving systems into finding roots of univariate polynomials - the engine behind solve_polynomial_system in Part III.

7. Saturation and Radical Membership

Definition (Saturation):

\[ I : f^\infty = \{\, g \in \mathbb{K}[x]\ : f^k g \in I \text{ for some } k \geq 0 \,\} \]

Geometrically, saturation removes from \(V(I)\) the components lying inside \(\{f = 0\}\).

Definition (Radical):

\[ \sqrt{I} = \{\, f : f^k \in I \text{ for some } k > 0 \,\} \]

Membership in \(\sqrt{I}\) is decided by the Rabinowitsch trick: \(f \in \sqrt{I}\) if and only if the ideal \(I + \langle 1 - t f \rangle \subseteq \mathbb{K}[x, t]\) contains \(1\).

from src.alggeom.groebnerbasis import saturation, radical_membership

sat = saturation([v('x')**2 * v('y')], v('x'))
print([str(g) for g in sat])
# ['-y']        <- sat(<x^2*y>, x) = <y>: the x-factor was saturated away

print(radical_membership(v('x'), [v('x')**2]))
# True          <- x^2 vanishes on {x=0}, so does x

print(radical_membership(v('x') + one(1), [v('x')**2]))
# False         <- x+1 does not vanish on V(x^2)

Ideal.quotient(h) computes \(I : h\) via saturation, and Ideal.radical exposes the radical interface.

8. Primary Decomposition and Primality

Definition (Primary decomposition): Every ideal \(I\) can be written as a finite intersection

\[ I = Q_1 \cap Q_2 \cap \cdots \cap Q_r \]

of primary ideals. Geometrically this decomposes \(V(I)\) into its irreducible components.

The implementation splits using factorization heuristics (common variable factors such as \(xy = x \cdot y\)) combined with saturation, then removes redundant components.

from src.alggeom.groebnerbasis import primary_decomposition

decomp = primary_decomposition([v('x') * v('y')])
print([[str(g) for g in comp] for comp in decomp])
# [['x'], ['y']]     <- V(xy) = V(x) u V(y)

print(Ideal([v('x')]).is_prime())            # True   <- V(x) irreducible
print(Ideal([v('x') * v('y')]).is_prime())   # False  <- two components

An ideal is prime iff its decomposition has a single component (\(I\) prime \(\Leftrightarrow\) \(\mathbb{K}[x]/I\) is an integral domain \(\Leftrightarrow\) \(V(I)\) irreducible).

9. Dimension

Krull dimension of the quotient ring, \(\dim(\mathbb{K}[x]/I)\), equals the maximal number of algebraically independent coordinates on \(V(I)\). Computationally it is read off from the initial ideal: the dimension is the size of the largest set \(S\) of variables such that no leading monomial of the Groebner basis uses only variables from \(S\).

d0 = Ideal([v('x') - one(1), v('y') - one(2)])                  # two points
dc = Ideal([v('z') - v('x')**2, v('y') - v('x')**2])            # space curve

print(d0.dimension())   # 0   <- finite solution set
print(dc.dimension())   # 1   <- parametrized by x = t

10. The Macaulay Matrix

Buchberger processes pairs one at a time. Modern F4-style algorithms instead reduce many S-polynomials simultaneously as linear algebra: stack the polynomials into a Macaulay matrix

\[ M_{ij} = \text{coefficient of monomial } m_j \text{ in } f_i \]

with columns ordered by the monomial order, then row-reduce.

import numpy as np
from src.alggeom.groebnerbasis import build_macaulay_matrix

M, monomials = build_macaulay_matrix([f1, f2])
print([str(m) for m in monomials])
# ['x^2', 'x * y', 'y', '1']
print(M.real)
# [[ 0.  1.  0. -1.]      <- xy - 1
#  [ 1.  0. -1.  0.]]     <- x^2 - y

Row reduction of this matrix performs many Gaussian eliminations at once - the foundation for scaling Groebner bases to larger systems.