Skip to main content

gammalooprs/uv/approx/
mod.rs

1use crate::{
2    debug_tags,
3    graph::{Graph, LoopMomentumBasis, cuts::CutSet},
4    momentum::Sign,
5    settings::global::OrientationPattern,
6    utils::GS,
7    uv::{
8        ApproximationType, Spinney, UVgenerationSettings,
9        approx::{
10            final_integrand::{FinalIntegrandBuilder, FinalIntegrands},
11            integrated::{Integrated, IntegratedCts},
12            local_3d::{Local3DApproximation, Local3DCts, Localizer},
13            local_4d::{Full4dCts, Local4dCts},
14        },
15        marker::UvMarker,
16        settings::FinalIntegrandDimension,
17    },
18};
19use color_eyre::Result;
20use eyre::eyre;
21use gammaloop_tracing_filter::{LogMessage, debug_instrument};
22
23use std::hash::Hash;
24
25use symbolica::{
26    atom::{Atom, AtomOrView},
27    function,
28};
29
30use linnet::half_edge::involution::{EdgeIndex, EdgeVec, Orientation};
31use linnet::half_edge::subgraph::{InternalSubGraph, SuBitGraph, SubSetLike, SubSetOps};
32
33use super::IntegrandExpr;
34use vakint::Vakint;
35
36pub mod final_integrand;
37pub mod integrated;
38pub mod local_3d;
39pub mod local_4d;
40
41pub trait Rooted {
42    fn root() -> Self;
43}
44
45pub trait ForestNodeLike: LogMessage {
46    fn subgraph(&self) -> &SuBitGraph;
47    fn lmb(&self) -> &LoopMomentumBasis;
48    fn lmb_id(&self) -> EdgeIndex {
49        *self.lmb().loop_edges.first().unwrap()
50    }
51    // fn lmb_given(&self, subgraph: &SuBitGraph) -> &LoopMomentumBasis;
52    fn dod(&self) -> i32;
53    fn renormalization_scheme(&self) -> ApproximationType;
54    fn topo_order(&self) -> usize;
55    fn reduced_subgraph(&self, given: &Self) -> SuBitGraph;
56}
57
58pub trait ApproximationKernel<C> {
59    fn kernel<S: ForestNodeLike>(
60        &self,
61        ctx: &C,
62        current: &S,
63        given: &S,
64        atom: &Atom,
65    ) -> Result<Atom>;
66}
67
68pub struct UVCtx<'a> {
69    pub graph: &'a Graph,
70    pub settings: &'a UVgenerationSettings,
71}
72
73impl<'a> UVCtx<'a> {
74    pub fn new(graph: &'a Graph, settings: &'a UVgenerationSettings) -> Self {
75        Self { graph, settings }
76    }
77}
78
79pub trait ApproxKernel {
80    fn apply<'a, A: Into<AtomOrView<'a>>>(&self, atom: A) -> Atom;
81}
82
83#[derive(Clone, Debug, PartialEq, Eq, Hash)]
84pub enum ApproxOp {
85    NotComputed,
86    // Union operations must be computed before use.
87    Union {
88        t_args: Vec<IntegrandExpr>,
89        subgraphs: Vec<InternalSubGraph>,
90    },
91    Dependent {
92        t_arg: IntegrandExpr,
93        subgraph: InternalSubGraph,
94    },
95    Root,
96}
97
98#[derive(Clone)]
99pub struct SimpleApprox {
100    t_args: Vec<Atom>,
101    pub sign: Sign,
102    graph: InternalSubGraph,
103}
104
105impl SimpleApprox {
106    pub(crate) fn expr(&self, bigger_graph: &SuBitGraph) -> Atom {
107        let reduced = UvMarker::subgraph(bigger_graph, &self.graph.filter);
108        let mut mul = Atom::num(1);
109        for i in &self.t_args {
110            mul *= i;
111        }
112        reduced * mul
113    }
114
115    pub(crate) fn t_op(&self, bigger_graph: &SuBitGraph) -> Atom {
116        function!(GS.uv_approx, self.expr(bigger_graph))
117    }
118
119    pub(crate) fn root(subgraph: InternalSubGraph) -> Self {
120        if !subgraph.is_empty() {
121            panic!(
122                "Root approximation must be empty {} {:?}",
123                subgraph.string_label(),
124                subgraph
125            )
126        }
127        SimpleApprox {
128            t_args: vec![],
129            sign: Sign::Positive,
130            graph: subgraph,
131        }
132    }
133
134    pub(crate) fn dependent(&self, bigger_graph: InternalSubGraph) -> Self {
135        Self {
136            t_args: vec![self.t_op(&bigger_graph.filter)],
137            sign: -self.sign,
138            graph: bigger_graph,
139        }
140    }
141}
142
143#[derive(Clone)]
144pub struct Approximation {
145    pub spinney: Spinney,
146    local_3d: Option<Local3DCts>,
147    local: Option<Local4dCts>,
148    integrated: Option<IntegratedCts>,
149    final_integrand: Option<FinalIntegrands>,
150    pub topo_order: usize,
151    pub simple_approx: Option<SimpleApprox>,
152}
153
154impl Approximation {
155    pub(crate) fn integrated(&self, graph: &Graph) -> Result<&IntegratedCts> {
156        self.integrated
157            .as_ref()
158            .ok_or_else(|| eyre!("No integrated CT for {}", self.simple_display(graph)))
159    }
160
161    pub(crate) fn local(&self, graph: &Graph) -> Result<&Local4dCts> {
162        self.local
163            .as_ref()
164            .ok_or_else(|| eyre!("No local CT for {}", self.simple_display(graph)))
165    }
166
167    pub(crate) fn recursion_input_4d(&self, graph: &Graph) -> Result<Full4dCts> {
168        Full4dCts::recursion_input(
169            self.local(graph)?,
170            self.integrated(graph)?,
171            self.renormalization_scheme(),
172            self.spinney.subgraph.is_empty(),
173        )
174    }
175
176    pub(crate) fn local_3d(&self, graph: &Graph) -> Result<&Local3DCts> {
177        self.local_3d
178            .as_ref()
179            .ok_or_else(|| eyre!("No local 3D CT for {}", self.simple_display(graph)))
180    }
181
182    pub(crate) fn final_integrand(&self, graph: &Graph) -> Result<&FinalIntegrands> {
183        self.final_integrand
184            .as_ref()
185            .ok_or_else(|| eyre!("No final integrand for {}", self.simple_display(graph)))
186    }
187
188    pub fn simple_display(&self, graph: &Graph) -> String {
189        format!(
190            "{} of {}",
191            self.simple_approx
192                .as_ref()
193                .unwrap()
194                .expr(&graph.full_filter()),
195            graph.name
196        )
197    }
198}
199
200impl LogMessage for Approximation {
201    fn log_display(&self) -> String {
202        format!(
203            "subgraph={}, topo_order={}, dod={}",
204            self.spinney.filter().string_label(),
205            self.topo_order,
206            self.spinney.dod
207        )
208    }
209}
210
211impl ForestNodeLike for Approximation {
212    fn dod(&self) -> i32 {
213        self.spinney.dod
214    }
215
216    fn renormalization_scheme(&self) -> ApproximationType {
217        self.spinney.renormalization_scheme
218    }
219
220    fn lmb(&self) -> &LoopMomentumBasis {
221        &self.spinney.lmb
222    }
223
224    fn reduced_subgraph(&self, given: &Self) -> SuBitGraph {
225        self.spinney
226            .subgraph
227            .subtract(&given.spinney.subgraph)
228            .filter
229    }
230
231    fn subgraph(&self) -> &SuBitGraph {
232        self.spinney.filter()
233    }
234
235    fn topo_order(&self) -> usize {
236        self.topo_order
237    }
238}
239
240#[derive(Clone)]
241pub struct CutStructure {
242    pub cuts: Vec<CutSet>,
243}
244
245#[derive(Clone, Copy, Debug)]
246pub(crate) struct OrientationProjection<'a> {
247    pub(crate) valid_orientations: &'a [EdgeVec<Orientation>],
248    pub(crate) orientation_pattern: &'a OrientationPattern,
249}
250
251impl<'a> OrientationProjection<'a> {
252    pub(crate) fn new(
253        valid_orientations: &'a [EdgeVec<Orientation>],
254        orientation_pattern: &'a OrientationPattern,
255    ) -> Self {
256        Self {
257            valid_orientations,
258            orientation_pattern,
259        }
260    }
261}
262
263impl CutStructure {
264    pub(crate) fn empty(graph: &Graph) -> Self {
265        Self {
266            cuts: vec![CutSet::empty(graph.n_hedges())],
267        }
268    }
269}
270
271impl Approximation {
272    pub(crate) fn root(
273        &mut self,
274        graph: &mut Graph,
275        localizer: Localizer<'_>,
276        settings: &UVgenerationSettings,
277    ) -> Result<()> {
278        self.simple_approx = Some(SimpleApprox::root(self.spinney.subgraph.clone()));
279        self.local = Some(Local4dCts::root());
280        let integrated = IntegratedCts::root();
281        if let FinalIntegrandDimension::ThreeD = settings.final_integrand {
282            let local_3d = Local3DCts::root(graph, localizer)?;
283            self.final_integrand = Some(FinalIntegrandBuilder::new(localizer, settings).build_3d(
284                graph,
285                self,
286                &local_3d,
287                &integrated,
288            )?);
289            self.local_3d = Some(local_3d);
290        }
291        self.integrated = Some(integrated);
292
293        Ok(())
294    }
295
296    pub(crate) fn new(spinney: Spinney) -> Approximation {
297        Approximation {
298            spinney,
299            topo_order: 0,
300            final_integrand: None,
301            simple_approx: None,
302            local: None,
303            local_3d: None,
304            integrated: None,
305        }
306    }
307
308    #[debug_instrument(
309        graph = %graph.log_display(),
310        current = %self.log_display(),
311        given = %dependent.log_display(),
312        reduced = ?self.reduced_subgraph(dependent),
313    )]
314    pub(crate) fn compute_4d(
315        &mut self,
316        graph: &Graph,
317        vakint: (&Vakint, &vakint::VakintSettings),
318        dependent: &Self,
319        settings: &UVgenerationSettings,
320    ) -> Result<()> {
321        let ctx = UVCtx { graph, settings };
322        debug_tags!(#generation,#uv,#fourd;
323            simple = %self.simple_display(graph),
324            "Computing 4D",
325        );
326
327        let old_full = dependent.recursion_input_4d(graph)?;
328        let local = local_4d::uv_limit(&old_full, &ctx, self, dependent, self, dependent)?;
329        let integrated = if settings.generate_integrated {
330            Integrated::new(vakint.0, vakint.1)
331                .run(&local, &ctx, self, dependent, self, dependent)?
332        } else {
333            IntegratedCts::root()
334        };
335
336        self.local = Some(local);
337        self.integrated = Some(integrated);
338
339        Ok(())
340    }
341
342    /// Computes the 3d approximation of the UV
343    #[allow(clippy::too_many_arguments)]
344    #[debug_instrument(
345        graph = %graph.log_display(),
346        current = %self.log_display(),
347        given = %dependent.log_display(),
348    )]
349    pub(crate) fn compute_3d(
350        &mut self,
351        dependent: &Self,
352        graph: &mut Graph,
353        localizer: Localizer<'_>,
354        settings: &UVgenerationSettings,
355    ) -> Result<()> {
356        let parent_local = dependent.local_3d(graph)?;
357        let parent_integrated = dependent.integrated(graph)?;
358        let local_3d = Local3DApproximation::new(localizer, graph, settings).run(
359            parent_local,
360            parent_integrated,
361            self,
362            dependent,
363            self,
364            dependent,
365        )?;
366
367        let integrated = self.integrated(graph)?;
368
369        self.final_integrand = Some(
370            FinalIntegrandBuilder::new(localizer, settings)
371                .build_3d(graph, self, &local_3d, integrated)?,
372        );
373        self.local_3d = Some(local_3d);
374        Ok(())
375    }
376}