1use crate::utils::{GS, W_};
2use idenso::color::CS;
3use idenso::dirac::AGS;
4use idenso::representations::{
5 Bispinor, ColorAdjoint, ColorAntiFundamental, ColorAntiSextet, ColorFundamental, ColorSextet,
6};
7use linnet::half_edge::involution::{EdgeIndex, Flow};
8use spenso::network::library::symbolic::ETS;
9use spenso::structure::representation::{LibraryRep, Minkowski, RepName};
10use spenso::structure::slot::{IsAbstractSlot, Slot};
11use spenso::structure::{OrderedStructure, TensorStructure};
12use symbolica::atom::FunctionBuilder;
13use symbolica::atom::{AtomCore, AtomView};
14use symbolica::domains::rational::Rational;
15use symbolica::symbol;
16use symbolica::{
17 atom::{Atom, Symbol},
18 function,
19 id::Replacement,
20};
21
22use color_eyre::Result;
23
24use super::aind::Aind;
25
26#[allow(dead_code)]
27pub struct UFOSymbols {
28 pub identity: Symbol,
29 pub identityl: Symbol,
30 pub gamma: Symbol,
31 pub gamma5: Symbol,
32 pub projm: Symbol,
33 pub projp: Symbol,
34 pub sigma: Symbol,
35 pub charge_conj: Symbol,
36 pub metric: Symbol,
37 pub momentum: Symbol,
38 pub levicivita: Symbol,
39 pub t: Symbol,
40 pub f: Symbol,
41 pub d: Symbol,
42 pub antilevicivita: Symbol,
43 pub t6: Symbol,
44 pub k6: Symbol,
45 pub idx: Symbol,
46 pub dummy: Symbol,
47 pub k6bar: Symbol,
48 pub complexconjugate: Symbol,
49 pub pslash: Symbol,
50 pub complex: Symbol,
51}
52
53spenso::symbolica_init_lazy_static! {
54pub static UFO, UFO_INNER: UFOSymbols = || UFOSymbols {
55 identity: symbol!("UFO::Identity"),
56 identityl: symbol!("UFO::IdentityL"),
57 gamma: symbol!("UFO::Gamma"),
58 complexconjugate: symbol!(
59 "UFO::complexconjugate",
60 norm = |f, out| {
61 if let AtomView::Fun(ff) = f
62 && ff.get_nargs() == 1
63 {
64 **out = ff.iter().next().unwrap().conj();
65 }
66 }
67 ),
68 idx: symbol!("UFO::idx"),
69 dummy: symbol!("UFO::dummy"),
70 gamma5: symbol!("UFO::Gamma5"),
71 projm: symbol!("UFO::ProjM"),
72 projp: symbol!("UFO::ProjP"),
73 sigma: symbol!("UFO::Sigma"),
74 charge_conj: symbol!("UFO::C"),
75 metric: symbol!("UFO::Metric"),
76 momentum: symbol!("UFO::P"),
77 levicivita: symbol!("UFO::Epsilon"),
78 t: symbol!("UFO::T"),
79 f: symbol!("UFO::f"),
80 d: symbol!("UFO::d"),
81 antilevicivita: symbol!("UFO::EpsilonBar"),
82 t6: symbol!("UFO::T6"),
83 k6: symbol!("UFO::K6"),
84 k6bar: symbol!("UFO::K6Bar"),
85 pslash: symbol!("UFO::PSlash"),
86 complex: symbol!(
87 "UFO::complex",
88 norm = |f, out| {
89 if let AtomView::Fun(ff) = f {
90 let mut re = Rational::zero();
91 let mut im = Rational::zero();
92 let mut count = 0;
93 for i in ff.iter() {
94 if let Ok(i) = Rational::try_from(i) {
95 if count == 0 {
96 re = i;
97 } else {
98 im = i;
99 }
100 count += 1;
101 } else if let Ok(i) = i64::try_from(i) {
102 if count == 0 {
103 re = i.into();
104 } else {
105 im = i.into();
106 }
107 count += 1;
108 }
109
110 if count > 1 {
111 break;
112 }
113 }
114
115 if count == 2 {
116 **out = Atom::num(symbolica::domains::float::Complex { re, im });
117 }
118 }
119 }
120 ),
121};
122}
123
124impl UFOSymbols {
125 pub fn idx(&self, id: usize, shift: usize) -> Atom {
126 function!(self.idx, shift, id)
127 }
128 pub(crate) fn reindex_spin(
129 &self,
130 slots: &[&OrderedStructure<LibraryRep, Aind>],
131 momenta: &[(Flow, EdgeIndex)],
132 mut atom: Atom,
133 dummy: impl Fn(usize) -> Aind,
134 ) -> Result<Atom> {
135 let mink: LibraryRep = Minkowski {}.into();
136 let bis: LibraryRep = Bispinor {}.into();
137
138 atom = atom
146 .replace(self.momentum.call_args([W_.a_, W_.b___]))
147 .with(self.momentum.call((W_.b___, mink.to_symbolic([W_.a_]))))
148 .replace(self.momentum.call((self.idx.call((1, W_.a_)), W_.b_)))
149 .with(self.momentum.call_args([W_.a_, W_.b_]));
150
151 let reps: Vec<_> = [
153 (
154 self.gamma.call_args([W_.a_, W_.i_, W_.j_]),
155 self.gamma.call_args([
156 mink.to_symbolic([W_.a_]),
157 bis.to_symbolic([W_.i_]),
158 bis.to_symbolic([W_.j_]),
159 ]),
160 ),
161 (
162 self.identityl.call_args([W_.a_, W_.b_]),
163 self.identityl
164 .call_args([mink.to_symbolic([W_.a_]), mink.to_symbolic([W_.b_])]),
165 ),
166 (
167 self.metric.call_args([W_.a_, W_.b_]),
168 self.metric
169 .call_args([mink.to_symbolic([W_.a_]), mink.to_symbolic([W_.b_])]),
170 ),
171 (
172 self.sigma.call_args([W_.a_, W_.b_, W_.i_, W_.j_]),
173 self.sigma.call_args([
174 mink.to_symbolic([W_.a_]),
175 mink.to_symbolic([W_.b_]),
176 bis.to_symbolic([W_.i_]),
177 bis.to_symbolic([W_.j_]),
178 ]),
179 ),
180 (
181 self.pslash.call_args([W_.i_, W_.j_, W_.a___]),
182 self.pslash
183 .call((bis.to_symbolic([W_.i_]), bis.to_symbolic([W_.j_]), W_.a___)),
184 ),
185 (
186 self.identity.call_args([W_.i_, W_.j_]),
187 self.identity
188 .call_args([bis.to_symbolic([W_.i_]), bis.to_symbolic([W_.j_])]),
189 ),
190 (
191 self.gamma5.call_args([W_.i_, W_.j_]),
192 self.gamma5
193 .call_args([bis.to_symbolic([W_.i_]), bis.to_symbolic([W_.j_])]),
194 ),
195 (
196 self.projm.call_args([W_.i_, W_.j_]),
197 self.projm
198 .call_args([bis.to_symbolic([W_.i_]), bis.to_symbolic([W_.j_])]),
199 ),
200 (
201 self.projp.call_args([W_.i_, W_.j_]),
202 self.projp
203 .call_args([bis.to_symbolic([W_.i_]), bis.to_symbolic([W_.j_])]),
204 ),
205 (
206 self.charge_conj.call_args([W_.i_, W_.j_]),
207 self.charge_conj
208 .call_args([bis.to_symbolic([W_.i_]), bis.to_symbolic([W_.j_])]),
209 ),
210 ]
211 .into_iter()
212 .map(|(pat, rep)| Replacement::new(pat.to_pattern(), rep))
213 .collect();
214
215 atom = atom
216 .replace_multiple(&reps)
217 .replace(self.levicivita.call((W_.a___, W_.i_, W_.b___)))
218 .repeat()
219 .with(
220 self.levicivita
221 .call((W_.a___, mink.to_symbolic([W_.i_]), W_.b___)),
222 );
223
224 for (i, s) in slots.iter().enumerate() {
225 let i = i + 1;
226 for (shift, r) in s.external_structure_iter().enumerate() {
227 let rep = r.rep_name();
228 let wrappedi = self.idx(i, shift + 1);
229 let wrappedi = wrappedi.as_view();
230
231 atom = atom
233 .replace(rep.to_symbolic([wrappedi]))
234 .level_range((1, Some(1)))
235 .with(r.to_atom())
236 .replace(rep.to_symbolic([i]))
237 .level_range((1, Some(1)))
238 .with(r.to_atom())
239 }
240 }
241 let mut max_dummy = 0;
246
247 for rep in [mink, bis] {
250 let mut max_dummy = 0;
251
252 atom = atom.replace_map(|term, _, out| {
253 let AtomView::Fun(f) = term else {
254 return;
255 };
256
257 let name = f.get_symbol();
258 if name != rep.symbol() {
259 return;
260 }
261
262 let mut fbuilder = FunctionBuilder::new(name);
263
264 if f.get_nargs() == 1 {
265 fbuilder = fbuilder.add_arg(4);
266 let arg = f.iter().next().unwrap();
267 let a = if let Ok(a) = i64::try_from(arg) {
268 if a < 0 {
269 (-a) as usize
270 } else {
271 return;
272 }
273 } else if let AtomView::Fun(f) = arg
274 && f.get_symbol() == self.dummy
275 && f.get_nargs() == 1
276 && let Ok(a) = usize::try_from(f.iter().next().unwrap())
277 {
278 a
279 } else {
280 return;
281 };
282 if a > max_dummy {
283 max_dummy = a;
284 }
285 fbuilder = fbuilder.add_arg(Atom::from(dummy(a)));
286 **out = fbuilder.finish();
287 }
288 });
289 }
290
291 let mink = Minkowski {}.new_rep(4);
292 let bis = Bispinor {}.new_rep(4);
293 let reps: Vec<_> = [
297 (
298 self.identity
299 .call_args([bis.pattern(W_.a_), bis.pattern(W_.b_)]),
300 bis.g(W_.a_, W_.b_),
301 ),
302 (
303 self.identityl
304 .call_args([mink.pattern(W_.a_), mink.pattern(W_.b_)]),
305 mink.g(W_.a_, W_.b_),
306 ),
307 (
308 self.metric
309 .call_args([mink.pattern(W_.a_), mink.pattern(W_.b_)]),
310 mink.g(W_.a_, W_.b_),
311 ),
312 (
313 self.gamma
314 .call_args([mink.pattern(W_.i_), bis.pattern(W_.a_), bis.pattern(W_.b_)]),
315 AGS.gamma
316 .call_args([bis.pattern(W_.a_), bis.pattern(W_.b_), mink.pattern(W_.i_)]),
317 ),
318 (
319 self.gamma5
320 .call_args([bis.pattern(W_.a_), bis.pattern(W_.b_)]),
321 AGS.gamma5
322 .call_args([bis.pattern(W_.a_), bis.pattern(W_.b_)]),
323 ),
324 (
325 self.projm
326 .call_args([bis.pattern(W_.a_), bis.pattern(W_.b_)]),
327 AGS.projm
328 .call_args([bis.pattern(W_.a_), bis.pattern(W_.b_)]),
329 ),
330 (
331 self.projp
332 .call_args([bis.pattern(W_.a_), bis.pattern(W_.b_)]),
333 AGS.projp
334 .call_args([bis.pattern(W_.a_), bis.pattern(W_.b_)]),
335 ),
336 (
337 self.sigma.call_args([
338 bis.pattern(W_.a_),
339 bis.pattern(W_.b_),
340 mink.pattern(W_.i_),
341 mink.pattern(W_.j_),
342 ]),
343 AGS.sigma.call_args([
344 bis.pattern(W_.a_),
345 bis.pattern(W_.b_),
346 mink.pattern(W_.i_),
347 mink.pattern(W_.j_),
348 ]),
349 ),
350 ]
351 .into_iter()
352 .map(|(pat, rep)| Replacement::new(pat.to_pattern(), rep))
353 .collect();
354
355 atom = atom.replace_multiple(&reps);
357
358 atom = atom.replace_map(|term, _, out| {
359 if let AtomView::Fun(f) = term
360 && f.get_symbol() == self.pslash
361 {
362 let mut gamma = FunctionBuilder::new(AGS.gamma);
363
364 let mut count = 0;
365
366 for a in f.iter() {
367 count += 1;
368 if count <= 2 {
369 gamma = gamma.add_arg(a);
370 } else if let Ok(i) = i64::try_from(a) {
371 max_dummy += 1;
372
373 let minki: Slot<Minkowski, Aind> = mink.slot(dummy(max_dummy));
374
375 gamma = gamma.add_arg(minki.to_atom());
376
377 **out = gamma.finish() * GS.emr_mom(momenta[i as usize].1, minki.to_atom());
378 return;
379 }
380 }
381
382 if count == 2 {
383 max_dummy += 1;
384
385 let minki: Slot<Minkowski, Aind> = mink.slot(dummy(max_dummy));
386
387 gamma = gamma.add_arg(minki.to_atom());
388
389 **out = gamma.finish() * GS.emr_mom(momenta[0].1, minki.to_atom());
390 }
391 }
392 });
393
394 for (i, (f, e)) in momenta.iter().enumerate() {
395 atom = atom
396 .replace(function!(UFO.momentum, (i + 1) as i64, W_.a_))
397 .with(match f {
398 Flow::Sink => GS.emr_mom(*e, W_.a_),
399 Flow::Source => -GS.emr_mom(*e, W_.a_),
400 })
401 }
402
403 atom = atom
404 .replace(function!(UFO.momentum, W_.a_))
405 .with(GS.emr_mom(momenta[0].1, W_.a_));
406
407 if atom
411 .replace(self.gamma.call_args([W_.a__]))
412 .match_iter()
413 .next()
414 .is_some()
415 || atom
416 .replace(self.sigma.call_args([W_.a___]))
417 .match_iter()
418 .next()
419 .is_some()
420 || atom
421 .replace(self.pslash.call_args([W_.a___]))
422 .match_iter()
423 .next()
424 .is_some()
425 || atom
426 .replace(self.gamma5.call_args([W_.a__]))
427 .match_iter()
428 .next()
429 .is_some()
430 || atom
431 .replace(self.projm.call_args([W_.a__]))
432 .match_iter()
433 .next()
434 .is_some()
435 || atom
436 .replace(self.projp.call_args([W_.a__]))
437 .match_iter()
438 .next()
439 .is_some()
440 || atom
441 .replace(self.charge_conj.call_args([W_.a__]))
442 .match_iter()
443 .next()
444 .is_some()
445 {
446 return Err(color_eyre::eyre::eyre!(
447 "Some spinor structures were not fully re-indexed: {}",
448 atom
449 ));
450 }
451
452 Ok(atom)
453 }
454
455 pub(crate) fn reindex_color(
456 &self,
457 slots: &[&OrderedStructure<LibraryRep, Aind>],
458 mut atom: Atom,
459 dummy: impl Fn(usize) -> Aind,
460 ) -> Result<Atom> {
461 let adj = ColorAdjoint {};
462 let fund = ColorFundamental {};
463 let antifund = ColorAntiFundamental {};
464 let sext = ColorSextet {};
465 let antisext = ColorAntiSextet {};
466 let reps: Vec<_> = [
473 (
474 self.t.call_args([W_.a_, W_.i_, W_.j_]),
475 self.t.call_args([
476 adj.to_symbolic([W_.a_]),
477 fund.to_symbolic([W_.i_]),
478 antifund.to_symbolic([W_.j_]),
479 ]),
480 ),
481 (
482 self.f.call_args([W_.a_, W_.b_, W_.c_]),
483 self.f.call_args([
484 adj.to_symbolic([W_.a_]),
485 adj.to_symbolic([W_.b_]),
486 adj.to_symbolic([W_.c_]),
487 ]),
488 ),
489 (
490 self.d.call_args([W_.a_, W_.b_, W_.c_]),
491 self.d.call_args([
492 adj.to_symbolic([W_.a_]),
493 adj.to_symbolic([W_.b_]),
494 adj.to_symbolic([W_.c_]),
495 ]),
496 ),
497 (
498 self.levicivita.call_args([W_.i_, W_.j_, W_.k_]),
499 self.levicivita.call_args([
500 fund.to_symbolic([W_.i_]),
501 fund.to_symbolic([W_.j_]),
502 fund.to_symbolic([W_.k_]),
503 ]),
504 ),
505 (
506 self.antilevicivita.call_args([W_.i_, W_.j_, W_.k_]),
507 self.levicivita.call_args([
508 antifund.to_symbolic([W_.i_]),
509 antifund.to_symbolic([W_.j_]),
510 antifund.to_symbolic([W_.k_]),
511 ]),
512 ),
513 (
514 self.t6.call_args([W_.a_, W_.i_, W_.j_]),
515 self.t6.call_args([
516 adj.to_symbolic([W_.a_]),
517 sext.to_symbolic([W_.i_]),
518 antisext.to_symbolic([W_.j_]),
519 ]),
520 ),
521 (
522 self.k6.call_args([W_.a_, W_.i_, W_.j_]),
523 self.k6.call_args([
524 sext.to_symbolic([W_.a_]),
525 antifund.to_symbolic([W_.i_]),
526 antifund.to_symbolic([W_.j_]),
527 ]),
528 ),
529 (
530 self.k6bar.call_args([W_.a_, W_.i_, W_.j_]),
531 self.k6bar.call_args([
532 antisext.to_symbolic([W_.a_]),
533 fund.to_symbolic([W_.i_]),
534 fund.to_symbolic([W_.j_]),
535 ]),
536 ),
537 ]
538 .into_iter()
539 .map(|(pat, rep)| Replacement::new(pat.to_pattern(), rep))
540 .collect();
541
542 atom = atom.replace_multiple(&reps);
544
545 atom = atom
548 .replace(
549 function!(self.identity, W_.a_, W_.b_)
550 * function!(W_.f_, W_.a___, function!(W_.g_, W_.a_), W_.b___),
551 )
552 .repeat()
553 .with(
554 self.identity.call((W_.g_.call_args([W_.a_]), W_.b_))
555 * W_.f_.call((W_.a___, W_.g_.call_args([W_.a_]), W_.b___)),
556 );
557
558 for (i, s) in slots.iter().enumerate() {
563 let i = i + 1;
564 for (shift, r) in s.external_structure_iter().enumerate() {
565 let rep = r.rep_name();
566 let wrappedi = self.idx(i, shift + 1);
567 let wrappedi = wrappedi.as_view();
568
569 atom = atom
571 .replace(rep.to_symbolic([wrappedi]))
572 .level_range((1, Some(1)))
573 .with(r.to_atom())
574 .replace(self.identity.call((wrappedi, W_.i_)))
575 .with(self.identity.call((r.to_atom(), W_.i_)))
576 .replace(self.identity.call((W_.i_, wrappedi)))
577 .with(self.identity.call((W_.i_, r.to_atom())))
578 .replace(rep.to_symbolic([i]))
579 .level_range((1, Some(1)))
580 .with(r.to_atom())
581 .replace(self.identity.call((i, W_.i_)))
582 .with(self.identity.call((r.to_atom(), W_.i_)))
583 .replace(self.identity.call((W_.i_, i)))
584 .with(self.identity.call((W_.i_, r.to_atom())));
585 }
586 }
587
588 for rep in [
592 LibraryRep::from(adj).new_rep(8),
593 fund.new_rep(3).to_lib(),
594 antifund.new_rep(3).to_lib(),
595 sext.new_rep(6).to_lib(),
596 antisext.new_rep(6).to_lib(),
597 ] {
598 let mut max_dummy = 0;
599
600 atom = atom.replace_map(|term, _, out| {
601 let AtomView::Fun(f) = term else {
602 return;
603 };
604
605 let name = f.get_symbol();
606 if name != rep.rep.symbol() {
607 return;
608 }
609
610 let mut fbuilder = FunctionBuilder::new(name);
611
612 if f.get_nargs() == 1 {
613 fbuilder = fbuilder.add_arg(rep.dim.to_symbolic());
614 let arg = f.iter().next().unwrap();
615 let a = if let Ok(a) = i64::try_from(arg) {
616 if a < 0 {
617 (-a) as usize
618 } else {
619 return;
620 }
621 } else if let AtomView::Fun(f) = arg
622 && f.get_symbol() == self.dummy
623 && f.get_nargs() == 1
624 && let Ok(a) = usize::try_from(f.iter().next().unwrap())
625 {
626 a
627 } else {
628 return;
629 };
630 if a > max_dummy {
631 max_dummy = a;
632 }
633 fbuilder = fbuilder.add_arg(Atom::from(dummy(a)));
634 **out = fbuilder.finish();
635 }
636 });
637 }
638
639 let reps: Vec<_> = [
640 (
641 self.identity.call_args([W_.a_, W_.b_]),
642 ETS.metric.call_args([W_.a_, W_.b_]),
643 ),
644 (
645 self.t.call_args([
646 adj.to_symbolic([W_.a__]),
647 fund.to_symbolic([W_.i__]),
648 antifund.to_symbolic([W_.j__]),
649 ]),
650 CS.t.call_args([
651 adj.to_symbolic([W_.a__]),
652 fund.to_symbolic([W_.i__]),
653 antifund.to_symbolic([W_.j__]),
654 ]),
655 ),
656 (
657 self.f.call_args([
658 adj.to_symbolic([W_.a__]),
659 adj.to_symbolic([W_.b__]),
660 adj.to_symbolic([W_.c__]),
661 ]),
662 CS.f.call_args([
663 adj.to_symbolic([W_.a__]),
664 adj.to_symbolic([W_.b__]),
665 adj.to_symbolic([W_.c__]),
666 ]),
667 ),
668 ]
669 .into_iter()
670 .map(|(pat, rep)| Replacement::new(pat.to_pattern(), rep))
671 .collect();
672
673 atom = atom.replace_multiple(&reps);
674
675 if atom
676 .replace(self.t.call_args([W_.a___]))
677 .match_iter()
678 .next()
679 .is_some()
680 || atom
681 .replace(self.f.call_args([W_.a___]))
682 .match_iter()
683 .next()
684 .is_some()
685 || atom
686 .replace(self.d.call_args([W_.a___]))
687 .match_iter()
688 .next()
689 .is_some()
690 || atom
691 .replace(self.levicivita.call_args([W_.a___]))
692 .match_iter()
693 .next()
694 .is_some()
695 || atom
696 .replace(self.antilevicivita.call_args([W_.a___]))
697 .match_iter()
698 .next()
699 .is_some()
700 || atom
701 .replace(self.t6.call_args([W_.a___]))
702 .match_iter()
703 .next()
704 .is_some()
705 || atom
706 .replace(self.k6.call_args([W_.a___]))
707 .match_iter()
708 .next()
709 .is_some()
710 || atom
711 .replace(self.k6bar.call_args([W_.a___]))
712 .match_iter()
713 .next()
714 .is_some()
715 {
716 return Err(color_eyre::eyre::eyre!(
717 "Some color structures were not fully re-indexed: {}",
718 atom
719 ));
720 }
721
722 Ok(atom)
724 }
725}
726
727#[cfg(test)]
728pub mod test {
729
730 #[test]
731 fn ufo_spin_processing() {}
732}