1use crate::GammaLoopContext;
2use crate::cff::esurface::EsurfaceCollection;
3use crate::cff::esurface::EsurfaceID;
4use crate::cff::esurface::ExistingEsurfaceId;
5use crate::cff::esurface::ExistingEsurfaces;
6use crate::cff::esurface::GroupEsurfaceId;
7use crate::cff::esurface::RaisedEsurfaceData;
8use crate::cff::esurface::RaisedEsurfaceId;
9use crate::cff::esurface::{esurface_value_is_strictly_inside, get_representative};
10use crate::graph::GraphGroupPosition;
11use crate::graph::LoopMomentumBasis;
12use crate::integrands::process::GenericEvaluator;
13use crate::momentum::ThreeMomentum;
14use crate::momentum::sample::ExternalFourMomenta;
15use crate::momentum::sample::LoopMomenta;
16use crate::momentum::signature::LoopExtSignature;
17use crate::processes::EvaluatorSettings;
18use crate::settings::RuntimeSettings;
19use crate::utils::F;
20use crate::utils::GS;
21use crate::utils::compute_shift_part;
22use crate::utils::hyperdual_utils::simple_n_deriv_shape;
23use ahash::HashMap;
24use ahash::HashMapExt;
25use ahash::HashSet;
26use bincode_trait_derive::Decode;
27use bincode_trait_derive::Encode;
28use clarabel::algebra::*;
29use clarabel::solver::*;
30use eyre::{Result, eyre};
31use itertools::Itertools;
32use linnet::half_edge::involution::EdgeVec;
33use spenso::algebra::algebraic_traits::IsZero;
34use std::cell::RefCell;
35use std::fmt::Display;
36use symbolica::atom::Atom;
37use symbolica::atom::AtomCore;
38use symbolica::evaluate::FunctionMap;
39use symbolica::evaluate::OptimizationSettings;
40use symbolica::function;
41use typed_index_collections::TiVec;
42
43#[derive(Debug, Clone, Encode, Decode)]
44#[trait_decode(trait = GammaLoopContext)]
45pub struct OverlapGroup {
46 pub existing_esurfaces: Vec<ExistingEsurfaceId>,
47 pub complement: Vec<ExistingEsurfaceId>,
48 pub center: LoopMomenta<F<f64>>,
51 pub prefactor_evaluator: Option<Vec<RefCell<GenericEvaluator>>>,
52}
53
54#[derive(Debug, Clone, Encode, Decode)]
55#[trait_decode(trait = GammaLoopContext)]
56pub struct OverlapStructure {
57 pub overlap_groups: Vec<OverlapGroup>,
58 pub existing_esurfaces: ExistingEsurfaces,
59}
60
61impl Display for OverlapStructure {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 let existing_esurfaces: Vec<_> = self.existing_esurfaces.iter().map(|id| id.0).collect();
64 writeln!(f, "existing esurfaces: {:?}", existing_esurfaces)?;
65
66 for (i, group) in self.overlap_groups.iter().enumerate() {
67 writeln!(f, "Group {}:", i)?;
68 let existing_esurfaces_in_group: Vec<_> = group
69 .existing_esurfaces
70 .iter()
71 .map(|id| self.existing_esurfaces[*id].0)
72 .collect();
73
74 writeln!(f, "center:\n {}", group.center)?;
75 writeln!(
76 f,
77 "existing esurfaces in group: {:?}",
78 existing_esurfaces_in_group
79 )?;
80 }
81
82 Ok(())
83 }
84}
85
86impl OverlapStructure {
87 pub fn fill_in_complements(&mut self) {
88 for group in self.overlap_groups.iter_mut() {
89 group.complement = self
90 .existing_esurfaces
91 .iter_enumerated()
92 .map(|(existing_esurface_id, _)| existing_esurface_id)
93 .filter(|&existing_esurface_id| {
94 !group.existing_esurfaces.contains(&existing_esurface_id)
95 })
96 .collect();
97 }
98 }
99
100 pub fn build_evaluators(
101 &mut self,
102 atoms: &TiVec<GroupEsurfaceId, Atom>,
103 optimization_settings: &OptimizationSettings,
104 num_loops: usize,
105 num_externals: usize,
106 model_params: Vec<Atom>,
107 power: i32,
108 ) -> Result<()> {
109 let group_square_atoms = self
110 .overlap_groups
111 .iter()
112 .map(|group| {
113 group
114 .complement
115 .iter()
116 .map(|&existing_esurface_id| {
117 let esurface = self.existing_esurfaces[existing_esurface_id];
118 let atom = &atoms[esurface];
119 atom.pow(power)
120 })
121 .reduce(|prod, atom| prod * atom)
122 .unwrap_or_else(|| Atom::num(1))
123 })
124 .collect_vec();
125
126 let denominator = group_square_atoms
127 .iter()
128 .fold(Atom::new(), |sum, atom| sum + atom);
129
130 let params = (0..num_loops)
131 .flat_map(|loop_index| {
132 (1..=3).map(move |spatial_index| function!(GS.loop_mom, loop_index, spatial_index))
133 })
134 .chain((0..num_externals).flat_map(|external_index| {
135 (0..=3).map(move |spatial_index| {
136 function!(GS.external_mom, external_index, spatial_index)
137 })
138 }))
139 .chain(model_params)
140 .collect_vec();
141
142 for (group, square_atom) in self.overlap_groups.iter_mut().zip(group_square_atoms) {
143 let atom = square_atom / &denominator;
144 let num_orders = power.saturating_sub(1).max(1) as usize;
145
146 let evaluators = (0..num_orders)
147 .map(|order_index| {
148 GenericEvaluator::new_from_raw_params(
149 [atom.clone()],
150 ¶ms,
151 &FunctionMap::new(),
152 vec![],
153 optimization_settings.clone(),
154 (order_index > 0)
155 .then(|| simple_n_deriv_shape(order_index))
156 .map(|shape| (shape, Vec::new())),
157 &EvaluatorSettings::default(),
158 )
159 .map(RefCell::new)
160 })
161 .collect::<Result<Vec<_>>>()?;
162
163 group.prefactor_evaluator = Some(evaluators);
164 }
165
166 Ok(())
167 }
168
169 pub fn new_empty() -> Self {
170 Self {
171 overlap_groups: vec![],
172 existing_esurfaces: ExistingEsurfaces::new(),
173 }
174 }
175
176 pub(crate) fn localized_to_existing_surfaces(
177 &self,
178 local_esurface_exists: &TiVec<GroupEsurfaceId, bool>,
179 ) -> Self {
180 let mut remapped_existing_esurfaces = ExistingEsurfaces::new();
181 let mut existing_esurface_map: TiVec<ExistingEsurfaceId, Option<ExistingEsurfaceId>> =
182 TiVec::with_capacity(self.existing_esurfaces.len());
183
184 for &group_esurface_id in self.existing_esurfaces.iter() {
185 if local_esurface_exists[group_esurface_id] {
186 let remapped_existing_esurface_id =
187 ExistingEsurfaceId::from(remapped_existing_esurfaces.len());
188 remapped_existing_esurfaces.push(group_esurface_id);
189 existing_esurface_map.push(Some(remapped_existing_esurface_id));
190 } else {
191 existing_esurface_map.push(None);
192 }
193 }
194
195 let mut localized_overlap_groups = Vec::with_capacity(self.overlap_groups.len());
196 for overlap_group in &self.overlap_groups {
197 let mut localized_existing_esurfaces = overlap_group
198 .existing_esurfaces
199 .iter()
200 .filter_map(|&existing_esurface_id| existing_esurface_map[existing_esurface_id])
201 .collect_vec();
202 localized_existing_esurfaces.sort_unstable();
203 localized_existing_esurfaces.dedup();
204
205 if localized_existing_esurfaces.is_empty()
206 || localized_overlap_groups.iter().any(|group: &OverlapGroup| {
207 group.existing_esurfaces == localized_existing_esurfaces
208 })
209 {
210 continue;
211 }
212
213 localized_overlap_groups.push(OverlapGroup {
214 existing_esurfaces: localized_existing_esurfaces,
215 complement: vec![],
216 center: overlap_group.center.clone(),
217 prefactor_evaluator: None,
218 });
219 }
220
221 let mut localized = Self {
222 overlap_groups: localized_overlap_groups,
223 existing_esurfaces: remapped_existing_esurfaces,
224 };
225 localized.fill_in_complements();
226 localized
227 }
228}
229struct PropagatorConstraint<'a> {
231 mass_pointer: Option<usize>, signature: &'a LoopExtSignature,
233}
234
235impl PropagatorConstraint<'_> {
236 fn get_dimension(&self) -> usize {
237 let mass_value = if self.mass_pointer.is_some() { 1 } else { 0 };
238
239 3 + mass_value + 1
240 }
241}
242
243fn extract_center(num_loops: usize, solution: &[f64]) -> LoopMomenta<F<f64>> {
244 let len = solution.len();
245 let num_loop_vars = 3 * num_loops;
246
247 solution[len - num_loop_vars..]
248 .chunks(3)
249 .map(|window| {
250 ThreeMomentum::new(
251 F::from_f64(window[0]),
252 F::from_f64(window[1]),
253 F::from_f64(window[2]),
254 )
255 })
256 .collect()
257}
258
259fn construct_solver(
260 overlap_input: &OverlapInput,
261 esurfaces_to_consider: &[ExistingEsurfaceId],
262 existing_esurfaces: &ExistingEsurfaces,
263 external_momenta: &ExternalFourMomenta<F<f64>>,
264 verbose: bool,
265) -> DefaultSolver {
266 let num_loops = overlap_input
267 .graph_data
268 .first()
269 .expect("no graphs passed to overlap")
270 .lmb
271 .loop_edges
272 .len();
273
274 let num_loop_vars = 3 * num_loops;
275
276 let mut propagator_constraints: Vec<PropagatorConstraint> = Vec::with_capacity(20);
278
279 let mut inequivalent_masses: Vec<F<f64>> = vec![];
280
281 let mut esurface_constraints: Vec<Vec<usize>> = Vec::with_capacity(esurfaces_to_consider.len());
282 let local_esurfaces_to_consider = esurfaces_to_consider
283 .iter()
284 .flat_map(|existing_esurface_id| {
285 let group_esurface_id = existing_esurfaces[*existing_esurface_id];
286 overlap_input.group_esurface_map[group_esurface_id]
287 .iter_enumerated()
288 .filter_map(move |(graph_group_pos, option_raised_esurface_id)| {
289 option_raised_esurface_id.and_then(|raised_esurface_id| {
290 overlap_input.local_esurface_exists[graph_group_pos][group_esurface_id]
291 .then_some((*existing_esurface_id, graph_group_pos, raised_esurface_id))
292 })
293 })
294 })
295 .collect_vec();
296
297 for (_, graph_group_pos, raised_esurface_id) in local_esurfaces_to_consider.iter().copied() {
298 let esurface_id = representative_local_esurface_id(
299 &overlap_input.graph_data[graph_group_pos],
300 raised_esurface_id,
301 );
302
303 let esurface = &overlap_input.graph_data[graph_group_pos].esurfaces[esurface_id];
304 let lmb = overlap_input.graph_data[graph_group_pos].lmb;
305 let edge_masses = &overlap_input.graph_data[graph_group_pos].edge_masses;
306
307 let mut esurface_constraint_indices: Vec<usize> = Vec::with_capacity(6);
308
309 for &edge_id in &esurface.energies {
310 if let Some(edge_position) = propagator_constraints
311 .iter()
312 .position(|constraint| *constraint.signature == lmb.edge_signatures[edge_id])
313 {
314 esurface_constraint_indices.push(edge_position);
315 } else {
316 let mass_pointer = if edge_masses[edge_id].is_zero() {
317 None
318 } else {
319 Some(
320 if let Some(mass_position) = inequivalent_masses
321 .iter()
322 .position(|&x| x == edge_masses[edge_id])
323 {
324 mass_position
325 } else {
326 inequivalent_masses.push(edge_masses[edge_id]);
327 inequivalent_masses.len() - 1
328 },
329 )
330 };
331
332 let signature = &lmb.edge_signatures[edge_id];
333
334 let propagator_constraint = PropagatorConstraint {
335 mass_pointer,
336 signature,
337 };
338
339 propagator_constraints.push(propagator_constraint);
340 esurface_constraint_indices.push(propagator_constraints.len() - 1);
341 };
342 }
343
344 esurface_constraints.push(esurface_constraint_indices);
345 }
346
347 let propagator_index_offset = 1;
350 let loop_momentum_offset = propagator_index_offset + propagator_constraints.len();
351
352 let num_primal_variables = loop_momentum_offset + num_loop_vars;
353
354 let cone_dimension_sum = propagator_constraints
355 .iter()
356 .map(|constraint| constraint.get_dimension())
357 .sum::<usize>();
358
359 let num_constaints = 1 + esurface_constraints.len() + cone_dimension_sum;
360
361 let p_matrix: CscMatrix<f64> =
363 CscMatrix::spalloc((num_primal_variables, num_primal_variables), 0);
364
365 let mut q_vector = vec![0.0; num_primal_variables];
367 q_vector[0] = 1.0;
368
369 let mut cones: Vec<SupportedConeT<f64>> = Vec::with_capacity(1 + propagator_constraints.len());
371 cones.push(NonnegativeConeT(1));
372 cones.push(NonnegativeConeT(esurface_constraints.len()));
373
374 for prop_constraint in &propagator_constraints {
375 cones.push(SecondOrderConeT(prop_constraint.get_dimension()));
376 }
377
378 let mut a_matrix = vec![vec![0.0; num_primal_variables]; num_constaints];
382 let mut b_vector = vec![0.0; num_constaints];
383
384 a_matrix[0][0] = 1.0;
385 for (constraint_index, ((_, graph_group_pos, raised_esurface_id), esurface_constraint)) in
387 local_esurfaces_to_consider
388 .iter()
389 .zip(esurface_constraints.iter())
390 .enumerate()
391 {
392 for prop_index in esurface_constraint {
393 a_matrix[constraint_index + 1][*prop_index + propagator_index_offset] = 1.0;
394 }
395 let esurface_id = representative_local_esurface_id(
396 &overlap_input.graph_data[*graph_group_pos],
397 *raised_esurface_id,
398 );
399 let lmb = overlap_input.graph_data[*graph_group_pos].lmb;
400 let esurface = &overlap_input.graph_data[*graph_group_pos].esurfaces[esurface_id];
401
402 let shift_part = esurface.compute_shift_part_from_momenta(external_momenta, lmb);
403 b_vector[constraint_index + 1] = -shift_part.0;
404 a_matrix[constraint_index + 1][0] = -1.0;
405 }
406
407 let mut vertical_offset = esurface_constraints.len() + 1;
409 for (cone_index, propagator_constraint) in propagator_constraints.iter().enumerate() {
410 a_matrix[vertical_offset][propagator_index_offset + cone_index] = -1.0;
411 vertical_offset += 1;
412
413 let spatial_shift =
414 compute_shift_part(&propagator_constraint.signature.external, external_momenta);
415
416 b_vector[vertical_offset] = spatial_shift.spatial.px.0;
417 b_vector[vertical_offset + 1] = spatial_shift.spatial.py.0;
418 b_vector[vertical_offset + 2] = spatial_shift.spatial.pz.0;
419
420 for (loop_index, individual_loop_signature) in
421 propagator_constraint.signature.internal.iter().enumerate()
422 {
423 if individual_loop_signature.is_sign() {
424 a_matrix[vertical_offset][loop_momentum_offset + 3 * loop_index] =
425 -(*individual_loop_signature as i8) as f64;
426 a_matrix[vertical_offset + 1][loop_momentum_offset + 3 * loop_index + 1] =
427 -(*individual_loop_signature as i8) as f64;
428 a_matrix[vertical_offset + 2][loop_momentum_offset + 3 * loop_index + 2] =
429 -(*individual_loop_signature as i8) as f64;
430 }
431 }
432
433 vertical_offset += 3;
434
435 if let Some(mass_index) = propagator_constraint.mass_pointer {
436 b_vector[vertical_offset] = inequivalent_masses[mass_index].0;
437 vertical_offset += 1;
438 }
439 }
440
441 let a_matrix_sparse = CscMatrix::from(&a_matrix);
442
443 let settings = DefaultSettingsBuilder::default()
444 .verbose(verbose)
445 .build()
446 .unwrap();
447
448 DefaultSolver::new(
449 &p_matrix,
450 &q_vector,
451 &a_matrix_sparse,
452 &b_vector,
453 &cones,
454 settings,
455 )
456 .unwrap()
457}
458
459pub(crate) fn find_center(
460 overlap_input: &OverlapInput,
461 esurfaces_to_consider: &[ExistingEsurfaceId],
462 existing_esurfaces: &ExistingEsurfaces,
463 external_momenta: &ExternalFourMomenta<F<f64>>,
464 verbose: bool,
465) -> Result<Option<LoopMomenta<F<f64>>>> {
466 let mut solver = construct_solver(
467 overlap_input,
468 esurfaces_to_consider,
469 existing_esurfaces,
470 external_momenta,
471 verbose,
472 );
473
474 solver.solve();
475
476 crate::debug_tags!(#subtraction, #threshold, #overlap, #socp;
477 stage = "threshold_socp_result",
478 surfaces = ?esurfaces_to_consider,
479 status = ?solver.solution.status,
480 iterations = solver.solution.iterations,
481 primal_objective = solver.solution.obj_val,
482 dual_objective = solver.solution.obj_val_dual,
483 primal_residual = solver.solution.r_prim,
484 dual_residual = solver.solution.r_dual,
485 file.solution = ?solver.solution,
486 "finished threshold overlap solve"
487 );
488
489 let loop_number = overlap_input
490 .graph_data
491 .first()
492 .expect("no graphs passed to overlap")
493 .lmb
494 .loop_edges
495 .len();
496
497 let group_esurfaces_to_check = esurfaces_to_consider
498 .iter()
499 .map(|&existing_esurface_id| existing_esurfaces[existing_esurface_id])
500 .collect_vec();
501
502 let center = extract_center(loop_number, &solver.solution.x);
504 if check_center_for_group_esurfaces(
505 overlap_input,
506 &group_esurfaces_to_check,
507 ¢er,
508 external_momenta,
509 ) {
510 return Ok(Some(center));
511 }
512
513 if solver.solution.status == SolverStatus::PrimalInfeasible {
514 return Ok(None);
515 }
516
517 if verbose {
518 println!("{:?}", solver.solution.x);
519 }
520
521 Err(eyre!(
522 "Threshold overlap solve has no valid interior center or primal-infeasibility certificate: status={:?}, existing_surface_ids={:?}, group_surface_ids={:?}, primal_objective={}, dual_objective={}, primal_residual={}, dual_residual={}, iterations={}",
523 solver.solution.status,
524 esurfaces_to_consider,
525 group_esurfaces_to_check,
526 solver.solution.obj_val,
527 solver.solution.obj_val_dual,
528 solver.solution.r_prim,
529 solver.solution.r_dual,
530 solver.solution.iterations,
531 ))
532}
533
534pub struct SingleGraphOverlapData<'a> {
535 pub lmb: &'a LoopMomentumBasis,
536 pub esurfaces: &'a EsurfaceCollection,
537 pub raised_data: &'a RaisedEsurfaceData,
538 pub edge_masses: EdgeVec<F<f64>>,
539}
540
541pub struct OverlapInput<'a> {
542 pub graph_data: TiVec<GraphGroupPosition, SingleGraphOverlapData<'a>>,
543 pub settings: &'a RuntimeSettings,
544 pub group_esurface_map:
545 TiVec<GroupEsurfaceId, TiVec<GraphGroupPosition, Option<RaisedEsurfaceId>>>,
546 pub local_esurface_exists: TiVec<GraphGroupPosition, TiVec<GroupEsurfaceId, bool>>,
547}
548
549fn representative_local_esurface_id(
550 graph_data: &SingleGraphOverlapData,
551 raised_esurface_id: RaisedEsurfaceId,
552) -> EsurfaceID {
553 graph_data.raised_data.raised_groups[raised_esurface_id].esurface_ids[0]
554}
555
556fn check_center_for_group_esurfaces(
557 overlap_input: &OverlapInput,
558 group_esurfaces: &[GroupEsurfaceId],
559 center: &LoopMomenta<F<f64>>,
560 external_momenta: &ExternalFourMomenta<F<f64>>,
561) -> bool {
562 group_esurfaces.iter().all(|&group_esurface_id| {
563 let mut has_local_esurface = false;
564
565 let all_local_valid = overlap_input.group_esurface_map[group_esurface_id]
566 .iter_enumerated()
567 .filter_map(|(graph_group_pos, option_raised_esurface_id)| {
568 option_raised_esurface_id.and_then(|raised_esurface_id| {
569 overlap_input.local_esurface_exists[graph_group_pos][group_esurface_id]
570 .then_some((graph_group_pos, raised_esurface_id))
571 })
572 })
573 .all(|(graph_group_position, raised_esurface_id)| {
574 has_local_esurface = true;
575 let esurface_id = representative_local_esurface_id(
576 &overlap_input.graph_data[graph_group_position],
577 raised_esurface_id,
578 );
579 let esurface =
580 &overlap_input.graph_data[graph_group_position].esurfaces[esurface_id];
581
582 let lmb = overlap_input.graph_data[graph_group_position].lmb;
583 let edge_masses = &overlap_input.graph_data[graph_group_position].edge_masses;
584
585 let esurface_val =
586 esurface.compute_from_momenta(lmb, edge_masses, center, external_momenta);
587
588 esurface_value_is_strictly_inside(
589 &esurface_val,
590 &F(overlap_input.settings.kinematics.e_cm),
591 )
592 });
593
594 has_local_esurface && all_local_valid
595 })
596}
597
598pub(crate) fn check_global_center(
599 overlap_input: &OverlapInput,
600 existing_esurfaces: &ExistingEsurfaces,
601 center: &LoopMomenta<F<f64>>,
602 external_momenta: &ExternalFourMomenta<F<f64>>,
603) -> bool {
604 let group_esurfaces = existing_esurfaces.iter().copied().collect_vec();
605 check_center_for_group_esurfaces(overlap_input, &group_esurfaces, center, external_momenta)
606}
607
608pub(crate) fn find_maximal_overlap(
611 overlap_input: &OverlapInput,
612 existing_esurfaces: &ExistingEsurfaces,
613 external_momenta: &ExternalFourMomenta<F<f64>>,
614) -> Result<OverlapStructure> {
615 let mut res = OverlapStructure {
616 overlap_groups: vec![],
617 existing_esurfaces: existing_esurfaces.clone(),
618 };
619
620 let settings = overlap_input.settings;
621
622 let all_existing_esurfaces = existing_esurfaces
623 .iter_enumerated()
624 .map(|a| a.0)
625 .collect_vec();
626
627 if let Some(global_center) = &settings.subtraction.overlap_settings.force_global_center {
628 let global_center_f = global_center
629 .iter()
630 .map(|coordinates| ThreeMomentum {
631 px: F(coordinates[0]),
632 py: F(coordinates[1]),
633 pz: F(coordinates[2]),
634 })
635 .collect();
636
637 if !settings.subtraction.overlap_settings.check_global_center {
638 tracing::warn!(
639 "overlap_settings.check_global_center=false is deprecated; forced centers are always validated"
640 );
641 }
642
643 let is_valid = check_global_center(
644 overlap_input,
645 existing_esurfaces,
646 &global_center_f,
647 external_momenta,
648 );
649
650 if !is_valid {
651 return Err(eyre!(
652 "Center provided is not finite and strictly inside all existing esurfaces"
653 ));
654 }
655
656 let single_group = OverlapGroup {
657 existing_esurfaces: all_existing_esurfaces,
658 center: global_center_f,
659 complement: vec![],
660 prefactor_evaluator: None,
661 };
662 res.overlap_groups.push(single_group);
663
664 res.fill_in_complements();
665 return Ok(res);
666 }
667
668 if settings.subtraction.overlap_settings.try_origin {
669 let global_loop_count = overlap_input
670 .graph_data
671 .first()
672 .unwrap()
673 .lmb
674 .loop_edges
675 .len();
676 let origin = LoopMomenta::from_iter(
677 (0..global_loop_count).map(|_| ThreeMomentum::new(F(0.0), F(0.0), F(0.0))),
678 );
679
680 let is_valid =
681 check_global_center(overlap_input, existing_esurfaces, &origin, external_momenta);
682
683 if is_valid {
684 let single_group = OverlapGroup {
685 existing_esurfaces: all_existing_esurfaces,
686 center: origin,
687 complement: vec![],
688 prefactor_evaluator: None,
689 };
690 res.overlap_groups.push(single_group);
691 res.fill_in_complements();
692 return Ok(res);
693 }
694 }
695
696 if settings.subtraction.overlap_settings.try_origin_all_lmbs {
697 todo!("Not all heuristics implemented")
698 }
699
700 let option_center = find_center(
703 overlap_input,
704 &all_existing_esurfaces,
705 existing_esurfaces,
706 external_momenta,
707 false,
708 )?;
709
710 if let Some(center) = option_center {
711 let single_group = OverlapGroup {
712 existing_esurfaces: all_existing_esurfaces,
713 center,
714 complement: vec![],
715 prefactor_evaluator: None,
716 };
717 res.overlap_groups.push(single_group);
718 res.fill_in_complements();
719 return Ok(res);
720 }
721
722 let esurface_pairs = EsurfacePairs::new(overlap_input, existing_esurfaces, external_momenta)?;
724
725 let mut num_disconnected_surfaces = 0;
730
731 for (existing_esurface_id, &esurface_id) in existing_esurfaces.iter_enumerated() {
732 if esurface_pairs.has_no_overlap(existing_esurface_id) {
734 let center = find_center(
735 overlap_input,
736 &[existing_esurface_id],
737 existing_esurfaces,
738 external_momenta,
739 false,
740 )?
741 .ok_or_else(|| {
742 let (graph_group_pos, raised_esurface_id) =
743 get_representative(&overlap_input.group_esurface_map[esurface_id])
744 .expect("overlap corrupted");
745 let esurface_id = representative_local_esurface_id(
746 &overlap_input.graph_data[graph_group_pos],
747 raised_esurface_id,
748 );
749
750 let esurface = &overlap_input.graph_data[graph_group_pos].esurfaces[esurface_id];
751
752 let mut error_message = String::new();
753
754 error_message.push_str(&format!(
755 "Could not find center of esuface {:?}\n",
756 esurface_id
757 ));
758 error_message.push_str(&format!("edges: {:?}\n", esurface.energies));
759
760 error_message.push_str(&format!("external shift: {:?}\n", esurface.external_shift));
761
762 error_message.push_str(&format!("External momenta: {:#?}\n", external_momenta));
763
764 eyre!("{}", error_message)
765 })?;
766
767 res.overlap_groups.push(OverlapGroup {
768 existing_esurfaces: vec![existing_esurface_id],
769 center,
770 complement: vec![],
771 prefactor_evaluator: None,
772 });
773 num_disconnected_surfaces += 1;
774 }
775 }
776
777 if num_disconnected_surfaces == existing_esurfaces.len() {
782 res.fill_in_complements();
783 return Ok(res);
784 }
785
786 let mut subset_size =
787 if let Some(size) = esurface_pairs.has_pair_with.iter().map(Vec::len).max() {
788 size + 1
789 } else {
790 1
791 };
792
793 while subset_size > 1 {
794 let possible_subsets =
795 esurface_pairs.construct_possible_subsets_of_len(existing_esurfaces, subset_size, &res);
796
797 for subset in possible_subsets.iter() {
798 let option_center = find_center(
799 overlap_input,
800 subset,
801 existing_esurfaces,
802 external_momenta,
803 false,
804 )?;
805
806 if let Some(center) = option_center {
807 res.overlap_groups.push(OverlapGroup {
808 existing_esurfaces: subset.clone(),
809 center,
810 complement: vec![],
811 prefactor_evaluator: None,
812 });
813 }
814 }
815
816 subset_size -= 1;
824 }
825
826 res.fill_in_complements();
827 Ok(res)
828}
829
830fn is_subset_of_result(subset: &[ExistingEsurfaceId], result: &OverlapStructure) -> bool {
831 result.overlap_groups.iter().any(|group| {
832 subset
833 .iter()
834 .all(|&x| group.existing_esurfaces.contains(&x))
835 })
836}
837
838#[derive(Debug)]
839struct EsurfacePairs {
840 data: HashMap<(ExistingEsurfaceId, ExistingEsurfaceId), LoopMomenta<F<f64>>>,
841 has_pair_with: Vec<Vec<ExistingEsurfaceId>>,
842}
843
844impl EsurfacePairs {
845 fn insert(
846 &mut self,
847 pair: (ExistingEsurfaceId, ExistingEsurfaceId),
848 center: LoopMomenta<F<f64>>,
849 ) {
850 if pair.0 > pair.1 {
851 self.data.insert((pair.1, pair.0), center);
852 } else {
853 self.data.insert(pair, center);
854 }
855 }
856
857 fn pair_exists(&self, pair: (ExistingEsurfaceId, ExistingEsurfaceId)) -> bool {
858 if pair.0 > pair.1 {
859 self.data.contains_key(&(pair.1, pair.0))
860 } else {
861 self.data.contains_key(&pair)
862 }
863 }
864
865 fn new_empty(num_existing_esurfaces: usize) -> Self {
866 let capacity = match num_existing_esurfaces {
867 0 => 0,
868 1 => 0,
869 _ => num_existing_esurfaces * (num_existing_esurfaces - 1) / 2,
870 };
871
872 Self {
873 data: HashMap::with_capacity(capacity),
874 has_pair_with: vec![Vec::with_capacity(num_existing_esurfaces); num_existing_esurfaces],
875 }
876 }
877
878 fn new(
879 overlap_input: &OverlapInput,
880 existing_esurfaces: &ExistingEsurfaces,
881 external_momenta: &ExternalFourMomenta<F<f64>>,
882 ) -> Result<Self> {
883 let mut res = Self::new_empty(existing_esurfaces.len());
884
885 let all_existing_esurfaces = existing_esurfaces
886 .iter_enumerated()
887 .map(|a| a.0)
888 .collect_vec();
889
890 for (i, &esurface_id_1) in all_existing_esurfaces.iter().enumerate() {
891 for &esurface_id_2 in all_existing_esurfaces.iter().skip(i + 1) {
892 let center = find_center(
893 overlap_input,
894 &[esurface_id_1, esurface_id_2],
895 existing_esurfaces,
896 external_momenta,
897 false,
898 )?;
899
900 if let Some(center) = center {
901 res.insert((esurface_id_1, esurface_id_2), center);
902 res.has_pair_with[Into::<usize>::into(esurface_id_1)].push(esurface_id_2);
903 res.has_pair_with[Into::<usize>::into(esurface_id_2)].push(esurface_id_1);
904 }
905 }
906 }
907
908 Ok(res)
909 }
910
911 fn has_no_overlap(&self, esurface_id: ExistingEsurfaceId) -> bool {
912 self.has_pair_with[Into::<usize>::into(esurface_id)].is_empty()
913 }
914
915 fn construct_possible_subsets_of_len(
916 &self,
917 existing_esurfaces: &ExistingEsurfaces,
918 subset_len: usize,
919 result: &OverlapStructure,
920 ) -> HashSet<Vec<ExistingEsurfaceId>> {
921 let mut res = HashSet::default();
922 for (first, _) in existing_esurfaces.iter_enumerated() {
925 for remaining in self.has_pair_with[usize::from(first)]
926 .iter()
927 .copied()
928 .filter(|&id| id > first)
929 .combinations(subset_len - 1)
930 {
931 if !remaining
932 .iter()
933 .tuple_combinations()
934 .all(|(&left, &right)| self.pair_exists((left, right)))
935 {
936 continue;
937 }
938 let mut candidate = vec![first];
939 candidate.extend(remaining);
940 candidate.sort_unstable();
941 if !is_subset_of_result(&candidate, result) {
942 res.insert(candidate);
943 }
944 }
945 }
946 res
947 }
948}
949
950#[cfg(test)]
951#[allow(dead_code, unused_variables)]
952mod tests {
953 use super::*;
954 use itertools::Itertools;
955 use linnet::half_edge::{
956 involution::EdgeIndex,
957 subgraph::{SuBitGraph, SubSetLike},
958 };
959 use typed_index_collections::ti_vec;
960
961 use crate::{
962 cff::{
963 VertexSet,
964 esurface::{
965 Esurface, EsurfaceExistence, EsurfaceID, RaisedEsurfaceData, RaisedEsurfaceGroup,
966 RaisedEsurfaceId,
967 },
968 },
969 graph::LoopMomentumBasis,
970 momentum::FourMomentum,
971 momentum::signature::LoopExtSignature,
972 settings::RuntimeSettings,
973 utils::test_utils::dummy_hedge_graph,
974 };
975
976 #[test]
977 fn overlap_structure_localizes_to_graph_existing_surfaces() {
978 let center = LoopMomenta::from_iter([ThreeMomentum::new(F(0.0), F(0.0), F(0.0))]);
979 let overlap = OverlapStructure {
980 existing_esurfaces: ti_vec![
981 GroupEsurfaceId::from(0),
982 GroupEsurfaceId::from(1),
983 GroupEsurfaceId::from(2),
984 ],
985 overlap_groups: vec![
986 OverlapGroup {
987 existing_esurfaces: vec![
988 ExistingEsurfaceId::from(0),
989 ExistingEsurfaceId::from(1),
990 ],
991 complement: vec![],
992 center: center.clone(),
993 prefactor_evaluator: None,
994 },
995 OverlapGroup {
996 existing_esurfaces: vec![
997 ExistingEsurfaceId::from(1),
998 ExistingEsurfaceId::from(2),
999 ],
1000 complement: vec![],
1001 center: center.clone(),
1002 prefactor_evaluator: None,
1003 },
1004 ],
1005 };
1006
1007 let localized = overlap.localized_to_existing_surfaces(&ti_vec![true, false, true]);
1008
1009 assert_eq!(
1010 localized
1011 .existing_esurfaces
1012 .iter()
1013 .map(|group_esurface_id| group_esurface_id.0)
1014 .collect_vec(),
1015 vec![0, 2]
1016 );
1017 assert_eq!(localized.overlap_groups.len(), 2);
1018 assert_eq!(
1019 localized.overlap_groups[0].existing_esurfaces,
1020 vec![ExistingEsurfaceId::from(0)]
1021 );
1022 assert_eq!(
1023 localized.overlap_groups[0].complement,
1024 vec![ExistingEsurfaceId::from(1)]
1025 );
1026 assert_eq!(
1027 localized.overlap_groups[1].existing_esurfaces,
1028 vec![ExistingEsurfaceId::from(1)]
1029 );
1030 assert_eq!(
1031 localized.overlap_groups[1].complement,
1032 vec![ExistingEsurfaceId::from(0)]
1033 );
1034 }
1035
1036 struct HelperBoxStructure {
1037 external_momenta: ExternalFourMomenta<F<f64>>,
1038 lmb: LoopMomentumBasis,
1039 esurfaces: EsurfaceCollection,
1040 raised_data: RaisedEsurfaceData,
1041 existing_esurfaces: ExistingEsurfaces,
1042 edge_masses: EdgeVec<F<f64>>,
1043 }
1044
1045 struct HelperBananaStructure {
1046 external_momenta: ExternalFourMomenta<F<f64>>,
1047 lmb: LoopMomentumBasis,
1048 esurfaces: EsurfaceCollection,
1049 raised_data: RaisedEsurfaceData,
1050 existing_esurfaces: ExistingEsurfaces,
1051 edge_masses: EdgeVec<F<f64>>,
1052 }
1053
1054 fn trivial_raised_data(num_esurfaces: usize) -> RaisedEsurfaceData {
1055 RaisedEsurfaceData {
1056 raised_groups: (0..num_esurfaces)
1057 .map(|index| RaisedEsurfaceGroup {
1058 esurface_ids: vec![EsurfaceID::from(index)],
1059 max_occurence: 1,
1060 })
1061 .collect(),
1062 pass_two_evaluator: None,
1063 }
1064 }
1065
1066 impl HelperBoxStructure {
1067 fn new(masses: Option<[F<f64>; 4]>) -> Self {
1068 let external_momenta = ExternalFourMomenta::from_iter([
1069 FourMomentum::from_args(F(14.0), F(-6.6), F(-40.0), F(0.0)),
1070 FourMomentum::from_args(F(-43.0), F(15.2), F(33.0), F(0.0)),
1071 FourMomentum::from_args(F(-17.9), F(-50.0), F(11.8), F(0.0)),
1072 ]);
1073
1074 let dummy_hedge_graph = dummy_hedge_graph(8);
1075
1076 let box_basis = ti_vec![EdgeIndex::from(4)];
1077 let box_signatures = dummy_hedge_graph
1078 .new_edgevec_from_iter(vec![
1079 (vec![0], vec![1, 0, 0]).into(),
1080 (vec![0], vec![0, 1, 0]).into(),
1081 (vec![0], vec![0, 0, 1]).into(),
1082 (vec![0], vec![-1, -1, -1]).into(),
1083 (vec![1], vec![0, 0, 0]).into(),
1084 (vec![1], vec![1, 0, 0]).into(),
1085 (vec![1], vec![1, 1, 0]).into(),
1086 (vec![1], vec![1, 1, 1]).into(),
1087 ])
1088 .unwrap();
1089
1090 let box_lmb = LoopMomentumBasis {
1091 tree: SuBitGraph::empty(0),
1092 ext_edges: vec![].into(),
1093 loop_edges: box_basis,
1094 edge_signatures: box_signatures,
1095 };
1096
1097 let esurfaces_array = [
1098 Esurface {
1099 energies: vec![EdgeIndex::from(5), EdgeIndex::from(6)],
1100 external_shift: vec![(EdgeIndex::from(1), 1)],
1101 vertex_set: VertexSet::dummy(),
1102 },
1104 Esurface {
1105 energies: vec![EdgeIndex::from(5), EdgeIndex::from(7)],
1106 external_shift: vec![(EdgeIndex::from(1), 1), (EdgeIndex::from(2), 1)],
1107 vertex_set: VertexSet::dummy(),
1108 },
1110 Esurface {
1111 energies: vec![EdgeIndex::from(4), EdgeIndex::from(6)],
1112 external_shift: vec![(EdgeIndex::from(0), 1), (EdgeIndex::from(1), 1)],
1113 vertex_set: VertexSet::dummy(),
1114 },
1116 Esurface {
1117 energies: vec![EdgeIndex::from(4), EdgeIndex::from(7)],
1118 external_shift: vec![
1119 (EdgeIndex::from(0), 1),
1120 (EdgeIndex::from(1), 1),
1121 (EdgeIndex::from(2), 1),
1122 ],
1123 vertex_set: VertexSet::dummy(),
1124 },
1126 ];
1127
1128 let esurfaces = esurfaces_array.to_vec().into();
1129
1130 let edge_masses = match masses {
1131 Some(masses) => {
1132 let mut edge_masses = vec![F(0.0); 4];
1133 let mut real_masses = masses.iter().copied().collect_vec();
1134
1135 edge_masses.append(&mut real_masses);
1136 edge_masses
1137 }
1138 None => vec![F(0.0); 8],
1139 };
1140
1141 let existing_esurfaces = (0..4).map(Into::<GroupEsurfaceId>::into).collect();
1142
1143 Self {
1144 external_momenta,
1145 lmb: box_lmb,
1146 existing_esurfaces,
1147 esurfaces,
1148 raised_data: trivial_raised_data(4),
1149 edge_masses: dummy_hedge_graph
1150 .new_edgevec_from_iter(edge_masses)
1151 .unwrap(),
1152 }
1153 }
1154 }
1155
1156 impl HelperBananaStructure {
1157 fn new() -> Self {
1158 let external_momenta = ExternalFourMomenta::from_iter([FourMomentum::from_args(
1159 F(10.0),
1160 F(-10.00000000),
1161 F(0.0),
1162 F(0.0),
1163 )]);
1164 let banana_basis = ti_vec![EdgeIndex::from(2), EdgeIndex::from(3)];
1165
1166 let dummy_hedge_graph = dummy_hedge_graph(5);
1167
1168 let banana_edge_sigs = dummy_hedge_graph
1169 .new_edgevec_from_iter(vec![
1170 LoopExtSignature {
1171 internal: vec![0, 0].into(),
1172 external: vec![1].into(),
1173 },
1174 LoopExtSignature {
1175 internal: vec![0, 0].into(),
1176 external: vec![-1].into(),
1177 },
1178 LoopExtSignature {
1179 internal: vec![1, 0].into(),
1180 external: vec![0].into(),
1181 },
1182 LoopExtSignature {
1183 internal: vec![0, 1].into(),
1184 external: vec![0].into(),
1185 },
1186 LoopExtSignature {
1187 internal: vec![1, 1].into(),
1188 external: vec![-1].into(),
1189 },
1190 ])
1191 .unwrap();
1192
1193 let banana_lmb = LoopMomentumBasis {
1194 tree: SuBitGraph::empty(0),
1195 loop_edges: banana_basis,
1196 ext_edges: vec![].into(),
1197 edge_signatures: banana_edge_sigs,
1198 };
1199
1200 let only_esurface = Esurface {
1201 energies: vec![EdgeIndex::from(2), EdgeIndex::from(3), EdgeIndex::from(4)],
1202 external_shift: vec![(EdgeIndex::from(0), -1)],
1203 vertex_set: VertexSet::dummy(),
1204 };
1206
1207 let esurfaces = vec![only_esurface].into();
1208
1209 let existing_esurfaces = vec![Into::<GroupEsurfaceId>::into(0)].into();
1210 let edge_masses = dummy_hedge_graph
1211 .new_edgevec_from_iter(vec![F(0.0); 5])
1212 .unwrap();
1213
1214 Self {
1215 external_momenta,
1216 lmb: banana_lmb,
1217 esurfaces,
1218 raised_data: trivial_raised_data(1),
1219 existing_esurfaces,
1220 edge_masses,
1221 }
1222 }
1223 }
1224
1225 #[test]
1226 fn test_is_subset_of_result() {
1227 let fake_res = vec![
1228 (
1229 vec![
1230 Into::<ExistingEsurfaceId>::into(1),
1231 Into::<ExistingEsurfaceId>::into(2),
1232 Into::<ExistingEsurfaceId>::into(3),
1233 ],
1234 LoopMomenta::from(vec![]),
1235 ),
1236 (
1237 vec![
1238 Into::<ExistingEsurfaceId>::into(1),
1239 Into::<ExistingEsurfaceId>::into(2),
1240 Into::<ExistingEsurfaceId>::into(4),
1241 ],
1242 LoopMomenta::from(vec![]),
1243 ),
1244 (
1245 vec![
1246 Into::<ExistingEsurfaceId>::into(2),
1247 Into::<ExistingEsurfaceId>::into(3),
1248 Into::<ExistingEsurfaceId>::into(4),
1249 ],
1250 LoopMomenta::from(vec![]),
1251 ),
1252 ];
1253
1254 let fake_res = OverlapStructure {
1255 overlap_groups: fake_res
1256 .into_iter()
1257 .map(|(group, center)| OverlapGroup {
1258 existing_esurfaces: group,
1259 center,
1260 complement: vec![],
1261 prefactor_evaluator: None,
1262 })
1263 .collect_vec(),
1264 existing_esurfaces: ti_vec![],
1265 };
1266
1267 let fake_subset = vec![
1268 Into::<ExistingEsurfaceId>::into(1),
1269 Into::<ExistingEsurfaceId>::into(2),
1270 ];
1271
1272 assert!(is_subset_of_result(&fake_subset, &fake_res));
1273
1274 let fake_subset_2 = vec![
1275 Into::<ExistingEsurfaceId>::into(0),
1276 Into::<ExistingEsurfaceId>::into(4),
1277 ];
1278
1279 assert!(!is_subset_of_result(&fake_subset_2, &fake_res));
1280 }
1281
1282 #[test]
1283 fn test_pair_creator() {
1284 let box4e = HelperBoxStructure::new(None);
1285
1286 let massless_overlap_input = OverlapInput {
1287 graph_data: ti_vec![SingleGraphOverlapData {
1288 lmb: &box4e.lmb,
1289 esurfaces: &box4e.esurfaces,
1290 raised_data: &box4e.raised_data,
1291 edge_masses: box4e.edge_masses.clone(),
1292 }],
1293 settings: &RuntimeSettings::default(),
1294 group_esurface_map: (0..4)
1295 .map(|i| ti_vec![Some(Into::<RaisedEsurfaceId>::into(i))])
1296 .collect(),
1297 local_esurface_exists: ti_vec![ti_vec![true; 4]],
1298 };
1299
1300 let esurface_pairs = EsurfacePairs::new(
1301 &massless_overlap_input,
1302 &box4e.existing_esurfaces,
1303 &box4e.external_momenta,
1304 )
1305 .unwrap();
1306
1307 assert_eq!(esurface_pairs.data.len(), 4);
1308
1309 let box4e_massive = HelperBoxStructure::new(Some([F(10.5); 4]));
1310
1311 let massive_overlap_input = OverlapInput {
1312 graph_data: ti_vec![SingleGraphOverlapData {
1313 lmb: &box4e_massive.lmb,
1314 esurfaces: &box4e_massive.esurfaces,
1315 raised_data: &box4e_massive.raised_data,
1316 edge_masses: box4e_massive.edge_masses.clone(),
1317 }],
1318 settings: &RuntimeSettings::default(),
1319 group_esurface_map: (0..4)
1320 .map(|i| ti_vec![Some(Into::<RaisedEsurfaceId>::into(i))])
1321 .collect(),
1322 local_esurface_exists: ti_vec![ti_vec![true; 4]],
1323 };
1324
1325 let esurface_pairs_massive = EsurfacePairs::new(
1326 &massive_overlap_input,
1327 &box4e_massive.existing_esurfaces,
1328 &box4e_massive.external_momenta,
1329 )
1330 .unwrap();
1331
1332 assert_eq!(esurface_pairs_massive.data.len(), 0);
1333 }
1334
1335 #[test]
1336 fn maximal_overlap_candidates_include_cliques_whose_pairs_are_already_covered() {
1337 let existing: ExistingEsurfaces = (0..9).map(GroupEsurfaceId::from).collect();
1338 let center = LoopMomenta::from_iter([ThreeMomentum::new(F(0.0), F(0.0), F(0.0))]);
1339 let larger = [[0, 1, 3, 4], [0, 2, 5, 6], [1, 2, 7, 8]];
1342 let result = OverlapStructure {
1343 existing_esurfaces: existing.clone(),
1344 overlap_groups: larger
1345 .map(|members| OverlapGroup {
1346 existing_esurfaces: members.into_iter().map(ExistingEsurfaceId::from).collect(),
1347 complement: vec![],
1348 center: center.clone(),
1349 prefactor_evaluator: None,
1350 })
1351 .to_vec(),
1352 };
1353 let mut pairs = EsurfacePairs::new_empty(existing.len());
1354 for members in larger {
1355 for (left, right) in members
1356 .into_iter()
1357 .map(ExistingEsurfaceId::from)
1358 .tuple_combinations()
1359 {
1360 if !pairs.pair_exists((left, right)) {
1361 pairs.insert((left, right), center.clone());
1362 pairs.has_pair_with[usize::from(left)].push(right);
1363 pairs.has_pair_with[usize::from(right)].push(left);
1364 }
1365 }
1366 }
1367 let central = (0..3).map(ExistingEsurfaceId::from).collect_vec();
1368 assert!(
1369 central
1370 .iter()
1371 .tuple_combinations()
1372 .all(|(&left, &right)| { is_subset_of_result(&[left, right], &result) })
1373 );
1374 assert_eq!(
1375 pairs.construct_possible_subsets_of_len(&existing, 3, &result),
1376 HashSet::from_iter([central]),
1377 );
1378 }
1379
1380 #[test]
1381 fn test_subset_generator() {
1382 let box4e = HelperBoxStructure::new(None);
1383
1384 let overlap_input = OverlapInput {
1385 graph_data: ti_vec![SingleGraphOverlapData {
1386 lmb: &box4e.lmb,
1387 esurfaces: &box4e.esurfaces,
1388 raised_data: &box4e.raised_data,
1389 edge_masses: box4e.edge_masses.clone(),
1390 }],
1391 settings: &RuntimeSettings::default(),
1392 group_esurface_map: (0..4)
1393 .map(|i| ti_vec![Some(Into::<RaisedEsurfaceId>::into(i))])
1394 .collect(),
1395 local_esurface_exists: ti_vec![ti_vec![true; 4]],
1396 };
1397
1398 let esurface_pairs = EsurfacePairs::new(
1399 &overlap_input,
1400 &box4e.existing_esurfaces,
1401 &box4e.external_momenta,
1402 )
1403 .unwrap();
1404
1405 let res = OverlapStructure {
1406 overlap_groups: vec![],
1407 existing_esurfaces: box4e.existing_esurfaces.clone(),
1408 };
1409 let subsets_3 =
1410 esurface_pairs.construct_possible_subsets_of_len(&box4e.existing_esurfaces, 3, &res);
1411
1412 assert_eq!(subsets_3.len(), 0);
1413
1414 let subsets_2 =
1415 esurface_pairs.construct_possible_subsets_of_len(&box4e.existing_esurfaces, 2, &res);
1416 assert_eq!(subsets_2.len(), 4);
1417 }
1418
1419 #[test]
1420 fn test_box_4e() {
1421 let box4e = HelperBoxStructure::new(None);
1423
1424 let massless_overlap_input = OverlapInput {
1425 graph_data: ti_vec![SingleGraphOverlapData {
1426 lmb: &box4e.lmb,
1427 esurfaces: &box4e.esurfaces,
1428 raised_data: &box4e.raised_data,
1429 edge_masses: box4e.edge_masses.clone(),
1430 }],
1431 settings: &RuntimeSettings::default(),
1432 group_esurface_map: (0..4)
1433 .map(|i| ti_vec![Some(Into::<RaisedEsurfaceId>::into(i))])
1434 .collect(),
1435 local_esurface_exists: ti_vec![ti_vec![true; 4]],
1436 };
1437
1438 let maximal_overlap = find_maximal_overlap(
1439 &massless_overlap_input,
1440 &box4e.existing_esurfaces,
1441 &box4e.external_momenta,
1442 )
1443 .unwrap();
1444
1445 assert_eq!(maximal_overlap.overlap_groups.len(), 4);
1446
1447 for overlap_group in maximal_overlap.overlap_groups.iter() {
1448 let esurfaces = &overlap_group.existing_esurfaces;
1449 let center = &overlap_group.center;
1450
1451 assert_eq!(esurfaces.len(), 2);
1452 assert_eq!(overlap_group.complement.len(), 2);
1453
1454 for esurface in esurfaces.iter() {
1455 let raised_esurface_id = massless_overlap_input.group_esurface_map
1456 [box4e.existing_esurfaces[*esurface]][GraphGroupPosition::from(0)]
1457 .unwrap();
1458 let esurface_id =
1459 box4e.raised_data.raised_groups[raised_esurface_id].esurface_ids[0];
1460 let esurfaec_val = box4e.esurfaces[esurface_id].compute_from_momenta(
1461 &box4e.lmb,
1462 &box4e.edge_masses,
1463 center,
1464 &box4e.external_momenta,
1465 );
1466
1467 assert!(esurfaec_val.0 < 0.0);
1468 }
1469 }
1470 }
1471
1472 #[test]
1474 fn test_disconnected_box_4e() {
1475 let box4e = HelperBoxStructure::new(Some([F(10.5); 4]));
1476
1477 let overlap_input = OverlapInput {
1478 graph_data: ti_vec![SingleGraphOverlapData {
1479 lmb: &box4e.lmb,
1480 esurfaces: &box4e.esurfaces,
1481 raised_data: &box4e.raised_data,
1482 edge_masses: box4e.edge_masses.clone(),
1483 }],
1484 settings: &RuntimeSettings::default(),
1485 group_esurface_map: (0..4)
1486 .map(|i| ti_vec![Some(Into::<RaisedEsurfaceId>::into(i))])
1487 .collect(),
1488 local_esurface_exists: ti_vec![ti_vec![true; 4]],
1489 };
1490
1491 let maximal_overlap = find_maximal_overlap(
1492 &overlap_input,
1493 &box4e.existing_esurfaces,
1494 &box4e.external_momenta,
1495 )
1496 .unwrap();
1497
1498 assert_eq!(maximal_overlap.overlap_groups.len(), 4);
1499
1500 for overlap_group in maximal_overlap.overlap_groups.iter() {
1501 let esurfaces = &overlap_group.existing_esurfaces;
1502 let center = &overlap_group.center;
1503
1504 assert_eq!(esurfaces.len(), 1);
1505
1506 for esurface in esurfaces.iter() {
1507 let raised_esurface_id = overlap_input.group_esurface_map
1508 [box4e.existing_esurfaces[*esurface]][GraphGroupPosition::from(0)]
1509 .unwrap();
1510 let esurface_id =
1511 box4e.raised_data.raised_groups[raised_esurface_id].esurface_ids[0];
1512 let esurfaec_val = box4e.esurfaces[esurface_id].compute_from_momenta(
1513 &box4e.lmb,
1514 &box4e.edge_masses,
1515 center,
1516 &box4e.external_momenta,
1517 );
1518
1519 assert!(esurfaec_val < F(0.0));
1520 }
1521
1522 assert_eq!(overlap_group.complement.len(), 3);
1523 }
1524 }
1525
1526 #[test]
1527 fn test_banana() {
1528 let banana = HelperBananaStructure::new();
1529
1530 let classification = banana.esurfaces[EsurfaceID::from(0)].classify_existence(
1531 &banana.external_momenta,
1532 &banana.lmb,
1533 &banana.edge_masses,
1534 &F(10.0),
1535 &F(crate::utils::DEFAULT_ESURFACE_EXISTENCE_THRESHOLD),
1536 );
1537 assert!(matches!(classification, EsurfaceExistence::Pinched { .. }));
1538
1539 let overlap_input = OverlapInput {
1540 graph_data: ti_vec![SingleGraphOverlapData {
1541 lmb: &banana.lmb,
1542 esurfaces: &banana.esurfaces,
1543 raised_data: &banana.raised_data,
1544 edge_masses: banana.edge_masses.clone(),
1545 }],
1546 settings: &RuntimeSettings::default(),
1547 group_esurface_map: ti_vec![ti_vec![Some(Into::<RaisedEsurfaceId>::into(0)),]],
1548 local_esurface_exists: ti_vec![ti_vec![true]],
1549 };
1550
1551 let result = find_maximal_overlap(
1552 &overlap_input,
1553 &banana.existing_esurfaces,
1554 &banana.external_momenta,
1555 );
1556
1557 assert!(
1558 result.is_err(),
1559 "a caller that manually marks a pinched surface as existing must be rejected"
1560 );
1561
1562 let mut forced_settings = RuntimeSettings::default();
1563 forced_settings
1564 .subtraction
1565 .overlap_settings
1566 .force_global_center = Some(vec![[0.0, 0.0, 0.0]; 2]);
1567 forced_settings
1568 .subtraction
1569 .overlap_settings
1570 .check_global_center = false;
1571 let forced_overlap_input = OverlapInput {
1572 graph_data: ti_vec![SingleGraphOverlapData {
1573 lmb: &banana.lmb,
1574 esurfaces: &banana.esurfaces,
1575 raised_data: &banana.raised_data,
1576 edge_masses: banana.edge_masses.clone(),
1577 }],
1578 settings: &forced_settings,
1579 group_esurface_map: ti_vec![ti_vec![Some(Into::<RaisedEsurfaceId>::into(0)),]],
1580 local_esurface_exists: ti_vec![ti_vec![true]],
1581 };
1582 assert!(
1583 find_maximal_overlap(
1584 &forced_overlap_input,
1585 &banana.existing_esurfaces,
1586 &banana.external_momenta,
1587 )
1588 .is_err(),
1589 "check_global_center=false must not allow a forced center to bypass the invariant"
1590 );
1591 }
1592}