Skip to main content

gammalooprs/uv/
uv_graph.rs

1use std::{cell::RefCell, collections::BTreeSet, ops::Deref};
2
3use ahash::{AHashMap, AHashSet};
4use idenso::shorthands::schoonschip::Schoonschip;
5use linnet::half_edge::{
6    HedgeGraph, PowersetIterator,
7    involution::{Flow, Hedge, HedgePair},
8    subgraph::{
9        Cycle, InternalSubGraph, ModifySubSet, PairwiseSubSetOps, SuBitGraph, SubGraphLike,
10        SubGraphOps, SubSetLike, SubSetOps, subset::SubSet,
11    },
12};
13use symbolica::{
14    atom::{Atom, AtomCore, Symbol},
15    domains::atom::AtomField,
16    function,
17    poly::series::Series,
18};
19use tracing::debug;
20
21use crate::{
22    graph::{Edge, FeynmanGraph, Graph, HedgeData, LMBext, LoopMomentumBasis, Vertex},
23    integrands::process::param_builder::ParamBuilderGraph,
24    momentum::sample::LoopIndex,
25    numerator::{AppliedFeynmanRule, Numerator},
26    utils::{GS, W_, symbolica_ext::DOD},
27    uv::{ApproximationType, UVgenerationSettings, settings::CTIdentifier},
28};
29
30use super::{Spinney, Wood, spenso_lor_atom};
31
32pub trait UltravioletGraph: LMBext + FeynmanGraph + ParamBuilderGraph {
33    fn n_loops<S: SubGraphLike, E, V, H>(&self, subgraph: &S) -> usize
34    where
35        Self: AsRef<HedgeGraph<E, V, H>>,
36    {
37        self.as_ref().cyclotomatic_number(subgraph)
38    }
39
40    // fn concretize_spinney<S: SubGraphLike<Base = SuBitGraph>>(&self, subgraph: &S) -> Graph;
41
42    fn dummy_less_full_crown<S: SubGraphLike>(&self, subgraph: &S) -> S::Base
43    where
44        S::Base: ModifySubSet<Hedge> + SubGraphOps;
45
46    ///Get the numerator of the graph.
47    /// If multiply_prefactor is true, the numerator is multiplied by the global  prefactor. (just num not projector)
48    fn numerator<S: SubGraphLike + SubSetOps>(
49        &self,
50        subgraph: &S,
51        without: &S,
52    ) -> Numerator<AppliedFeynmanRule>;
53    fn denominator<S: SubGraphLike, T: Fn(&Edge) -> isize>(
54        &self,
55        subgraph: &S,
56        edge_powers: T,
57    ) -> Atom;
58
59    fn boundary_pdg_set<E: UVE, V, H, S: SubGraphLike<Base = SuBitGraph>>(
60        &self,
61        subgraph: &S,
62    ) -> BTreeSet<isize>
63    where
64        Self: AsRef<HedgeGraph<E, V, H>>,
65    {
66        let graph = self.as_ref();
67        graph
68            .full_crown(subgraph)
69            .included_iter()
70            .filter_map(|hedge| {
71                let edge_id = graph[&hedge];
72                graph[edge_id].particle_pdg_code().map(|pdg| {
73                    if graph.flow(hedge) == Flow::Source {
74                        -pdg
75                    } else {
76                        pdg
77                    }
78                })
79            })
80            .collect()
81    }
82
83    fn internal_pdg_set<E: UVE, V, H, S: SubGraphLike>(&self, subgraph: &S) -> BTreeSet<isize>
84    where
85        Self: AsRef<HedgeGraph<E, V, H>>,
86    {
87        self.as_ref()
88            .iter_edges_of(subgraph)
89            .filter_map(|(pair, edge_id, _)| {
90                pair.is_paired()
91                    .then(|| self.as_ref()[edge_id].particle_pdg_code())
92                    .flatten()
93            })
94            .collect()
95    }
96
97    fn has_massive_boundary_external<E: UVE, V, H, S: SubGraphLike<Base = SuBitGraph>>(
98        &self,
99        subgraph: &S,
100    ) -> bool
101    where
102        Self: AsRef<HedgeGraph<E, V, H>>,
103    {
104        let graph = self.as_ref();
105        graph.full_crown(subgraph).included_iter().any(|hedge| {
106            let edge_id = graph[&hedge];
107            graph[edge_id].is_massive()
108        })
109    }
110
111    fn ct_identifier<E: UVE, V, H, S: SubGraphLike<Base = SuBitGraph>>(
112        &self,
113        subgraph: &S,
114    ) -> CTIdentifier
115    where
116        Self: AsRef<HedgeGraph<E, V, H>>,
117    {
118        CTIdentifier::new(
119            self.boundary_pdg_set(subgraph),
120            Some(self.internal_pdg_set(subgraph)),
121        )
122    }
123
124    fn approximation_scheme<E: UVE, V, H, S: SubGraphLike<Base = SuBitGraph>>(
125        &self,
126        subgraph: &S,
127        settings: &UVgenerationSettings,
128        dod: i32,
129    ) -> ApproximationType
130    where
131        Self: AsRef<HedgeGraph<E, V, H>>,
132    {
133        settings.approximation_scheme_for(
134            &self.ct_identifier(subgraph),
135            dod,
136            self.has_massive_boundary_external(subgraph),
137        )
138    }
139
140    fn classify_spinney<E: UVE, V, H>(
141        &self,
142        spinney: InternalSubGraph,
143        settings: &UVgenerationSettings,
144        lmb: &LoopMomentumBasis,
145    ) -> Option<Spinney>
146    where
147        Self: AsRef<HedgeGraph<E, V, H>>,
148    {
149        let dod = self.compute_dod(&spinney.filter);
150        if dod < 0 {
151            return None;
152        }
153
154        let renormalization_scheme = self.approximation_scheme(&spinney.filter, settings, dod);
155
156        if renormalization_scheme != ApproximationType::Unsubtracted {
157            Spinney::with_scheme(spinney, self, lmb, renormalization_scheme, dod)
158        } else {
159            None
160        }
161    }
162
163    fn classified_spinneys<E: UVE, V, H, S: SubGraphLike<Base = SuBitGraph>>(
164        &self,
165        subgraph: &S,
166        settings: &UVgenerationSettings,
167        lmb: &LoopMomentumBasis,
168    ) -> Vec<Spinney>
169    where
170        Self: AsRef<HedgeGraph<E, V, H>>,
171    {
172        if !settings.subtract_uv {
173            return vec![Spinney::empty(self)];
174        }
175
176        self.spinneys(subgraph)
177            .into_iter()
178            .filter_map(|spinney| self.classify_spinney(spinney, settings, lmb))
179            .collect()
180    }
181
182    fn all_cycle_unions<E, V, H, S: SubGraphLike<Base = SuBitGraph>>(
183        &self,
184        subgraph: &S,
185    ) -> AHashSet<InternalSubGraph>
186    where
187        Self: AsRef<HedgeGraph<E, V, H>>,
188    {
189        let ref_graph = self.as_ref();
190        let _init_node = ref_graph.iter_nodes_of(subgraph).next().unwrap().0;
191        let all_subcycles: Vec<_> =
192            Cycle::all_sum_powerset_filter_map(&ref_graph.cycle_basis_of(subgraph).0, &Some)
193                .map(|a| a.into_iter().map(|c| c.internal_graph(ref_graph)).collect())
194                .unwrap();
195
196        // println!("{}", self.base_dot());
197        let spinneys: AHashSet<_> = InternalSubGraph::all_unions_iterative(&all_subcycles);
198
199        spinneys
200    }
201    fn all_limits<E, V, H, S: SubGraphLike>(
202        &self,
203        subgraph: &S,
204        expr: &Atom,
205        expansion: Symbol,
206        lmb: &LoopMomentumBasis,
207    ) -> Vec<(SubSet<LoopIndex>, Series<AtomField>)>
208    where
209        Self: AsRef<HedgeGraph<E, V, H>>,
210    {
211        let mom_reps = self.normal_emr_replacement(subgraph, lmb, &[W_.x___], |_s| true);
212
213        let ose_reps = self.get_ose_replacements();
214        // for x in &mom_reps {
215        //     println!("LMB replacement: {x}");
216        // }
217
218        // for r in &ose_reps {
219        //     println!("ose{r}");
220        // }
221
222        let expr = expr
223            .replace(function!(GS.broadcasting_sqrt, W_.a_))
224            .with(Atom::var(W_.a_).sqrt())
225            .replace_multiple(&ose_reps)
226            .replace_multiple(&mom_reps);
227        // .replace_multiple(&q3_reps);
228        let mut loops = PowersetIterator::<LoopIndex>::new(lmb.loop_edges.len() as u8);
229
230        let mut limits = Vec::new();
231
232        loops.next();
233
234        for ls in loops {
235            let mut expr = expr.clone();
236            for l in ls.included_iter() {
237                let e = usize::from(lmb.loop_edges[l]) as i64;
238                expr = expr
239                    .replace(function!(GS.emr_mom, e, W_.x___))
240                    .with(function!(GS.emr_mom, e, W_.x___) / expansion);
241
242                expr /= Atom::var(expansion).pow(3);
243            }
244
245            let series = expr.series(expansion, Atom::Zero, 0).unwrap();
246
247            // expr = series.to_atom().expand();
248
249            limits.push((ls, series));
250        }
251        limits
252    }
253
254    fn wood<E: UVE, V, H, S: SubGraphLike<Base = SuBitGraph>>(&self, subgraph: &S) -> Wood
255    where
256        Self: AsRef<HedgeGraph<E, V, H>>,
257    {
258        self.wood_with_settings(
259            subgraph,
260            &UVgenerationSettings::default(),
261            &self.as_ref().lmb_of(&self.as_ref().full_filter()),
262        )
263    }
264
265    fn wood_with_settings<E: UVE, V, H, S: SubGraphLike<Base = SuBitGraph>>(
266        &self,
267        subgraph: &S,
268        settings: &UVgenerationSettings,
269        lmb: &LoopMomentumBasis,
270    ) -> Wood
271    where
272        Self: AsRef<HedgeGraph<E, V, H>>,
273    {
274        Wood::from_spinneys(self.classified_spinneys(subgraph, settings, lmb), self)
275    }
276
277    fn compute_dod<S: SubGraphLike<Base = SuBitGraph> + SubSetOps>(&self, subgraph: &S) -> i32;
278    fn local_dod<S: SubGraphLike>(&self, subgraph: &S) -> i32;
279
280    fn spinneys<E, V, H, S: SubGraphLike<Base = SuBitGraph>>(
281        &self,
282        subgraph: &S,
283    ) -> AHashSet<InternalSubGraph>
284    where
285        Self: AsRef<HedgeGraph<E, V, H>>,
286    {
287        let ref_graph: &HedgeGraph<E, V, H> = self.as_ref();
288        debug!(subgraph=%ref_graph.dot(subgraph),"Spinneys of subgraph");
289        let _b: SuBitGraph = ref_graph.empty_subgraph();
290
291        if subgraph.is_empty() {
292            let mut spinneys = AHashSet::new();
293            spinneys.insert(ref_graph.empty_subgraph());
294            return spinneys;
295        }
296
297        let cycles = ref_graph.cycle_basis_of(subgraph).0;
298
299        let _init_node = ref_graph.iter_nodes_of(subgraph).next().unwrap().0;
300        let all_subcycles: Vec<_> = Cycle::all_sum_powerset_filter_map(&cycles, &Some)
301            .map(|a| a.into_iter().map(|c| c.internal_graph(ref_graph)).collect())
302            .unwrap();
303
304        let dod_cache = RefCell::new(AHashMap::new());
305
306        let mut spinneys: AHashSet<_> = InternalSubGraph::all_ops_iterative_filter_map(
307            &all_subcycles,
308            &|a, b| a.union(b),
309            &|union| {
310                // println!("{}", self.as_ref().dot(&union));
311                let cached_dod = dod_cache.borrow().get(&union).copied();
312                let keep = match cached_dod {
313                    Some(keep) => keep,
314                    None => {
315                        let keep = self.local_dod(&union) >= 0;
316                        dod_cache.borrow_mut().insert(union.clone(), keep);
317                        keep
318                    }
319                };
320                if keep { Some(union) } else { None }
321            },
322        );
323
324        spinneys.insert(ref_graph.empty_subgraph());
325
326        spinneys
327    }
328}
329
330impl AsRef<HedgeGraph<Edge, Vertex, HedgeData>> for Graph {
331    fn as_ref(&self) -> &HedgeGraph<Edge, Vertex, HedgeData> {
332        &self.underlying
333    }
334}
335
336impl UltravioletGraph for Graph {
337    fn dummy_less_full_crown<S: SubGraphLike>(&self, subgraph: &S) -> S::Base
338    where
339        S::Base: ModifySubSet<Hedge>,
340    {
341        let a = self.full_crown(subgraph);
342        let mut ac = a.clone();
343
344        a.included_iter().for_each(|a| {
345            if self[self[&a]].is_dummy {
346                ac.sub(a);
347            }
348        });
349
350        ac
351    }
352
353    // fn concretize_spinney<S: SubGraphLike<Base = SuBitGraph>>(&self, s: &S) -> Graph {
354    //     let mut underlying = self.underlying.clone();
355    //     let dod = self.dod(s);
356    //     let cc = underlying.connected_components(s);
357
358    //     for c in cc{
359
360    //     let v = Vertex {
361
362    //     }
363    //     underlying.identify_nodes_without_self_edges(s, node_data_merge);
364    //     };
365    //     }
366
367    //     self.clone()
368    // }
369
370    fn denominator<S: SubGraphLike, T: Fn(&Edge) -> isize>(
371        &self,
372        subgraph: &S,
373        edge_powers: T,
374    ) -> Atom {
375        let mut den = Atom::num(1);
376
377        for (pair, eid, d) in self.underlying.iter_edges_of(subgraph) {
378            if matches!(pair, HedgePair::Paired { .. }) {
379                let m2 = d.data.mass_atom().pow(2);
380                let edge_power = edge_powers(d.data);
381                let is_power_negative = edge_power < 0;
382                let prop_den = GS.den(
383                    usize::from(eid),
384                    function!(GS.emr_mom, usize::from(eid)),
385                    &m2,
386                    spenso_lor_atom(usize::from(eid) as i32, usize::from(eid), GS.dim)
387                        .pow(2)
388                        .to_dots()
389                        - &m2,
390                );
391                for _i in 0..edge_power.abs() {
392                    if is_power_negative {
393                        den /= prop_den.clone();
394                    } else {
395                        den *= prop_den.clone();
396                    }
397                }
398            }
399        }
400
401        den
402    }
403    fn numerator<S: SubGraphLike + SubSetOps>(
404        &self,
405        subgraph: &S,
406        without: &S,
407    ) -> Numerator<AppliedFeynmanRule> {
408        let num = Numerator::default();
409
410        num.fill_in_reduced(self, subgraph, without)
411    }
412
413    fn compute_dod<S: SubGraphLike<Base = SuBitGraph> + SubSetOps>(&self, subgraph: &S) -> i32 {
414        let lmb = self.lmb_of(subgraph);
415        let empty = self.underlying.empty_subgraph();
416        let integrand = self
417            .numerator(subgraph, &empty)
418            .to_d_dim(GS.dim)
419            .get_single_atom()
420            .unwrap()
421            / self.denominator(subgraph, |_| 1);
422        let nloops: usize = self.n_loops(subgraph);
423        self.uv_rescaled(subgraph.included(), nloops, &lmb, &integrand)
424            .trailing_exponent()
425    }
426
427    fn local_dod<S: SubGraphLike>(&self, subgraph: &S) -> i32 {
428        let mut dod: i32 = 4 * self.n_loops(subgraph) as i32;
429        for (p, _, e) in self.underlying.iter_edges_of(subgraph) {
430            if p.is_paired() {
431                dod += e.data.dod.deref();
432            }
433        }
434
435        for (_, _, n) in self.underlying.iter_nodes_of(subgraph) {
436            dod += n.dod.deref();
437        }
438
439        dod
440    }
441}
442
443pub trait UVE {
444    fn mass_atom(&self) -> Atom;
445    fn particle_pdg_code(&self) -> Option<isize>;
446    fn is_massive(&self) -> bool;
447}