1#![allow(dead_code)]
2
3use crate::momentum::sample::{
4 ExternalFourMomenta, ExternalIndex, ExternalThreeMomenta, LoopIndex, LoopMomenta,
5};
6use crate::momentum::{FourMomentum, SignOrZero, ThreeMomentum};
7use crate::utils::{F, FloatLike, Length};
8use bincode::{BorrowDecode, Decode, Encode};
9use serde::{Deserialize, Serialize};
10use spenso::algebra::algebraic_traits::RefZero;
11use std::fmt::Display;
12use std::ops::{Add, AddAssign, Index, IndexMut, Neg, SubAssign};
13use symbolica::atom::{Atom, AtomOrView, FunctionBuilder, Symbol};
14use typed_index_collections::TiVec;
15
16#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Ord, Hash)]
17pub struct SignatureLike<T: From<usize>>(TiVec<T, SignOrZero>);
18pub type LoopSignature = SignatureLike<LoopIndex>;
19pub type ExternalSignature = SignatureLike<ExternalIndex>;
20
21impl<T: Encode + From<usize>> Encode for SignatureLike<T> {
23 fn encode<E: bincode::enc::Encoder>(
24 &self,
25 encoder: &mut E,
26 ) -> Result<(), bincode::error::EncodeError> {
27 self.0.raw.encode(encoder)
28 }
29}
30
31impl<'de, Context, T: Decode<Context> + From<usize>> BorrowDecode<'de, Context>
32 for SignatureLike<T>
33{
34 fn borrow_decode<D: bincode::de::Decoder>(
35 decoder: &mut D,
36 ) -> Result<Self, bincode::error::DecodeError> {
37 Ok(SignatureLike(Vec::decode(decoder)?.into()))
38 }
39}
40
41impl<Context, T: Decode<Context> + From<usize>> Decode<Context> for SignatureLike<T> {
42 fn decode<D: bincode::de::Decoder>(
43 decoder: &mut D,
44 ) -> Result<Self, bincode::error::DecodeError> {
45 Ok(SignatureLike(Vec::decode(decoder)?.into()))
46 }
47}
48
49#[derive(
50 Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Ord, Hash, Encode, Decode,
51)]
52pub struct LoopExtSignature {
53 pub internal: LoopSignature,
54 pub external: ExternalSignature,
55}
56
57impl LoopExtSignature {
58 pub(crate) fn swap_loops(&mut self, i: LoopIndex, j: LoopIndex) {
59 if !self.internal.is_empty() {
61 self.internal.0.swap(i, j);
62 }
63 }
64
65 pub(crate) fn put_loop_to_ext(&mut self, l: LoopIndex) {
66 let a = self.internal.0.remove(l);
67 self.external.0.push(a);
68 }
69 pub(crate) fn swap_external(&mut self, i: ExternalIndex, j: ExternalIndex) {
70 self.external.0.swap(i, j);
71 }
72
73 pub(crate) fn loop_atom<'a, I>(
74 &self,
75 mom_symbol: Symbol,
76 additional_args: &'a [I],
77 id_map: impl Fn(LoopIndex) -> Atom,
78 ) -> Atom
79 where
80 &'a I: Into<AtomOrView<'a>>,
81 {
82 self.internal.atom(mom_symbol, additional_args, id_map)
83 }
84
85 pub(crate) fn ext_atom<'a, I>(
86 &self,
87 mom_symbol: Symbol,
88 additional_args: &'a [I],
89 id_map: impl Fn(ExternalIndex) -> Atom,
90 ) -> Atom
91 where
92 &'a I: Into<AtomOrView<'a>>,
93 {
94 self.external.atom(mom_symbol, additional_args, id_map)
95 }
96
97 pub(crate) fn equality_up_to_sign(&self, other: &Self) -> bool {
98 self.internal.first_abs() == other.internal.first_abs()
99 && self.external.first_abs() == other.external.first_abs()
100 }
101}
102
103impl From<(Vec<isize>, Vec<isize>)> for LoopExtSignature {
104 fn from(value: (Vec<isize>, Vec<isize>)) -> Self {
105 Self {
106 internal: LoopSignature::from_iter(value.0),
107 external: ExternalSignature::from_iter(value.1),
108 }
109 }
110}
111
112impl<T> Index<T> for SignatureLike<T>
113where
114 usize: From<T>,
115 T: From<usize>,
116{
117 type Output = SignOrZero;
118
119 fn index(&self, index: T) -> &Self::Output {
120 &self.0[index]
121 }
122}
123
124impl<T> IndexMut<T> for SignatureLike<T>
125where
126 usize: From<T>,
127 T: From<usize>,
128{
129 fn index_mut(&mut self, index: T) -> &mut Self::Output {
130 &mut self.0[index]
131 }
132}
133
134impl<T> Display for SignatureLike<T>
135where
136 T: From<usize>,
137{
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 for sign in &self.0 {
140 write!(f, "{}", sign)?;
141 }
142 Ok(())
143 }
144}
145
146impl<T: From<usize>> Default for SignatureLike<T> {
147 fn default() -> Self {
148 SignatureLike(TiVec::new())
149 }
150}
151
152impl<T> FromIterator<SignOrZero> for SignatureLike<T>
153where
154 T: From<usize>,
155{
156 fn from_iter<I: IntoIterator<Item = SignOrZero>>(iter: I) -> Self {
157 SignatureLike(iter.into_iter().collect())
158 }
159}
160
161impl<T> FromIterator<i8> for SignatureLike<T>
162where
163 T: From<usize>,
164{
165 fn from_iter<I: IntoIterator<Item = i8>>(iter: I) -> Self {
166 SignatureLike(
167 iter.into_iter()
168 .map(|x| match x {
169 0 => SignOrZero::Zero,
170 1 => SignOrZero::Plus,
171 -1 => SignOrZero::Minus,
172 _ => panic!("Invalid value for Signature"),
173 })
174 .collect(),
175 )
176 }
177}
178
179impl<T> FromIterator<isize> for SignatureLike<T>
180where
181 T: From<usize>,
182{
183 fn from_iter<I: IntoIterator<Item = isize>>(iter: I) -> Self {
184 SignatureLike(
185 iter.into_iter()
186 .map(|x| match x {
187 0 => SignOrZero::Zero,
188 1 => SignOrZero::Plus,
189 -1 => SignOrZero::Minus,
190 _ => panic!("Invalid value for Signature"),
191 })
192 .collect(),
193 )
194 }
195}
196
197impl<T> From<Vec<i8>> for SignatureLike<T>
198where
199 T: From<usize>,
200{
201 fn from(value: Vec<i8>) -> Self {
202 SignatureLike::from_iter(value)
203 }
204}
205
206impl<T> IntoIterator for SignatureLike<T>
207where
208 T: From<usize>,
209{
210 type Item = SignOrZero;
211 type IntoIter = std::vec::IntoIter<Self::Item>;
212
213 fn into_iter(self) -> Self::IntoIter {
214 self.0.into_iter()
215 }
216}
217
218impl<'a, T> IntoIterator for &'a SignatureLike<T>
219where
220 T: From<usize>,
221{
222 type Item = SignOrZero;
223 type IntoIter = std::iter::Copied<std::slice::Iter<'a, Self::Item>>;
224
225 fn into_iter(self) -> Self::IntoIter {
226 self.0.iter().copied()
227 }
228}
229
230impl<T> AddAssign<()> for SignatureLike<T>
231where
232 T: From<usize> + Copy,
233 usize: From<T>,
234{
235 fn add_assign(&mut self, _rhs: ()) {
236 self.0.push(SignOrZero::Plus);
237 }
238}
239
240impl<T> SignatureLike<T>
241where
242 T: From<usize> + Copy,
243 usize: From<T>,
244{
245 pub(crate) fn atom<'a, I>(
246 &self,
247 mom_symbol: Symbol,
248 additional_args: &'a [I],
249 id_map: impl Fn(T) -> Atom,
250 ) -> Atom
251 where
252 &'a I: Into<AtomOrView<'a>>,
253 {
254 let mut rep = Atom::Zero;
255 for (l, s) in self.iter_enumerated() {
256 let mom = FunctionBuilder::new(mom_symbol)
257 .add_arg(id_map(l))
258 .add_args(additional_args)
259 .finish();
260 rep += *s * mom;
263 }
264
265 rep
268 }
269
270 pub(crate) fn iter_enumerated(&self) -> impl Iterator<Item = (T, &SignOrZero)> {
271 self.0.iter_enumerated()
272 }
273 pub(crate) fn validate_basis<B>(&self, basis: &[B]) -> bool {
274 self.len() == basis.len()
275 }
276
277 pub(crate) fn sum(&mut self, other: &Self) {
278 for (i, sign) in other.iter_enumerated() {
279 match (self[i], sign) {
280 (SignOrZero::Zero, SignOrZero::Zero) => self.0[i] = SignOrZero::Zero,
281 (SignOrZero::Zero, SignOrZero::Plus) => self.0[i] = SignOrZero::Plus,
282 (SignOrZero::Zero, SignOrZero::Minus) => self.0[i] = SignOrZero::Minus,
283 (SignOrZero::Plus, SignOrZero::Zero) => self.0[i] = SignOrZero::Plus,
284 (SignOrZero::Plus, SignOrZero::Plus) => panic!("cannot add two positive signs"),
285 (SignOrZero::Plus, SignOrZero::Minus) => self.0[i] = SignOrZero::Zero,
286 (SignOrZero::Minus, SignOrZero::Zero) => self.0[i] = SignOrZero::Minus,
287 (SignOrZero::Minus, SignOrZero::Plus) => self.0[i] = SignOrZero::Zero,
288 (SignOrZero::Minus, SignOrZero::Minus) => panic!("cannot add two negative signs"),
289 }
290 }
291 }
292
293 pub(crate) fn panic_validate_basis<B>(&self, basis: &[B]) {
294 if !self.validate_basis(basis) {
295 panic!(
296 "Invalid basis for Signature, expected length {}, got length {}",
297 self.len(),
298 basis.len()
299 );
300 }
301 }
302
303 pub(crate) fn to_momtrop_format(&self) -> Vec<isize> {
304 self.0
305 .iter()
306 .map(|x| match x {
307 SignOrZero::Zero => 0,
308 SignOrZero::Plus => 1,
309 SignOrZero::Minus => -1,
310 })
311 .collect()
312 }
313 pub(crate) fn len(&self) -> usize {
314 self.0.len()
315 }
316
317 pub fn iter(&'_ self) -> std::slice::Iter<'_, SignOrZero> {
318 self.0.iter()
319 }
320
321 pub(crate) fn is_empty(&self) -> bool {
322 self.0.is_empty()
323 }
324
325 pub(crate) fn apply<B>(&self, basis: &[B]) -> B
326 where
327 B: RefZero + Clone + Neg<Output = B> + AddAssign<B>,
328 {
329 let mut result = basis[0].ref_zero();
331 for (&sign, t) in self.0.iter().zip(basis.iter().cloned()) {
332 result += sign * t;
333 }
334 result
335 }
336
337 pub(crate) fn try_apply<B>(&self, basis: &[B]) -> Option<B>
338 where
339 B: Clone + Neg<Output = B> + Add<B, Output = B>,
340 {
341 self.0
342 .iter()
343 .zip(basis.iter().cloned())
344 .filter_map(|(sign, t)| match sign {
345 SignOrZero::Zero => None,
346 SignOrZero::Plus => Some(t),
347 SignOrZero::Minus => Some(-t),
348 })
349 .reduce(|sum, t| sum + t)
350 }
351
352 pub(crate) fn apply_typed<O, I, V>(&self, basis: &V) -> O
353 where
354 V: Index<I, Output = O>,
355 O: RefZero + Neg<Output = O> + AddAssign<O> + Clone,
356 I: From<usize>,
357 usize: From<I>,
358 {
359 let mut result = basis[I::from(0)].ref_zero();
360 for (&sign, i) in self.0.iter().zip(0..) {
361 result += sign * basis[I::from(i)].clone();
362 }
363
364 result
365 }
366
367 pub(crate) fn apply_iter<I, O>(&self, basis: I) -> Option<O>
368 where
369 I: IntoIterator,
370 I::Item: RefZero<O>,
371 O: Clone + SubAssign<I::Item> + AddAssign<I::Item>,
372 {
373 let mut basis_iter = basis.into_iter();
374 let mut signature_iter = self.into_iter();
375
376 while let (Some(sign), Some(item)) = (signature_iter.next(), basis_iter.next()) {
377 if sign.is_sign() {
378 let mut result = item.ref_zero();
380 match sign {
381 SignOrZero::Zero => {
382 panic!("unreachable");
383 }
385 SignOrZero::Plus => {
386 result += item;
387 }
388 SignOrZero::Minus => {
389 result -= item;
390 }
391 }
392
393 while let (Some(sign), Some(item)) = (signature_iter.next(), basis_iter.next()) {
395 match sign {
396 SignOrZero::Zero => {}
397 SignOrZero::Plus => {
398 result += item;
399 }
400 SignOrZero::Minus => {
401 result -= item;
402 }
403 }
404 }
405
406 return Some(result);
407 }
408 }
409
410 None
412 }
413
414 pub(crate) fn label_with(&self, label: &str) -> String {
415 let mut result = String::new();
416 let mut first = true;
417 for (i, sign) in self.0.iter().enumerate() {
418 if !first {
419 result.push_str(&sign.to_string());
420 } else {
421 first = false;
422 }
423 if sign.is_sign() {
424 result.push_str(&format!("{}_{}", label, i));
425 }
426 }
427 result
428 }
429
430 pub(crate) fn first_abs(&self) -> Self {
434 let sign = self.iter().find(|x| x.is_sign());
435
436 if let Some(sign) = sign {
437 if sign.is_positive() {
438 self.clone()
439 } else {
440 self.iter().map(|x| -*x).collect()
441 }
442 } else {
443 self.clone()
444 }
445 }
446
447 pub(crate) fn pop(&mut self) -> Option<SignOrZero> {
448 self.0.pop()
449 }
450}
451
452#[test]
453fn test_signature() {
454 use crate::momentum::FourMomentum;
455 let sig = LoopSignature::from_iter(vec![SignOrZero::Plus, SignOrZero::Minus]);
456 let basis: Vec<i32> = vec![1, 2];
457 assert_eq!(sig.apply(&basis), 1 - 2);
458 assert_eq!(sig.apply_iter(basis.iter()), Some(-1));
459
460 let basis: [FourMomentum<i32>; 4] = [
461 FourMomentum::from_args(1, 1, 0, 0),
462 FourMomentum::from_args(1, 0, 1, 0),
463 FourMomentum::from_args(1, 0, 0, 1),
464 FourMomentum::from_args(1, 1, 1, 1),
465 ];
466
467 let sig = LoopSignature::from_iter(vec![
468 SignOrZero::Plus,
469 SignOrZero::Minus,
470 SignOrZero::Zero,
471 SignOrZero::Plus,
472 ]);
473
474 assert_eq!(sig.apply(&basis), FourMomentum::from_args(1, 2, 0, 1));
475 let sig = ExternalSignature::from_iter(vec![
476 SignOrZero::Zero,
477 SignOrZero::Zero,
478 SignOrZero::Zero,
479 SignOrZero::Zero,
480 ]);
481 assert_eq!(sig.apply_iter(basis.iter()), None);
482 let sig = ExternalSignature::from_iter(vec![
483 SignOrZero::Zero,
484 SignOrZero::Zero,
485 SignOrZero::Zero,
486 SignOrZero::Minus,
487 ]);
488 assert_eq!(sig.apply_iter(basis.iter()), Some(-basis[3]));
489}
490
491impl LoopExtSignature {
492 pub(crate) fn compute_momentum_untyped<'a, 'b: 'a, T>(
493 &self,
494 loop_moms: &'a [T],
495 external_moms: &'b [T],
496 ) -> T
497 where
498 T: RefZero + Clone + Neg<Output = T> + AddAssign<T>,
499 {
500 if loop_moms.is_empty() {
501 return self.external.apply(external_moms);
502 }
503 if external_moms.is_empty() {
504 return self.internal.apply(loop_moms);
505 }
506 let mut res = self.internal.apply(loop_moms);
507 res += self.external.apply(external_moms);
508 res
509 }
510
511 pub(crate) fn try_compute_momentum<'a, 'b: 'a, T>(
512 &self,
513 loop_moms: &'a [T],
514 external_moms: &'b [T],
515 ) -> Option<T>
516 where
517 T: Clone + Neg<Output = T> + Add<T, Output = T>,
518 {
519 let loop_part = self.internal.try_apply(loop_moms);
520 let external_part = self.external.try_apply(external_moms);
521
522 match (loop_part, external_part) {
523 (Some(l), Some(e)) => Some(l + e),
524 (Some(l), None) => Some(l),
525 (None, Some(e)) => Some(e),
526 (None, None) => None,
527 }
528 }
529
530 pub(crate) fn compute_momentum<L, E, M>(&self, loop_momenta: &L, external_momenta: &E) -> M
531 where
532 M: RefZero + Clone + Neg<Output = M> + AddAssign<M>,
533 L: Index<LoopIndex, Output = M> + Length,
534 E: Index<ExternalIndex, Output = M> + Length,
535 {
536 if loop_momenta.is_empty() {
537 return self.external.apply_typed(external_momenta);
538 }
539
540 if external_momenta.is_empty() {
541 return self.internal.apply_typed(loop_momenta);
542 }
543
544 let mut res = self.internal.apply_typed(loop_momenta);
545 res += self.external.apply_typed(external_momenta);
546 res
547 }
548
549 pub(crate) fn to_momtrop_format(&self) -> (Vec<isize>, Vec<isize>) {
550 (
551 self.internal.to_momtrop_format(),
552 self.external.to_momtrop_format(),
553 )
554 }
555
556 pub(crate) fn format_momentum(&self) -> String {
558 let mut res = String::new();
559 let mut first = true;
560
561 for (i, sign) in (&self.internal).into_iter().enumerate() {
562 if !first {
563 res.push_str(&sign.to_string());
564 } else {
565 first = false;
566 }
567 if sign.is_sign() {
568 res.push_str(&format!("k_{}", i));
569 }
570 }
571
572 for (i, sign) in (&self.external).into_iter().enumerate() {
573 if !first {
574 res.push_str(&sign.to_string());
575 } else {
576 first = false;
577 }
578 if sign.is_sign() {
579 res.push_str(&format!("l_{}", i));
580 }
581 }
582 res
583 }
584
585 #[allow(unused)]
586 pub(crate) fn compute_four_momentum_from_three<T: FloatLike>(
587 &self,
588 loop_moms: &LoopMomenta<F<T>>,
589 external_moms: &ExternalFourMomenta<F<T>>,
590 ) -> FourMomentum<F<T>> {
591 let loop_moms = loop_moms
592 .iter()
593 .map(|m| m.clone().into_on_shell_four_momentum(None))
594 .collect::<TiVec<LoopIndex, _>>();
595
596 self.compute_momentum(&loop_moms, external_moms)
597 }
598
599 pub(crate) fn compute_three_momentum_from_four<T: FloatLike>(
600 &self,
601 loop_moms: &LoopMomenta<F<T>>,
602 external_moms: &ExternalFourMomenta<F<T>>,
603 ) -> ThreeMomentum<F<T>> {
604 let external_moms: ExternalThreeMomenta<F<T>> =
605 external_moms.iter().map(|m| m.spatial.clone()).collect();
606 self.compute_momentum(loop_moms, &external_moms)
607 }
608}