1use crate::{
2 debug_tags,
3 graph::{Graph, LoopMomentumBasis},
4 uv::{Spinney, UVgenerationSettings, approx::CutStructure, forest::CutForests},
5};
6use gammaloop_tracing_filter::{LogMessage, debug_instrument};
7use slotmap::SecondaryMap;
8use std::collections::VecDeque;
9
10use linnet::half_edge::{
11 HedgeGraph,
12 subgraph::{InternalSubGraph, SubSetOps},
13};
14
15use super::{
18 Forest, Poset, UltravioletGraph,
19 approx::Approximation,
20 poset::{DAG, DagNode, PosetNode},
21};
22
23pub struct CutWoods {
24 pub cuts: CutStructure,
25 pub woods: Vec<Wood>,
26 pub settings: Vec<vakint::VakintSettings>,
27}
28
29impl CutWoods {
30 #[debug_instrument(graph = %graph.log_display())]
31 pub(crate) fn new(cuts: CutStructure, graph: &Graph, settings: &UVgenerationSettings) -> Self {
32 let mut woods = vec![];
33 let mut vakint_settings = vec![];
34 for cut in cuts.cuts.iter() {
35 let mut subgraph = graph.full_filter();
36 subgraph.subtract_with(&graph.initial_state_cut.left);
37 subgraph.subtract_with(&cut.union);
38
39 let spinneys =
40 graph.classified_spinneys(&subgraph, settings, &graph.loop_momentum_basis);
41
42 for spinney in spinneys.iter() {
43 debug_tags!(#uv, #graph, #spinney,#generation;
44 log.cutset = cut,
45 local_dod = %graph.local_dod(&spinney.subgraph),
46 log.spinney = spinney,
47 "spinneys",
48 );
49 }
50
51 let wood = Wood::from_spinneys(spinneys, graph);
52
53 let mut lvk_settings = settings.vakint.true_settings();
54 lvk_settings.number_of_terms_in_epsilon_expansion = wood.max_loops as i64 + 1;
58 vakint_settings.push(lvk_settings);
59 woods.push(wood);
60 }
61 CutWoods {
62 cuts,
63 woods,
64 settings: vakint_settings,
65 }
66 }
67
68 pub(crate) fn unfold(self, graph: &Graph) -> CutForests {
69 CutForests {
70 cuts: self.cuts,
71 forests: self
72 .woods
73 .iter()
74 .map(|a| a.unfold(graph, &graph.loop_momentum_basis))
75 .collect(),
76 settings: self.settings,
77 }
78 }
79}
80
81pub struct Wood {
82 poset: Poset<Spinney, ()>,
83 pub max_loops: usize,
84 additional_unions: SecondaryMap<PosetNode, Vec<PosetNode>>,
85}
86
87impl Wood {
88 pub(crate) fn from_spinneys<E, V, H, I: IntoIterator<Item = Spinney>>(
89 s: I,
90 graph: impl AsRef<HedgeGraph<E, V, H>>,
91 ) -> Self {
92 let mut poset = Poset::from_iter(s.into_iter().map(|s| (s, ())));
93 let ref_graph = graph.as_ref();
94
95 poset.invert();
96 poset.compute_topological_order();
97 let mut max_loops = 0;
98
99 let mut unions = SecondaryMap::new();
100
101 for (i, sg) in poset.nodes.iter() {
102 let cs = ref_graph.connected_components(sg.data.filter());
103 max_loops = max_loops.max(sg.data.max_comp_loop_count());
104
105 if cs.len() > 1 {
106 let mut union = vec![];
108
109 for &c in sg.parents.iter() {
110 let mut is_in = 0;
111 for comp in &cs {
112 let comp =
113 InternalSubGraph::cleaned_filter_optimist(comp.clone(), ref_graph);
114 if comp == poset.nodes[c].data.subgraph {
115 union.push(c);
117 is_in += 1;
118 }
119 }
120
121 if is_in > 1 {
122 panic!("is in too many components")
123 }
124 }
125
126 unions.insert(i, union.clone());
127 }
129 }
130
131 Wood {
133 max_loops,
134 poset,
135 additional_unions: unions,
136 }
137 }
138
139 #[allow(clippy::type_complexity)]
140 fn unfold_bfs<E, V, H, G>(
141 &self,
142 _graph: &G,
143 _lmb: &LoopMomentumBasis,
144 dag: &mut DAG<Approximation, DagNode, ()>,
145 unions: &mut SecondaryMap<PosetNode, Option<Vec<(PosetNode, Option<DagNode>)>>>,
146 root: PosetNode,
147 ) -> DagNode
148 where
149 G: UltravioletGraph + AsRef<HedgeGraph<E, V, H>>,
150 {
151 let mut search_front = VecDeque::new();
153
154 let tree_root = dag.add_node(Approximation::new(self.poset.data(root).clone()));
155 search_front.push_front((root, tree_root));
156
157 while let Some((node, parent)) = search_front.pop_front() {
158 for c in &self.poset.nodes[node].children {
159 if let Some(tagged_union) = unions.get_mut(*c) {
160 if let Some(mut union) = tagged_union.take() {
162 let mut all_supplied = true;
163 for (p, d) in &mut union {
164 if self.poset.nodes[*p].data == self.poset.nodes[node].data {
165 *d = Some(parent);
166 }
167 if d.is_none() {
168 all_supplied = false;
169 }
170 }
171 if all_supplied {
172 let child =
173 dag.add_node(Approximation::new(self.poset.data(*c).clone()));
174 for (_, d) in union {
175 dag.add_edge(d.unwrap(), child);
176 }
177 search_front.push_front((*c, child));
178 } else {
179 *tagged_union = Some(union);
180 }
181 }
182 } else {
183 let child = dag.add_node(Approximation::new(self.poset.data(*c).clone()));
184 dag.add_edge(parent, child);
185 search_front.push_front((*c, child));
186 }
187 }
188 }
189 tree_root
190 }
191
192 pub(crate) fn unfold<E, V, H, G>(&self, graph: &G, lmb: &LoopMomentumBasis) -> Forest
193 where
194 G: UltravioletGraph + AsRef<HedgeGraph<E, V, H>>,
195 {
196 let mut dag: DAG<Approximation, DagNode, ()> = DAG::new();
197
198 let root = self.poset.minimum().unwrap();
199
200 let mut unions = SecondaryMap::new();
201
202 for (p, u) in self.additional_unions.iter() {
203 let union: Vec<(PosetNode, Option<DagNode>)> = u.iter().map(|i| (*i, None)).collect();
204 unions.insert(p, Some(union));
205 }
206
207 let _ = self.unfold_bfs(graph, lmb, &mut dag, &mut unions, root);
208
209 Forest { dag }
210 }
211}