Skip to main content

amari_enumerative/
moduli_space.rs

1//! Moduli spaces of curves and algebraic varieties
2//!
3//! This module implements computations on moduli spaces, including
4//! the moduli space of curves, stable maps, and intersection numbers
5//! on these spaces.
6
7use crate::{EnumerativeError, EnumerativeResult};
8use num_rational::Rational64;
9use std::collections::HashMap;
10
11/// Moduli space of curves M_{g,n}
12#[derive(Debug, Clone)]
13pub struct ModuliSpace {
14    /// Genus of the curves
15    pub genus: usize,
16    /// Number of marked points
17    pub marked_points: usize,
18    /// Whether to consider stable curves (compactified moduli space)
19    pub stable: bool,
20}
21
22impl ModuliSpace {
23    /// Create a new moduli space M_{g,n}
24    pub fn new(genus: usize, marked_points: usize, stable: bool) -> EnumerativeResult<Self> {
25        // Check stability condition: 2g - 2 + n > 0 for stable curves
26        // For the stable compactification, we allow (g=0, n=0) as it gives M̄_{0,0} = point
27        if stable && 2 * genus + marked_points < 3 && !(genus == 0 && marked_points == 0) {
28            return Err(EnumerativeError::InvalidDimension(format!(
29                "Unstable parameters: g={}, n={}",
30                genus, marked_points
31            )));
32        }
33
34        Ok(Self {
35            genus,
36            marked_points,
37            stable,
38        })
39    }
40
41    /// Compute the dimension of the moduli space
42    pub fn dimension(&self) -> usize {
43        if self.stable {
44            // Dimension of \bar{M}_{g,n}
45            // Handle case where 3*genus < 3 to avoid underflow
46            if 3 * self.genus >= 3 {
47                3 * self.genus - 3 + self.marked_points
48            } else {
49                // For genus 0, dimension is marked_points - 3 (when stable)
50                self.marked_points.saturating_sub(3)
51            }
52        } else {
53            // Dimension of M_{g,n} (if it exists)
54            if self.genus == 0 && self.marked_points >= 3 {
55                self.marked_points - 3
56            } else if self.genus >= 2 {
57                3 * self.genus - 3
58            } else {
59                0
60            }
61        }
62    }
63
64    /// Check if the moduli space is non-empty
65    pub fn is_nonempty(&self) -> bool {
66        if self.stable {
67            2 * self.genus - 2 + self.marked_points > 0
68        } else {
69            true // genus and marked_points are non-negative by type definition
70        }
71    }
72
73    /// Compute intersection numbers on the moduli space
74    pub fn intersection_number(
75        &self,
76        classes: &[TautologicalClass],
77    ) -> EnumerativeResult<Rational64> {
78        // This is highly non-trivial and requires knowledge of the intersection theory
79        // of moduli spaces. For now, we provide some basic cases.
80
81        if classes.is_empty() {
82            return Ok(Rational64::from(1));
83        }
84
85        // Check dimension compatibility
86        let total_degree: usize = classes.iter().map(|c| c.degree).sum();
87        if total_degree != self.dimension() {
88            return Ok(Rational64::from(0));
89        }
90
91        // Some classical intersection numbers
92        match (self.genus, self.marked_points, classes.len()) {
93            (0, 3, 0) => Ok(Rational64::from(1)), // \bar{M}_{0,3} is a point
94            (1, 1, 1) => {
95                // Some intersection on \bar{M}_{1,1}
96                Ok(Rational64::new(1, 24))
97            }
98            _ => {
99                // General case requires Witten's conjecture/Kontsevich's theorem
100                Ok(Rational64::from(0))
101            }
102        }
103    }
104}
105
106/// Tautological classes on moduli spaces (ψ, κ, λ classes)
107#[derive(Debug, Clone, PartialEq)]
108pub struct TautologicalClass {
109    /// Type of the class
110    pub class_type: TautologicalType,
111    /// Degree of the class
112    pub degree: usize,
113    /// Index (for ψ classes, this is which marked point)
114    pub index: Option<usize>,
115}
116
117impl TautologicalClass {
118    /// Create a ψ class
119    pub fn psi(index: usize) -> Self {
120        Self {
121            class_type: TautologicalType::Psi,
122            degree: 1,
123            index: Some(index),
124        }
125    }
126
127    /// Create a κ class
128    pub fn kappa(degree: usize) -> Self {
129        Self {
130            class_type: TautologicalType::Kappa,
131            degree,
132            index: None,
133        }
134    }
135
136    /// Create a λ class
137    pub fn lambda(degree: usize) -> Self {
138        Self {
139            class_type: TautologicalType::Lambda,
140            degree,
141            index: None,
142        }
143    }
144}
145
146/// Types of tautological classes
147#[derive(Debug, Clone, PartialEq)]
148pub enum TautologicalType {
149    /// ψ classes (cotangent line classes at marked points)
150    Psi,
151    /// κ classes (κ_m = π_*(ψ^{m+1}))
152    Kappa,
153    /// λ classes (Chern classes of Hodge bundle)
154    Lambda,
155}
156
157/// Curve class in a target variety
158#[derive(Debug, Clone, PartialEq)]
159pub struct CurveClass {
160    /// The target variety (simplified as string for now)
161    pub target: String,
162    /// Degree information
163    pub degree_data: HashMap<String, i64>,
164    /// Genus of curves in this class
165    pub genus: usize,
166}
167
168impl CurveClass {
169    /// Create a new curve class
170    pub fn new(target: String, genus: usize) -> Self {
171        Self {
172            target,
173            degree_data: HashMap::new(),
174            genus,
175        }
176    }
177
178    /// Set degree in a particular homology class
179    pub fn set_degree(&mut self, class_name: String, degree: i64) {
180        self.degree_data.insert(class_name, degree);
181    }
182
183    /// Get degree in a particular homology class
184    pub fn get_degree(&self, class_name: &str) -> i64 {
185        self.degree_data.get(class_name).copied().unwrap_or(0)
186    }
187
188    /// Check if this is a rational curve class
189    pub fn is_rational(&self) -> bool {
190        self.genus == 0
191    }
192}
193
194/// Moduli space of stable maps \bar{M}_{g,n}(X, β)
195#[derive(Debug, Clone)]
196pub struct ModuliOfStableMaps {
197    /// Domain moduli space
198    pub domain: ModuliSpace,
199    /// Target variety (simplified)
200    pub target: String,
201    /// Curve class being mapped to
202    pub curve_class: CurveClass,
203}
204
205impl ModuliOfStableMaps {
206    /// Create a new moduli space of stable maps
207    pub fn new(domain: ModuliSpace, target: String, curve_class: CurveClass) -> Self {
208        Self {
209            domain,
210            target,
211            curve_class,
212        }
213    }
214
215    /// Compute expected dimension of the moduli space
216    pub fn expected_dimension(&self) -> EnumerativeResult<i64> {
217        // Expected dimension formula:
218        // dim(M_{g,n}(X, β)) = dim(M_{g,n}) + ∫_β c_1(TX) + (dim(X) - 3)(1-g)
219
220        let moduli_dim = self.domain.dimension() as i64;
221
222        // Simplified: assume target is projective space P^n
223        let target_dim = match self.target.as_str() {
224            "P1" => 1,
225            "P2" => 2,
226            "P3" => 3,
227            _ => 2, // default to P^2
228        };
229
230        let degree = self.curve_class.get_degree("H"); // degree in hyperplane class
231        let first_chern_integral = (target_dim + 1) * degree;
232
233        let expected_dim =
234            moduli_dim + first_chern_integral + (target_dim - 3) * (1 - self.domain.genus as i64);
235
236        Ok(expected_dim)
237    }
238
239    /// Check if the moduli space has the expected dimension
240    pub fn has_expected_dimension(&self) -> EnumerativeResult<bool> {
241        let expected = self.expected_dimension()?;
242        Ok(expected >= 0)
243    }
244}