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,
292 pub quantum_correction: bool,
294}
295
296impl QuantumProduct {
297 pub fn has_classical_part(&self) -> bool {
299 self.classical_part
300 }
301
302 pub fn has_quantum_correction(&self) -> bool {
304 self.quantum_correction
305 }
306}
307
308impl IntersectionRing for Grassmannian {
309 fn intersect(&self, class1: &ChowClass, class2: &ChowClass) -> IntersectionNumber {
310 IntersectionNumber::new(class1.degree * class2.degree)
313 }
314
315 fn count_objects(&self, _object_class: ChowClass, _constraints: Vec<Constraint>) -> i64 {
316 1
318 }
319
320 fn hyperplane_class(&self) -> ChowClass {
321 ChowClass::linear_subspace(1)
322 }
323}
324
325#[derive(Debug, Clone)]
327pub struct AlgebraicVariety {
328 pub dimension: usize,
330 pub degree: Rational64,
332 pub equations: Vec<String>,
334}
335
336impl AlgebraicVariety {
337 pub fn from_multivector(_mv: crate::MockMultivector) -> Self {
339 Self {
340 dimension: 1,
341 degree: Rational64::from(2),
342 equations: vec!["x^2 + y^2 - z^2".to_string()],
343 }
344 }
345
346 pub fn line_through_points(_p1: [i32; 3], _p2: [i32; 3]) -> crate::MockMultivector {
348 crate::MockMultivector
349 }
350
351 pub fn intersect_with(&self, _other: &Self) -> Vec<IntersectionPoint> {
353 vec![
355 IntersectionPoint {
356 coordinates: vec![1.0, 0.0, 1.0],
357 },
358 IntersectionPoint {
359 coordinates: vec![-1.0, 0.0, 1.0],
360 },
361 ]
362 }
363}
364
365#[derive(Debug, Clone, PartialEq)]
367pub struct IntersectionPoint {
368 pub coordinates: Vec<f64>,
370}
371
372#[derive(Debug, Clone)]
374pub struct MockMultivector;
375
376impl MockMultivector {
377 pub fn from_polynomial(_poly: &str) -> Self {
379 Self
380 }
381}