gammalooprs/uv/
spinney.rs1use std::hash::{Hash, Hasher};
2
3use color_eyre::Result;
4use eyre::WrapErr;
5use gammaloop_tracing_filter::LogMessage;
6use linnet::half_edge::{
7 HedgeGraph,
8 subgraph::{Inclusion, InternalSubGraph, SuBitGraph, SubSetLike},
9};
10use tracing::debug;
11
12use crate::{
13 graph::{LMBext, LoopMomentumBasis, cuts::CutSet},
14 uv::{ApproximationType, UltravioletGraph},
15};
16
17#[derive(Clone, Debug, Eq)]
18pub struct Spinney {
19 pub subgraph: InternalSubGraph,
20 components: Vec<SuBitGraph>,
21 pub dod: i32,
22 pub lmb: LoopMomentumBasis,
23 pub renormalization_scheme: ApproximationType,
26 max_comp_loop_count: usize,
27}
28
29impl LogMessage for Spinney {
30 fn log_display(&self) -> String {
31 format!(
32 "Spinney {{ dod: {}, components: {}, max_comp_loop_count: {},subgraph: {} }}",
33 self.dod,
34 self.components.len(),
35 self.max_comp_loop_count,
36 self.subgraph.string_label(),
37 )
38 }
39}
40
41impl Spinney {
42 pub fn compatible_with(&self, cut: &CutSet) -> bool {
43 !self.subgraph.filter.intersects(&cut.union)
44 }
45
46 pub fn empty<E, V, H, G: AsRef<HedgeGraph<E, V, H>> + LMBext + ?Sized>(g: &G) -> Self {
47 Self {
48 subgraph: InternalSubGraph::empty(g.as_ref().n_hedges()),
49 components: vec![],
50 dod: 0,
51 lmb: g.empty_lmb(),
52 renormalization_scheme: ApproximationType::MUV,
53 max_comp_loop_count: 0,
54 }
55 }
56
57 pub fn new<E, V, H, G: UltravioletGraph + AsRef<HedgeGraph<E, V, H>> + ?Sized>(
58 subgraph: InternalSubGraph,
59 g: &G,
60 lmb: &LoopMomentumBasis,
61 ) -> Result<Option<Self>> {
62 let dod = g.compute_dod(&subgraph);
63 Self::with_scheme(subgraph, g, lmb, ApproximationType::MUV, dod)
64 }
65
66 pub fn with_scheme<E, V, H, G: UltravioletGraph + AsRef<HedgeGraph<E, V, H>> + ?Sized>(
67 subgraph: InternalSubGraph,
68 g: &G,
69 lmb: &LoopMomentumBasis,
70 renormalization_scheme: ApproximationType,
71 dod: i32,
72 ) -> Result<Option<Self>> {
73 let components = g.as_ref().connected_components(&subgraph);
74 let max_comp_loop_count = components
75 .iter()
76 .map(|component| g.as_ref().cyclotomatic_number(component))
77 .max()
78 .unwrap_or(0);
79
80 if dod < 0 && components.len() <= 1 {
84 return Ok(None);
85 }
86
87 let lmb = g
88 .try_compatible_sub_lmb(&subgraph, g.dummy_less_full_crown(&subgraph), lmb)
89 .wrap_err_with(|| {
90 format!(
91 "failed to select a compatible loop-momentum route for spinney {} (scheme {renormalization_scheme}, DOD {dod})",
92 subgraph.string_label()
93 )
94 })?;
95
96 debug!(
97 dod = %dod,
98 graph = %g.dot_lmb_of(&subgraph,&lmb),
99 renormalization_scheme = %renormalization_scheme,
100 string_label =%subgraph.string_label(),
101 "Constucted spinney with scheme: {:?}",
102 renormalization_scheme
103 );
104
105 Ok(Some(Self {
106 components,
107 dod,
108 lmb,
109 subgraph,
110 renormalization_scheme,
111 max_comp_loop_count,
112 }))
113 }
114
115 pub fn filter(&self) -> &SuBitGraph {
116 &self.subgraph.filter
117 }
118
119 pub fn max_comp_loop_count(&self) -> usize {
120 self.max_comp_loop_count
121 }
122
123 pub fn n_components(&self) -> usize {
124 self.components.len()
125 }
126}
127
128impl Hash for Spinney {
129 fn hash<H: Hasher>(&self, state: &mut H) {
130 self.subgraph.hash(state);
131 }
132}
133
134impl PartialEq for Spinney {
135 fn eq(&self, other: &Self) -> bool {
136 self.subgraph == other.subgraph
137 }
138}
139
140impl PartialOrd for Spinney {
141 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
142 if self.subgraph == other.subgraph {
143 Some(std::cmp::Ordering::Equal)
144 } else if self.subgraph.includes(&other.subgraph) {
145 Some(std::cmp::Ordering::Greater)
146 } else if other.subgraph.includes(&self.subgraph) {
147 Some(std::cmp::Ordering::Less)
148 } else {
149 None
150 }
151 }
152}