Skip to main content

gammalooprs/uv/
spinney.rs

1use 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    /// Disconnected unions use `MUV` as a neutral tag; their component factors
24    /// carry the renormalization schemes that determine the approximation.
25    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        // Divergence is a property of each connected factor of a spinney.
81        // A disconnected union may have a negative aggregate DOD even though
82        // every factor was selected as divergent and must still be retained.
83        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}