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 .min_level(1)
235 .max_level(Some(1))
236 .with(r.to_atom())
237 .replace(rep.to_symbolic([i]))
238 .min_level(1)
239 .max_level(Some(1))
240 .with(r.to_atom())
241 }
242 }
243 let mut max_dummy = 0;
248
249 for rep in [mink, bis] {
252 let mut max_dummy = 0;
253
254 atom = atom.replace_map(|term, _, out| {
255 let AtomView::Fun(f) = term else {
256 return;
257 };
258
259 let name = f.get_symbol();
260 if name != rep.symbol() {
261 return;
262 }
263
264 let mut fbuilder = FunctionBuilder::new(name);
265
266 if f.get_nargs() == 1 {
267 fbuilder = fbuilder.add_arg(4);
268 let arg = f.iter().next().unwrap();
269 let a = if let Ok(a) = i64::try_from(arg) {
270 if a < 0 {
271 (-a) as usize
272 } else {
273 return;
274 }
275 } else if let AtomView::Fun(f) = arg
276 && f.get_symbol() == self.dummy
277 && f.get_nargs() == 1
278 && let Ok(a) = usize::try_from(f.iter().next().unwrap())
279 {
280 a
281 } else {
282 return;
283 };
284 if a > max_dummy {
285 max_dummy = a;
286 }
287 fbuilder = fbuilder.add_arg(Atom::from(dummy(a)));
288 **out = fbuilder.finish();
289 }
290 });
291 }
292
293 let mink = Minkowski {}.new_rep(4);
294 let bis = Bispinor {}.new_rep(4);
295 let reps: Vec<_> = [
299 (
300 self.identity
301 .call_args([bis.pattern(W_.a_), bis.pattern(W_.b_)]),
302 bis.g(W_.a_, W_.b_),
303 ),
304 (
305 self.identityl
306 .call_args([mink.pattern(W_.a_), mink.pattern(W_.b_)]),
307 mink.g(W_.a_, W_.b_),
308 ),
309 (
310 self.metric
311 .call_args([mink.pattern(W_.a_), mink.pattern(W_.b_)]),
312 mink.g(W_.a_, W_.b_),
313 ),
314 (
315 self.gamma
316 .call_args([mink.pattern(W_.i_), bis.pattern(W_.a_), bis.pattern(W_.b_)]),
317 AGS.gamma
318 .call_args([bis.pattern(W_.a_), bis.pattern(W_.b_), mink.pattern(W_.i_)]),
319 ),
320 (
321 self.gamma5
322 .call_args([bis.pattern(W_.a_), bis.pattern(W_.b_)]),
323 AGS.gamma5
324 .call_args([bis.pattern(W_.a_), bis.pattern(W_.b_)]),
325 ),
326 (
327 self.projm
328 .call_args([bis.pattern(W_.a_), bis.pattern(W_.b_)]),
329 AGS.projm
330 .call_args([bis.pattern(W_.a_), bis.pattern(W_.b_)]),
331 ),
332 (
333 self.projp
334 .call_args([bis.pattern(W_.a_), bis.pattern(W_.b_)]),
335 AGS.projp
336 .call_args([bis.pattern(W_.a_), bis.pattern(W_.b_)]),
337 ),
338 (
339 self.sigma.call_args([
340 bis.pattern(W_.a_),
341 bis.pattern(W_.b_),
342 mink.pattern(W_.i_),
343 mink.pattern(W_.j_),
344 ]),
345 AGS.sigma.call_args([
346 bis.pattern(W_.a_),
347 bis.pattern(W_.b_),
348 mink.pattern(W_.i_),
349 mink.pattern(W_.j_),
350 ]),
351 ),
352 ]
353 .into_iter()
354 .map(|(pat, rep)| Replacement::new(pat.to_pattern(), rep))
355 .collect();
356
357 atom = atom.replace_multiple(&reps);
359
360 atom = atom.replace_map(|term, _, out| {
361 if let AtomView::Fun(f) = term
362 && f.get_symbol() == self.pslash
363 {
364 let mut gamma = FunctionBuilder::new(AGS.gamma);
365
366 let mut count = 0;
367
368 for a in f.iter() {
369 count += 1;
370 if count <= 2 {
371 gamma = gamma.add_arg(a);
372 } else if let Ok(i) = i64::try_from(a) {
373 max_dummy += 1;
374
375 let minki: Slot<Minkowski, Aind> = mink.slot(dummy(max_dummy));
376
377 gamma = gamma.add_arg(minki.to_atom());
378
379 **out = gamma.finish() * GS.emr_mom(momenta[i as usize].1, minki.to_atom());
380 return;
381 }
382 }
383
384 if count == 2 {
385 max_dummy += 1;
386
387 let minki: Slot<Minkowski, Aind> = mink.slot(dummy(max_dummy));
388
389 gamma = gamma.add_arg(minki.to_atom());
390
391 **out = gamma.finish() * GS.emr_mom(momenta[0].1, minki.to_atom());
392 }
393 }
394 });
395
396 for (i, (f, e)) in momenta.iter().enumerate() {
397 atom = atom
398 .replace(function!(UFO.momentum, (i + 1) as i64, W_.a_))
399 .with(match f {
400 Flow::Sink => GS.emr_mom(*e, W_.a_),
401 Flow::Source => -GS.emr_mom(*e, W_.a_),
402 })
403 }
404
405 atom = atom
406 .replace(function!(UFO.momentum, W_.a_))
407 .with(GS.emr_mom(momenta[0].1, W_.a_));
408
409 if atom
413 .replace(self.gamma.call_args([W_.a__]))
414 .match_iter()
415 .next()
416 .is_some()
417 || atom
418 .replace(self.sigma.call_args([W_.a___]))
419 .match_iter()
420 .next()
421 .is_some()
422 || atom
423 .replace(self.pslash.call_args([W_.a___]))
424 .match_iter()
425 .next()
426 .is_some()
427 || atom
428 .replace(self.gamma5.call_args([W_.a__]))
429 .match_iter()
430 .next()
431 .is_some()
432 || atom
433 .replace(self.projm.call_args([W_.a__]))
434 .match_iter()
435 .next()
436 .is_some()
437 || atom
438 .replace(self.projp.call_args([W_.a__]))
439 .match_iter()
440 .next()
441 .is_some()
442 || atom
443 .replace(self.charge_conj.call_args([W_.a__]))
444 .match_iter()
445 .next()
446 .is_some()
447 {
448 return Err(color_eyre::eyre::eyre!(
449 "Some spinor structures were not fully re-indexed: {}",
450 atom
451 ));
452 }
453
454 Ok(atom)
455 }
456
457 pub(crate) fn reindex_color(
458 &self,
459 slots: &[&OrderedStructure<LibraryRep, Aind>],
460 mut atom: Atom,
461 dummy: impl Fn(usize) -> Aind,
462 ) -> Result<Atom> {
463 let adj = ColorAdjoint {};
464 let fund = ColorFundamental {};
465 let antifund = ColorAntiFundamental {};
466 let sext = ColorSextet {};
467 let antisext = ColorAntiSextet {};
468 let reps: Vec<_> = [
475 (
476 self.t.call_args([W_.a_, W_.i_, W_.j_]),
477 self.t.call_args([
478 adj.to_symbolic([W_.a_]),
479 fund.to_symbolic([W_.i_]),
480 antifund.to_symbolic([W_.j_]),
481 ]),
482 ),
483 (
484 self.f.call_args([W_.a_, W_.b_, W_.c_]),
485 self.f.call_args([
486 adj.to_symbolic([W_.a_]),
487 adj.to_symbolic([W_.b_]),
488 adj.to_symbolic([W_.c_]),
489 ]),
490 ),
491 (
492 self.d.call_args([W_.a_, W_.b_, W_.c_]),
493 self.d.call_args([
494 adj.to_symbolic([W_.a_]),
495 adj.to_symbolic([W_.b_]),
496 adj.to_symbolic([W_.c_]),
497 ]),
498 ),
499 (
500 self.levicivita.call_args([W_.i_, W_.j_, W_.k_]),
501 self.levicivita.call_args([
502 fund.to_symbolic([W_.i_]),
503 fund.to_symbolic([W_.j_]),
504 fund.to_symbolic([W_.k_]),
505 ]),
506 ),
507 (
508 self.antilevicivita.call_args([W_.i_, W_.j_, W_.k_]),
509 self.levicivita.call_args([
510 antifund.to_symbolic([W_.i_]),
511 antifund.to_symbolic([W_.j_]),
512 antifund.to_symbolic([W_.k_]),
513 ]),
514 ),
515 (
516 self.t6.call_args([W_.a_, W_.i_, W_.j_]),
517 self.t6.call_args([
518 adj.to_symbolic([W_.a_]),
519 sext.to_symbolic([W_.i_]),
520 antisext.to_symbolic([W_.j_]),
521 ]),
522 ),
523 (
524 self.k6.call_args([W_.a_, W_.i_, W_.j_]),
525 self.k6.call_args([
526 sext.to_symbolic([W_.a_]),
527 antifund.to_symbolic([W_.i_]),
528 antifund.to_symbolic([W_.j_]),
529 ]),
530 ),
531 (
532 self.k6bar.call_args([W_.a_, W_.i_, W_.j_]),
533 self.k6bar.call_args([
534 antisext.to_symbolic([W_.a_]),
535 fund.to_symbolic([W_.i_]),
536 fund.to_symbolic([W_.j_]),
537 ]),
538 ),
539 ]
540 .into_iter()
541 .map(|(pat, rep)| Replacement::new(pat.to_pattern(), rep))
542 .collect();
543
544 atom = atom.replace_multiple(&reps);
546
547 atom = atom
550 .replace(
551 function!(self.identity, W_.a_, W_.b_)
552 * function!(W_.f_, W_.a___, function!(W_.g_, W_.a_), W_.b___),
553 )
554 .repeat()
555 .with(
556 self.identity.call((W_.g_.call_args([W_.a_]), W_.b_))
557 * W_.f_.call((W_.a___, W_.g_.call_args([W_.a_]), W_.b___)),
558 );
559
560 for (i, s) in slots.iter().enumerate() {
565 let i = i + 1;
566 for (shift, r) in s.external_structure_iter().enumerate() {
567 let rep = r.rep_name();
568 let wrappedi = self.idx(i, shift + 1);
569 let wrappedi = wrappedi.as_view();
570
571 atom = atom
573 .replace(rep.to_symbolic([wrappedi]))
574 .min_level(1)
575 .max_level(Some(1))
576 .with(r.to_atom())
577 .replace(self.identity.call((wrappedi, W_.i_)))
578 .with(self.identity.call((r.to_atom(), W_.i_)))
579 .replace(self.identity.call((W_.i_, wrappedi)))
580 .with(self.identity.call((W_.i_, r.to_atom())))
581 .replace(rep.to_symbolic([i]))
582 .min_level(1)
583 .max_level(Some(1))
584 .with(r.to_atom())
585 .replace(self.identity.call((i, W_.i_)))
586 .with(self.identity.call((r.to_atom(), W_.i_)))
587 .replace(self.identity.call((W_.i_, i)))
588 .with(self.identity.call((W_.i_, r.to_atom())));
589 }
590 }
591
592 for rep in [
596 LibraryRep::from(adj).new_rep(8),
597 fund.new_rep(3).to_lib(),
598 antifund.new_rep(3).to_lib(),
599 sext.new_rep(6).to_lib(),
600 antisext.new_rep(6).to_lib(),
601 ] {
602 let mut max_dummy = 0;
603
604 atom = atom.replace_map(|term, _, out| {
605 let AtomView::Fun(f) = term else {
606 return;
607 };
608
609 let name = f.get_symbol();
610 if name != rep.rep.symbol() {
611 return;
612 }
613
614 let mut fbuilder = FunctionBuilder::new(name);
615
616 if f.get_nargs() == 1 {
617 fbuilder = fbuilder.add_arg(rep.dim.to_symbolic());
618 let arg = f.iter().next().unwrap();
619 let a = if let Ok(a) = i64::try_from(arg) {
620 if a < 0 {
621 (-a) as usize
622 } else {
623 return;
624 }
625 } else if let AtomView::Fun(f) = arg
626 && f.get_symbol() == self.dummy
627 && f.get_nargs() == 1
628 && let Ok(a) = usize::try_from(f.iter().next().unwrap())
629 {
630 a
631 } else {
632 return;
633 };
634 if a > max_dummy {
635 max_dummy = a;
636 }
637 fbuilder = fbuilder.add_arg(Atom::from(dummy(a)));
638 **out = fbuilder.finish();
639 }
640 });
641 }
642
643 let reps: Vec<_> = [
644 (
645 self.identity.call_args([W_.a_, W_.b_]),
646 ETS.metric.call_args([W_.a_, W_.b_]),
647 ),
648 (
649 self.t.call_args([
650 adj.to_symbolic([W_.a__]),
651 fund.to_symbolic([W_.i__]),
652 antifund.to_symbolic([W_.j__]),
653 ]),
654 CS.t.call_args([
655 adj.to_symbolic([W_.a__]),
656 fund.to_symbolic([W_.i__]),
657 antifund.to_symbolic([W_.j__]),
658 ]),
659 ),
660 (
661 self.f.call_args([
662 adj.to_symbolic([W_.a__]),
663 adj.to_symbolic([W_.b__]),
664 adj.to_symbolic([W_.c__]),
665 ]),
666 CS.f.call_args([
667 adj.to_symbolic([W_.a__]),
668 adj.to_symbolic([W_.b__]),
669 adj.to_symbolic([W_.c__]),
670 ]),
671 ),
672 ]
673 .into_iter()
674 .map(|(pat, rep)| Replacement::new(pat.to_pattern(), rep))
675 .collect();
676
677 atom = atom.replace_multiple(&reps);
678
679 if atom
680 .replace(self.t.call_args([W_.a___]))
681 .match_iter()
682 .next()
683 .is_some()
684 || atom
685 .replace(self.f.call_args([W_.a___]))
686 .match_iter()
687 .next()
688 .is_some()
689 || atom
690 .replace(self.d.call_args([W_.a___]))
691 .match_iter()
692 .next()
693 .is_some()
694 || atom
695 .replace(self.levicivita.call_args([W_.a___]))
696 .match_iter()
697 .next()
698 .is_some()
699 || atom
700 .replace(self.antilevicivita.call_args([W_.a___]))
701 .match_iter()
702 .next()
703 .is_some()
704 || atom
705 .replace(self.t6.call_args([W_.a___]))
706 .match_iter()
707 .next()
708 .is_some()
709 || atom
710 .replace(self.k6.call_args([W_.a___]))
711 .match_iter()
712 .next()
713 .is_some()
714 || atom
715 .replace(self.k6bar.call_args([W_.a___]))
716 .match_iter()
717 .next()
718 .is_some()
719 {
720 return Err(color_eyre::eyre::eyre!(
721 "Some color structures were not fully re-indexed: {}",
722 atom
723 ));
724 }
725
726 Ok(atom)
728 }
729}
730
731#[cfg(test)]
732pub mod test {
733
734 #[test]
735 fn ufo_spin_processing() {}
736}