gammalooprs/graph/
global.rs1use eyre::Context;
2use linnet::parser::GlobalData;
3use symbolica::atom::Atom;
4
5use crate::{
6 feyngen::diagram_generator::evaluate_overall_factor,
7 graph::{GroupId, threshold_counterterms::ThresholdCountertermSpec},
8};
9
10use super::{
11 Graph,
12 parse::{ParseGraph, StripParse, ToQuoted},
13};
14
15#[derive(Clone, Debug)]
16pub struct ParseData {
17 pub name: String,
18 pub overall_factor: Atom,
19 pub projectors: Option<Atom>,
20 pub num: Atom,
21 pub parameters: Vec<Atom>,
22 pub group_id: Option<GroupId>,
23 pub is_group_master: bool,
24 pub threshold_counterterms: Option<ThresholdCountertermSpec>,
25}
26
27impl Default for ParseData {
28 fn default() -> Self {
29 ParseData {
30 name: String::new(),
31 overall_factor: Atom::one(),
32 projectors: None,
33 parameters: Vec::new(),
34 num: Atom::one(),
35 group_id: None,
36 is_group_master: false,
37 threshold_counterterms: None,
38 }
39 }
40}
41
42impl ParseData {
43 pub(crate) fn with_overall_factor(self, overall_factor: Atom) -> Self {
44 ParseData {
45 name: self.name,
46 overall_factor,
47 projectors: self.projectors,
48 num: self.num,
49 parameters: self.parameters,
50 group_id: self.group_id,
51 is_group_master: self.is_group_master,
52 threshold_counterterms: self.threshold_counterterms,
53 }
54 }
55
56 pub(crate) fn with_projectors(self, polarizations: Atom) -> Self {
57 ParseData {
58 name: self.name,
59 overall_factor: self.overall_factor,
60 projectors: Some(polarizations),
61 num: self.num,
62 parameters: self.parameters,
63 group_id: self.group_id,
64 is_group_master: self.is_group_master,
65 threshold_counterterms: self.threshold_counterterms,
66 }
67 }
68
69 pub(crate) fn with_num(self, num: Atom) -> Self {
70 ParseData {
71 name: self.name,
72 overall_factor: self.overall_factor,
73 projectors: self.projectors,
74 num,
75 parameters: self.parameters,
76 group_id: self.group_id,
77 is_group_master: self.is_group_master,
78 threshold_counterterms: self.threshold_counterterms,
79 }
80 }
81}
82
83impl TryFrom<linnet::parser::GlobalData> for ParseData {
84 type Error = color_eyre::Report;
85
86 fn try_from(value: linnet::parser::GlobalData) -> color_eyre::Result<Self> {
87 let mut parse_data = ParseData {
88 name: value.name,
89 ..Default::default()
90 };
91
92 if let Some(factor) = value.statements.get("overall_factor") {
93 parse_data = parse_data
94 .with_overall_factor(factor.strip_parse().context("overall_factor").unwrap());
95 }
96
97 if let Some(polarizations) = value.statements.get("projector") {
98 parse_data = parse_data
99 .with_projectors(polarizations.strip_parse().context("projector").unwrap());
100 }
101
102 if let Some(factor) = value.statements.get("num") {
103 parse_data = parse_data.with_num(factor.strip_parse().context("num").unwrap());
104 }
105
106 if let Some(is_group_master) = value.statements.get("is_group_master") {
107 parse_data.is_group_master = is_group_master
108 .strip_parse()
109 .context("is_group_master")
110 .unwrap();
111 }
112
113 if let Some(group_id) = value.statements.get("group_id") {
114 parse_data.group_id =
115 Some(GroupId(group_id.strip_parse().context("group_id").unwrap()));
116 }
117
118 if let Some(params) = value.statements.get("params") {
119 let params: String = params.strip_parse().context("params").unwrap();
120 parse_data.parameters = params
121 .split(';')
122 .map(str::trim)
123 .filter(|param| !param.is_empty())
124 .map(|param| {
125 param
126 .strip_parse()
127 .with_context(|| format!("params entry {param}"))
128 .unwrap()
129 })
130 .collect();
131 }
132
133 if let Some(threshold_counterterms) = value.statements.get("threshold_counterterms") {
134 parse_data.threshold_counterterms = Some(
137 ThresholdCountertermSpec::parse_toml(threshold_counterterms)
138 .context("threshold_counterterms graph attribute")?,
139 );
140 }
141
142 Ok(parse_data)
143 }
144}
145
146impl Graph {
147 pub(crate) fn global_data(&self, include_autogenerated_fields: bool) -> GlobalData {
148 let mut g = GlobalData::from(());
149
150 g.add_name(self.name.clone());
152
153 g.statements
154 .insert("num".to_string(), self.global_prefactor.num.to_quoted());
155
156 g.statements.insert(
157 "projector".to_string(),
158 self.global_prefactor.projector.to_quoted(),
159 );
160 g.statements.insert(
165 "overall_factor".to_string(),
166 self.overall_factor.to_quoted(),
167 );
168
169 g.statements.insert(
170 "overall_factor_evaluated".to_string(),
171 evaluate_overall_factor(self.overall_factor.as_view()).to_quoted(),
172 );
173
174 if !self.param_builder.pairs.additional_params.params.is_empty() {
175 let params = self
176 .param_builder
177 .pairs
178 .additional_params
179 .params
180 .iter()
181 .map(ToQuoted::to_quoted)
182 .collect::<Vec<_>>()
183 .join(";");
184 g.statements.insert("params".to_string(), params);
185 }
186
187 if !self.threshold_counterterms.autogenerated
190 || (include_autogenerated_fields && (self.group_id.is_none() || self.is_group_master))
191 {
192 let threshold_counterterms = self
193 .threshold_counterterms
194 .to_toml()
195 .expect("validated threshold_counterterms should serialize to TOML");
196 g.statements.insert(
197 "threshold_counterterms".to_string(),
198 format!("\n{threshold_counterterms}"),
201 );
202 }
203
204 g
205 }
206}
207
208impl ParseGraph {
209 pub(crate) fn global_data(&self) -> GlobalData {
210 let mut g = GlobalData::from(());
211
212 g.add_name(self.global_data.name.clone());
214
215 g.statements
216 .insert("num".to_string(), self.global_data.num.to_quoted());
217 if let Some(proj) = &self.global_data.projectors {
218 g.statements
219 .insert("projector".to_string(), proj.to_quoted());
220 }
221
222 g.statements.insert(
227 "overall_factor".to_string(),
228 self.global_data.overall_factor.to_quoted(),
229 );
230
231 if !self.global_data.parameters.is_empty() {
232 let params = self
233 .global_data
234 .parameters
235 .iter()
236 .map(ToQuoted::to_quoted)
237 .collect::<Vec<_>>()
238 .join(";");
239 g.statements.insert("params".to_string(), params);
240 }
241
242 if let Some(threshold_counterterms) = &self.global_data.threshold_counterterms {
243 let threshold_counterterms = threshold_counterterms
244 .to_toml()
245 .expect("validated threshold_counterterms should serialize to TOML");
246 g.statements.insert(
247 "threshold_counterterms".to_string(),
248 format!("\n{threshold_counterterms}"),
249 );
250 }
251
252 g
253 }
254}
255
256#[cfg(test)]
257mod tests {
258 use linnet::{
259 half_edge::nodestore::NodeStorageVec,
260 parser::{DotGraph, DotVertexData},
261 };
262
263 use crate::{
264 dot,
265 graph::{Graph, parse::IntoGraph},
266 initialisation::test_initialise,
267 processes::DotExportSettings,
268 };
269
270 #[test]
271 fn params_roundtrip_in_global_data() {
272 test_initialise().unwrap();
273 match dot!(digraph params_roundtrip {
274 graph [
275 overall_factor = 1;
276 multiplicity_factor = 1;
277 params = "a;b;c";
278 ]
279 edge [pdg=1000]
280 ext [style=invis]
281 ext -> v4
282 ext -> v5
283 v6 -> ext
284 v5 -> v4 [lmb_index=0];
285 v6 -> v5;
286 v4 -> v6;
287 },"scalars")
288 {
289 Ok(g) => {
290 let g: Graph = g;
291 let serialized = g.dot_serialize(&DotExportSettings::default());
293 let parsed: DotGraph<NodeStorageVec<DotVertexData>> =
294 DotGraph::from_string(serialized).unwrap();
295
296 assert_eq!(
297 parsed.global_data.statements.get("params"),
298 Some(&"a;b;c".to_string())
299 );
300 }
301 Err(e) => {
302 eprintln!("Graph parsing failed: {:?}", e);
303 }
304 }
305 }
306}