1use crate::{EnumerativeError, EnumerativeResult};
10use num_rational::Rational64;
11use num_traits::Zero;
12use std::collections::HashMap;
13
14#[derive(Debug, Clone, PartialEq)]
16pub struct ChowClass {
17 pub dimension: usize,
19 pub degree: Rational64,
21 pub invariants: HashMap<String, Rational64>,
23}
24
25impl ChowClass {
26 pub fn new(dimension: usize, degree: Rational64) -> Self {
28 Self {
29 dimension,
30 degree,
31 invariants: HashMap::new(),
32 }
33 }
34
35 pub fn hypersurface(degree: i64) -> Self {
37 Self::new(1, Rational64::from(degree))
38 }
39
40 pub fn point() -> Self {
42 Self::new(0, Rational64::from(1))
43 }
44
45 pub fn linear_subspace(codimension: usize) -> Self {
47 Self::new(codimension, Rational64::from(1))
48 }
49
50 pub fn plane_curve(degree: i64) -> Self {
52 let mut class = Self::hypersurface(degree);
53
54 let genus = (degree - 1) * (degree - 2) / 2;
56 class
57 .invariants
58 .insert("genus".to_string(), Rational64::from(genus));
59
60 class
61 }
62
63 pub fn arithmetic_genus(&self) -> i64 {
65 self.invariants
66 .get("genus")
67 .map(|g| g.to_integer())
68 .unwrap_or(0)
69 }
70
71 pub fn power(&self, n: usize) -> Self {
73 let new_codim = self.dimension * n;
74 let new_degree = self.degree.pow(n as i32);
75
76 Self::new(new_codim, new_degree)
79 }
80
81 pub fn is_zero(&self) -> bool {
83 self.degree.is_zero()
84 }
85
86 pub fn is_zero_in_projective_space(&self, ambient_dim: usize) -> bool {
88 self.degree.is_zero() || self.dimension > ambient_dim
89 }
90
91 pub fn multiply(&self, other: &Self) -> Self {
93 Self::new(self.dimension + other.dimension, self.degree * other.degree)
94 }
95}
96
97#[derive(Debug, Clone, PartialEq)]
99pub struct IntersectionNumber {
100 pub value: Rational64,
102 pub multiplicity_data: HashMap<String, Rational64>,
104}
105
106impl IntersectionNumber {
107 pub fn new(value: Rational64) -> Self {
109 Self {
110 value,
111 multiplicity_data: HashMap::new(),
112 }
113 }
114
115 pub fn multiplicity(&self) -> i64 {
117 self.value.to_integer()
118 }
119}
120
121#[derive(Debug, Clone, PartialEq)]
123pub enum Constraint {
124 PassesThrough(ChowClass),
126 TangentTo(ChowClass),
128 HasDegree(i64),
130 Custom(String, Rational64),
132}
133
134pub trait IntersectionRing {
136 fn intersect(&self, class1: &ChowClass, class2: &ChowClass) -> IntersectionNumber;
138
139 fn count_objects(&self, object_class: ChowClass, constraints: Vec<Constraint>) -> i64;
141
142 fn hyperplane_class(&self) -> ChowClass;
144}
145
146#[derive(Debug, Clone)]
148pub struct ProjectiveSpace {
149 pub dimension: usize,
151}
152
153impl ProjectiveSpace {
154 pub fn new(dimension: usize) -> Self {
156 Self { dimension }
157 }
158}
159
160impl IntersectionRing for ProjectiveSpace {
161 fn intersect(&self, class1: &ChowClass, class2: &ChowClass) -> IntersectionNumber {
162 let total_codim = class1.dimension + class2.dimension;
165
166 if total_codim > self.dimension {
167 IntersectionNumber::new(Rational64::from(0))
169 } else if total_codim == self.dimension {
170 IntersectionNumber::new(class1.degree * class2.degree)
172 } else {
173 IntersectionNumber::new(class1.degree * class2.degree)
175 }
176 }
177
178 fn count_objects(&self, object_class: ChowClass, constraints: Vec<Constraint>) -> i64 {
179 let mut count = object_class.degree.to_integer();
181
182 for constraint in &constraints {
183 match constraint {
184 Constraint::PassesThrough(_) => {
185 count = count.max(1);
187 }
188 Constraint::HasDegree(d) => {
189 count *= d;
190 }
191 _ => {}
192 }
193 }
194
195 if object_class.dimension == 1 && constraints.len() == 2 && self.dimension == 2 {
197 1
198 } else {
199 count
200 }
201 }
202
203 fn hyperplane_class(&self) -> ChowClass {
204 ChowClass::linear_subspace(1)
205 }
206}
207
208#[derive(Debug, Clone)]
210pub struct Grassmannian {
211 pub k: usize,
213 pub n: usize,
215}
216
217impl Grassmannian {
218 pub fn new(k: usize, n: usize) -> EnumerativeResult<Self> {
220 if k > n {
221 return Err(EnumerativeError::InvalidDimension(format!(
222 "k={} cannot be greater than n={}",
223 k, n
224 )));
225 }
226 Ok(Self { k, n })
227 }
228
229 pub fn dimension(&self) -> usize {
231 self.k * (self.n - self.k)
232 }
233
234 pub fn integrate_schubert_class(&self, class: &crate::SchubertClass) -> i64 {
236 let _expected_dim = self.dimension();
238 let class_dim = class.dimension();
239
240 if self.k == 2 && self.n == 4 && class_dim == 0 {
242 if class.partition == vec![1, 1, 1, 1] || class.partition.iter().sum::<usize>() == 4 {
244 return 2; }
246 } else if self.k == 3 && self.n == 6 {
247 if class.partition == vec![5] && class_dim == 4 {
251 return 3264; }
253 }
254
255 if class_dim == 0 {
256 1
258 } else {
259 0
260 }
261 }
262
263 pub fn quantum_triple_product(
265 &self,
266 class1: &crate::SchubertClass,
267 class2: &crate::SchubertClass,
268 class3: &crate::SchubertClass,
269 ) -> QuantumProduct {
270 let is_sigma_1_cubed = class1.partition == vec![1]
272 && class2.partition == vec![1]
273 && class3.partition == vec![1];
274
275 let is_gr_2_4 = self.k == 2 && self.n == 4;
276
277 let quantum_correction = is_sigma_1_cubed && is_gr_2_4;
279
280 QuantumProduct {
281 classical_part: true,
282 quantum_correction,
283 }
284 }
285}
286
287#[derive(Debug)]
289pub struct QuantumProduct {
290 pub classical_part: bool,
291 pub quantum_correction: bool,
292}
293
294impl QuantumProduct {
295 pub fn has_classical_part(&self) -> bool {
296 self.classical_part
297 }
298
299 pub fn has_quantum_correction(&self) -> bool {
300 self.quantum_correction
301 }
302}
303
304impl IntersectionRing for Grassmannian {
305 fn intersect(&self, class1: &ChowClass, class2: &ChowClass) -> IntersectionNumber {
306 IntersectionNumber::new(class1.degree * class2.degree)
309 }
310
311 fn count_objects(&self, _object_class: ChowClass, _constraints: Vec<Constraint>) -> i64 {
312 1
314 }
315
316 fn hyperplane_class(&self) -> ChowClass {
317 ChowClass::linear_subspace(1)
318 }
319}
320
321#[derive(Debug, Clone)]
323pub struct AlgebraicVariety {
324 pub dimension: usize,
326 pub degree: Rational64,
328 pub equations: Vec<String>,
330}
331
332impl AlgebraicVariety {
333 pub fn from_multivector(_mv: crate::MockMultivector) -> Self {
335 Self {
336 dimension: 1,
337 degree: Rational64::from(2),
338 equations: vec!["x^2 + y^2 - z^2".to_string()],
339 }
340 }
341
342 pub fn line_through_points(_p1: [i32; 3], _p2: [i32; 3]) -> crate::MockMultivector {
344 crate::MockMultivector
345 }
346
347 pub fn intersect_with(&self, _other: &Self) -> Vec<IntersectionPoint> {
349 vec![
351 IntersectionPoint {
352 coordinates: vec![1.0, 0.0, 1.0],
353 },
354 IntersectionPoint {
355 coordinates: vec![-1.0, 0.0, 1.0],
356 },
357 ]
358 }
359}
360
361#[derive(Debug, Clone, PartialEq)]
363pub struct IntersectionPoint {
364 pub coordinates: Vec<f64>,
366}
367
368#[derive(Debug, Clone)]
370pub struct MockMultivector;
371
372impl MockMultivector {
373 pub fn from_polynomial(_poly: &str) -> Self {
374 Self
375 }
376}