Skip to main content

gammalooprs/momentum/
signature.rs

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
21// manual implementations because TiVec is not Encode/Devode
22impl<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        // println!("i{i},j{j}");÷
60        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            // println!("mom: {mom} {s}");
261
262            rep += *s * mom;
263        }
264
265        // println!("rep{rep}");
266
267        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        // self.panic_validate_basis(basis);
330        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                // Initialize the result based on the first non-zero sign
379                let mut result = item.ref_zero();
380                match sign {
381                    SignOrZero::Zero => {
382                        panic!("unreachable");
383                        // return None;
384                    }
385                    SignOrZero::Plus => {
386                        result += item;
387                    }
388                    SignOrZero::Minus => {
389                        result -= item;
390                    }
391                }
392
393                // Continue processing the rest of the iterator
394                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        // Return None if no non-zero sign was found
411        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    /// Canonization function to compare two signatures up to an overall sign,
431    /// If the first nonzero entry is positive, it will return itself,
432    /// otherwise it will return the negative of itself.
433    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    /// Usefull for debugging
557    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}