1use crate::{
8 ChowClass, EnumerativeError, EnumerativeResult, IntersectionNumber, ProjectiveSpace,
9 SchubertClass,
10};
11use amari_core::{Multivector, Rotor};
12use num_rational::Rational64;
13use std::collections::HashMap;
14
15pub mod signatures {
17 use amari_core::Multivector;
18
19 pub type Euclidean3D = Multivector<3, 0, 0>;
21
22 pub type Projective3D = Multivector<3, 1, 0>;
24
25 pub type Conformal4D = Multivector<4, 1, 0>;
27
28 pub type ComplexProjective = Multivector<4, 0, 0>;
30
31 pub type GrassmannianGA<const K: usize, const N_MINUS_K: usize> = Multivector<K, N_MINUS_K, 0>;
33}
34
35#[derive(Debug, Clone)]
37pub struct GeometricVariety<const P: usize, const Q: usize, const R: usize> {
38 pub multivector: Multivector<P, Q, R>,
40 pub dimension: usize,
42 pub degree: Rational64,
44 pub invariants: HashMap<String, f64>,
46}
47
48impl<const P: usize, const Q: usize, const R: usize> GeometricVariety<P, Q, R> {
49 pub fn new(multivector: Multivector<P, Q, R>, dimension: usize, degree: Rational64) -> Self {
51 Self {
52 multivector,
53 dimension,
54 degree,
55 invariants: HashMap::new(),
56 }
57 }
58
59 pub fn point(coordinates: &[f64]) -> EnumerativeResult<Self> {
61 if coordinates.len() != P + Q + R {
62 return Err(EnumerativeError::InvalidDimension(format!(
63 "Expected {} coordinates, got {}",
64 P + Q + R,
65 coordinates.len()
66 )));
67 }
68
69 let mut mv = Multivector::zero();
70 for (i, &coord) in coordinates.iter().enumerate() {
71 mv = mv + Multivector::basis_vector(i) * coord;
72 }
73
74 Ok(Self::new(mv, 0, Rational64::from(1)))
75 }
76
77 pub fn line_through_points(p1: &Self, p2: &Self) -> EnumerativeResult<Self> {
79 if p1.dimension != 0 || p2.dimension != 0 {
80 return Err(EnumerativeError::InvalidDimension(
81 "Line construction requires point varieties".to_string(),
82 ));
83 }
84
85 let line_mv = p1.multivector.outer_product(&p2.multivector);
87 Ok(Self::new(line_mv, 1, Rational64::from(1)))
88 }
89
90 pub fn plane_through_points(p1: &Self, p2: &Self, p3: &Self) -> EnumerativeResult<Self> {
92 if p1.dimension != 0 || p2.dimension != 0 || p3.dimension != 0 {
93 return Err(EnumerativeError::InvalidDimension(
94 "Plane construction requires point varieties".to_string(),
95 ));
96 }
97
98 let plane_mv = p1
100 .multivector
101 .outer_product(&p2.multivector)
102 .outer_product(&p3.multivector);
103 Ok(Self::new(plane_mv, 2, Rational64::from(1)))
104 }
105
106 pub fn to_chow_class(&self) -> ChowClass {
108 ChowClass::new(self.dimension, self.degree)
109 }
110
111 pub fn intersect_with(&self, other: &Self) -> EnumerativeResult<Vec<Self>> {
113 let self_dual = self.multivector.hodge_dual();
116 let other_dual = other.multivector.hodge_dual();
117 let intersection_mv = self_dual.outer_product(&other_dual).hodge_dual();
118 let intersection_dim = (P + Q + R).saturating_sub(self.dimension + other.dimension);
119
120 let intersection_degree = self.degree * other.degree;
121
122 Ok(vec![Self::new(
123 intersection_mv,
124 intersection_dim,
125 intersection_degree,
126 )])
127 }
128
129 pub fn transform(&self, rotor: &Rotor<P, Q, R>) -> Self {
131 let transformed_mv = rotor.apply(&self.multivector);
132 Self {
133 multivector: transformed_mv,
134 dimension: self.dimension,
135 degree: self.degree,
136 invariants: self.invariants.clone(),
137 }
138 }
139
140 pub fn geometric_degree(&self) -> f64 {
142 self.multivector.magnitude()
143 }
144
145 pub fn contains_point(&self, point: &Self) -> bool {
147 if point.dimension != 0 {
148 return false;
149 }
150
151 let self_dual = self.multivector.hodge_dual();
154 let point_dual = point.multivector.hodge_dual();
155 let meet = self_dual.outer_product(&point_dual).hodge_dual();
156 (meet - point.multivector.clone()).magnitude() < 1e-10
157 }
158}
159
160#[derive(Debug, Clone)]
162pub struct GeometricSchubertClass<const P: usize, const Q: usize, const R: usize> {
163 pub schubert_class: SchubertClass,
165 pub multivector: Multivector<P, Q, R>,
167 pub grassmannian_dim: (usize, usize),
169}
170
171impl<const P: usize, const Q: usize, const R: usize> GeometricSchubertClass<P, Q, R> {
172 pub fn new(partition: Vec<usize>, grassmannian_dim: (usize, usize)) -> EnumerativeResult<Self> {
174 let schubert_class = SchubertClass::new(partition, grassmannian_dim)?;
175
176 let mut mv = Multivector::scalar(1.0);
178 for (i, &part) in schubert_class.partition.iter().enumerate() {
179 if part > 0 {
180 let blade = Multivector::basis_vector(i + part);
183 mv = mv.outer_product(&blade);
184 }
185 }
186
187 Ok(Self {
188 schubert_class,
189 multivector: mv,
190 grassmannian_dim,
191 })
192 }
193
194 pub fn geometric_intersection(&self, other: &Self) -> EnumerativeResult<Self> {
196 if self.grassmannian_dim != other.grassmannian_dim {
197 return Err(EnumerativeError::InvalidDimension(
198 "Schubert classes must be on the same Grassmannian".to_string(),
199 ));
200 }
201
202 let intersection_mv = self.multivector.geometric_product(&other.multivector);
204
205 let mut new_partition = self.schubert_class.partition.clone();
207 for (i, &part) in other.schubert_class.partition.iter().enumerate() {
208 if i < new_partition.len() {
209 new_partition[i] = new_partition[i].saturating_add(part);
210 } else {
211 new_partition.push(part);
212 }
213 }
214
215 Ok(Self {
216 schubert_class: SchubertClass::new(new_partition, self.grassmannian_dim)?,
217 multivector: intersection_mv,
218 grassmannian_dim: self.grassmannian_dim,
219 })
220 }
221
222 pub fn to_schubert_class(&self) -> &SchubertClass {
224 &self.schubert_class
225 }
226}
227
228#[derive(Debug, Clone)]
230pub struct GeometricProjectiveSpace<const P: usize, const Q: usize, const R: usize> {
231 pub projective_space: ProjectiveSpace,
233 pub _phantom: std::marker::PhantomData<Multivector<P, Q, R>>,
235}
236
237impl<const P: usize, const Q: usize, const R: usize> GeometricProjectiveSpace<P, Q, R> {
238 pub fn new(dimension: usize) -> Self {
240 Self {
241 projective_space: ProjectiveSpace::new(dimension),
242 _phantom: std::marker::PhantomData,
243 }
244 }
245
246 pub fn variety_from_multivector(
248 &self,
249 multivector: Multivector<P, Q, R>,
250 dimension: usize,
251 degree: Rational64,
252 ) -> GeometricVariety<P, Q, R> {
253 GeometricVariety::new(multivector, dimension, degree)
254 }
255
256 pub fn geometric_intersection_number(
258 &self,
259 variety1: &GeometricVariety<P, Q, R>,
260 variety2: &GeometricVariety<P, Q, R>,
261 ) -> IntersectionNumber {
262 let intersection_mv = variety1
264 .multivector
265 .geometric_product(&variety2.multivector);
266 let multiplicity = intersection_mv.magnitude();
267
268 IntersectionNumber::new(Rational64::from(multiplicity as i64))
269 }
270
271 pub fn hyperplane_from_normal(
273 &self,
274 normal: &[f64],
275 ) -> EnumerativeResult<GeometricVariety<P, Q, R>> {
276 if normal.len() != P + Q + R {
277 return Err(EnumerativeError::InvalidDimension(format!(
278 "Normal vector must have {} components",
279 P + Q + R
280 )));
281 }
282
283 let mut normal_mv = Multivector::zero();
284 for (i, &component) in normal.iter().enumerate() {
285 normal_mv = normal_mv + Multivector::basis_vector(i) * component;
286 }
287
288 Ok(GeometricVariety::new(
289 normal_mv,
290 self.projective_space.dimension - 1,
291 Rational64::from(1),
292 ))
293 }
294}
295
296pub mod quantum_k_theory {
302 use super::*;
303 use std::collections::BTreeMap;
304
305 #[derive(Debug, Clone)]
307 pub struct QuantumKClass<const P: usize, const Q: usize, const R: usize> {
308 pub multivector: Multivector<P, Q, R>,
310 pub k_degree: i32,
312 pub q_power: i32,
314 pub todd_coefficients: Vec<Rational64>,
316 pub chern_character: BTreeMap<usize, Rational64>,
318 }
319
320 impl<const P: usize, const Q: usize, const R: usize> QuantumKClass<P, Q, R> {
321 pub fn new(multivector: Multivector<P, Q, R>, k_degree: i32, q_power: i32) -> Self {
323 Self {
324 multivector,
325 k_degree,
326 q_power,
327 todd_coefficients: vec![Rational64::from(1)], chern_character: BTreeMap::new(),
329 }
330 }
331
332 pub fn structure_sheaf_point() -> Self {
334 Self::new(Multivector::scalar(1.0), 0, 0)
335 }
336
337 pub fn line_bundle(c1: i64) -> Self {
339 let mut class = Self::new(Multivector::scalar(1.0), 0, 0);
340 class.chern_character.insert(1, Rational64::from(c1));
341 class
342 }
343
344 pub fn tangent_bundle_projective(dimension: usize) -> Self {
346 let mut class = Self::new(Multivector::scalar(1.0), dimension as i32, 0);
347 for i in 1..=dimension {
350 class.chern_character.insert(i, Rational64::from(1));
351 }
352 class
353 }
354
355 pub fn quantum_product(&self, other: &Self) -> EnumerativeResult<Self> {
357 let classical_mv = self.multivector.geometric_product(&other.multivector);
359 let classical_degree = self.k_degree + other.k_degree;
360
361 let mut quantum_power = self.q_power + other.q_power;
363 let mut result_class = Self::new(classical_mv, classical_degree, quantum_power);
364
365 for (°1, &coeff1) in &self.chern_character {
367 for (°2, &coeff2) in &other.chern_character {
368 let total_deg = deg1 + deg2;
369 let combined_coeff = coeff1 * coeff2;
370 *result_class
371 .chern_character
372 .entry(total_deg)
373 .or_insert(Rational64::from(0)) += combined_coeff;
374 }
375 }
376
377 let gw_correction = self.compute_gw_correction(other)?;
380 quantum_power += gw_correction;
381 result_class.q_power = quantum_power;
382
383 Ok(result_class)
384 }
385
386 fn compute_gw_correction(&self, other: &Self) -> EnumerativeResult<i32> {
388 let total_degree = (self.k_degree + other.k_degree).abs();
395
396 if total_degree > 0
398 && self.has_positive_chern_class()
399 && other.has_positive_chern_class()
400 {
401 Ok(total_degree) } else {
403 Ok(0) }
405 }
406
407 fn has_positive_chern_class(&self) -> bool {
409 self.chern_character
410 .values()
411 .any(|&coeff| coeff > Rational64::from(0))
412 }
413
414 pub fn chern_character_total(&self) -> Rational64 {
416 self.chern_character.values().sum()
417 }
418
419 pub fn euler_characteristic(&self, ambient_dimension: usize) -> Rational64 {
421 let ch_total = self.chern_character_total();
422 let todd_correction = self.todd_class_value(ambient_dimension);
423 ch_total * todd_correction
424 }
425
426 fn todd_class_value(&self, dimension: usize) -> Rational64 {
428 if dimension == 0 {
431 Rational64::from(1)
432 } else {
433 Rational64::from(1) + Rational64::from(dimension as i64) / Rational64::from(2)
435 }
436 }
437
438 pub fn dual(&self) -> Self {
440 let dual_mv = self.multivector.reverse(); let mut dual_class = Self::new(dual_mv, -self.k_degree, -self.q_power);
442
443 for (°, &coeff) in &self.chern_character {
445 dual_class.chern_character.insert(deg, -coeff);
446 }
447
448 dual_class
449 }
450
451 pub fn adams_operation(&self, k: i32) -> Self {
453 let powered_mv = if k >= 0 {
454 let mut result = self.multivector.clone();
456 for _ in 1..k {
457 result = result.geometric_product(&self.multivector);
458 }
459 result
460 } else {
461 self.multivector
462 .inverse()
463 .unwrap_or_else(|| self.multivector.clone())
464 };
465
466 let mut adams_class = Self::new(powered_mv, self.k_degree * k, self.q_power * k);
467
468 for (°, &coeff) in &self.chern_character {
470 let k_power = (k as i64).pow(deg as u32);
471 let adams_coeff = coeff * Rational64::from(k_power);
472 adams_class.chern_character.insert(deg, adams_coeff);
473 }
474
475 adams_class
476 }
477
478 pub fn riemann_roch_euler(&self, ambient_todd: &[Rational64]) -> Rational64 {
480 let mut result = Rational64::from(0);
481
482 for (°, &ch_coeff) in &self.chern_character {
483 if deg < ambient_todd.len() {
484 result += ch_coeff * ambient_todd[deg];
485 }
486 }
487
488 result
489 }
490
491 pub fn localized_integral(
493 &self,
494 fixed_points: &[GeometricVariety<P, Q, R>],
495 ) -> EnumerativeResult<Rational64> {
496 let mut total = Rational64::from(0);
497
498 for point in fixed_points {
501 let point_contribution = self.evaluate_at_point(point)?;
502 let normal_euler = self.normal_bundle_euler_class(point);
503
504 if normal_euler != Rational64::from(0) {
505 total += point_contribution / normal_euler;
506 }
507 }
508
509 Ok(total)
510 }
511
512 fn evaluate_at_point(
514 &self,
515 point: &GeometricVariety<P, Q, R>,
516 ) -> EnumerativeResult<Rational64> {
517 let geometric_eval = point.geometric_degree();
519 Ok(Rational64::from(geometric_eval as i64))
520 }
521
522 fn normal_bundle_euler_class(&self, _point: &GeometricVariety<P, Q, R>) -> Rational64 {
524 Rational64::from((P + Q + R) as i64) }
528
529 pub fn from_quantum_cohomology(
531 _qh_class: &crate::QuantumCohomology,
532 ) -> EnumerativeResult<Self> {
533 let multivector = Multivector::scalar(1.0); let mut k_class = Self::new(multivector, 0, 0);
538
539 k_class.chern_character.insert(0, Rational64::from(1));
542
543 Ok(k_class)
544 }
545 }
546
547 #[derive(Debug)]
549 pub struct QuantumKRing<const P: usize, const Q: usize, const R: usize> {
550 pub generators: Vec<QuantumKClass<P, Q, R>>,
552 pub relations: Vec<String>,
554 pub quantum_parameter: String,
556 }
557
558 impl<const P: usize, const Q: usize, const R: usize> Default for QuantumKRing<P, Q, R> {
559 fn default() -> Self {
560 Self::new()
561 }
562 }
563
564 impl<const P: usize, const Q: usize, const R: usize> QuantumKRing<P, Q, R> {
565 pub fn new() -> Self {
567 Self {
568 generators: Vec::new(),
569 relations: Vec::new(),
570 quantum_parameter: "q".to_string(),
571 }
572 }
573
574 pub fn add_generator(&mut self, class: QuantumKClass<P, Q, R>) {
576 self.generators.push(class);
577 }
578
579 pub fn projective_space(dimension: usize) -> Self {
581 let mut ring = Self::new();
582
583 let line_bundle = QuantumKClass::line_bundle(1);
585 ring.add_generator(line_bundle);
586
587 ring.relations.push(format!("L^{} = 0", dimension + 1));
589
590 ring
591 }
592
593 pub fn grassmannian(k: usize, n: usize) -> Self {
595 let mut ring = Self::new();
596
597 let tautological_sub = QuantumKClass::new(Multivector::scalar(1.0), k as i32, 0);
599 let quotient_bundle = QuantumKClass::new(Multivector::scalar(1.0), (n - k) as i32, 0);
600
601 ring.add_generator(tautological_sub);
602 ring.add_generator(quotient_bundle);
603
604 ring.relations.push("Quantum Pieri rules".to_string());
606
607 ring
608 }
609 }
610}
611
612#[cfg(test)]
613mod tests {
614 use super::*;
615
616 #[test]
617 fn test_point_creation() {
618 let point = GeometricVariety::<3, 0, 0>::point(&[1.0, 2.0, 3.0]).unwrap();
619 assert_eq!(point.dimension, 0);
620 assert_eq!(point.degree, Rational64::from(1));
621 }
622
623 #[test]
624 fn test_line_through_points() {
625 let p1 = GeometricVariety::<3, 0, 0>::point(&[1.0, 0.0, 0.0]).unwrap();
626 let p2 = GeometricVariety::<3, 0, 0>::point(&[0.0, 1.0, 0.0]).unwrap();
627 let line = GeometricVariety::line_through_points(&p1, &p2).unwrap();
628
629 assert_eq!(line.dimension, 1);
630 assert_eq!(line.degree, Rational64::from(1));
631 }
632
633 #[test]
634 fn test_geometric_projective_space() {
635 let gp2 = GeometricProjectiveSpace::<2, 1, 0>::new(2);
636 let hyperplane = gp2.hyperplane_from_normal(&[1.0, 1.0, 1.0]).unwrap();
637
638 assert_eq!(hyperplane.dimension, 1); }
640
641 #[test]
642 fn test_schubert_class_creation() {
643 let schubert = GeometricSchubertClass::<2, 2, 0>::new(vec![1], (2, 4)).unwrap();
644 assert_eq!(schubert.grassmannian_dim, (2, 4));
645 assert_eq!(schubert.schubert_class.partition, vec![1]);
646 }
647
648 #[test]
650 fn test_quantum_k_class_creation() {
651 use super::quantum_k_theory::QuantumKClass;
652
653 let mv = Multivector::<3, 0, 0>::scalar(1.0);
654 let qk_class = QuantumKClass::new(mv, 1, 0);
655
656 assert_eq!(qk_class.k_degree, 1);
657 assert_eq!(qk_class.q_power, 0);
658 assert_eq!(qk_class.todd_coefficients, vec![Rational64::from(1)]);
659 }
660
661 #[test]
662 fn test_line_bundle_creation() {
663 use super::quantum_k_theory::QuantumKClass;
664
665 let line_bundle = QuantumKClass::<3, 0, 0>::line_bundle(2);
666 assert_eq!(line_bundle.k_degree, 0);
667 assert_eq!(
668 *line_bundle.chern_character.get(&1).unwrap(),
669 Rational64::from(2)
670 );
671 }
672
673 #[test]
674 fn test_quantum_product_basic() {
675 use super::quantum_k_theory::QuantumKClass;
676
677 let bundle1 = QuantumKClass::<3, 0, 0>::line_bundle(1);
678 let bundle2 = QuantumKClass::<3, 0, 0>::line_bundle(1);
679
680 let product = bundle1.quantum_product(&bundle2).unwrap();
681
682 assert!(product.q_power >= 0);
684 assert!(!product.chern_character.is_empty());
685 }
686
687 #[test]
688 fn test_adams_operations() {
689 use super::quantum_k_theory::QuantumKClass;
690
691 let line_bundle = QuantumKClass::<3, 0, 0>::line_bundle(2);
692 let adams_2 = line_bundle.adams_operation(2);
693
694 assert_eq!(
696 *adams_2.chern_character.get(&1).unwrap(),
697 Rational64::from(4)
698 );
699 }
700
701 #[test]
702 fn test_euler_characteristic() {
703 use super::quantum_k_theory::QuantumKClass;
704
705 let structure_sheaf = QuantumKClass::<3, 0, 0>::structure_sheaf_point();
706 let euler_char = structure_sheaf.euler_characteristic(2);
707
708 assert!(euler_char >= Rational64::from(0));
710 }
711
712 #[test]
713 fn test_dual_bundle() {
714 use super::quantum_k_theory::QuantumKClass;
715
716 let line_bundle = QuantumKClass::<3, 0, 0>::line_bundle(3);
717 let dual = line_bundle.dual();
718
719 assert_eq!(dual.k_degree, -line_bundle.k_degree);
721 assert_eq!(*dual.chern_character.get(&1).unwrap(), Rational64::from(-3));
722 }
723
724 #[test]
725 fn test_quantum_k_ring_projective_space() {
726 use super::quantum_k_theory::QuantumKRing;
727
728 let ring = QuantumKRing::<3, 0, 0>::projective_space(2);
729 assert_eq!(ring.generators.len(), 1);
730 assert!(ring.relations.contains(&"L^3 = 0".to_string()));
731 }
732
733 #[test]
734 fn test_quantum_k_ring_grassmannian() {
735 use super::quantum_k_theory::QuantumKRing;
736
737 let ring = QuantumKRing::<4, 0, 0>::grassmannian(2, 4);
738 assert_eq!(ring.generators.len(), 2); assert!(!ring.relations.is_empty());
740 }
741
742 #[test]
743 fn test_riemann_roch_computation() {
744 use super::quantum_k_theory::QuantumKClass;
745
746 let line_bundle = QuantumKClass::<3, 0, 0>::line_bundle(1);
747 let todd_classes = vec![Rational64::from(1), Rational64::from(1)];
748
749 let rr_result = line_bundle.riemann_roch_euler(&todd_classes);
750 assert!(rr_result >= Rational64::from(0));
751 }
752
753 #[test]
754 fn test_chern_character_total() {
755 use super::quantum_k_theory::QuantumKClass;
756
757 let mut bundle = QuantumKClass::<3, 0, 0>::new(Multivector::scalar(1.0), 2, 0);
758 bundle.chern_character.insert(0, Rational64::from(1));
759 bundle.chern_character.insert(1, Rational64::from(3));
760 bundle.chern_character.insert(2, Rational64::from(2));
761
762 let total = bundle.chern_character_total();
763 assert_eq!(total, Rational64::from(6)); }
765}