Skip to main content

gammalooprs/graph/parse/
serialization.rs

1use idenso::{
2    color::{ColorSimplifier, ColorSimplifySettings},
3    dirac::GammaSimplifier,
4    shorthands::metric::MetricSimplifier,
5};
6use linnet::{
7    half_edge::{
8        HedgeGraph,
9        involution::{Flow, HedgePair, Orientation},
10        subgraph::SubSetLike,
11    },
12    parser::{DotEdgeData, DotGraph, DotHedgeData, DotVertexData},
13};
14use spenso::shadowing::symbolica_utils::SpensoPrintSettings;
15use symbolica::atom::AtomCore;
16
17use crate::{
18    graph::Graph,
19    processes::DotExportSettings,
20    utils::{GS, W_},
21    uv::UltravioletGraph,
22};
23
24use super::{
25    ParseGraph,
26    string_utils::{ToQuoted, dot_statement_value},
27};
28
29impl Graph {
30    pub fn to_dot_graph_with_settings(&self, settings: &DotExportSettings) -> DotGraph {
31        let mut dotgraph = if settings.split_xs_by_initial_states {
32            self.to_split_dotgraph_with_autogenerated_fields(settings.include_autogenerated_fields)
33        } else {
34            self.to_dot_graph_with_autogenerated_fields(settings.include_autogenerated_fields)
35        };
36
37        if settings.output_full_numerator {
38            let mut num = (self
39                .numerator(&self.full_filter(), &self.empty_subgraph())
40                .get_single_atom()
41                .unwrap()
42                * &self.global_prefactor.num
43                * &self.global_prefactor.projector
44                * &self.overall_factor)
45                .simplify_metrics();
46
47            if settings.do_color_algebra {
48                num = num.simplify_color_with(
49                    ColorSimplifySettings::default().with_cof_dimension_invariants(),
50                );
51            }
52
53            if settings.do_gamma_algebra {
54                num = num.simplify_gamma();
55                crate::debug_tags!(#generation, #graph, #inspect, #dump;
56                    stage = "graph_serialization_after_simplify_gamma",
57                    log.after_gamma = num,
58                    "Graph serialization after gamma simplification"
59                );
60            }
61
62            dotgraph.global_data.statements.insert(
63                "full_num".into(),
64                dot_statement_value(
65                    &num.printer(SpensoPrintSettings::typst_options())
66                        .to_string(),
67                ),
68            );
69        }
70
71        dotgraph
72    }
73
74    pub fn to_split_dotgraph(&self) -> DotGraph {
75        self.to_split_dotgraph_with_autogenerated_fields(false)
76    }
77
78    fn to_split_dotgraph_with_autogenerated_fields(
79        &self,
80        include_autogenerated_fields: bool,
81    ) -> DotGraph {
82        let global_data = self.global_data();
83
84        let mut graph: HedgeGraph<DotEdgeData, DotVertexData, DotHedgeData> =
85            if self.initial_state_cut.is_empty() {
86                self.underlying.map_data_ref(
87                    |_, _, v| v.to_dot_data(include_autogenerated_fields),
88                    |_, _, p, e| {
89                        if let HedgePair::Unpaired { flow, .. } = p {
90                            e.map(|e| {
91                                let mut dot = e.to_dot_data(include_autogenerated_fields);
92                                match flow {
93                                    Flow::Source => {
94                                        dot.add_statement("pos", "x:@-left!".to_string());
95                                    }
96                                    Flow::Sink => {
97                                        dot.add_statement("pos", "x:@+right!".to_string());
98                                    }
99                                }
100                                dot
101                            })
102                        } else {
103                            e.map(|e| e.to_dot_data(include_autogenerated_fields))
104                        }
105                    },
106                    |_, d| d.into(),
107                )
108            } else {
109                self.initial_state_cut
110                    .clone()
111                    .to_owned_graph_ref(&self.underlying)
112                    .map(
113                        |_, _, v| v.to_dot_data(include_autogenerated_fields),
114                        |_, _, p, _, e| {
115                            e.map(|e| {
116                                let mut dot =
117                                    e.edge_data().to_dot_data(include_autogenerated_fields);
118
119                                match e.orientation() {
120                                    Orientation::Default => {
121                                        dot.add_statement("is_cut", self.inv(p.any_hedge()));
122                                        dot.add_statement(
123                                            "pos",
124                                            format!("x:@-left!,y:@edge{}!", e.index),
125                                        );
126                                    }
127                                    Orientation::Reversed => {
128                                        dot.add_statement("is_cut", p.any_hedge());
129                                        dot.add_statement(
130                                            "pos",
131                                            format!("x:@+right!,y:@edge{}!", e.index),
132                                        );
133                                    }
134                                    Orientation::Undirected => {}
135                                }
136                                dot
137                            })
138                        },
139                        |_, d| d.into(),
140                    )
141            };
142
143        // self.normal_emr_replacement(subgraph, lmb, rep_args, filter_pair)
144        for (_, i, _) in self.iter_edges() {
145            let loop_expr = self
146                .loop_momentum_basis
147                .loop_atom(i, GS.loop_mom, &[W_.a___], false);
148            let external_expr =
149                self.loop_momentum_basis
150                    .ext_atom(i, GS.external_mom, &[W_.a___], false);
151
152            graph[i].add_statement("lmb_rep", (loop_expr + external_expr).to_quoted());
153        }
154
155        for (l, i) in self.loop_momentum_basis.loop_edges.iter_enumerated() {
156            graph[i].0.add_statement("lmb_id", l);
157        }
158
159        DotGraph { global_data, graph }
160    }
161
162    fn to_dot_graph_with_autogenerated_fields(
163        &self,
164        include_autogenerated_fields: bool,
165    ) -> DotGraph {
166        let global_data = self.global_data();
167
168        let mut graph: HedgeGraph<DotEdgeData, DotVertexData, DotHedgeData> =
169            self.underlying.map_data_ref(
170                |_, _, v| v.to_dot_data(include_autogenerated_fields),
171                |_, _, _, e| e.map(|e| e.to_dot_data(include_autogenerated_fields)),
172                |_, d| d.into(),
173            );
174
175        for (_, i, _) in self.iter_edges() {
176            let loop_expr = self
177                .loop_momentum_basis
178                .loop_atom(i, GS.loop_mom, &[W_.a___], false);
179            let external_expr =
180                self.loop_momentum_basis
181                    .ext_atom(i, GS.external_mom, &[W_.a___], false);
182
183            graph[i].add_statement("lmb_rep", (loop_expr + external_expr).to_quoted());
184        }
185
186        for i in self.initial_state_cut.left.included_iter() {
187            let eid = graph[&i];
188            graph[eid].add_statement("is_cut", i);
189        }
190
191        for (l, i) in self.loop_momentum_basis.loop_edges.iter_enumerated() {
192            graph[i].0.add_statement("lmb_id", l);
193        }
194
195        DotGraph { global_data, graph }
196    }
197}
198
199impl From<&Graph> for DotGraph {
200    fn from(value: &Graph) -> Self {
201        value.to_dot_graph_with_autogenerated_fields(false)
202    }
203}
204
205impl From<&ParseGraph> for DotGraph {
206    fn from(value: &ParseGraph) -> Self {
207        let global_data = value.global_data();
208        let graph: HedgeGraph<DotEdgeData, DotVertexData, DotHedgeData> = value.graph.map_data_ref(
209            |_, _, v| v.into(),
210            |_, _, _, e| e.map(|e| e.into()),
211            |_, d| d.into(),
212        );
213
214        DotGraph { global_data, graph }
215    }
216}
217
218impl ParseGraph {
219    #[allow(dead_code)]
220    fn to_simple_dot(&self) -> DotGraph {
221        let global_data = self.global_data();
222        let graph: HedgeGraph<DotEdgeData, DotVertexData, DotHedgeData> = self.graph.map_data_ref(
223            |_, _, v| v.into(),
224            |_, _, _, e| e.map(|e| e.into()),
225            |_, d| d.into(),
226        );
227
228        DotGraph { global_data, graph }
229    }
230}
231
232#[cfg(test)]
233mod tests {
234    use symbolica::{atom::Atom, function};
235
236    use super::Graph;
237    use crate::{dot, graph::parse::IntoGraph, processes::DotExportSettings, utils::GS};
238
239    #[test]
240    fn full_numerator_is_a_spenso_aware_typst_fragment() {
241        let graph: Graph = dot!(digraph G {
242            ext [style=invis]
243            node [num=1]
244            ext -> A
245            C -> A
246            A -> D
247            D -> B
248            B -> C
249            C -> D
250            B -> ext
251        })
252        .unwrap();
253        let graph = graph.with_global_numerator_only(
254            "typst_full_num".to_string(),
255            function!(GS.uv_truncate, Atom::num(1)),
256        );
257        let dot = graph.to_dot_graph_with_settings(&DotExportSettings {
258            output_full_numerator: true,
259            ..DotExportSettings::default()
260        });
261        let full_num = &dot.global_data.statements["full_num"];
262
263        assert_eq!(full_num, r#"op(\"Tr\")(1)"#);
264        assert!(!full_num.contains("gammalooprs::Truncate"));
265    }
266}