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