Algebraic Geometry - Part-I¶
Table of Contents¶
1. Symbolic Representation¶
In algebraic geometry variable is a formal symbolic representation of indeterminate element of a polynomial ring\(\mathbb{F}[x_1, \dots, x_n]\) over a field \(\mathbb{F}\). It has following key properties
- Variables are formal - has no numeric value until evaluation
- Variables are distinct - \(x \neq y\) even if they evaluate to same number
- Variables support indexing - \(x_1, x_2, \dots\) for multivariate systems
from src.utils.symbolic import Variable
theta = Variable(name="theta", index=None)
omega = Variable(name="omega", index=None)
print(theta.name)
print(theta.index)
## Variable Comparision
def VarComparision(theta, omega):
if theta.name != omega.name:
return False
if theta.index != omega.index:
return False
return True
print(VarComparision(theta, omega))
2. Monomial Representation as Product of Variable's Power¶
Definition (Monomial): A monomial in variables \(x_1, \dots, x_n\) is a product
where \(e_i \in \mathbb{Z}_{\geq 0}\) are non-negative integer exponents.
Key Operations:
- Multiplication: \(x^a \cdot x^b = x^{a+b}\)
- Power: \((x^a)^b = x^{ab}\)
- Degree: \(\deg(x_1^{e_1}\cdots x_n^{e_n}) = \sum e_i\)
- Division: \(x^a / x^b = x^{a-b}\) if \(a \geq b\) (component-wise)
3. Polynomial Representation as a linear combination of monomials¶
Definition (Polynomial): A polynomial in variables \(x_1, \dots, x_n\) is a finite linear combination of monomials:
where \(c_i \in \mathbb{K}\) are coefficients and \(m_i\) are distinct monomials.
Key Operations:
- Addition: Combine like terms (same monomial)
- Multiplication: Distribute and combine: \((a+b)(c+d) = ac+ad+bc+bd\)
- Differentiation: \(\frac{\partial}{\partial x_j}(c \cdot x^\alpha) = c \cdot \alpha_j \cdot x^{\alpha - e_j}\)
- Evaluation: Substitute numeric values for variables
Problem: Given \(p = 3x^2y + 2xy^2 - 5\) and \(q = x^2y - xy^2 + 1\), compute:
- \(p + q\)
- \(p \cdot q\) (first two terms only)
- \(\frac{\partial p}{\partial x}\)
- \(p\) evaluated at \((x,y) = (2, -1)\)
4. Polynomial Arithmetic¶
A polynomial is stored internally as a dictionary mapping monomials to coefficients:
Coefficients whose magnitude falls below TOLERANCE = 1e-12 are treated as zero and dropped automatically, so like terms always combine cleanly.
from src.alggeom.polynomial import Variable, Monomial, Polynomial, parse_polynomial_string
x, y = Variable("x"), Variable("y")
# Build p = 3x^2*y + 2x*y^2 - 5 directly ...
p = Polynomial({
Monomial(((x, 2), (y, 1))): 3.0,
Monomial(((x, 1), (y, 2))): 2.0,
Monomial(()): -5.0
})
print(p) # 3.0000*x^2 * y + 2.0000*x * y^2 - 5.0000
print(p.degree()) # 3
# ... or parse it from a string
q = parse_polynomial_string('x^2*y - x*y^2 + 1', [x, y])
print(q) # x^2 * y - x * y^2 + 1.0000
Key Operations:
- Addition / Subtraction - combine like terms, cancel near-zero results
- Multiplication - distribute over all term pairs, then collect
- Scalar multiplication -
2 * pandp * 2both work (__rmul__) - Power - binary exponentiation, so
p ** 8costs 3 multiplications, not 7
print(p + q) # 4.0000*x^2 * y + x * y^2 - 4.0000
print(p - q) # 2.0000*x^2 * y + 3.0000*x * y^2 - 6.0000
print(p * q) # degree 6 polynomial, leading terms 3x^4*y^2 - x^3*y^3 + ...
print(2 * q) # 2.0000*x^2 * y - 2.0000*x * y^2 + 2.0000
Solution to the Problem of Section 3:
# 1. p + q
print(p + q) # 4.0000*x^2 * y + x * y^2 - 4.0000
# 2. p * q, first two terms (highest total degree)
prod = p * q
# 3.0000*x^4*y^2 + (-1.0)*x^3*y^3 + ...
# 3. partial derivative
print(p.differentiate(x)) # 6.0000*x * y + 2.0000*y^2
# 4. evaluation
print(p.evaluate({x: 2.0, y: -1.0})) # -13.0
5. Calculus on Polynomials¶
Differentiation follows the power rule term-wise:
Evaluation substitutes numeric values for every variable; missing variables are treated as \(0\).
print(p.differentiate(x)) # 6.0000*x * y + 2.0000*y^2
print(p.differentiate(y)) # 3.0000*x^2 + 4.0000*x * y
print(p.evaluate({x: 2.0, y: -1.0})) # -13.0
6. Monomial Orderings and Leading Terms¶
A monomial ordering is a total order on \(\mathbb{Z}_{\geq 0}^n\) compatible with multiplication. Three classical orders are supported:
- lex - compare exponent vectors left to right: \(x^a >_{lex} x^b\) iff the leftmost nonzero entry of \(a - b\) is positive
- grlex - total degree first, then lex as tie-breaker
- grevlex - total degree first, then reverse lex: the smaller exponent vector in reversed order wins
The leading monomial \(\text{LM}(f)\), leading coefficient \(\text{LC}(f)\) and leading term \(\text{LT}(f) = \text{LC}(f)\cdot\text{LM}(f)\) depend on this choice.
from src.alggeom.polynomial import parse_polynomial_string
x, y, z = Variable("x"), Variable("y"), Variable("z")
g = parse_polynomial_string('x^2*z + y^3', [x, y, z])
for order in ('lex', 'grlex', 'grevlex'):
lm, lc = g.leading_term(order)
print(order, "->", lm, lc)
# lex -> x^2 * z 1.0 (x dominates)
# grlex -> x^2 * z 1.0 (both degree 3, lex tie-break)
# grevlex -> y^3 1.0 (smaller last exponents win)
Leading terms drive everything in Parts II and III: division, Groebner bases, elimination and dimension.
7. Multivariate Division¶
Theorem (Quotient-Remainder): Fix a monomial order. For polynomials \(f, g\) with \(g \neq 0\) there exist unique \(r\) with \(\text{LM}(r)\) not divisible by \(\text{LM}(g)\) such that
divide_by returns the pair (quotient, remainder):
a = parse_polynomial_string('x^3*y^2 + x^4', [x, y])
b = parse_polynomial_string('x^2*y', [x, y])
quo, rem = a.divide_by(b)
print(quo) # x * y
print(rem) # x^4 <- LM(x^4) is NOT divisible by LM(x^2*y)
The remainder is fully reduced: no term of it can be cancelled by the divisor any more.
8. Parsing Polynomials from Strings¶
parse_polynomial_string is a small recursive-descent parser for the grammar
expr := term (('+' | '-') term)*
term := factor ('*' factor)*
factor := ('-')? atom ('^' integer)?
atom := number | variable | '(' expr ')'
so parentheses, powers of sums and distribution all behave correctly. Implicit multiplication (2x, 3(x+1)) is inserted automatically.
print(parse_polynomial_string('(x+y)^2', [x, y]))
# x^2 + 2.0000*x * y + y^2
print(parse_polynomial_string('3*(x+1)^2', [x, y]))
# 3.0000*x^2 + 6.0000*x + 3.0000
print(parse_polynomial_string('(x+1)*(y-1)', [x, y]))
# x * y - x + y - 1.0000
Unknown variable names raise a ValueError listing the known variables.
9. Polynomial Systems and the Jacobian¶
A SymbolicPolynomialSystem bundles variables with equations and provides matrix calculus:
import numpy as np
from src.alggeom.polynomial import SymbolicPolynomialSystem
system = SymbolicPolynomialSystem([x, y], [p, q])
print(system)
# Variables: ['x', 'y']
# Polynomials:
# f_0 = 3.0000*x^2 * y + 2.0000*x * y^2 - 5.0000
# f_1 = x^2 * y - x * y^2 + 1.0000
print(system.evaluate(np.array([1.0, 2.0]))) # [ 9. -1.]
print(system.jacobian(np.array([1.0, 2.0])))
# [[20. 11.]
# [ 0. -3.]]
The Jacobian is used in Part III for detecting singular points of varieties.