Skip to main content

gammalooprs/subtraction/
overlap_subspace.rs

1use crate::GammaLoopContext;
2use crate::cff::esurface::EsurfaceCollection;
3use crate::cff::esurface::EsurfaceID;
4use crate::cff::esurface::ExistingEsurfaceId;
5use crate::cff::esurface::ExistingThresholds;
6use crate::cff::esurface::esurface_value_is_strictly_inside;
7use crate::graph::FeynmanGraph;
8use crate::graph::Graph;
9use crate::graph::LmbIndex;
10use crate::graph::LoopMomentumBasis;
11use crate::integrands::process::sampling::maps::SamplingEvaluationError;
12use crate::momentum::sample::ExternalFourMomenta;
13use crate::momentum::sample::LoopIndex;
14use crate::momentum::sample::LoopMomenta;
15use crate::momentum::sample::SubspaceData;
16use crate::momentum::signature::LoopExtSignature;
17use crate::momentum::{Rotation, ThreeMomentum};
18use crate::settings::RuntimeSettings;
19use crate::utils::F;
20use crate::utils::Length;
21use crate::utils::compute_shift_part_subspace;
22use ahash::HashMap;
23use ahash::HashMapExt;
24use ahash::HashSet;
25use bincode_trait_derive::Decode;
26use bincode_trait_derive::Encode;
27use clarabel::algebra::*;
28use clarabel::solver::*;
29use eyre::{Result, eyre};
30use itertools::Itertools;
31use linnet::half_edge::involution::EdgeVec;
32use spenso::algebra::algebraic_traits::IsZero;
33#[cfg(test)]
34use std::collections::BTreeMap;
35use std::fmt::Display;
36use typed_index_collections::TiVec;
37
38#[cfg(test)]
39#[path = "overlap_subspace_tests.rs"]
40mod regression_tests;
41
42#[derive(Debug, Clone, Encode, Decode)]
43#[trait_decode(trait = GammaLoopContext)]
44pub struct OverlapGroup {
45    pub existing_esurfaces: Vec<ExistingEsurfaceId>,
46    pub complement: Vec<ExistingEsurfaceId>,
47    /// Centers use the preparation frame and cut-side LMB. Physical LU prepares
48    /// once at canonical identity; consumption promotes the stored bits exactly
49    /// before rotating active vectors in the native precision. Other direct
50    /// solver callers retain their explicitly supplied preparation frame.
51    pub center: LoopMomenta<F<f64>>,
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: ExistingThresholds,
59}
60
61/// Kinematic data for one threshold instance. Different Cutkosky cuts may have different
62/// solved `t*` values and external data even when they share a solve signature.
63#[derive(Debug, Clone)]
64pub(crate) struct OverlapKinematics {
65    pub loop_moms: LoopMomenta<F<f64>>,
66    pub external_momenta: ExternalFourMomenta<F<f64>>,
67    /// Graph-group members can share routing while carrying different propagator masses.
68    pub edge_masses: Option<EdgeVec<F<f64>>>,
69}
70
71impl Display for OverlapStructure {
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        let existing_esurfaces: Vec<_> = self.existing_esurfaces.iter().map(|id| id.0).collect();
74        writeln!(f, "existing esurfaces: {:?}", existing_esurfaces)?;
75
76        for (i, group) in self.overlap_groups.iter().enumerate() {
77            writeln!(f, "Group {}:", i)?;
78            let existing_esurfaces_in_group: Vec<_> = group
79                .existing_esurfaces
80                .iter()
81                .map(|id| self.existing_esurfaces[*id].0)
82                .collect();
83
84            writeln!(f, "center:\n{}", group.center)?;
85            writeln!(
86                f,
87                "existing esurfaces in group: {:?}",
88                existing_esurfaces_in_group
89            )?;
90        }
91
92        Ok(())
93    }
94}
95
96impl OverlapStructure {
97    pub fn fill_in_complements(&mut self) {
98        for group in self.overlap_groups.iter_mut() {
99            group.complement = self
100                .existing_esurfaces
101                .iter_enumerated()
102                .map(|(existing_esurface_id, _)| existing_esurface_id)
103                .filter(|&existing_esurface_id| {
104                    !group.existing_esurfaces.contains(&existing_esurface_id)
105                })
106                .collect();
107        }
108    }
109
110    pub fn new_empty() -> Self {
111        Self {
112            overlap_groups: vec![],
113            existing_esurfaces: ExistingThresholds::new(),
114        }
115    }
116}
117/// Helper struct to construct the socp problem
118struct PropagatorConstraint<'a> {
119    mass_pointer: Option<usize>, // pointer to value of unique mass
120    signature: &'a LoopExtSignature,
121    subspace: &'a SubspaceData,
122    loop_moms: LoopMomenta<F<f64>>,
123    external_momenta: ExternalFourMomenta<F<f64>>,
124}
125
126impl PropagatorConstraint<'_> {
127    fn get_dimension(&self) -> usize {
128        let mass_value = if self.mass_pointer.is_some() { 1 } else { 0 };
129
130        3 + mass_value + 1
131    }
132}
133
134fn extract_center(
135    global_loop_num: usize,
136    subspace: &SubspaceData,
137    solution: &[f64],
138) -> LoopMomenta<F<f64>> {
139    let len = solution.len();
140    let num_loop_vars = 3 * subspace.loopcount();
141
142    let mut loop_chunks = solution[len - num_loop_vars..].chunks(3);
143
144    (0..global_loop_num)
145        .map(LoopIndex::from)
146        .map(|loop_index| {
147            if subspace.contains_loop_index(loop_index) {
148                let window = loop_chunks.next().expect("not enough loop momenta");
149                ThreeMomentum::new(
150                    F::from_f64(window[0]),
151                    F::from_f64(window[1]),
152                    F::from_f64(window[2]),
153                )
154            } else {
155                ThreeMomentum::new(F(0.0), F(0.0), F(0.0))
156            }
157        })
158        .collect()
159}
160
161fn construct_solver(
162    overlap_input: &OverlapInput,
163    esurfaces_to_consider: &[ExistingEsurfaceId],
164    existing_esurfaces: &ExistingThresholds,
165    loop_moms: &LoopMomenta<F<f64>>,
166    external_momenta: &ExternalFourMomenta<F<f64>>,
167    verbose: bool,
168    variable_scale: f64,
169) -> Option<DefaultSolver> {
170    let num_loops = overlap_input.subspace.loopcount();
171
172    let num_loop_vars = 3 * num_loops;
173
174    // first we study the structure of the problem
175    let mut propagator_constraints: Vec<PropagatorConstraint> = Vec::with_capacity(20);
176
177    let mut inequivalent_masses: Vec<F<f64>> = vec![];
178
179    let mut esurface_constraints: Vec<Vec<usize>> = Vec::with_capacity(esurfaces_to_consider.len());
180
181    for existing_esurface_id in esurfaces_to_consider.iter() {
182        let surface_id = existing_esurfaces[*existing_esurface_id];
183
184        let esurface = &overlap_input.thresholds[surface_id];
185        let threshold_subspace = overlap_input.threshold_subspace(surface_id);
186        let (surface_loop_moms, surface_external_momenta, edge_masses) = overlap_input
187            .surface_kinematics
188            .and_then(|kinematics| kinematics.get(surface_id.0))
189            .map(|kinematics| {
190                (
191                    &kinematics.loop_moms,
192                    &kinematics.external_momenta,
193                    kinematics
194                        .edge_masses
195                        .as_ref()
196                        .unwrap_or(&overlap_input.edge_masses),
197                )
198            })
199            .unwrap_or((loop_moms, external_momenta, &overlap_input.edge_masses));
200        let lmb = threshold_subspace.get_lmb(overlap_input.lmbs);
201
202        let mut esurface_constraint_indices: Vec<usize> = Vec::with_capacity(6);
203
204        for edge_id in threshold_subspace.contains(&esurface.energies, overlap_input.graph) {
205            if let Some(edge_position) = propagator_constraints.iter().position(|constraint| {
206                overlap_input.surface_kinematics.is_none()
207                    && *constraint.signature == lmb.edge_signatures[edge_id]
208                    && constraint.subspace.solve_signature(overlap_input.lmbs)
209                        == threshold_subspace.solve_signature(overlap_input.lmbs)
210            }) {
211                esurface_constraint_indices.push(edge_position);
212            } else {
213                let mass_pointer = if edge_masses[edge_id].is_zero() {
214                    None
215                } else {
216                    Some(
217                        if let Some(mass_position) = inequivalent_masses
218                            .iter()
219                            .position(|&x| x == edge_masses[edge_id])
220                        {
221                            mass_position
222                        } else {
223                            inequivalent_masses.push(edge_masses[edge_id]);
224                            inequivalent_masses.len() - 1
225                        },
226                    )
227                };
228
229                let signature = &lmb.edge_signatures[edge_id];
230
231                let propagator_constraint = PropagatorConstraint {
232                    mass_pointer,
233                    signature,
234                    subspace: threshold_subspace,
235                    loop_moms: surface_loop_moms.clone(),
236                    external_momenta: surface_external_momenta.clone(),
237                };
238
239                propagator_constraints.push(propagator_constraint);
240                esurface_constraint_indices.push(propagator_constraints.len() - 1);
241            };
242        }
243
244        esurface_constraints.push(esurface_constraint_indices);
245    }
246
247    // now we know the structure, so we can put it in matrix form.
248    // variables are stored as [r , x_p_0, ... x_p_m, k1_x, k1_y, ... k_n_x, k_n_y, k_n_z]
249    let propagator_index_offset = 1;
250    let loop_momentum_offset = propagator_index_offset + propagator_constraints.len();
251
252    let num_primal_variables = loop_momentum_offset + num_loop_vars;
253
254    let cone_dimension_sum = propagator_constraints
255        .iter()
256        .map(|constraint| constraint.get_dimension())
257        .sum::<usize>();
258
259    let num_constaints = 1 + esurface_constraints.len() + cone_dimension_sum;
260
261    // quadratic part of the objective function, we don't need this for now
262    let p_matrix: CscMatrix<f64> =
263        CscMatrix::spalloc((num_primal_variables, num_primal_variables), 0);
264
265    // objective function
266    let mut q_vector = vec![0.0; num_primal_variables];
267    q_vector[0] = 1.0;
268
269    // construct the cones
270    let mut cones: Vec<SupportedConeT<f64>> = Vec::with_capacity(1 + propagator_constraints.len());
271    cones.push(NonnegativeConeT(1));
272    cones.push(NonnegativeConeT(esurface_constraints.len()));
273
274    for prop_constraint in &propagator_constraints {
275        cones.push(SecondOrderConeT(prop_constraint.get_dimension()));
276    }
277
278    // write the constaint equations
279
280    // perhaps it is faster to build the sparse matrix directly, but it is also extremely unreadable
281    let mut a_matrix = vec![vec![0.0; num_primal_variables]; num_constaints];
282    let mut b_vector = vec![0.0; num_constaints];
283
284    a_matrix[0][0] = 1.0;
285    // esurface constraints
286    for (constraint_index, (existing_esurface_id, esurface_constraint)) in esurfaces_to_consider
287        .iter()
288        .zip(esurface_constraints.iter())
289        .enumerate()
290    {
291        for prop_index in esurface_constraint {
292            a_matrix[constraint_index + 1][*prop_index + propagator_index_offset] = 1.0;
293        }
294
295        let esurface_id = existing_esurfaces[*existing_esurface_id];
296        let esurface = &overlap_input.thresholds[esurface_id];
297        let threshold_subspace = overlap_input.threshold_subspace(esurface_id);
298        let (surface_loop_moms, surface_external_momenta, edge_masses) = overlap_input
299            .surface_kinematics
300            .and_then(|kinematics| kinematics.get(esurface_id.0))
301            .map(|kinematics| {
302                (
303                    &kinematics.loop_moms,
304                    &kinematics.external_momenta,
305                    kinematics
306                        .edge_masses
307                        .as_ref()
308                        .unwrap_or(&overlap_input.edge_masses),
309                )
310            })
311            .unwrap_or((loop_moms, external_momenta, &overlap_input.edge_masses));
312
313        let shift_part = esurface.compute_shift_part_from_momenta_in_subspace(
314            surface_loop_moms,
315            surface_external_momenta,
316            threshold_subspace,
317            overlap_input.lmbs,
318            overlap_input.graph,
319            edge_masses,
320        );
321        b_vector[constraint_index + 1] = -shift_part.0;
322        a_matrix[constraint_index + 1][0] = -1.0;
323    }
324
325    // propagator constraints
326    let mut vertical_offset = esurface_constraints.len() + 1;
327    for (cone_index, propagator_constraint) in propagator_constraints.iter().enumerate() {
328        a_matrix[vertical_offset][propagator_index_offset + cone_index] = -1.0;
329        vertical_offset += 1;
330
331        let spatial_shift = compute_shift_part_subspace(
332            &propagator_constraint.signature.internal,
333            &propagator_constraint.signature.external,
334            &propagator_constraint.loop_moms,
335            &propagator_constraint
336                .external_momenta
337                .iter()
338                .map(|p| p.spatial)
339                .collect(),
340            propagator_constraint.subspace,
341        );
342
343        b_vector[vertical_offset] = spatial_shift.px.0;
344        b_vector[vertical_offset + 1] = spatial_shift.py.0;
345        b_vector[vertical_offset + 2] = spatial_shift.pz.0;
346
347        for (subspace_loop_index, loop_index) in
348            overlap_input.subspace.iter_lmb_indices().enumerate()
349        {
350            if !propagator_constraint
351                .subspace
352                .contains_loop_index(loop_index)
353            {
354                continue;
355            }
356            let individual_loop_signature = propagator_constraint.signature.internal[loop_index];
357            if individual_loop_signature.is_sign() {
358                a_matrix[vertical_offset][loop_momentum_offset + 3 * subspace_loop_index] =
359                    -(individual_loop_signature as i8) as f64;
360                a_matrix[vertical_offset + 1][loop_momentum_offset + 3 * subspace_loop_index + 1] =
361                    -(individual_loop_signature as i8) as f64;
362                a_matrix[vertical_offset + 2][loop_momentum_offset + 3 * subspace_loop_index + 2] =
363                    -(individual_loop_signature as i8) as f64;
364            }
365        }
366
367        vertical_offset += 3;
368
369        if let Some(mass_index) = propagator_constraint.mass_pointer {
370            b_vector[vertical_offset] = inequivalent_masses[mass_index].0;
371            vertical_offset += 1;
372        }
373    }
374
375    // Refuse a retry whose change of units rounds subnormal input data.
376    if variable_scale != 1.0
377        && b_vector
378            .iter()
379            .any(|value| (value / variable_scale) * variable_scale != *value)
380    {
381        return None;
382    }
383    // All primal variables and cone slacks have energy units. With P=0,
384    // b -> b/scale and x -> x/scale preserve this homogeneous cone problem.
385    // Restore the physical coordinates before checking any candidate center.
386    for value in &mut b_vector {
387        *value /= variable_scale;
388    }
389
390    let a_matrix_sparse = CscMatrix::from(&a_matrix);
391
392    let settings = DefaultSettingsBuilder::default()
393        .verbose(verbose)
394        .build()
395        .unwrap();
396
397    crate::debug_tags!(#subtraction, #threshold, #overlap, #socp;
398        stage = "threshold_socp_input",
399        variable_scale,
400        graph = %overlap_input.graph.name,
401        surfaces = ?esurfaces_to_consider,
402        file.a = ?a_matrix,
403        file.b = ?b_vector,
404        file.q = ?q_vector,
405        file.cones = ?cones,
406        "constructed threshold overlap problem"
407    );
408
409    Some(
410        DefaultSolver::new(
411            &p_matrix,
412            &q_vector,
413            &a_matrix_sparse,
414            &b_vector,
415            &cones,
416            settings,
417        )
418        .unwrap(),
419    )
420}
421
422pub(crate) fn find_center(
423    overlap_input: &OverlapInput,
424    esurfaces_to_consider: &[ExistingEsurfaceId],
425    existing_esurfaces: &ExistingThresholds,
426    loop_moms: &LoopMomenta<F<f64>>,
427    external_momenta: &ExternalFourMomenta<F<f64>>,
428    verbose: bool,
429) -> Result<Option<LoopMomenta<F<f64>>>> {
430    let global_loop_number = overlap_input.graph.get_loop_number();
431    let esurfaces_to_check = esurfaces_to_consider
432        .iter()
433        .map(|existing_esurface_id| existing_esurfaces[*existing_esurface_id])
434        .collect();
435    OverlapInput::solve_with_rescaling(
436        &overlap_input.graph.name,
437        &esurfaces_to_check,
438        |variable_scale| {
439            construct_solver(
440                overlap_input,
441                esurfaces_to_consider,
442                existing_esurfaces,
443                loop_moms,
444                external_momenta,
445                verbose,
446                variable_scale,
447            )
448        },
449        |coordinates| {
450            let center = extract_center(global_loop_number, overlap_input.subspace, coordinates);
451            check_global_center(
452                overlap_input,
453                &esurfaces_to_check,
454                &center,
455                loop_moms,
456                external_momenta,
457            )
458        },
459    )
460    .map(|coordinates| {
461        coordinates.map(|coordinates| {
462            extract_center(global_loop_number, overlap_input.subspace, &coordinates)
463        })
464    })
465}
466
467pub(crate) struct OverlapInput<'a> {
468    pub graph: &'a Graph,
469    pub settings: &'a RuntimeSettings,
470    /// Union of the active coordinates used to represent a common center.
471    pub subspace: &'a SubspaceData,
472    /// Optional per-threshold active coordinates. `None` retains the homogeneous legacy path.
473    pub threshold_subspaces: Option<&'a [SubspaceData]>,
474    pub lmbs: &'a TiVec<LmbIndex, LoopMomentumBasis>,
475    pub thresholds: &'a EsurfaceCollection,
476    pub edge_masses: EdgeVec<F<f64>>,
477    /// Optional per-threshold kinematics for a multi-cut solve. If absent, the single sample
478    /// passed to the overlap routine is used for every threshold.
479    pub surface_kinematics: Option<&'a [OverlapKinematics]>,
480}
481
482impl OverlapInput<'_> {
483    fn solve_with_rescaling(
484        graph_name: &str,
485        esurfaces: &ExistingThresholds,
486        mut construct: impl FnMut(f64) -> Option<DefaultSolver>,
487        mut center_is_interior: impl FnMut(&[f64]) -> bool,
488    ) -> Result<Option<Vec<f64>>> {
489        let mut attempts = Vec::with_capacity(2);
490        // Retry only uncertified solves. An exact power-of-two change of energy
491        // units can recover a full infeasibility certificate from Clarabel without
492        // changing the surfaces, their grouping, or the strict physical-center test.
493        for variable_scale in [1.0, 2.0] {
494            let Some(mut solver) = construct(variable_scale) else {
495                attempts.push(format!(
496                    "scale={variable_scale}: change of energy units is not exactly representable"
497                ));
498                continue;
499            };
500            solver.solve();
501            crate::debug_tags!(#subtraction, #threshold, #overlap, #socp;
502                stage = "threshold_socp_result",
503                graph = graph_name,
504                surfaces = ?esurfaces,
505                variable_scale,
506                status = ?solver.solution.status,
507                iterations = solver.solution.iterations,
508                primal_objective = solver.solution.obj_val,
509                dual_objective = solver.solution.obj_val_dual,
510                primal_residual = solver.solution.r_prim,
511                dual_residual = solver.solution.r_dual,
512                file.solution = ?solver.solution,
513                "finished threshold overlap solve"
514            );
515
516            // Even if optimization did not converge, a physically valid center certifies overlap.
517            // Conversely, a failed solve must not silently remove an overlap from the catalogue.
518            for coordinate in &mut solver.solution.x {
519                *coordinate *= variable_scale;
520            }
521            if center_is_interior(&solver.solution.x) {
522                return Ok(Some(solver.solution.x));
523            }
524            if solver.solution.status == SolverStatus::PrimalInfeasible {
525                return Ok(None);
526            }
527            attempts.push(format!(
528                "scale={variable_scale}: {:?} without a strictly interior center: primal objective={}, dual objective={}, primal residual={}, dual residual={}, iterations={}",
529                solver.solution.status,
530                solver.solution.obj_val,
531                solver.solution.obj_val_dual,
532                solver.solution.r_prim,
533                solver.solution.r_dual,
534                solver.solution.iterations,
535            ));
536        }
537        Err(SamplingEvaluationError::UncertifiedOverlap {
538            detail: format!(
539                "Threshold SOCP for graph '{graph_name}' and E-surfaces {esurfaces:?}: {}",
540                attempts.join("; retry "),
541            ),
542        }
543        .into())
544    }
545
546    fn threshold_subspace(&self, esurface_id: EsurfaceID) -> &SubspaceData {
547        self.threshold_subspaces
548            .map_or(self.subspace, |subspaces| &subspaces[esurface_id.0])
549    }
550
551    fn validate_subspaces(&self) -> Result<()> {
552        let Some(threshold_subspaces) = self.threshold_subspaces else {
553            return Ok(());
554        };
555        if threshold_subspaces.len() != self.thresholds.len() {
556            return Err(eyre!(
557                "Projected threshold overlap has {} subspaces for {} E-surfaces",
558                threshold_subspaces.len(),
559                self.thresholds.len(),
560            ));
561        }
562        let common_parent = self.subspace.parent_lmb_index();
563        for (esurface_index, subspace) in threshold_subspaces.iter().enumerate() {
564            if subspace.parent_lmb_index() != common_parent {
565                return Err(eyre!(
566                    "Projected threshold E-surface instance {} uses parent LMB {}, while its common center uses parent LMB {}",
567                    esurface_index,
568                    usize::from(subspace.parent_lmb_index()),
569                    usize::from(common_parent),
570                ));
571            }
572            if subspace
573                .iter_lmb_indices()
574                .any(|loop_index| !self.subspace.contains_loop_index(loop_index))
575            {
576                return Err(eyre!(
577                    "Projected threshold E-surface instance {} contains coordinates outside the common-center subspace",
578                    esurface_index,
579                ));
580            }
581        }
582        Ok(())
583    }
584}
585
586pub(crate) fn check_global_center(
587    overlap_input: &OverlapInput,
588    existing_esurfaces: &ExistingThresholds,
589    center: &LoopMomenta<F<f64>>,
590    loop_moms: &LoopMomenta<F<f64>>,
591    external_momenta: &ExternalFourMomenta<F<f64>>,
592) -> bool {
593    existing_esurfaces.iter().all(|esurface_id| {
594        let esurface = &overlap_input.thresholds[*esurface_id];
595        let threshold_subspace = overlap_input.threshold_subspace(*esurface_id);
596        let (surface_loop_moms, surface_external_momenta, edge_masses) = overlap_input
597            .surface_kinematics
598            .and_then(|kinematics| kinematics.get(esurface_id.0))
599            .map(|kinematics| {
600                (
601                    &kinematics.loop_moms,
602                    &kinematics.external_momenta,
603                    kinematics
604                        .edge_masses
605                        .as_ref()
606                        .unwrap_or(&overlap_input.edge_masses),
607                )
608            })
609            .unwrap_or((loop_moms, external_momenta, &overlap_input.edge_masses));
610        let mut center_with_fixed_complement = surface_loop_moms.clone();
611        for loop_index in threshold_subspace.iter_lmb_indices() {
612            center_with_fixed_complement[loop_index] = center[loop_index];
613        }
614
615        let lmb = threshold_subspace.get_lmb(overlap_input.lmbs);
616
617        let esurface_val = esurface.compute_from_momenta(
618            lmb,
619            edge_masses,
620            &center_with_fixed_complement,
621            surface_external_momenta,
622        );
623
624        esurface_value_is_strictly_inside(&esurface_val, &F(overlap_input.settings.kinematics.e_cm))
625    })
626}
627
628/// Runtime overlap failures are returned to the stability machinery. An unresolved
629/// canonical f64 solve stays invalid in every native precision and is recorded as NaN.
630/// Structural generation invariants are still asserted where malformed generated data is unrecoverable.
631/// Solver-derived centers are already found in the current probe frame. `probe_rotation` is
632/// needed only for a configured forced center, whose coordinates are defined in the identity
633/// frame and must therefore be rotated exactly once before the cut-side LMB transform.
634pub(crate) fn find_maximal_overlap(
635    overlap_input: &OverlapInput,
636    existing_esurfaces: &ExistingThresholds,
637    loop_moms: &LoopMomenta<F<f64>>,
638    external_momenta: &ExternalFourMomenta<F<f64>>,
639    probe_rotation: &Rotation,
640) -> Result<OverlapStructure> {
641    if let Some(surface_kinematics) = overlap_input.surface_kinematics
642        && surface_kinematics.len() != overlap_input.thresholds.len()
643    {
644        return Err(eyre!(
645            "per-surface overlap kinematics has {} entries for {} threshold surfaces",
646            surface_kinematics.len(),
647            overlap_input.thresholds.len()
648        ));
649    }
650    overlap_input.validate_subspaces()?;
651    // An empty catalogue has no automatic partitions, but an explicit center still needs
652    // its identity-frame rotation and parent-LMB projection.
653    if existing_esurfaces.is_empty()
654        && overlap_input
655            .settings
656            .subtraction
657            .overlap_settings
658            .force_global_center
659            .is_some()
660    {
661        return find_maximal_overlap_single(
662            overlap_input,
663            existing_esurfaces,
664            loop_moms,
665            external_momenta,
666            probe_rotation,
667        );
668    }
669    let mut partitions: Vec<(SubspaceData, ExistingThresholds)> = Vec::new();
670    for existing_id in existing_esurfaces.iter_enumerated().map(|(id, _)| id) {
671        let surface_id = existing_esurfaces[existing_id];
672        let threshold_subspace = overlap_input.threshold_subspace(surface_id);
673        if let Some((_, members)) = partitions.iter_mut().find(|(representative, _)| {
674            representative.solve_signature(overlap_input.lmbs)
675                == threshold_subspace.solve_signature(overlap_input.lmbs)
676        }) {
677            members.push(surface_id);
678        } else {
679            let mut members = ExistingThresholds::new();
680            members.push(surface_id);
681            partitions.push((threshold_subspace.clone(), members));
682        }
683    }
684
685    let mut combined = OverlapStructure {
686        overlap_groups: Vec::new(),
687        existing_esurfaces: existing_esurfaces.clone(),
688    };
689    let mut original_ids_by_partition = Vec::new();
690    for (_, members) in &partitions {
691        original_ids_by_partition.push(
692            members
693                .iter()
694                .map(|surface_id| {
695                    existing_esurfaces
696                        .iter_enumerated()
697                        .find_map(|(id, candidate)| (candidate == surface_id).then_some(id))
698                        .expect("partition surface must belong to original threshold set")
699                })
700                .collect::<Vec<_>>(),
701        );
702    }
703    for (partition_index, (representative, members)) in partitions.into_iter().enumerate() {
704        let partition_input = OverlapInput {
705            graph: overlap_input.graph,
706            settings: overlap_input.settings,
707            subspace: &representative,
708            threshold_subspaces: None,
709            lmbs: overlap_input.lmbs,
710            thresholds: overlap_input.thresholds,
711            edge_masses: overlap_input.edge_masses.clone(),
712            surface_kinematics: overlap_input.surface_kinematics,
713        };
714        let local = find_maximal_overlap_single(
715            &partition_input,
716            &members,
717            loop_moms,
718            external_momenta,
719            probe_rotation,
720        )?;
721        for mut group in local.overlap_groups {
722            for id in group
723                .existing_esurfaces
724                .iter_mut()
725                .chain(&mut group.complement)
726            {
727                *id = original_ids_by_partition[partition_index][usize::from(*id)];
728            }
729            combined.overlap_groups.push(group);
730        }
731    }
732    Ok(combined)
733}
734
735/// Variant of [`find_maximal_overlap`] that keeps explicitly tagged thresholds in separate
736/// solve problems. Unlabelled thresholds still use the automatic exact-subspace partition.
737#[cfg(test)]
738pub(crate) fn find_maximal_overlap_with_group_ids(
739    overlap_input: &OverlapInput,
740    existing_esurfaces: &ExistingThresholds,
741    group_ids: &[Option<usize>],
742    loop_moms: &LoopMomenta<F<f64>>,
743    external_momenta: &ExternalFourMomenta<F<f64>>,
744    probe_rotation: &Rotation,
745) -> Result<OverlapStructure> {
746    if let Some(surface_kinematics) = overlap_input.surface_kinematics
747        && surface_kinematics.len() != overlap_input.thresholds.len()
748    {
749        return Err(eyre!(
750            "per-surface overlap kinematics has {} entries for {} threshold surfaces",
751            surface_kinematics.len(),
752            overlap_input.thresholds.len()
753        ));
754    }
755    if group_ids.len() < overlap_input.thresholds.len() {
756        return Err(eyre!(
757            "threshold solve-group metadata has {} entries for {} threshold surfaces",
758            group_ids.len(),
759            overlap_input.thresholds.len()
760        ));
761    }
762    overlap_input.validate_subspaces()?;
763    let mut labelled = BTreeMap::<Option<usize>, ExistingThresholds>::new();
764    for existing_id in existing_esurfaces.iter_enumerated().map(|(id, _)| id) {
765        let surface_id = existing_esurfaces[existing_id];
766        let label = group_ids[surface_id.0];
767        labelled.entry(label).or_default().push(surface_id);
768    }
769    for (label, members) in &labelled {
770        let Some(group_id) = label else {
771            continue;
772        };
773        let Some(first_surface_id) = members.first() else {
774            continue;
775        };
776        let first_subspace = overlap_input.threshold_subspace(*first_surface_id);
777        let first_signature = first_subspace.solve_signature(overlap_input.lmbs);
778        let incompatible = members.iter().skip(1).any(|surface_id| {
779            overlap_input
780                .threshold_subspace(*surface_id)
781                .solve_signature(overlap_input.lmbs)
782                != first_signature
783        });
784        if incompatible {
785            let members = members
786                .iter()
787                .map(|surface_id| {
788                    let subspace = overlap_input.threshold_subspace(*surface_id);
789                    format!(
790                        "E-surface instance {}: parent {:?}, selected cycles {:?}",
791                        surface_id.0,
792                        subspace.get_lmb(overlap_input.lmbs).loop_edges,
793                        subspace.solve_signature(overlap_input.lmbs)
794                    )
795                })
796                .join("; ");
797            return Err(eyre!(
798                "explicit threshold group_id={} contains incompatible solve subspaces: {}",
799                group_id,
800                members,
801            ));
802        }
803    }
804    let mut combined = OverlapStructure {
805        overlap_groups: Vec::new(),
806        existing_esurfaces: existing_esurfaces.clone(),
807    };
808    for members in labelled.into_values() {
809        let local = find_maximal_overlap(
810            overlap_input,
811            &members,
812            loop_moms,
813            external_momenta,
814            probe_rotation,
815        )?;
816        for mut group in local.overlap_groups {
817            for id in group
818                .existing_esurfaces
819                .iter_mut()
820                .chain(&mut group.complement)
821            {
822                let surface_id = local.existing_esurfaces[*id];
823                *id = existing_esurfaces
824                    .iter_enumerated()
825                    .find_map(|(original_id, candidate)| {
826                        (candidate == &surface_id).then_some(original_id)
827                    })
828                    .expect("labelled overlap surface must belong to original threshold set");
829            }
830            combined.overlap_groups.push(group);
831        }
832    }
833    Ok(combined)
834}
835
836fn find_maximal_overlap_single(
837    overlap_input: &OverlapInput,
838    existing_esurfaces: &ExistingThresholds,
839    loop_moms: &LoopMomenta<F<f64>>,
840    external_momenta: &ExternalFourMomenta<F<f64>>,
841    probe_rotation: &Rotation,
842) -> Result<OverlapStructure> {
843    overlap_input.validate_subspaces()?;
844    let mut res = OverlapStructure {
845        overlap_groups: vec![],
846        existing_esurfaces: existing_esurfaces.clone(),
847    };
848
849    let settings = overlap_input.settings;
850
851    let all_existing_esurfaces = existing_esurfaces
852        .iter_enumerated()
853        .map(|a| a.0)
854        .collect_vec();
855
856    if let Some(global_center) = &settings.subtraction.overlap_settings.force_global_center {
857        let global_center_identity: LoopMomenta<F<f64>> = global_center
858            .iter()
859            .map(|coordinates| ThreeMomentum {
860                px: F(coordinates[0]),
861                py: F(coordinates[1]),
862                pz: F(coordinates[2]),
863            })
864            .collect();
865        let center_externals = existing_esurfaces
866            .first()
867            .and_then(|id| {
868                overlap_input
869                    .surface_kinematics
870                    .map(|kinematics| &kinematics[id.0].external_momenta)
871            })
872            .unwrap_or(external_momenta);
873        let rotated_external_spatial = center_externals
874            .iter()
875            .map(|momentum| momentum.spatial)
876            .collect();
877        // Forced centers are configured in the graph's identity-frame LMB. Apply the probe
878        // rotation once, then express that rotated point in the cut-side LMB used by the solver.
879        let mut global_center_probe = global_center_identity.rotate(probe_rotation).lmb_transform(
880            &overlap_input.graph.loop_momentum_basis,
881            overlap_input.subspace.get_lmb(overlap_input.lmbs),
882            &rotated_external_spatial,
883        );
884        if let Some(surface_kinematics) = overlap_input.surface_kinematics {
885            for surface_id in existing_esurfaces {
886                let surface_externals = &surface_kinematics[surface_id.0].external_momenta;
887                if surface_externals == center_externals {
888                    continue;
889                }
890                let surface_center = global_center_identity.rotate(probe_rotation).lmb_transform(
891                    &overlap_input.graph.loop_momentum_basis,
892                    overlap_input.subspace.get_lmb(overlap_input.lmbs),
893                    &surface_externals
894                        .iter()
895                        .map(|momentum| momentum.spatial)
896                        .collect(),
897                );
898                if overlap_input
899                    .subspace
900                    .iter_lmb_indices()
901                    .any(|index| surface_center[index] != global_center_probe[index])
902                {
903                    return Err(eyre!(
904                        "forced global threshold center gives incompatible active coordinates for E-surface instance {}: selected edges {:?}, center {}, expected {}",
905                        surface_id.0,
906                        overlap_input
907                            .subspace
908                            .iter_basis_edges(overlap_input.lmbs)
909                            .collect_vec(),
910                        surface_center,
911                        global_center_probe,
912                    ));
913                }
914            }
915        }
916        // A subspace center specifies only its active coordinates. The complementary loop
917        // coordinates remain fixed at the sampled point throughout center validation, radial
918        // solving, and final CT reconstruction. Affine LMB transforms can populate inactive
919        // components even when the identity-frame center is zero, so project them out explicitly.
920        for loop_index in (0..global_center_probe.len()).map(LoopIndex::from) {
921            if !overlap_input.subspace.contains_loop_index(loop_index) {
922                global_center_probe[loop_index] = ThreeMomentum::new(F(0.0), F(0.0), F(0.0));
923            }
924        }
925
926        tracing::debug!(
927            graph = %overlap_input.graph.name,
928            rotation_id = %probe_rotation.method,
929            center_provenance = "forced_identity_frame_rotated_lmb_transformed_and_projected_once",
930            identity_center = %global_center_identity,
931            probe_cut_lmb_center = %global_center_probe,
932            "prepared forced LU overlap center"
933        );
934
935        if !settings.subtraction.overlap_settings.check_global_center {
936            tracing::warn!(
937                graph = %overlap_input.graph.name,
938                "overlap_settings.check_global_center=false is deprecated; forced centers are always validated"
939            );
940        }
941
942        let is_valid = check_global_center(
943            overlap_input,
944            existing_esurfaces,
945            &global_center_probe,
946            loop_moms,
947            external_momenta,
948        );
949
950        if !is_valid {
951            return Err(eyre!(
952                "Forced identity-frame center is not finite and strictly inside all existing esurfaces after applying probe rotation {} and the cut-side LMB transform",
953                probe_rotation.method,
954            ));
955        }
956
957        let single_group = OverlapGroup {
958            existing_esurfaces: all_existing_esurfaces,
959            center: global_center_probe,
960            complement: vec![],
961        };
962        res.overlap_groups.push(single_group);
963
964        res.fill_in_complements();
965        return Ok(res);
966    }
967
968    if settings.subtraction.overlap_settings.try_origin {
969        let global_loop_count = overlap_input.graph.get_loop_number();
970        let origin = LoopMomenta::from_iter(
971            (0..global_loop_count).map(|_| ThreeMomentum::new(F(0.0), F(0.0), F(0.0))),
972        );
973
974        let is_valid = check_global_center(
975            overlap_input,
976            existing_esurfaces,
977            &origin,
978            loop_moms,
979            external_momenta,
980        );
981
982        if is_valid {
983            let single_group = OverlapGroup {
984                existing_esurfaces: all_existing_esurfaces,
985                center: origin,
986                complement: vec![],
987            };
988            res.overlap_groups.push(single_group);
989            res.fill_in_complements();
990            return Ok(res);
991        }
992    }
993
994    if settings.subtraction.overlap_settings.try_origin_all_lmbs {
995        todo!("Not all heuristics implemented")
996    }
997
998    // first try if all esurfaces have a single center, we explitely seach a center instead of trying the
999    // origin. This is because the origin might not be optimal.
1000    let option_center = find_center(
1001        overlap_input,
1002        &all_existing_esurfaces,
1003        existing_esurfaces,
1004        loop_moms,
1005        external_momenta,
1006        false,
1007    )?;
1008
1009    if let Some(center) = option_center {
1010        let single_group = OverlapGroup {
1011            existing_esurfaces: all_existing_esurfaces,
1012            center,
1013            complement: vec![],
1014        };
1015        res.overlap_groups.push(single_group);
1016        res.fill_in_complements();
1017        return Ok(res);
1018    }
1019
1020    // if the center is not valid, create a table of all pairs
1021    let esurface_pairs = EsurfacePairs::new(
1022        overlap_input,
1023        existing_esurfaces,
1024        loop_moms,
1025        external_momenta,
1026    )?;
1027
1028    // if settings.general.debug > 3 {
1029    //     DEBUG_LOGGER.write("overlap_pairs", &esurface_pairs);
1030    // }
1031
1032    let mut num_disconnected_surfaces = 0;
1033
1034    for (existing_esurface_id, &esurface_id) in existing_esurfaces.iter_enumerated() {
1035        // if an esurface overlaps with no other esurface, it is part of the maximal overlap structure
1036        if esurface_pairs.has_no_overlap(existing_esurface_id) {
1037            let center = find_center(
1038                overlap_input,
1039                &[existing_esurface_id],
1040                existing_esurfaces,
1041                loop_moms,
1042                external_momenta,
1043                false,
1044            )?
1045            .ok_or_else(|| {
1046                let esurface = &overlap_input.thresholds[esurface_id];
1047
1048                let mut error_message = String::new();
1049
1050                error_message.push_str(&format!(
1051                    "Could not find center of esuface {:?}\n",
1052                    esurface_id
1053                ));
1054                error_message.push_str(&format!("edges: {:?}\n", esurface.energies));
1055
1056                error_message.push_str(&format!("external shift: {:?}\n", esurface.external_shift));
1057
1058                error_message.push_str(&format!("External momenta: {:#?}\n", external_momenta));
1059
1060                eyre!("{}", error_message)
1061            })?;
1062
1063            res.overlap_groups.push(OverlapGroup {
1064                existing_esurfaces: vec![existing_esurface_id],
1065                center,
1066                complement: vec![],
1067            });
1068            num_disconnected_surfaces += 1;
1069        }
1070    }
1071
1072    // if settings.general.debug > 3 {
1073    //     DEBUG_LOGGER.write("num_disconnected_surfaces", &num_disconnected_surfaces);
1074    // }
1075
1076    if num_disconnected_surfaces == existing_esurfaces.len() {
1077        res.fill_in_complements();
1078        return Ok(res);
1079    }
1080
1081    let mut subset_size =
1082        if let Some(size) = esurface_pairs.has_pair_with.iter().map(Vec::len).max() {
1083            size + 1
1084        } else {
1085            1
1086        };
1087
1088    while subset_size > 1 {
1089        let possible_subsets =
1090            esurface_pairs.construct_possible_subsets_of_len(existing_esurfaces, subset_size, &res);
1091
1092        for subset in possible_subsets.iter() {
1093            let option_center = find_center(
1094                overlap_input,
1095                subset,
1096                existing_esurfaces,
1097                loop_moms,
1098                external_momenta,
1099                false,
1100            )?;
1101
1102            if let Some(center) = option_center {
1103                res.overlap_groups.push(OverlapGroup {
1104                    existing_esurfaces: subset.clone(),
1105                    center,
1106                    complement: vec![],
1107                });
1108            }
1109        }
1110
1111        // if settings.general.debug > 3 {
1112        //     DEBUG_LOGGER.write(
1113        //         "subset_size_and_num_possible_subsets_and_res",
1114        //         &(subset_size, possible_subsets.len(), &res),
1115        //     );
1116        // }
1117
1118        subset_size -= 1;
1119    }
1120
1121    res.fill_in_complements();
1122    Ok(res)
1123}
1124
1125fn is_subset_of_result(subset: &[ExistingEsurfaceId], result: &OverlapStructure) -> bool {
1126    result.overlap_groups.iter().any(|group| {
1127        subset
1128            .iter()
1129            .all(|&x| group.existing_esurfaces.contains(&x))
1130    })
1131}
1132
1133#[derive(Debug)]
1134struct EsurfacePairs {
1135    data: HashMap<(ExistingEsurfaceId, ExistingEsurfaceId), LoopMomenta<F<f64>>>,
1136    has_pair_with: Vec<Vec<ExistingEsurfaceId>>,
1137}
1138
1139impl EsurfacePairs {
1140    fn insert(
1141        &mut self,
1142        pair: (ExistingEsurfaceId, ExistingEsurfaceId),
1143        center: LoopMomenta<F<f64>>,
1144    ) {
1145        if pair.0 > pair.1 {
1146            self.data.insert((pair.1, pair.0), center);
1147        } else {
1148            self.data.insert(pair, center);
1149        }
1150    }
1151
1152    fn pair_exists(&self, pair: (ExistingEsurfaceId, ExistingEsurfaceId)) -> bool {
1153        if pair.0 > pair.1 {
1154            self.data.contains_key(&(pair.1, pair.0))
1155        } else {
1156            self.data.contains_key(&pair)
1157        }
1158    }
1159
1160    fn new_empty(num_existing_esurfaces: usize) -> Self {
1161        let capacity = match num_existing_esurfaces {
1162            0 => 0,
1163            1 => 0,
1164            _ => num_existing_esurfaces * (num_existing_esurfaces - 1) / 2,
1165        };
1166
1167        Self {
1168            data: HashMap::with_capacity(capacity),
1169            has_pair_with: vec![Vec::with_capacity(num_existing_esurfaces); num_existing_esurfaces],
1170        }
1171    }
1172
1173    fn new(
1174        overlap_input: &OverlapInput,
1175        existing_esurfaces: &ExistingThresholds,
1176        loop_moms: &LoopMomenta<F<f64>>,
1177        external_momenta: &ExternalFourMomenta<F<f64>>,
1178    ) -> Result<Self> {
1179        let mut res = Self::new_empty(existing_esurfaces.len());
1180
1181        let all_existing_esurfaces = existing_esurfaces
1182            .iter_enumerated()
1183            .map(|a| a.0)
1184            .collect_vec();
1185
1186        for (i, &esurface_id_1) in all_existing_esurfaces.iter().enumerate() {
1187            for &esurface_id_2 in all_existing_esurfaces.iter().skip(i + 1) {
1188                let center = find_center(
1189                    overlap_input,
1190                    &[esurface_id_1, esurface_id_2],
1191                    existing_esurfaces,
1192                    loop_moms,
1193                    external_momenta,
1194                    false,
1195                )?;
1196
1197                if let Some(center) = center {
1198                    res.insert((esurface_id_1, esurface_id_2), center);
1199                    res.has_pair_with[Into::<usize>::into(esurface_id_1)].push(esurface_id_2);
1200                    res.has_pair_with[Into::<usize>::into(esurface_id_2)].push(esurface_id_1);
1201                }
1202            }
1203        }
1204
1205        Ok(res)
1206    }
1207
1208    fn has_no_overlap(&self, esurface_id: ExistingEsurfaceId) -> bool {
1209        self.has_pair_with[Into::<usize>::into(esurface_id)].is_empty()
1210    }
1211
1212    fn construct_possible_subsets_of_len(
1213        &self,
1214        existing_esurfaces: &ExistingThresholds,
1215        subset_len: usize,
1216        result: &OverlapStructure,
1217    ) -> HashSet<Vec<ExistingEsurfaceId>> {
1218        let mut res = HashSet::default();
1219        // A maximal overlap can have every pair covered by different larger overlaps.
1220        // Only containment of the complete candidate permits skipping its SOCP problem.
1221        for (first, _) in existing_esurfaces.iter_enumerated() {
1222            for remaining in self.has_pair_with[usize::from(first)]
1223                .iter()
1224                .copied()
1225                .filter(|&id| id > first)
1226                .combinations(subset_len - 1)
1227            {
1228                if !remaining
1229                    .iter()
1230                    .tuple_combinations()
1231                    .all(|(&left, &right)| self.pair_exists((left, right)))
1232                {
1233                    continue;
1234                }
1235                let mut candidate = vec![first];
1236                candidate.extend(remaining);
1237                candidate.sort_unstable();
1238                if !is_subset_of_result(&candidate, result) {
1239                    res.insert(candidate);
1240                }
1241            }
1242        }
1243        res
1244    }
1245}
1246
1247#[cfg(test)]
1248mod tests {
1249    use super::*;
1250    use crate::{
1251        cff::{
1252            VertexSet,
1253            esurface::{Esurface, EsurfaceExistence, EsurfaceID},
1254        },
1255        dot,
1256        graph::{LMBext, parse::from_dot::IntoGraph},
1257        initialisation::test_initialise,
1258        momentum::{FourMomentum, Rotatable, Rotation, RotationMethod},
1259    };
1260    use linnet::half_edge::involution::EdgeIndex;
1261    use linnet::half_edge::subgraph::{SuBitGraph, SubSetOps};
1262    use typed_index_collections::ti_vec;
1263
1264    #[test]
1265    fn gl297_recorded_uncertified_socp_problems_are_rescued_without_dropping_overlaps() {
1266        let fixtures: Vec<serde_json::Value> = serde_json::from_str(include_str!(
1267            "../../../../tests/resources/graphs/ir_safe_thresholds/GL297_socp_failures.json"
1268        ))
1269        .unwrap();
1270        for fixture in fixtures {
1271            let a: Vec<Vec<f64>> = serde_json::from_value(fixture["a"].clone()).unwrap();
1272            let b: Vec<f64> = serde_json::from_value(fixture["b"].clone()).unwrap();
1273            let q: Vec<f64> = serde_json::from_value(fixture["q"].clone()).unwrap();
1274            let dimensions: Vec<(String, usize)> =
1275                serde_json::from_value(fixture["cones"].clone()).unwrap();
1276            let cones = dimensions
1277                .iter()
1278                .map(|(kind, size)| match kind.as_str() {
1279                    "NonnegativeConeT" => NonnegativeConeT(*size),
1280                    "SecondOrderConeT" => SecondOrderConeT(*size),
1281                    _ => panic!("unexpected fixture cone {kind}"),
1282                })
1283                .collect::<Vec<_>>();
1284            let surfaces: ExistingThresholds = (0..dimensions[1].1).map(EsurfaceID).collect();
1285            let mut attempts = Vec::new();
1286            let result = OverlapInput::solve_with_rescaling(
1287                "GL297",
1288                &surfaces,
1289                |scale| {
1290                    attempts.push(scale);
1291                    Some(
1292                        DefaultSolver::new(
1293                            &CscMatrix::spalloc((q.len(), q.len()), 0),
1294                            &q,
1295                            &CscMatrix::from(&a),
1296                            &b.iter().map(|value| value / scale).collect::<Vec<_>>(),
1297                            &cones,
1298                            DefaultSettingsBuilder::default()
1299                                .verbose(false)
1300                                .build()
1301                                .unwrap(),
1302                        )
1303                        .unwrap(),
1304                    )
1305                },
1306                |coordinates| {
1307                    // Evaluate the physical energy sums in the ORIGINAL units, not
1308                    // the auxiliary epigraph energies or the solver's objective.
1309                    let active_offset = q.len() - 3;
1310                    let mut offset = 1 + surfaces.len();
1311                    let energies = dimensions[2..]
1312                        .iter()
1313                        .map(|(_, size)| {
1314                            let energy = (offset + 1..offset + size)
1315                                .map(|row| {
1316                                    let shift = b[row]
1317                                        - (active_offset..q.len())
1318                                            .map(|column| a[row][column] * coordinates[column])
1319                                            .sum::<f64>();
1320                                    shift * shift
1321                                })
1322                                .sum::<f64>()
1323                                .sqrt();
1324                            offset += size;
1325                            energy
1326                        })
1327                        .collect::<Vec<_>>();
1328                    (1..=surfaces.len()).all(|row| {
1329                        energies
1330                            .iter()
1331                            .enumerate()
1332                            .map(|(index, energy)| a[row][index + 1] * energy)
1333                            .sum::<f64>()
1334                            < b[row]
1335                    })
1336                },
1337            )
1338            .unwrap();
1339            // The native failure was AlmostPrimalInfeasible. Permit a future solver
1340            // to certify the original problem directly, but never accept a failed
1341            // solve as an empty overlap or bypass the physical-center predicate.
1342            assert!(result.is_none(), "{}", fixture["strategy"]);
1343            assert!(attempts == [1.0] || attempts == [1.0, 2.0]);
1344        }
1345    }
1346
1347    #[test]
1348    fn maximal_overlap_candidates_include_cliques_whose_pairs_are_already_covered() {
1349        let existing: ExistingThresholds = (0..9).map(EsurfaceID::from).collect();
1350        let center = LoopMomenta::from_iter([ThreeMomentum::new(F(0.0), F(0.0), F(0.0))]);
1351        // X,Y,Z have a common region. Two small balls in each pair's exclusive region
1352        // produce the larger overlaps XYuv, XZwx, YZyz, while XYZ remains maximal too.
1353        let larger = [[0, 1, 3, 4], [0, 2, 5, 6], [1, 2, 7, 8]];
1354        let result = OverlapStructure {
1355            existing_esurfaces: existing.clone(),
1356            overlap_groups: larger
1357                .map(|members| OverlapGroup {
1358                    existing_esurfaces: members.into_iter().map(ExistingEsurfaceId::from).collect(),
1359                    complement: vec![],
1360                    center: center.clone(),
1361                })
1362                .to_vec(),
1363        };
1364        let mut pairs = EsurfacePairs::new_empty(existing.len());
1365        for members in larger {
1366            for (left, right) in members
1367                .into_iter()
1368                .map(ExistingEsurfaceId::from)
1369                .tuple_combinations()
1370            {
1371                if !pairs.pair_exists((left, right)) {
1372                    pairs.insert((left, right), center.clone());
1373                    pairs.has_pair_with[usize::from(left)].push(right);
1374                    pairs.has_pair_with[usize::from(right)].push(left);
1375                }
1376            }
1377        }
1378        let central = (0..3).map(ExistingEsurfaceId::from).collect_vec();
1379        assert!(
1380            central
1381                .iter()
1382                .tuple_combinations()
1383                .all(|(&left, &right)| { is_subset_of_result(&[left, right], &result) })
1384        );
1385        assert_eq!(
1386            pairs.construct_possible_subsets_of_len(&existing, 3, &result),
1387            HashSet::from_iter([central]),
1388        );
1389    }
1390
1391    #[test]
1392    fn overlap_partitions_remap_complements_and_keep_instance_kinematics() {
1393        test_initialise().unwrap();
1394        let graph: Graph = dot!(digraph partitioned_centers {
1395            ext [style=invis]
1396            edge [num=1 mass=0]
1397            node [num=1]
1398            ext->a:0 [id=0]
1399            a->b [id=1]
1400            b->a [id=2]
1401            a->b [id=3]
1402            b->a [id=4]
1403            ext->b:1 [id=5]
1404        })
1405        .unwrap();
1406        let lmbs = ti_vec![graph.loop_momentum_basis.clone()];
1407        let subspaces = [LoopIndex(0), LoopIndex(1)].map(|index| {
1408            SubspaceData::new_from_parent_basis_edges(
1409                &[graph.loop_momentum_basis.loop_edges[index]],
1410                &graph.full_filter(),
1411                LmbIndex::from(0),
1412                &graph,
1413                &lmbs,
1414            )
1415            .unwrap()
1416        });
1417        let common = SubspaceData::union_in_common_parent(subspaces.iter(), &graph, &lmbs).unwrap();
1418        let support = graph
1419            .iter_loop_edges()
1420            .map(|(_, edge, _)| edge)
1421            .find(|edge| !graph.loop_momentum_basis.loop_edges.contains(edge))
1422            .unwrap();
1423        let external =
1424            graph.loop_momentum_basis.ext_edges[crate::momentum::sample::ExternalIndex(0)];
1425        let thresholds: EsurfaceCollection = (0..4)
1426            .map(|_| Esurface {
1427                energies: vec![support],
1428                external_shift: vec![(external, -1)],
1429                vertex_set: VertexSet::dummy(),
1430            })
1431            .collect::<Vec<_>>()
1432            .into();
1433        let threshold_subspaces = [0, 1, 0, 1].map(|index| subspaces[index].clone());
1434        let masses = graph.underlying.new_edgevec(|_, _, _| F(0.0));
1435        let externals: ExternalFourMomenta<_> = [
1436            FourMomentum::from_args(F(1.0), F(0.0), F(0.0), F(0.0)),
1437            FourMomentum::from_args(F(-1.0), F(0.0), F(0.0), F(0.0)),
1438        ]
1439        .into_iter()
1440        .collect();
1441        let mut kinematics = (0..4)
1442            .map(|index| {
1443                let mut loop_moms = LoopMomenta::from_iter(
1444                    (0..3).map(|_| ThreeMomentum::new(F(0.0), F(0.0), F(0.0))),
1445                );
1446                loop_moms[LoopIndex(2)].px = F(10.0 * (index + 1) as f64);
1447                OverlapKinematics {
1448                    loop_moms,
1449                    external_momenta: externals.clone(),
1450                    edge_masses: None,
1451                }
1452            })
1453            .collect_vec();
1454        let mut member_masses = masses.clone();
1455        member_masses[support] = F(0.5);
1456        kinematics[0].edge_masses = Some(member_masses);
1457        let settings = RuntimeSettings::default();
1458        let input = OverlapInput {
1459            graph: &graph,
1460            settings: &settings,
1461            subspace: &common,
1462            threshold_subspaces: Some(&threshold_subspaces),
1463            lmbs: &lmbs,
1464            thresholds: &thresholds,
1465            edge_masses: masses.clone(),
1466            surface_kinematics: Some(&kinematics),
1467        };
1468        let existing: ExistingThresholds = thresholds.keys().collect();
1469        let rotation = Rotation::new(RotationMethod::Identity);
1470        let automatic = find_maximal_overlap(
1471            &input,
1472            &existing,
1473            &kinematics[0].loop_moms,
1474            &externals,
1475            &rotation,
1476        )
1477        .unwrap();
1478        let labelled = find_maximal_overlap_with_group_ids(
1479            &input,
1480            &existing,
1481            &[Some(0), Some(1), Some(0), Some(1)],
1482            &kinematics[0].loop_moms,
1483            &externals,
1484            &rotation,
1485        )
1486        .unwrap();
1487        let conflict = find_maximal_overlap_with_group_ids(
1488            &input,
1489            &existing,
1490            &[Some(0); 4],
1491            &kinematics[0].loop_moms,
1492            &externals,
1493            &rotation,
1494        )
1495        .unwrap_err()
1496        .to_string();
1497        assert!(conflict.contains("group_id=0"));
1498        for member in 0..4 {
1499            assert!(conflict.contains(&format!("E-surface instance {member}:")));
1500        }
1501        for overlap in [&automatic, &labelled] {
1502            assert_eq!(overlap.overlap_groups.len(), 4);
1503            for group in &overlap.overlap_groups {
1504                assert_eq!(group.existing_esurfaces.len(), 1);
1505                let member = usize::from(group.existing_esurfaces[0]);
1506                assert_eq!(
1507                    group.complement,
1508                    vec![ExistingEsurfaceId::from((member + 2) % 4)]
1509                );
1510                assert!(check_global_center(
1511                    &input,
1512                    &ti_vec![EsurfaceID(member)],
1513                    &group.center,
1514                    &kinematics[member].loop_moms,
1515                    &externals
1516                ));
1517                assert!(group.center.hyper_radius_squared(None) > F(1.0));
1518            }
1519        }
1520        let first = automatic
1521            .overlap_groups
1522            .iter()
1523            .find(|group| group.existing_esurfaces == vec![ExistingEsurfaceId::from(0)])
1524            .unwrap();
1525        let mut outside_massive_surface = first.center.clone();
1526        outside_massive_surface[LoopIndex(0)].px += F(0.95);
1527        assert!(!check_global_center(
1528            &input,
1529            &ti_vec![EsurfaceID(0)],
1530            &outside_massive_surface,
1531            &kinematics[0].loop_moms,
1532            &externals
1533        ));
1534
1535        let mut forced_settings = settings.clone();
1536        forced_settings
1537            .subtraction
1538            .overlap_settings
1539            .force_global_center = Some(
1540            first
1541                .center
1542                .iter()
1543                .map(|momentum| [momentum.px.0, momentum.py.0, momentum.pz.0])
1544                .collect(),
1545        );
1546        // Other independent groups may have different external data; they must not participate
1547        // in validating this group's configured center.
1548        kinematics[1].external_momenta[crate::momentum::sample::ExternalIndex(0)]
1549            .temporal
1550            .value = F(2.0);
1551        let forced_input = OverlapInput {
1552            graph: &graph,
1553            settings: &forced_settings,
1554            subspace: &common,
1555            threshold_subspaces: Some(&threshold_subspaces),
1556            lmbs: &lmbs,
1557            thresholds: &thresholds,
1558            edge_masses: masses,
1559            surface_kinematics: Some(&kinematics),
1560        };
1561        assert!(
1562            find_maximal_overlap(
1563                &forced_input,
1564                &ti_vec![EsurfaceID(0)],
1565                &kinematics[0].loop_moms,
1566                &externals,
1567                &rotation
1568            )
1569            .is_ok()
1570        );
1571    }
1572
1573    #[test]
1574    fn global_center_check_preserves_fixed_complement_for_multidimensional_subspace() {
1575        test_initialise().unwrap();
1576        let graph: Graph = dot!(digraph subspace_center {
1577            ext [style=invis]
1578            edge [num=1 mass=0]
1579            node [num=1]
1580            ext->v1:0 [id=0]
1581            v1->v2 [id=1]
1582            v2->v1 [id=2]
1583            v1->v2 [id=3]
1584            v2->v1 [id=4]
1585            ext->v2:1 [id=5]
1586        })
1587        .unwrap();
1588
1589        assert_eq!(graph.loop_momentum_basis.loop_edges.len(), 3);
1590        let all_lmbs = ti_vec![graph.loop_momentum_basis.clone()];
1591
1592        let mut parallel_edges_only: SuBitGraph = graph.empty_subgraph();
1593        for active_loop_index in [LoopIndex(0), LoopIndex(1)] {
1594            let active_loop_edge = graph.loop_momentum_basis.loop_edges[active_loop_index];
1595            parallel_edges_only.union_with(&graph.get_edge_subgraph(active_loop_edge));
1596        }
1597        let parallel_edge_subspace = SubspaceData::new_with_user_selected_lmb(
1598            parallel_edges_only,
1599            LmbIndex::from(0),
1600            &graph,
1601            &all_lmbs,
1602        )
1603        .unwrap();
1604        assert_eq!(
1605            parallel_edge_subspace.loopcount(),
1606            1,
1607            "two graph-parallel defining edges without their spanning support contain only one independent loop"
1608        );
1609
1610        let raised_graph: Graph = dot!(digraph raised_signature_subspace {
1611            ext [style=invis]
1612            edge [num=1 mass=0]
1613            node [num=1]
1614            ext->a [id=0]
1615            a->b [id=1]
1616            b->c [id=2]
1617            c->a [id=3]
1618            ext->c [id=4]
1619        })
1620        .unwrap();
1621        let raised_group = raised_graph
1622            .get_raised_edge_groups()
1623            .into_iter()
1624            .find(|group| group.len() >= 2)
1625            .expect("test graph must contain a raised equal-signature edge group");
1626        assert!(raised_group.iter().tuple_windows().all(|(left, right)| {
1627            raised_graph.loop_momentum_basis.edge_signatures[*left]
1628                .equality_up_to_sign(&raised_graph.loop_momentum_basis.edge_signatures[*right])
1629        }));
1630        let mut raised_subgraph: SuBitGraph = raised_graph.empty_subgraph();
1631        for &edge in &raised_group {
1632            raised_subgraph.union_with(&raised_graph.get_edge_subgraph(edge));
1633        }
1634        let raised_lmbs = ti_vec![raised_graph.loop_momentum_basis.clone()];
1635        let raised_edges_only = SubspaceData::new_with_user_selected_lmb(
1636            raised_subgraph.clone(),
1637            LmbIndex::from(0),
1638            &raised_graph,
1639            &raised_lmbs,
1640        )
1641        .unwrap();
1642        assert_eq!(
1643            raised_edges_only.loopcount(),
1644            0,
1645            "a chain of raised equal-signature edges is not multiple independent loops"
1646        );
1647
1648        let support_edge = raised_graph
1649            .iter_loop_edges()
1650            .map(|(_, edge, _)| edge)
1651            .find(|edge| !raised_group.contains(edge))
1652            .expect("test graph must contain the support edge closing the loop");
1653        raised_subgraph.union_with(&raised_graph.get_edge_subgraph(support_edge));
1654        let raised_cycle = SubspaceData::new_with_user_selected_lmb(
1655            raised_subgraph,
1656            LmbIndex::from(0),
1657            &raised_graph,
1658            &raised_lmbs,
1659        )
1660        .unwrap();
1661        assert_eq!(
1662            raised_cycle.loopcount(),
1663            1,
1664            "raised equal-signature edges must be counted by topology, not once per edge"
1665        );
1666
1667        // Include the parent LMB spanning-tree support. Two graph-parallel
1668        // defining edges alone contain only one independent cycle.
1669        let mut subgraph = graph.loop_momentum_basis.tree.clone();
1670        for active_loop_index in [LoopIndex(0), LoopIndex(1)] {
1671            let active_loop_edge = graph.loop_momentum_basis.loop_edges[active_loop_index];
1672            subgraph.union_with(&graph.get_edge_subgraph(active_loop_edge));
1673        }
1674        let subspace = SubspaceData::new_with_user_selected_lmb(
1675            subgraph.clone(),
1676            LmbIndex::from(0),
1677            &graph,
1678            &all_lmbs,
1679        )
1680        .unwrap();
1681        assert_eq!(subspace.loopcount(), 2);
1682
1683        let radial_surface = Esurface {
1684            energies: vec![graph.loop_momentum_basis.loop_edges[LoopIndex(0)]],
1685            external_shift: vec![(EdgeIndex::from(0), -1)],
1686            vertex_set: VertexSet::dummy(),
1687        };
1688        assert!(radial_surface.has_radial_dependence_in_subspace(&subspace, &all_lmbs, &graph,));
1689        let zero_loop_momenta = LoopMomenta::from_iter([
1690            ThreeMomentum::new(F(0.0), F(0.0), F(0.0)),
1691            ThreeMomentum::new(F(0.0), F(0.0), F(0.0)),
1692            ThreeMomentum::new(F(0.0), F(0.0), F(0.0)),
1693        ]);
1694        let classify_radial_surface = |energy| {
1695            let external_momenta = ExternalFourMomenta::from_iter([
1696                FourMomentum::from_args(F(energy), F(10.0), F(0.0), F(0.0)),
1697                FourMomentum::from_args(F(-energy), F(-10.0), F(0.0), F(0.0)),
1698            ]);
1699            radial_surface.classify_existence_subspace(
1700                &zero_loop_momenta,
1701                &external_momenta,
1702                &subspace,
1703                &all_lmbs,
1704                &graph,
1705                &graph.underlying.new_edgevec(|_, _, _| F(0.0)),
1706                &[],
1707                &F(10.0),
1708                &F(crate::utils::DEFAULT_ESURFACE_EXISTENCE_THRESHOLD),
1709            )
1710        };
1711        assert!(matches!(
1712            classify_radial_surface(11.0),
1713            EsurfaceExistence::Existing { .. }
1714        ));
1715        assert!(matches!(
1716            classify_radial_surface(10.0),
1717            EsurfaceExistence::Pinched { .. }
1718        ));
1719        assert!(matches!(
1720            classify_radial_surface(9.0),
1721            EsurfaceExistence::NonExisting { .. }
1722        ));
1723
1724        let complement_edge = graph.loop_momentum_basis.loop_edges[LoopIndex(2)];
1725        let thresholds: crate::cff::esurface::EsurfaceCollection = vec![Esurface {
1726            energies: vec![complement_edge],
1727            external_shift: vec![(EdgeIndex::from(0), -1)],
1728            vertex_set: VertexSet::dummy(),
1729        }]
1730        .into();
1731        assert!(
1732            !thresholds[EsurfaceID::from(0)]
1733                .has_radial_dependence_in_subspace(&subspace, &all_lmbs, &graph,)
1734        );
1735        let masses = graph.underlying.new_edgevec(|_, _, _| F(0.0));
1736        let settings = RuntimeSettings::default();
1737        let overlap_input = OverlapInput {
1738            graph: &graph,
1739            settings: &settings,
1740            subspace: &subspace,
1741            threshold_subspaces: None,
1742            lmbs: &all_lmbs,
1743            thresholds: &thresholds,
1744            edge_masses: masses,
1745            surface_kinematics: None,
1746        };
1747
1748        let external_momenta = ExternalFourMomenta::from_iter([
1749            FourMomentum::from_args(F(1.0), F(0.0), F(0.0), F(0.0)),
1750            FourMomentum::from_args(F(-1.0), F(0.0), F(0.0), F(0.0)),
1751        ]);
1752        let center = LoopMomenta::from_iter([
1753            ThreeMomentum::new(F(0.0), F(0.0), F(0.0)),
1754            ThreeMomentum::new(F(0.0), F(0.0), F(0.0)),
1755            ThreeMomentum::new(F(0.0), F(0.0), F(0.0)),
1756        ]);
1757        let sampled_momenta = LoopMomenta::from_iter([
1758            ThreeMomentum::new(F(0.0), F(0.0), F(0.0)),
1759            ThreeMomentum::new(F(0.0), F(0.0), F(0.0)),
1760            ThreeMomentum::new(F(2.0), F(0.0), F(0.0)),
1761        ]);
1762
1763        let origin_value = overlap_input.thresholds[EsurfaceID::from(0)].compute_from_momenta(
1764            subspace.get_lmb(&all_lmbs),
1765            &overlap_input.edge_masses,
1766            &center,
1767            &external_momenta,
1768        );
1769        assert!(origin_value < F(0.0));
1770        assert!(!check_global_center(
1771            &overlap_input,
1772            &ti_vec![EsurfaceID::from(0)],
1773            &center,
1774            &sampled_momenta,
1775            &external_momenta,
1776        ));
1777        assert!(check_global_center(
1778            &overlap_input,
1779            &ti_vec![EsurfaceID::from(0)],
1780            &center,
1781            &center,
1782            &external_momenta,
1783        ));
1784
1785        let full_parent_subspace = SubspaceData::new_with_user_selected_lmb(
1786            graph.full_filter(),
1787            LmbIndex::from(0),
1788            &graph,
1789            &all_lmbs,
1790        )
1791        .unwrap();
1792        let common_subspace = SubspaceData::union_in_common_parent(
1793            [&subspace, &full_parent_subspace],
1794            &graph,
1795            &all_lmbs,
1796        )
1797        .unwrap();
1798        // The same supergraph geometry is deliberately presented twice as distinct projected
1799        // E-surface instances, each retaining its own fixed complement.
1800        let projected_thresholds: crate::cff::esurface::EsurfaceCollection = vec![
1801            thresholds[EsurfaceID::from(0)].clone(),
1802            thresholds[EsurfaceID::from(0)].clone(),
1803        ]
1804        .into();
1805        let projected_subspaces: TiVec<EsurfaceID, _> =
1806            ti_vec![subspace.clone(), full_parent_subspace];
1807        let projected_input = OverlapInput {
1808            graph: &graph,
1809            settings: &settings,
1810            subspace: &common_subspace,
1811            threshold_subspaces: Some(&projected_subspaces.raw),
1812            lmbs: &all_lmbs,
1813            thresholds: &projected_thresholds,
1814            edge_masses: overlap_input.edge_masses.clone(),
1815            surface_kinematics: overlap_input.surface_kinematics,
1816        };
1817        assert!(!check_global_center(
1818            &projected_input,
1819            &ti_vec![EsurfaceID::from(0)],
1820            &center,
1821            &sampled_momenta,
1822            &external_momenta,
1823        ));
1824        assert!(check_global_center(
1825            &projected_input,
1826            &ti_vec![EsurfaceID::from(1)],
1827            &center,
1828            &sampled_momenta,
1829            &external_momenta,
1830        ));
1831
1832        let forced_center_coordinates = vec![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]];
1833        let identity_frame_center =
1834            LoopMomenta::from_iter(forced_center_coordinates.iter().map(|coordinates| {
1835                ThreeMomentum::new(F(coordinates[0]), F(coordinates[1]), F(coordinates[2]))
1836            }));
1837        let probe_rotation = Rotation::new(RotationMethod::Pi2Z);
1838        let expected_probe_center = identity_frame_center.rotate(&probe_rotation);
1839        let mut expected_subspace_probe_center = expected_probe_center.clone();
1840        for loop_index in (0..expected_subspace_probe_center.len()).map(LoopIndex::from) {
1841            if !subspace.contains_loop_index(loop_index) {
1842                expected_subspace_probe_center[loop_index] =
1843                    ThreeMomentum::new(F(0.0), F(0.0), F(0.0));
1844            }
1845        }
1846        let mut forced_settings = RuntimeSettings::default();
1847        forced_settings
1848            .subtraction
1849            .overlap_settings
1850            .force_global_center = Some(forced_center_coordinates.clone());
1851        let forced_overlap_input = OverlapInput {
1852            graph: &graph,
1853            settings: &forced_settings,
1854            subspace: &subspace,
1855            threshold_subspaces: None,
1856            lmbs: &all_lmbs,
1857            thresholds: &thresholds,
1858            edge_masses: overlap_input.edge_masses.clone(),
1859            surface_kinematics: overlap_input.surface_kinematics,
1860        };
1861
1862        let forced_overlap = find_maximal_overlap(
1863            &forced_overlap_input,
1864            &ti_vec![EsurfaceID::from(0)],
1865            &center,
1866            &external_momenta,
1867            &probe_rotation,
1868        )
1869        .unwrap();
1870        assert_eq!(forced_overlap.overlap_groups.len(), 1);
1871        assert_eq!(
1872            forced_overlap.overlap_groups[0].center, expected_subspace_probe_center,
1873            "an identity-frame forced center must be rotated exactly once and projected onto the active subspace"
1874        );
1875
1876        let alternate_lmb = graph
1877            .generate_loop_momentum_bases()
1878            .into_iter()
1879            .find(|lmb| lmb.loop_edges != graph.loop_momentum_basis.loop_edges)
1880            .expect("test graph must admit a non-default parent LMB");
1881        let alternate_lmbs = ti_vec![graph.loop_momentum_basis.clone(), alternate_lmb];
1882        let full_subspace = SubspaceData::new_with_user_selected_lmb(
1883            graph.full_filter(),
1884            LmbIndex::from(1),
1885            &graph,
1886            &alternate_lmbs,
1887        )
1888        .unwrap();
1889        let empty_thresholds: EsurfaceCollection = Vec::new().into();
1890        let alternate_overlap_input = OverlapInput {
1891            graph: &graph,
1892            settings: &forced_settings,
1893            subspace: &full_subspace,
1894            threshold_subspaces: None,
1895            lmbs: &alternate_lmbs,
1896            thresholds: &empty_thresholds,
1897            edge_masses: overlap_input.edge_masses.clone(),
1898            surface_kinematics: overlap_input.surface_kinematics,
1899        };
1900        let rotated_external_spatial = external_momenta
1901            .iter()
1902            .map(|momentum| momentum.spatial)
1903            .collect();
1904        let expected_alternate_lmb_center = expected_probe_center.lmb_transform(
1905            &graph.loop_momentum_basis,
1906            full_subspace.get_lmb(&alternate_lmbs),
1907            &rotated_external_spatial,
1908        );
1909        let alternate_overlap = find_maximal_overlap(
1910            &alternate_overlap_input,
1911            &ti_vec![],
1912            &center,
1913            &external_momenta,
1914            &probe_rotation,
1915        )
1916        .unwrap();
1917        assert_eq!(
1918            alternate_overlap.overlap_groups[0].center, expected_alternate_lmb_center,
1919            "a forced center must be transformed from the graph LMB into the selected parent LMB after its single probe rotation"
1920        );
1921
1922        let proper_alternate_subspace = SubspaceData::new_with_user_selected_lmb(
1923            subgraph,
1924            LmbIndex::from(1),
1925            &graph,
1926            &alternate_lmbs,
1927        )
1928        .expect("the non-default parent LMB must support the same proper two-loop subspace");
1929        assert_eq!(proper_alternate_subspace.loopcount(), 2);
1930        let affine_external_momenta = ExternalFourMomenta::from_iter([
1931            FourMomentum::from_args(F(5.0), F(1.0), F(2.0), F(3.0)),
1932            FourMomentum::from_args(F(-5.0), F(-1.0), F(-2.0), F(-3.0)),
1933        ]);
1934        let affine_external_spatial = affine_external_momenta
1935            .iter()
1936            .map(|momentum| momentum.spatial)
1937            .collect();
1938        let unprojected_affine_center = identity_frame_center.lmb_transform(
1939            &graph.loop_momentum_basis,
1940            proper_alternate_subspace.get_lmb(&alternate_lmbs),
1941            &affine_external_spatial,
1942        );
1943        assert!(
1944            unprojected_affine_center
1945                .iter_enumerated()
1946                .any(|(loop_index, momentum)| {
1947                    !proper_alternate_subspace.contains_loop_index(loop_index)
1948                        && momentum.norm_squared() > F(0.0)
1949                }),
1950            "the fixture must exercise an affine LMB transform with a nonzero inactive component"
1951        );
1952        let mut expected_projected_affine_center = unprojected_affine_center.clone();
1953        for loop_index in (0..expected_projected_affine_center.len()).map(LoopIndex::from) {
1954            if !proper_alternate_subspace.contains_loop_index(loop_index) {
1955                expected_projected_affine_center[loop_index] =
1956                    ThreeMomentum::new(F(0.0), F(0.0), F(0.0));
1957            }
1958        }
1959        let mut affine_forced_settings = RuntimeSettings::default();
1960        affine_forced_settings
1961            .subtraction
1962            .overlap_settings
1963            .force_global_center = Some(forced_center_coordinates);
1964        let affine_overlap_input = OverlapInput {
1965            graph: &graph,
1966            settings: &affine_forced_settings,
1967            subspace: &proper_alternate_subspace,
1968            threshold_subspaces: None,
1969            lmbs: &alternate_lmbs,
1970            thresholds: &empty_thresholds,
1971            edge_masses: overlap_input.edge_masses.clone(),
1972            surface_kinematics: overlap_input.surface_kinematics,
1973        };
1974        let affine_overlap = find_maximal_overlap(
1975            &affine_overlap_input,
1976            &ti_vec![],
1977            &sampled_momenta,
1978            &affine_external_momenta,
1979            &Rotation::new(RotationMethod::Identity),
1980        )
1981        .unwrap();
1982        assert_eq!(
1983            affine_overlap.overlap_groups[0].center, expected_projected_affine_center,
1984            "an affine parent-LMB transform must not displace the fixed complement of a forced subspace center"
1985        );
1986
1987        let support_edge = [1, 2, 3, 4]
1988            .into_iter()
1989            .map(EdgeIndex::from)
1990            .find(|edge| !graph.loop_momentum_basis.loop_edges.contains(edge))
1991            .unwrap();
1992        let covariant_thresholds = vec![Esurface {
1993            energies: vec![
1994                graph.loop_momentum_basis.loop_edges[LoopIndex(0)],
1995                support_edge,
1996            ],
1997            external_shift: vec![(EdgeIndex::from(0), -1)],
1998            vertex_set: VertexSet::dummy(),
1999        }]
2000        .into();
2001        let covariant_overlap_input = OverlapInput {
2002            graph: &graph,
2003            settings: &settings,
2004            subspace: &subspace,
2005            threshold_subspaces: None,
2006            lmbs: &all_lmbs,
2007            thresholds: &covariant_thresholds,
2008            edge_masses: overlap_input.edge_masses.clone(),
2009            surface_kinematics: overlap_input.surface_kinematics,
2010        };
2011        let covariant_loop_momenta = LoopMomenta::from_iter([
2012            ThreeMomentum::new(F(0.3), F(-0.4), F(0.5)),
2013            ThreeMomentum::new(F(-0.2), F(0.7), F(0.1)),
2014            ThreeMomentum::new(F(0.6), F(-0.8), F(0.9)),
2015        ]);
2016        let covariant_externals = ExternalFourMomenta::from_iter([
2017            FourMomentum::from_args(F(20.0), F(6.0), F(8.0), F(0.0)),
2018            FourMomentum::from_args(F(-20.0), F(-6.0), F(-8.0), F(0.0)),
2019        ]);
2020        let existing = ti_vec![EsurfaceID::from(0)];
2021        let identity_center = find_center(
2022            &covariant_overlap_input,
2023            &[ExistingEsurfaceId::from(0)],
2024            &existing,
2025            &covariant_loop_momenta,
2026            &covariant_externals,
2027            false,
2028        )
2029        .unwrap()
2030        .unwrap();
2031        assert!(check_global_center(
2032            &covariant_overlap_input,
2033            &existing,
2034            &identity_center,
2035            &covariant_loop_momenta,
2036            &covariant_externals,
2037        ));
2038
2039        for rotation_method in [
2040            RotationMethod::Identity,
2041            RotationMethod::Pi2X,
2042            RotationMethod::Pi2Y,
2043            RotationMethod::Pi2Z,
2044            RotationMethod::EulerAngles(0.1, 0.2, 0.3),
2045        ] {
2046            let stability_rotation = Rotation::new(rotation_method);
2047            let rotated_loop_momenta = covariant_loop_momenta.rotate(&stability_rotation);
2048            let rotated_externals = covariant_externals
2049                .iter()
2050                .map(|momentum| FourMomentum {
2051                    temporal: momentum.temporal,
2052                    spatial: momentum.spatial.rotate(&stability_rotation),
2053                })
2054                .collect();
2055            let rotated_center = find_center(
2056                &covariant_overlap_input,
2057                &[ExistingEsurfaceId::from(0)],
2058                &existing,
2059                &rotated_loop_momenta,
2060                &rotated_externals,
2061                false,
2062            )
2063            .unwrap()
2064            .unwrap();
2065            let expected_rotated_center = identity_center.rotate(&stability_rotation);
2066
2067            for (actual, expected) in rotated_center.iter().zip(expected_rotated_center.iter()) {
2068                for (actual_component, expected_component) in actual.into_iter().zip(expected) {
2069                    assert!(
2070                        (actual_component.0 - expected_component.0).abs() < 1.0e-8,
2071                        "solver-derived center is not covariant under stability rotation {rotation_method}: actual={actual_component}, expected={expected_component}"
2072                    );
2073                }
2074            }
2075            assert!(
2076                check_global_center(
2077                    &covariant_overlap_input,
2078                    &existing,
2079                    &rotated_center,
2080                    &rotated_loop_momenta,
2081                    &rotated_externals,
2082                ),
2083                "solver-derived center is not strictly interior after stability rotation {rotation_method}",
2084            );
2085        }
2086    }
2087}