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 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 {
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 #[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}