Skip to main content

gammalooprs/utils/
symbolica_ext.rs

1use std::{
2    fmt::Display,
3    ops::{Deref, DerefMut},
4    sync::LazyLock,
5};
6
7use bincode_trait_derive::{Decode, Encode};
8use linnet::half_edge::involution::EdgeIndex;
9use schemars::{JsonSchema, json_schema};
10use serde::{Deserialize, Serialize};
11use symbolica::{
12    atom::{Atom, AtomCore, AtomView, Symbol},
13    domains::{
14        algebraic_number::AlgebraicExtension,
15        integer::IntegerRing,
16        rational::{FractionField, Q},
17    },
18    function, parse,
19    poly::polynomial::PolynomialRing,
20    printer::{PrintMode, PrintOptions},
21    symbol,
22};
23
24use crate::GammaLoopContext;
25
26use super::{GS, W_};
27
28pub static Q_I: LazyLock<AlgebraicExtension<FractionField<IntegerRing>>> =
29    LazyLock::new(|| AlgebraicExtension::new_complex(Q));
30static RAW_UFO_MOMENTUM: LazyLock<Symbol> = LazyLock::new(|| symbol!("UFO::P"));
31static RAW_UFO_PSLASH: LazyLock<Symbol> = LazyLock::new(|| symbol!("UFO::PSlash"));
32
33pub static COMPLEXRATPOLYFIELD: LazyLock<
34    FractionField<PolynomialRing<AlgebraicExtension<FractionField<IntegerRing>>, u16>>,
35> = LazyLock::new(|| FractionField::new(PolynomialRing::<_, u16>::new(Q_I.clone())));
36
37pub static LOGPRINTOPTS: LazyLock<PrintOptions> = LazyLock::new(|| PrintOptions {
38    hide_all_namespaces: true,
39    color_namespace: false,
40    color_builtin_symbols: false,
41    color_top_level_sum: false,
42    terms_on_new_line: false,
43    print_ring: false,
44    include_attributes: false,
45    symmetric_representation_for_finite_field: false,
46    explicit_rational_polynomial: false,
47    number_thousands_separator: None,
48    multiplication_operator: '*',
49    double_star_for_exponentiation: false,
50    num_exp_as_superscript: false,
51    mode: PrintMode::Symbolica,
52    precision: None,
53    pretty_matrix: false,
54    max_terms: None,
55    custom_print_mode: Default::default(),
56    hide_namespace: Some(std::borrow::Cow::Borrowed("gammalooprs")),
57    ..PrintOptions::new()
58});
59
60#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Encode, Decode)]
61#[trait_decode(trait = GammaLoopContext)]
62pub struct StringSerializedAtom(pub Atom);
63
64impl JsonSchema for StringSerializedAtom {
65    fn schema_name() -> std::borrow::Cow<'static, str> {
66        "ParseableAtom".into()
67    }
68
69    fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
70        json_schema!({
71            "description": "An atom that is serialized as a string. Do not make any assumptions about the state",
72            "type": ["string"]
73        })
74    }
75}
76
77impl Deref for StringSerializedAtom {
78    type Target = Atom;
79
80    fn deref(&self) -> &Self::Target {
81        &self.0
82    }
83}
84
85impl DerefMut for StringSerializedAtom {
86    fn deref_mut(&mut self) -> &mut Self::Target {
87        &mut self.0
88    }
89}
90
91impl Display for StringSerializedAtom {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        write!(f, "{}", self.0)
94    }
95}
96
97impl Serialize for StringSerializedAtom {
98    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
99    where
100        S: serde::Serializer,
101    {
102        self.0.to_canonical_string().serialize(serializer)
103    }
104}
105
106impl<'de> Deserialize<'de> for StringSerializedAtom {
107    fn deserialize<D>(deserializer: D) -> std::result::Result<StringSerializedAtom, D::Error>
108    where
109        D: serde::Deserializer<'de>,
110    {
111        Ok(StringSerializedAtom(parse!(String::deserialize(
112            deserializer
113        )?)))
114    }
115}
116
117pub trait DOD {
118    /// Rescales momentum of edge `eid`, and computes the leading scaling.
119    fn edge_dod(&self, eid: EdgeIndex) -> i32;
120
121    /// Rescales all momenta, and computes the leading scaling.
122    fn all_dod(&self) -> i32;
123
124    fn trailing_exponent(&self) -> i32;
125}
126
127impl DOD for Atom {
128    fn edge_dod(&self, eid: EdgeIndex) -> i32 {
129        self.as_view().edge_dod(eid)
130    }
131
132    fn all_dod(&self) -> i32 {
133        self.as_view().all_dod()
134    }
135
136    fn trailing_exponent(&self) -> i32 {
137        self.as_view().trailing_exponent()
138    }
139}
140
141impl DOD for AtomView<'_> {
142    fn edge_dod(&self, eid: EdgeIndex) -> i32 {
143        self.replace(GS.emr_mom(eid, W_.a___))
144            .with(GS.emr_mom(eid, W_.a___) / GS.rescale)
145            .trailing_exponent()
146    }
147
148    fn all_dod(&self) -> i32 {
149        self.replace(function!(GS.emr_mom, W_.a___))
150            .with(function!(GS.emr_mom, W_.a___) / GS.rescale)
151            .replace(function!(*RAW_UFO_MOMENTUM, W_.a___))
152            .with(function!(*RAW_UFO_MOMENTUM, W_.a___) / GS.rescale)
153            .replace(function!(*RAW_UFO_PSLASH, W_.a___))
154            .with(function!(*RAW_UFO_PSLASH, W_.a___) / GS.rescale)
155            .trailing_exponent()
156    }
157
158    fn trailing_exponent(&self) -> i32 {
159        let series = self.series(GS.rescale, Atom::Zero, 1).unwrap();
160        let dod = series.get_trailing_exponent();
161
162        if dod.is_integer() {
163            -(dod.numerator().to_i64().unwrap() as i32)
164        } else {
165            panic!("{dod} for {self}")
166        }
167    }
168}
169
170#[test]
171fn test_dod() {
172    let (e1, e2) = (EdgeIndex(1), EdgeIndex(2));
173
174    let atom = (GS.emr_mom(e1, Atom::Zero) * GS.emr_mom(e2, Atom::Zero)
175        + GS.emr_mom(e2, Atom::Zero))
176        / (GS.emr_mom(e1, Atom::Zero) * GS.emr_mom(e1, Atom::Zero));
177
178    let atom2 =
179        Atom::num(1) / (GS.emr_mom(e1, Atom::Zero) * GS.emr_mom(e1, Atom::Zero) + parse!("m"));
180    let atom3 =
181        GS.emr_mom(e1, Atom::Zero) * GS.emr_mom(e2, Atom::Zero) + GS.emr_mom(e2, Atom::Zero);
182
183    assert_eq!(-1, atom.edge_dod(e1));
184    assert_eq!(1, atom.edge_dod(e2));
185    assert_eq!(-2, atom2.edge_dod(e1));
186    assert_eq!(1, atom3.edge_dod(e1));
187    assert_eq!(1, atom3.edge_dod(e2));
188}