Skip to main content

gammalooprs/subtraction/
lu_counterterm.rs

1use std::{collections::BTreeMap, path::Path, sync::Arc};
2
3use bincode_trait_derive::{Decode, Encode};
4use color_eyre::Result;
5use eyre::{Context, eyre};
6use itertools::Itertools;
7use linnet::half_edge::involution::{EdgeIndex, EdgeVec, Orientation};
8use smallvec::{SmallVec, smallvec};
9use spenso::algebra::{algebraic_traits::IsZero, complex::Complex};
10use symbolica::domains::{
11    dual::{DualNumberStructure, HyperDual},
12    float::{Real, RealLike},
13};
14use tracing::{debug, warn};
15use typed_index_collections::{TiVec, ti_vec};
16
17use crate::{
18    GammaLoopContext,
19    cff::{
20        CutCFFIndex,
21        esurface::{
22            Esurface, EsurfaceCollection, EsurfaceID, ExistingEsurfaceId, ExistingThresholds,
23            esurface_value_is_strictly_inside,
24        },
25        expression::OrientationID,
26    },
27    graph::{Graph, LmbIndex, LoopMomentumBasis},
28    integrands::{
29        evaluation::EvaluationMetaData,
30        process::{
31            GenericEvaluator, ParamBuilder, ThresholdParams,
32            evaluators::{
33                EvaluatorStack, SingleOrAllOrientations, evaluate_evaluator,
34                evaluate_evaluator_single,
35            },
36            param_builder::LUParams,
37            sampling::maps::SamplingEvaluationError,
38            threshold_multiplier::{
39                ThresholdMultiplierEvaluatorCollection, ThresholdMultiplierInputWorkspace,
40            },
41        },
42    },
43    momentum::{
44        Rotatable, Rotation, SignOrZero, ThreeMomentum,
45        sample::{
46            ExternalFourMomenta, ExternalIndex, LoopIndex, LoopMomenta, MomentumSample,
47            SubspaceData,
48        },
49        signature::LoopSignature,
50    },
51    observables::{
52        GenericThresholdCountertermComponentWeight, ThresholdCountertermComponentOccurrence,
53    },
54    processes::{
55        CutGroupId, EvaluatorBuildTimings, IteratedCtCollection, IteratedThresholdPieces,
56        LUCounterTermData, LUThresholdHelperOutputs, LeftThresholdId, RightThresholdId,
57        SingleThresholdPieces, ThresholdCountertermComponentKind,
58        ThresholdCountertermMetadataRegistry, ThresholdCountertermSide,
59        ThresholdCountertermVariantId, build_derivative_structure,
60    },
61    settings::{GlobalSettings, RuntimeSettings, global::FrozenCompilationMode},
62    subtraction::{
63        RstarTDependenceEvaluator, RstarTDependenceInput, evaluate_integrated_ct_normalisation,
64        evaluate_integrated_ct_normalisation_dual, evaluate_uv_damper, evaluate_uv_damper_dual,
65        overlap_subspace::{self, OverlapGroup, OverlapInput, OverlapKinematics, OverlapStructure},
66    },
67    utils::{
68        ArbPrec, F, FloatLike,
69        hyperdual_utils::{
70            DualOrNot, dualize_dual_t_to_dual_r_t, extract_coefficient_t_duals,
71            extract_t_derivatives, extract_t_derivatives_complex, new_constant,
72            shape_from_cut_cff_index, simple_n_deriv_shape, variable_indices_from_cut_cff_index,
73        },
74        newton_solver::{NewtonIterationResult, RadialRootIdentity},
75    },
76    uv::forest::ParametricIntegrands,
77};
78
79fn zero_dual_or_not_complex<T: FloatLike>(order: usize, zero: &F<T>) -> DualOrNot<Complex<F<T>>> {
80    if order == 0 {
81        DualOrNot::NonDual(Complex::new_re(zero.clone()))
82    } else {
83        DualOrNot::Dual(HyperDual::new(simple_n_deriv_shape(order)))
84    }
85}
86
87fn negate_dual_or_not_complex<T: FloatLike>(
88    value: DualOrNot<Complex<F<T>>>,
89) -> DualOrNot<Complex<F<T>>> {
90    match value {
91        DualOrNot::Dual(dual) => DualOrNot::Dual(-dual),
92        DualOrNot::NonDual(non_dual) => DualOrNot::NonDual(-non_dual),
93    }
94}
95
96fn multiply_dual_or_not_complex<T: FloatLike>(
97    lhs: DualOrNot<Complex<F<T>>>,
98    rhs: &DualOrNot<Complex<F<T>>>,
99) -> DualOrNot<Complex<F<T>>> {
100    match (lhs, rhs) {
101        (DualOrNot::NonDual(lhs), DualOrNot::NonDual(rhs)) => DualOrNot::NonDual(lhs * rhs),
102        (DualOrNot::Dual(lhs), DualOrNot::Dual(rhs)) => DualOrNot::Dual(lhs * rhs.clone()),
103        (DualOrNot::Dual(lhs), DualOrNot::NonDual(rhs)) => {
104            let rhs_dual = new_constant(&lhs, rhs);
105            DualOrNot::Dual(lhs * rhs_dual)
106        }
107        (DualOrNot::NonDual(lhs), DualOrNot::Dual(rhs)) => {
108            let lhs_dual = new_constant(rhs, &lhs);
109            DualOrNot::Dual(lhs_dual * rhs.clone())
110        }
111    }
112}
113
114struct ThresholdHelperEvaluation<T, P> {
115    legacy: Option<T>,
116    pieces: Option<P>,
117}
118
119type ThresholdHelperValue<T> = DualOrNot<Complex<F<T>>>;
120type SingleThresholdHelperEvaluation<T> = ThresholdHelperEvaluation<
121    ThresholdHelperValue<T>,
122    SingleThresholdPieces<ThresholdHelperValue<T>>,
123>;
124type IteratedThresholdHelperEvaluation<T> = ThresholdHelperEvaluation<
125    ThresholdHelperValue<T>,
126    IteratedThresholdPieces<ThresholdHelperValue<T>>,
127>;
128
129struct ThresholdHelperEvaluationContext<'a, T: FloatLike> {
130    helper: &'a mut GenericEvaluator,
131    cut_cff_index: &'a CutCFFIndex,
132    threshold_result: &'a ThresholdHelperValue<T>,
133    outputs: LUThresholdHelperOutputs,
134    evaluation_meta_data: &'a mut EvaluationMetaData,
135}
136
137impl<T, P> ThresholdHelperEvaluation<T, P> {
138    fn into_legacy(self) -> T {
139        self.legacy
140            .expect("legacy LU threshold helper output is unavailable")
141    }
142
143    fn into_pieces(self) -> P {
144        self.pieces
145            .expect("split LU threshold helper outputs are unavailable")
146    }
147}
148
149#[derive(Clone, Debug)]
150struct SingleMultiplierCoefficients<T: FloatLike> {
151    local: F<T>,
152    integrated: F<T>,
153}
154
155#[derive(Clone, Copy, Debug)]
156enum SingleMultiplierComponent {
157    Local,
158    Integrated,
159}
160
161fn single_multiplier_context<'a, S>(
162    component: SingleMultiplierComponent,
163    base: &'a S,
164    root: &'a S,
165) -> (&'a S, &'a S) {
166    // The absent side's threshold-rescaling map is the identity: its coordinates stay at `base`,
167    // while the original integrand factor on that side remains part of the expanded term.
168    match component {
169        SingleMultiplierComponent::Local => (base, root),
170        SingleMultiplierComponent::Integrated => (root, root),
171    }
172}
173
174impl<T: FloatLike> SingleMultiplierCoefficients<T> {
175    fn all_zero(&self) -> bool {
176        self.local.is_zero() && self.integrated.is_zero()
177    }
178}
179
180#[derive(Clone, Debug)]
181struct IteratedMultiplierCoefficients<T: FloatLike> {
182    local_local: PairMultiplierCoefficient<T>,
183    local_integrated: PairMultiplierCoefficient<T>,
184    integrated_local: PairMultiplierCoefficient<T>,
185    integrated_integrated: PairMultiplierCoefficient<T>,
186}
187
188#[derive(Clone, Debug)]
189struct PairMultiplierCoefficient<T: FloatLike> {
190    left: F<T>,
191    right: F<T>,
192}
193
194impl<T: FloatLike> PairMultiplierCoefficient<T> {
195    fn effective(&self) -> F<T> {
196        &self.left * &self.right
197    }
198
199    fn is_zero(&self) -> bool {
200        self.left.is_zero() || self.right.is_zero()
201    }
202}
203
204#[derive(Clone, Copy, Debug)]
205enum IteratedMultiplierComponent {
206    LocalLocal,
207    LocalIntegrated,
208    IntegratedLocal,
209    IntegratedIntegrated,
210}
211
212fn iterated_multiplier_context<'a, S>(
213    component: IteratedMultiplierComponent,
214    base: &'a S,
215    left_root: &'a S,
216    right_root: &'a S,
217    merged_root: &'a S,
218) -> (&'a S, &'a S) {
219    match component {
220        IteratedMultiplierComponent::LocalLocal => (base, merged_root),
221        IteratedMultiplierComponent::LocalIntegrated => (right_root, merged_root),
222        IteratedMultiplierComponent::IntegratedLocal => (left_root, merged_root),
223        IteratedMultiplierComponent::IntegratedIntegrated => (merged_root, merged_root),
224    }
225}
226
227impl<T: FloatLike> IteratedMultiplierCoefficients<T> {
228    fn all_zero(&self) -> bool {
229        self.local_local.is_zero()
230            && self.local_integrated.is_zero()
231            && self.integrated_local.is_zero()
232            && self.integrated_integrated.is_zero()
233    }
234}
235
236#[allow(clippy::too_many_arguments)]
237fn evaluate_single_multiplier_context<T: FloatLike>(
238    collection: &mut ThresholdMultiplierEvaluatorCollection,
239    workspace: &mut ThresholdMultiplierInputWorkspace<T>,
240    variant_id: ThresholdCountertermVariantId,
241    generation_lmb: &LoopMomentumBasis,
242    masses: &EdgeVec<F<T>>,
243    model_values: &[Complex<F<f64>>],
244    additional_values: &[F<T>],
245    effective: &MomentumSample<T>,
246    star: &MomentumSample<T>,
247    evaluation_meta_data: &mut EvaluationMetaData,
248) -> Result<F<T>> {
249    let Some(evaluator_id) = collection.evaluator_id_for_variant(variant_id)? else {
250        return Ok(effective.sample.one());
251    };
252    let values = workspace.bind(
253        collection.layout(),
254        generation_lmb,
255        masses,
256        model_values,
257        additional_values,
258        effective,
259        star,
260    )?;
261    collection.evaluators_mut()[evaluator_id.0].evaluate(values, evaluation_meta_data)
262}
263
264#[allow(clippy::too_many_arguments)]
265fn evaluate_pair_multiplier_context<T: FloatLike>(
266    collection: &mut ThresholdMultiplierEvaluatorCollection,
267    workspace: &mut ThresholdMultiplierInputWorkspace<T>,
268    left_variant_id: ThresholdCountertermVariantId,
269    right_variant_id: ThresholdCountertermVariantId,
270    generation_lmb: &LoopMomentumBasis,
271    masses: &EdgeVec<F<T>>,
272    model_values: &[Complex<F<f64>>],
273    additional_values: &[F<T>],
274    effective: &MomentumSample<T>,
275    star: &MomentumSample<T>,
276    evaluation_meta_data: &mut EvaluationMetaData,
277) -> Result<(F<T>, F<T>)> {
278    let left_evaluator_id = collection.evaluator_id_for_variant(left_variant_id)?;
279    let right_evaluator_id = collection.evaluator_id_for_variant(right_variant_id)?;
280    if left_evaluator_id.is_none() && right_evaluator_id.is_none() {
281        return Ok((effective.sample.one(), effective.sample.one()));
282    }
283
284    let values = workspace.bind(
285        collection.layout(),
286        generation_lmb,
287        masses,
288        model_values,
289        additional_values,
290        effective,
291        star,
292    )?;
293    if let (Some(left_evaluator_id), Some(right_evaluator_id)) =
294        (left_evaluator_id, right_evaluator_id)
295        && left_evaluator_id == right_evaluator_id
296    {
297        // Expression interning gives equal functions one evaluator ID. Both sides receive the
298        // same bound input slice, so evaluate the shared function once and reuse its value.
299        let value = collection.evaluators_mut()[left_evaluator_id.0]
300            .evaluate(values, evaluation_meta_data)?;
301        return Ok((value.clone(), value));
302    }
303    let left = if let Some(evaluator_id) = left_evaluator_id {
304        collection.evaluators_mut()[evaluator_id.0].evaluate(values, evaluation_meta_data)?
305    } else {
306        effective.sample.one()
307    };
308    // Deliberately evaluate the right factor even when the left factor is exactly zero. This
309    // keeps finite/real validation symmetric and makes one global context authoritative.
310    let right = if let Some(evaluator_id) = right_evaluator_id {
311        collection.evaluators_mut()[evaluator_id.0].evaluate(values, evaluation_meta_data)?
312    } else {
313        effective.sample.one()
314    };
315    Ok((left, right))
316}
317
318fn weight_single_multiplier_pieces<T: FloatLike>(
319    pieces: SingleThresholdPieces<DualOrNot<Complex<F<T>>>>,
320    coefficients: &SingleMultiplierCoefficients<T>,
321    order: usize,
322    zero: &F<T>,
323) -> DualOrNot<Complex<F<T>>> {
324    let mut result = zero_dual_or_not_complex(order, zero);
325    if !coefficients.local.is_zero() {
326        result += multiply_dual_or_not_complex(
327            pieces.local,
328            &DualOrNot::NonDual(Complex::new_re(coefficients.local.clone())),
329        );
330    }
331    if !coefficients.integrated.is_zero() {
332        result += multiply_dual_or_not_complex(
333            pieces.integrated,
334            &DualOrNot::NonDual(Complex::new_re(coefficients.integrated.clone())),
335        );
336    }
337    result
338}
339
340fn weight_iterated_multiplier_pieces<T: FloatLike>(
341    pieces: IteratedThresholdPieces<DualOrNot<Complex<F<T>>>>,
342    coefficients: &IteratedMultiplierCoefficients<T>,
343    order: usize,
344    zero: &F<T>,
345) -> DualOrNot<Complex<F<T>>> {
346    let mut result = zero_dual_or_not_complex(order, zero);
347    if !coefficients.local_local.is_zero() {
348        result += multiply_dual_or_not_complex(
349            pieces.local_local,
350            &DualOrNot::NonDual(Complex::new_re(coefficients.local_local.effective())),
351        );
352    }
353    if !coefficients.local_integrated.is_zero() {
354        result += multiply_dual_or_not_complex(
355            pieces.local_integrated,
356            &DualOrNot::NonDual(Complex::new_re(coefficients.local_integrated.effective())),
357        );
358    }
359    if !coefficients.integrated_local.is_zero() {
360        result += multiply_dual_or_not_complex(
361            pieces.integrated_local,
362            &DualOrNot::NonDual(Complex::new_re(coefficients.integrated_local.effective())),
363        );
364    }
365    if !coefficients.integrated_integrated.is_zero() {
366        result += multiply_dual_or_not_complex(
367            pieces.integrated_integrated,
368            &DualOrNot::NonDual(Complex::new_re(
369                coefficients.integrated_integrated.effective(),
370            )),
371        );
372    }
373    result
374}
375
376fn extract_zero_threshold_coefficient_t_dual<
377    T: Clone + spenso::algebra::algebraic_traits::RefZero + Default,
378>(
379    dual: &HyperDual<T>,
380    t_variable: usize,
381) -> HyperDual<T> {
382    let (coefficient_orders, coefficient_duals) = extract_coefficient_t_duals(dual, t_variable);
383    let zero_index = coefficient_orders
384        .iter()
385        .position(|orders| orders.iter().all(|order| *order == 0))
386        .expect("Could not find zero-threshold coefficient in mixed dual");
387
388    coefficient_duals[zero_index].clone()
389}
390
391fn embed_t_dual_in_target_shape<T: FloatLike>(
392    t_dual: &HyperDual<F<T>>,
393    target_shape: &HyperDual<F<T>>,
394    t_variable: Option<usize>,
395) -> HyperDual<F<T>> {
396    match t_variable {
397        Some(t_variable) => {
398            dualize_dual_t_to_dual_r_t(t_dual.clone(), target_shape.clone(), t_variable)
399        }
400        None => new_constant(target_shape, &t_dual.values[0]),
401    }
402}
403
404fn activate_threshold_variable_in_target_shape<T: FloatLike>(
405    dual: &mut HyperDual<F<T>>,
406    variable: Option<usize>,
407) {
408    let Some(variable) = variable else {
409        return;
410    };
411
412    let n_variables = dual.get_shape()[0].len();
413    let mut derivative_shape = vec![0; n_variables];
414    derivative_shape[variable] = 1;
415
416    let derivative_index = dual
417        .get_shape()
418        .iter()
419        .position(|shape| shape == &derivative_shape)
420        .expect("Could not find threshold-variable derivative slot in target shape");
421
422    dual.values[derivative_index] = dual.values[0].one();
423}
424
425fn embed_real_dual_or_not_in_target_shape<T: FloatLike>(
426    value: &DualOrNot<F<T>>,
427    target_shape: &Option<HyperDual<F<T>>>,
428    t_variable: Option<usize>,
429) -> DualOrNot<F<T>> {
430    match (value, target_shape) {
431        (DualOrNot::Dual(dual), Some(target_shape)) => {
432            DualOrNot::Dual(embed_t_dual_in_target_shape(dual, target_shape, t_variable))
433        }
434        (DualOrNot::NonDual(value), Some(target_shape)) => {
435            DualOrNot::Dual(new_constant(target_shape, value))
436        }
437        _ => value.clone(),
438    }
439}
440
441fn embedded_dual_loop_momenta_for_cut_cff_index<T: FloatLike>(
442    sample: &MomentumSample<T>,
443    target_shape: &Option<HyperDual<F<T>>>,
444    t_variable: Option<usize>,
445) -> Option<LoopMomenta<HyperDual<F<T>>>> {
446    match target_shape {
447        Some(target_shape) => Some(match sample.sample.dual_loop_moms.as_ref() {
448            Some(dual_loop_moms) => dual_loop_moms
449                .iter()
450                .map(|momentum| {
451                    momentum.map_ref(&|component| {
452                        embed_t_dual_in_target_shape(component, target_shape, t_variable)
453                    })
454                })
455                .collect(),
456            None => sample
457                .loop_moms()
458                .iter()
459                .map(|momentum| {
460                    momentum.map_ref(&|component| new_constant(target_shape, component))
461                })
462                .collect(),
463        }),
464        None => sample.sample.dual_loop_moms.clone(),
465    }
466}
467
468fn extend_plain_helper_real_params<T: FloatLike>(
469    params: &mut Vec<Complex<F<T>>>,
470    value: &DualOrNot<F<T>>,
471    t_variable: Option<usize>,
472) {
473    match value {
474        DualOrNot::Dual(dual) => {
475            if let Some(t_variable) = t_variable {
476                params.extend(
477                    extract_t_derivatives(extract_zero_threshold_coefficient_t_dual(
478                        dual, t_variable,
479                    ))
480                    .into_iter()
481                    .map(Complex::new_re),
482                );
483            } else {
484                params.push(Complex::new_re(dual.values[0].clone()));
485            }
486        }
487        DualOrNot::NonDual(value) => params.push(Complex::new_re(value.clone())),
488    }
489}
490
491fn extend_extracted_f_params<T: FloatLike>(
492    params: &mut Vec<Complex<F<T>>>,
493    value: &DualOrNot<Complex<F<T>>>,
494    t_variable: Option<usize>,
495) {
496    match value {
497        DualOrNot::Dual(dual) => {
498            if let Some(t_variable) = t_variable {
499                let (_, coefficient_duals) = extract_coefficient_t_duals(dual, t_variable);
500                for coefficient_dual in coefficient_duals {
501                    params.extend(extract_t_derivatives_complex(coefficient_dual));
502                }
503            } else {
504                params.extend(dual.values.iter().cloned());
505            }
506        }
507        DualOrNot::NonDual(value) => params.push(value.clone()),
508    }
509}
510
511fn extend_extracted_eta_params<T: FloatLike>(
512    params: &mut Vec<Complex<F<T>>>,
513    value: &DualOrNot<F<T>>,
514    lu_variable: Option<usize>,
515    threshold_variable: Option<usize>,
516) {
517    match value {
518        DualOrNot::Dual(dual) => {
519            if let Some(lu_variable) = lu_variable {
520                let (coefficient_orders, coefficient_duals) =
521                    extract_coefficient_t_duals(dual, lu_variable);
522                let projected_threshold_variable = threshold_variable.map(|variable| {
523                    if variable > lu_variable {
524                        variable - 1
525                    } else {
526                        variable
527                    }
528                });
529                for (orders, coefficient_dual) in
530                    coefficient_orders.into_iter().zip(coefficient_duals)
531                {
532                    if orders.iter().enumerate().any(|(variable, &order)| {
533                        Some(variable) != projected_threshold_variable && order != 0
534                    }) {
535                        continue;
536                    }
537                    params.extend(
538                        extract_t_derivatives(coefficient_dual)
539                            .into_iter()
540                            .map(Complex::new_re),
541                    );
542                }
543            } else {
544                params.extend(
545                    dual.get_shape()
546                        .iter()
547                        .zip(&dual.values)
548                        .filter(|(orders, _)| {
549                            !orders.iter().enumerate().any(|(variable, &order)| {
550                                Some(variable) != threshold_variable && order != 0
551                            })
552                        })
553                        .map(|(_, value)| Complex::new_re(value.clone())),
554                );
555            }
556        }
557        DualOrNot::NonDual(value) => params.push(Complex::new_re(value.clone())),
558    }
559}
560
561fn evaluate_threshold_helper_single<
562    T: FloatLike + crate::integrands::process::evaluators::GenericEvaluatorFloat,
563>(
564    context: ThresholdHelperEvaluationContext<'_, T>,
565    threshold_params: &ThresholdParams<T>,
566) -> SingleThresholdHelperEvaluation<T> {
567    let ThresholdHelperEvaluationContext {
568        helper,
569        cut_cff_index,
570        threshold_result,
571        outputs,
572        evaluation_meta_data,
573    } = context;
574    let variable_indices = variable_indices_from_cut_cff_index(cut_cff_index);
575    let t_variable = variable_indices.lu_cut;
576    let mut helper_params = Vec::new();
577    extend_extracted_f_params(&mut helper_params, threshold_result, t_variable);
578    extend_extracted_eta_params(
579        &mut helper_params,
580        &threshold_params.esurface_derivative,
581        t_variable,
582        variable_indices
583            .left_threshold
584            .or(variable_indices.right_threshold),
585    );
586    extend_plain_helper_real_params(&mut helper_params, &threshold_params.radius, t_variable);
587    extend_plain_helper_real_params(
588        &mut helper_params,
589        &threshold_params.radius_star,
590        t_variable,
591    );
592    extend_plain_helper_real_params(
593        &mut helper_params,
594        &threshold_params.uv_damp_plus,
595        t_variable,
596    );
597    extend_plain_helper_real_params(
598        &mut helper_params,
599        &threshold_params.uv_damp_minus,
600        t_variable,
601    );
602    extend_plain_helper_real_params(&mut helper_params, &threshold_params.h_function, t_variable);
603
604    debug!(
605        "LU threshold helper input (single): cut_cff_index={:?}, t_variable={:?}, param_count={}",
606        cut_cff_index,
607        t_variable,
608        helper_params.len()
609    );
610    for (idx, value) in helper_params.iter().enumerate() {
611        debug!(
612            "LU threshold helper param (single): cut_cff_index={:?}, index={}, value={}",
613            cut_cff_index, idx, value
614        );
615    }
616
617    let mut result = evaluate_evaluator(helper, &helper_params, evaluation_meta_data);
618
619    match outputs {
620        LUThresholdHelperOutputs::Legacy => {
621            assert_eq!(
622                result.len(),
623                1,
624                "legacy single LU threshold helper must return one combined output",
625            );
626            ThresholdHelperEvaluation {
627                legacy: result.pop(),
628                pieces: None,
629            }
630        }
631        LUThresholdHelperOutputs::Pieces => {
632            assert_eq!(
633                result.len(),
634                2,
635                "single LU threshold helper must return local and integrated pieces",
636            );
637            let integrated = result.pop().unwrap();
638            let local = result.pop().unwrap();
639            ThresholdHelperEvaluation {
640                legacy: None,
641                pieces: Some(SingleThresholdPieces { local, integrated }),
642            }
643        }
644        LUThresholdHelperOutputs::LegacyAndPieces => {
645            assert_eq!(
646                result.len(),
647                3,
648                "recording single LU threshold helper must return legacy, local, and integrated outputs",
649            );
650            let integrated = result.pop().unwrap();
651            let local = result.pop().unwrap();
652            let legacy = result.pop().unwrap();
653            ThresholdHelperEvaluation {
654                legacy: Some(legacy),
655                pieces: Some(SingleThresholdPieces { local, integrated }),
656            }
657        }
658    }
659}
660
661fn evaluate_threshold_helper_iterated<
662    T: FloatLike + crate::integrands::process::evaluators::GenericEvaluatorFloat,
663>(
664    context: ThresholdHelperEvaluationContext<'_, T>,
665    left_threshold_params: &ThresholdParams<T>,
666    right_threshold_params: &ThresholdParams<T>,
667) -> IteratedThresholdHelperEvaluation<T> {
668    let ThresholdHelperEvaluationContext {
669        helper,
670        cut_cff_index,
671        threshold_result,
672        outputs,
673        evaluation_meta_data,
674    } = context;
675    let variable_indices = variable_indices_from_cut_cff_index(cut_cff_index);
676    let t_variable = variable_indices.lu_cut;
677    let mut helper_params = Vec::new();
678    extend_extracted_f_params(&mut helper_params, threshold_result, t_variable);
679    extend_extracted_eta_params(
680        &mut helper_params,
681        &left_threshold_params.esurface_derivative,
682        t_variable,
683        variable_indices.left_threshold,
684    );
685    extend_extracted_eta_params(
686        &mut helper_params,
687        &right_threshold_params.esurface_derivative,
688        t_variable,
689        variable_indices.right_threshold,
690    );
691
692    extend_plain_helper_real_params(
693        &mut helper_params,
694        &left_threshold_params.radius,
695        t_variable,
696    );
697    extend_plain_helper_real_params(
698        &mut helper_params,
699        &left_threshold_params.radius_star,
700        t_variable,
701    );
702    extend_plain_helper_real_params(
703        &mut helper_params,
704        &left_threshold_params.uv_damp_plus,
705        t_variable,
706    );
707    extend_plain_helper_real_params(
708        &mut helper_params,
709        &left_threshold_params.uv_damp_minus,
710        t_variable,
711    );
712    extend_plain_helper_real_params(
713        &mut helper_params,
714        &left_threshold_params.h_function,
715        t_variable,
716    );
717
718    extend_plain_helper_real_params(
719        &mut helper_params,
720        &right_threshold_params.radius,
721        t_variable,
722    );
723    extend_plain_helper_real_params(
724        &mut helper_params,
725        &right_threshold_params.radius_star,
726        t_variable,
727    );
728    extend_plain_helper_real_params(
729        &mut helper_params,
730        &right_threshold_params.uv_damp_plus,
731        t_variable,
732    );
733    extend_plain_helper_real_params(
734        &mut helper_params,
735        &right_threshold_params.uv_damp_minus,
736        t_variable,
737    );
738    extend_plain_helper_real_params(
739        &mut helper_params,
740        &right_threshold_params.h_function,
741        t_variable,
742    );
743
744    debug!(
745        "LU threshold helper input (iterated): cut_cff_index={:?}, t_variable={:?}, param_count={}",
746        cut_cff_index,
747        t_variable,
748        helper_params.len()
749    );
750    for (idx, value) in helper_params.iter().enumerate() {
751        debug!(
752            "LU threshold helper param (iterated): cut_cff_index={:?}, index={}, value={}",
753            cut_cff_index, idx, value
754        );
755    }
756
757    let mut result = evaluate_evaluator(helper, &helper_params, evaluation_meta_data);
758
759    match outputs {
760        LUThresholdHelperOutputs::Legacy => {
761            assert_eq!(
762                result.len(),
763                1,
764                "legacy iterated LU threshold helper must return one combined output",
765            );
766            ThresholdHelperEvaluation {
767                legacy: result.pop(),
768                pieces: None,
769            }
770        }
771        LUThresholdHelperOutputs::Pieces | LUThresholdHelperOutputs::LegacyAndPieces => {
772            let expected = if outputs == LUThresholdHelperOutputs::Pieces {
773                4
774            } else {
775                5
776            };
777            assert_eq!(
778                result.len(),
779                expected,
780                "iterated LU threshold helper must return the configured legacy and/or LL, LI, IL, and II outputs",
781            );
782            let integrated_integrated = result.pop().unwrap();
783            let integrated_local = result.pop().unwrap();
784            let local_integrated = result.pop().unwrap();
785            let local_local = result.pop().unwrap();
786            let legacy = (outputs == LUThresholdHelperOutputs::LegacyAndPieces)
787                .then(|| result.pop().unwrap());
788            ThresholdHelperEvaluation {
789                legacy,
790                pieces: Some(IteratedThresholdPieces {
791                    local_local,
792                    local_integrated,
793                    integrated_local,
794                    integrated_integrated,
795                }),
796            }
797        }
798    }
799}
800
801fn real_dual_to_complex<T: FloatLike>(dual: HyperDual<F<T>>) -> HyperDual<Complex<F<T>>> {
802    let shape = dual.get_shape().iter().map(|v| v.to_vec()).collect();
803    let values = dual.values.into_iter().map(Complex::new_re).collect_vec();
804    HyperDual::from_values(shape, values)
805}
806
807fn dualize_external_momenta<T: FloatLike>(
808    shape: &HyperDual<F<T>>,
809    external_moms: &ExternalFourMomenta<F<T>>,
810) -> ExternalFourMomenta<HyperDual<F<T>>> {
811    external_moms
812        .iter()
813        .map(|momentum| momentum.map_ref(&|component| new_constant(shape, component)))
814        .collect()
815}
816
817fn dualize_loop_momenta<T: FloatLike>(
818    shape: &HyperDual<F<T>>,
819    loop_moms: &LoopMomenta<F<T>>,
820) -> LoopMomenta<HyperDual<F<T>>> {
821    loop_moms
822        .iter()
823        .map(|momentum| momentum.map_ref(&|component| new_constant(shape, component)))
824        .collect()
825}
826
827fn dual_shifted_radius<T: FloatLike>(
828    shifted_momenta: &LoopMomenta<HyperDual<F<T>>>,
829    subspace: &SubspaceData,
830) -> HyperDual<F<T>> {
831    let zero = new_constant(
832        &shifted_momenta[LoopIndex(0)].px,
833        &shifted_momenta[LoopIndex(0)].px.values[0].zero(),
834    );
835
836    subspace
837        .iter_lmb_indices()
838        .fold(zero, |acc, loop_index| {
839            acc + shifted_momenta[loop_index].norm_squared()
840        })
841        .sqrt()
842}
843
844fn compute_shift_part_from_dual_momenta_in_subspace<T: FloatLike>(
845    esurface: &Esurface,
846    loop_moms: &LoopMomenta<HyperDual<F<T>>>,
847    external_moms: &ExternalFourMomenta<HyperDual<F<T>>>,
848    subspace: &SubspaceData,
849    all_lmbs: &TiVec<LmbIndex, LoopMomentumBasis>,
850    graph: &Graph,
851    masses: &EdgeVec<F<T>>,
852) -> HyperDual<F<T>> {
853    let lmb = subspace.get_lmb(all_lmbs);
854    let zero = new_constant(
855        &external_moms[ExternalIndex(0)].temporal.value,
856        &external_moms[ExternalIndex(0)].temporal.value.values[0].zero(),
857    );
858
859    let full_external_shift = esurface
860        .external_shift
861        .iter()
862        .map(|(index, sign)| {
863            let external_signature = &lmb.edge_signatures[*index].external;
864            let sign = new_constant(&zero, &F::from_f64(*sign as f64));
865            let external_energy = external_signature
866                .try_apply(&external_moms.raw)
867                .map(|momentum| momentum.temporal.value)
868                .unwrap_or_else(|| zero.clone());
869
870            sign * external_energy
871        })
872        .reduce(|acc, value| acc + value)
873        .unwrap_or_else(|| zero.clone());
874
875    let spatial_externals = external_moms
876        .iter()
877        .map(|momentum| momentum.spatial.clone())
878        .collect::<TiVec<ExternalIndex, _>>();
879
880    let remaining_shift = subspace
881        .does_not_contain(&esurface.energies, graph)
882        .map(|index| {
883            let signature = &lmb.edge_signatures[index];
884            let momentum = signature
885                .try_compute_momentum(&loop_moms.0, &spatial_externals.raw)
886                .unwrap_or_else(|| unreachable!());
887            let mass = &masses[index];
888            let lifted_mass = new_constant(&momentum.px, mass);
889
890            (momentum.norm_squared() + lifted_mass.clone() * lifted_mass).sqrt()
891        })
892        .reduce(|acc, value| acc + value)
893        .unwrap_or_else(|| zero.clone());
894
895    full_external_shift + remaining_shift
896}
897
898#[allow(clippy::too_many_arguments)]
899fn compute_self_and_r_derivative_subspace_dual<T: FloatLike>(
900    esurface: &Esurface,
901    radius: &HyperDual<F<T>>,
902    shifted_unit_loops_in_subspace: &LoopMomenta<HyperDual<F<T>>>,
903    center_in_subspace: &LoopMomenta<HyperDual<F<T>>>,
904    external_moms: &ExternalFourMomenta<HyperDual<F<T>>>,
905    real_mass_vector: &EdgeVec<F<T>>,
906    subspace: &SubspaceData,
907    all_lmbs: &TiVec<LmbIndex, LoopMomentumBasis>,
908    graph: &Graph,
909) -> (HyperDual<F<T>>, HyperDual<F<T>>) {
910    let spatial_part_of_externals = external_moms
911        .iter()
912        .map(|momentum| momentum.spatial.clone())
913        .collect::<TiVec<ExternalIndex, _>>();
914
915    let loops: LoopMomenta<HyperDual<F<T>>> = shifted_unit_loops_in_subspace
916        .iter_enumerated()
917        .map(|(loop_index, shifted_unit_momenta)| {
918            if subspace.contains_loop_index(loop_index) {
919                shifted_unit_momenta * radius + &center_in_subspace[loop_index]
920            } else {
921                shifted_unit_momenta.clone()
922            }
923        })
924        .collect();
925
926    let shift = compute_shift_part_from_dual_momenta_in_subspace(
927        esurface,
928        shifted_unit_loops_in_subspace,
929        external_moms,
930        subspace,
931        all_lmbs,
932        graph,
933        real_mass_vector,
934    );
935
936    let lmb = subspace.get_lmb(all_lmbs);
937    let zero = new_constant(radius, &radius.values[0].zero());
938    let (derivative, energy_sum) = subspace
939        .contains(&esurface.energies, graph)
940        .map(|index| {
941            let signature = &lmb.edge_signatures[index];
942            let momentum = signature
943                .try_compute_momentum(&loops.0, &spatial_part_of_externals.raw)
944                .unwrap_or_else(|| unreachable!());
945            let unit_loop_part = compute_loop_part_subspace_dual(
946                &signature.internal,
947                shifted_unit_loops_in_subspace,
948                subspace,
949            );
950            let mass = &real_mass_vector[index];
951            let lifted_mass = new_constant(&momentum.px, mass);
952            let energy = (momentum.norm_squared() + lifted_mass.clone() * lifted_mass).sqrt();
953            let numerator = momentum * &unit_loop_part;
954
955            (numerator / &energy, energy)
956        })
957        .fold((zero.clone(), zero), |(der_sum, en_sum), (der, en)| {
958            (der_sum + der, en_sum + en)
959        });
960
961    (energy_sum + shift, derivative)
962}
963
964fn compute_loop_part_subspace_dual<T: FloatLike>(
965    loop_signature: &LoopSignature,
966    loop_moms: &LoopMomenta<HyperDual<F<T>>>,
967    subspace: &SubspaceData,
968) -> ThreeMomentum<HyperDual<F<T>>> {
969    let projected: LoopSignature = subspace.project_loop_signature(loop_signature).collect();
970    let zero = new_constant(
971        &loop_moms[LoopIndex(0)].px,
972        &loop_moms[LoopIndex(0)].px.values[0].zero(),
973    );
974    let mut result = ThreeMomentum::new(zero.clone(), zero.clone(), zero);
975
976    for (loop_index, sign) in projected.iter_enumerated() {
977        match sign {
978            SignOrZero::Zero => {}
979            SignOrZero::Plus => result += &loop_moms[loop_index],
980            SignOrZero::Minus => result -= &loop_moms[loop_index],
981        }
982    }
983
984    result
985}
986
987#[derive(Clone, Encode, Decode)]
988#[trait_decode(trait = GammaLoopContext)]
989pub struct LUThresholdHelperEvaluators {
990    pub left_thresholds: TiVec<LeftThresholdId, BTreeMap<CutCFFIndex, GenericEvaluator>>,
991    pub right_thresholds: TiVec<RightThresholdId, BTreeMap<CutCFFIndex, GenericEvaluator>>,
992    pub iterated: IteratedCtCollection<BTreeMap<CutCFFIndex, GenericEvaluator>>,
993}
994
995impl LUThresholdHelperEvaluators {
996    fn for_each_generic_evaluator_mut(
997        &mut self,
998        mut f: impl FnMut(&mut GenericEvaluator) -> color_eyre::Result<()>,
999    ) -> color_eyre::Result<()> {
1000        for evaluators in self.left_thresholds.iter_mut() {
1001            for evaluator in evaluators.values_mut() {
1002                f(evaluator)?;
1003            }
1004        }
1005        for evaluators in self.right_thresholds.iter_mut() {
1006            for evaluator in evaluators.values_mut() {
1007                f(evaluator)?;
1008            }
1009        }
1010        for evaluators in self.iterated.iter_mut() {
1011            for evaluator in evaluators.values_mut() {
1012                f(evaluator)?;
1013            }
1014        }
1015
1016        Ok(())
1017    }
1018}
1019
1020#[derive(Clone, Encode, Decode)]
1021#[trait_decode(trait = GammaLoopContext)]
1022pub struct LUCounterTermEvaluators {
1023    pub left_thresholds_evaluator: TiVec<LeftThresholdId, BTreeMap<CutCFFIndex, EvaluatorStack>>,
1024    pub right_thresholds_evaluator: TiVec<RightThresholdId, BTreeMap<CutCFFIndex, EvaluatorStack>>,
1025    pub iterated_evaluator: IteratedCtCollection<BTreeMap<CutCFFIndex, EvaluatorStack>>,
1026    pub threshold_helpers: LUThresholdHelperEvaluators,
1027    pub threshold_multipliers: Option<ThresholdMultiplierEvaluatorCollection>,
1028    pub residue_from_e_surface_evaluators: Vec<GenericEvaluator>,
1029}
1030
1031impl LUCounterTermEvaluators {
1032    fn residue_evaluators(
1033        parametric_integrands: &ParametricIntegrands,
1034        param_builder: &ParamBuilder,
1035        settings: &GlobalSettings,
1036        orientations: &TiVec<OrientationID, EdgeVec<Orientation>>,
1037        production_orientation_ids: &[OrientationID],
1038    ) -> (BTreeMap<CutCFFIndex, EvaluatorStack>, EvaluatorBuildTimings) {
1039        let mut timings = EvaluatorBuildTimings::default();
1040        let evaluators = parametric_integrands
1041            .integrands
1042            .iter()
1043            .map(|(index, atom)| {
1044                let dual_shape = shape_from_cut_cff_index(index);
1045                let (evaluator, evaluator_timings) = EvaluatorStack::from_integrand_with_timings(
1046                    atom,
1047                    param_builder,
1048                    parametric_integrands.integrands.numerators(),
1049                    (!settings.generation.explicit_orientation_sum_only)
1050                        .then_some((&orientations.raw, production_orientation_ids)),
1051                    dual_shape,
1052                    &settings.generation.evaluator,
1053                )
1054                .unwrap();
1055                timings += evaluator_timings;
1056                (*index, evaluator)
1057            })
1058            .collect();
1059
1060        (evaluators, timings)
1061    }
1062
1063    pub(crate) fn generic_compileable_evaluator_count(&self) -> usize {
1064        let left = self
1065            .left_thresholds_evaluator
1066            .iter()
1067            .flat_map(|evaluators| evaluators.values())
1068            .map(EvaluatorStack::generic_evaluator_count)
1069            .sum::<usize>();
1070        let right = self
1071            .right_thresholds_evaluator
1072            .iter()
1073            .flat_map(|evaluators| evaluators.values())
1074            .map(EvaluatorStack::generic_evaluator_count)
1075            .sum::<usize>();
1076        let iterated = self
1077            .iterated_evaluator
1078            .iter()
1079            .flat_map(|evaluators| evaluators.values())
1080            .map(EvaluatorStack::generic_evaluator_count)
1081            .sum::<usize>();
1082
1083        // Ignore eager-only helper and multiplier evaluators, as well as pass-two evaluators.
1084        left + right + iterated
1085    }
1086
1087    fn evaluate_residue_from_esurface<T: FloatLike>(
1088        &mut self,
1089        cut_group_id: CutGroupId,
1090        order: usize,
1091        pass_one_result: DualOrNot<Complex<F<T>>>,
1092        cut_esurface: &DualOrNot<F<T>>,
1093        evaluation_meta_data: &mut EvaluationMetaData,
1094    ) -> Complex<F<T>> {
1095        let mut params_for_pass_two = vec![];
1096        match pass_one_result {
1097            DualOrNot::Dual(dual_result) => {
1098                params_for_pass_two.extend_from_slice(&extract_t_derivatives_complex(dual_result));
1099            }
1100            DualOrNot::NonDual(non_dual_result) => {
1101                params_for_pass_two.push(non_dual_result);
1102            }
1103        }
1104
1105        match cut_esurface {
1106            DualOrNot::Dual(dual_e_surface) => {
1107                extract_t_derivatives(dual_e_surface.clone())[1..]
1108                    .iter()
1109                    .for_each(|value| params_for_pass_two.push(Complex::new_re(value.clone())));
1110            }
1111            DualOrNot::NonDual(non_dual_e_surface) => {
1112                debug!("non dual esurface: {}", non_dual_e_surface);
1113                params_for_pass_two.push(Complex::new_re(non_dual_e_surface.clone()));
1114            }
1115        }
1116
1117        debug!(
1118            "LU pass-two evaluator input: cut_group_id={}, lu_order={}, param_count={}",
1119            cut_group_id.0,
1120            order + 1,
1121            params_for_pass_two.len()
1122        );
1123        for (idx, value) in params_for_pass_two.iter().enumerate() {
1124            debug!(
1125                "LU pass-two evaluator param: cut_group_id={}, lu_order={}, index={}, value={}",
1126                cut_group_id.0,
1127                order + 1,
1128                idx,
1129                value
1130            );
1131        }
1132
1133        evaluate_evaluator_single(
1134            &mut self.residue_from_e_surface_evaluators[order],
1135            &params_for_pass_two,
1136            evaluation_meta_data,
1137        )
1138    }
1139
1140    #[allow(clippy::too_many_arguments)]
1141    pub fn from_atoms(
1142        counterterm_data: &LUCounterTermData,
1143        max_cut_order: usize,
1144        threshold_helpers: LUThresholdHelperEvaluators,
1145        threshold_multipliers: Option<ThresholdMultiplierEvaluatorCollection>,
1146        param_builder: &ParamBuilder,
1147        settings: &GlobalSettings,
1148        orientations: &TiVec<OrientationID, EdgeVec<Orientation>>,
1149        production_orientation_ids: &[OrientationID],
1150    ) -> (Self, EvaluatorBuildTimings) {
1151        let mut timings = EvaluatorBuildTimings::default();
1152        let left_thresholds_evaluator = counterterm_data
1153            .left_atoms
1154            .iter()
1155            .map(|parametric_integrands| {
1156                let (evaluators, evaluator_timings) = Self::residue_evaluators(
1157                    parametric_integrands,
1158                    param_builder,
1159                    settings,
1160                    orientations,
1161                    production_orientation_ids,
1162                );
1163                timings += evaluator_timings;
1164                evaluators
1165            })
1166            .collect();
1167
1168        let right_thresholds_evaluator = counterterm_data
1169            .right_atoms
1170            .iter()
1171            .map(|parametric_integrands| {
1172                let (evaluators, evaluator_timings) = Self::residue_evaluators(
1173                    parametric_integrands,
1174                    param_builder,
1175                    settings,
1176                    orientations,
1177                    production_orientation_ids,
1178                );
1179                timings += evaluator_timings;
1180                evaluators
1181            })
1182            .collect();
1183
1184        let iterated_timings = std::cell::Cell::new(EvaluatorBuildTimings::default());
1185        let iterated_evaluator = counterterm_data.iterated.map_ref(|parametric_integrands| {
1186            let (evaluators, evaluator_timings) = Self::residue_evaluators(
1187                parametric_integrands,
1188                param_builder,
1189                settings,
1190                orientations,
1191                production_orientation_ids,
1192            );
1193            let mut timings = iterated_timings.get();
1194            timings += evaluator_timings;
1195            iterated_timings.set(timings);
1196            evaluators
1197        });
1198        timings += iterated_timings.get();
1199
1200        let symbolica_started = std::time::Instant::now();
1201        let pass_two_evaluator = (1..=max_cut_order)
1202            .map(|order| {
1203                build_derivative_structure(order as u8, -1, &settings.generation.evaluator)
1204            })
1205            .collect();
1206
1207        timings.symbolica_time += symbolica_started.elapsed();
1208
1209        if let Some(multipliers) = &threshold_multipliers {
1210            multipliers
1211                .validate(
1212                    counterterm_data.left_thresholds.len(),
1213                    counterterm_data.right_thresholds.len(),
1214                )
1215                .expect("invalid threshold-multiplier registry generated for LU counterterms");
1216        }
1217
1218        (
1219            LUCounterTermEvaluators {
1220                left_thresholds_evaluator,
1221                right_thresholds_evaluator,
1222                iterated_evaluator,
1223                threshold_helpers,
1224                threshold_multipliers,
1225                residue_from_e_surface_evaluators: pass_two_evaluator,
1226            },
1227            timings,
1228        )
1229    }
1230
1231    pub(crate) fn compile(
1232        &mut self,
1233        path: impl AsRef<Path>,
1234        cut_group_id: CutGroupId,
1235        frozen_mode: &FrozenCompilationMode,
1236    ) -> color_eyre::Result<()> {
1237        for (threshold_id, evaluators) in self.left_thresholds_evaluator.iter_mut_enumerated() {
1238            for (index, evaluator) in evaluators.iter_mut() {
1239                let name = format!(
1240                    "cut_group_{}_left_threshold_{}_index_{}",
1241                    cut_group_id.0, threshold_id.0, index
1242                );
1243                evaluator.compile(&name, path.as_ref(), frozen_mode)?;
1244            }
1245        }
1246
1247        for (threshold_id, evaluators) in self.right_thresholds_evaluator.iter_mut_enumerated() {
1248            for (index, evaluator) in evaluators.iter_mut() {
1249                let name = format!(
1250                    "cut_group_{}_right_threshold_{}_index_{}",
1251                    cut_group_id.0, threshold_id.0, index
1252                );
1253                evaluator.compile(&name, path.as_ref(), frozen_mode)?;
1254            }
1255        }
1256
1257        for (iterated_index, evaluators) in self.iterated_evaluator.iter_mut().enumerate() {
1258            for (index, evaluator) in evaluators.iter_mut() {
1259                let name = format!(
1260                    "cut_group_{}_iterated_{}_index_{}",
1261                    cut_group_id.0, iterated_index, index
1262                );
1263                evaluator.compile(&name, path.as_ref(), frozen_mode)?;
1264            }
1265        }
1266
1267        for (order, pass_to_evaluator) in self
1268            .residue_from_e_surface_evaluators
1269            .iter_mut()
1270            .enumerate()
1271        {
1272            let name = format!("cut_group_{}_pass_two_{}", cut_group_id.0, order);
1273            pass_to_evaluator.compile_external(
1274                path.as_ref().join(&name).with_extension("cpp"),
1275                &name,
1276                path.as_ref().join(&name).with_extension("so"),
1277                frozen_mode,
1278            )?;
1279        }
1280
1281        Ok(())
1282    }
1283
1284    pub(crate) fn for_each_generic_evaluator_mut(
1285        &mut self,
1286        mut f: impl FnMut(&mut GenericEvaluator) -> color_eyre::Result<()>,
1287    ) -> color_eyre::Result<()> {
1288        for evaluators in self.left_thresholds_evaluator.iter_mut() {
1289            for evaluator in evaluators.values_mut() {
1290                evaluator.for_each_generic_evaluator_mut(&mut f)?;
1291            }
1292        }
1293        for evaluators in self.right_thresholds_evaluator.iter_mut() {
1294            for evaluator in evaluators.values_mut() {
1295                evaluator.for_each_generic_evaluator_mut(&mut f)?;
1296            }
1297        }
1298        for evaluators in self.iterated_evaluator.iter_mut() {
1299            for evaluator in evaluators.values_mut() {
1300                evaluator.for_each_generic_evaluator_mut(&mut f)?;
1301            }
1302        }
1303        self.threshold_helpers
1304            .for_each_generic_evaluator_mut(&mut f)?;
1305        if let Some(multipliers) = &mut self.threshold_multipliers {
1306            multipliers.for_each_generic_evaluator_mut(&mut f)?;
1307        }
1308        for pass_to_evaluator in self.residue_from_e_surface_evaluators.iter_mut() {
1309            f(pass_to_evaluator)?;
1310        }
1311
1312        Ok(())
1313    }
1314}
1315
1316type CutThresholds = (
1317    TiVec<LeftThresholdId, Esurface>,
1318    TiVec<RightThresholdId, Esurface>,
1319);
1320
1321#[derive(Clone, Encode, Decode)]
1322#[trait_decode(trait = GammaLoopContext)]
1323pub(crate) struct LUVariantSubspaces {
1324    pub left_variant_ids: TiVec<LeftThresholdId, ThresholdCountertermVariantId>,
1325    pub right_variant_ids: TiVec<RightThresholdId, ThresholdCountertermVariantId>,
1326    pub left: TiVec<LeftThresholdId, SubspaceData>,
1327    pub right: TiVec<RightThresholdId, SubspaceData>,
1328}
1329
1330#[derive(Clone, Encode, Decode)]
1331#[trait_decode(trait = GammaLoopContext)]
1332pub(crate) struct LUCounterTerm {
1333    pub evaluators: TiVec<CutGroupId, LUCounterTermEvaluators>,
1334    pub thresholds: TiVec<CutGroupId, CutThresholds>,
1335    pub subspaces: TiVec<CutGroupId, (SubspaceData, SubspaceData)>,
1336    /// Present only when directives require per-variant projected E-surface instances.
1337    pub variant_subspaces: Option<TiVec<CutGroupId, LUVariantSubspaces>>,
1338    /// Stable graph-local static metadata. The absent/no-directive path retains no allocation.
1339    pub metadata_registry: Option<ThresholdCountertermMetadataRegistry>,
1340    pub active_cut_groups: TiVec<CutGroupId, bool>,
1341    pub active_left_thresholds: TiVec<CutGroupId, TiVec<LeftThresholdId, bool>>,
1342    pub active_right_thresholds: TiVec<CutGroupId, TiVec<RightThresholdId, bool>>,
1343    pub active_iterated_thresholds: TiVec<CutGroupId, IteratedCtCollection<bool>>,
1344    pub rstar_dependence_calculator: TiVec<CutGroupId, RstarTDependenceEvaluator>,
1345}
1346
1347pub(crate) struct LUCountertermEvaluation<T: FloatLike> {
1348    pub total: Complex<F<T>>,
1349    pub components: Option<Vec<GenericThresholdCountertermComponentWeight<T>>>,
1350}
1351
1352#[derive(Clone, Debug, PartialEq, Eq)]
1353struct GlobalOverlapRecord {
1354    cut_group_id: CutGroupId,
1355    side: ThresholdCountertermSide,
1356    local_threshold_id: usize,
1357    variant_id: Option<ThresholdCountertermVariantId>,
1358}
1359
1360/// One complete solve group. Every sample is expressed in `subspace`'s parent,
1361/// while its cut's fixed data remain independent. Raised derivative packets
1362/// stay on the cut's `LUCTKinematicPoint`, outside shared center geometry.
1363#[derive(Debug)]
1364struct ThresholdSolveGroup<T: FloatLike> {
1365    thresholds: EsurfaceCollection,
1366    subspace: SubspaceData,
1367    kinematics: Vec<MomentumSample<T>>,
1368    records: Vec<GlobalOverlapRecord>,
1369    overlap: OverlapStructure,
1370    prefactor_power: usize,
1371}
1372
1373#[derive(Clone, Debug)]
1374pub struct LUSharedOverlaps<T: FloatLike> {
1375    left: OverlapStructure,
1376    right: OverlapStructure,
1377    // Local CT dispatch refers back to the complete group and its center. In
1378    // particular, foreign-cut complements never disappear during projection.
1379    left_groups: Vec<(Arc<ThresholdSolveGroup<T>>, usize)>,
1380    right_groups: Vec<(Arc<ThresholdSolveGroup<T>>, usize)>,
1381}
1382
1383struct PendingLUCountertermComponent<T: FloatLike> {
1384    component_id: usize,
1385    occurrence: ThresholdCountertermComponentOccurrence,
1386    multiplier_values: SmallVec<[F<T>; 2]>,
1387    effective_multiplier: F<T>,
1388    /// `None` is reserved for an exact-zero multiplier and avoids pass-one/helper/pass-two work.
1389    pass_one: Option<DualOrNot<Complex<F<T>>>>,
1390}
1391
1392impl<T: FloatLike> PendingLUCountertermComponent<T> {
1393    fn finish(
1394        self,
1395        zero: &F<T>,
1396        evaluate_pass_two: impl FnOnce(DualOrNot<Complex<F<T>>>) -> Complex<F<T>>,
1397    ) -> (Complex<F<T>>, GenericThresholdCountertermComponentWeight<T>) {
1398        let Some(pass_one) = self.pass_one else {
1399            let weighted = Complex::new_re(zero.clone());
1400            return (
1401                weighted.clone(),
1402                GenericThresholdCountertermComponentWeight {
1403                    component_id: self.component_id,
1404                    occurrence: self.occurrence,
1405                    multiplier_values: self.multiplier_values,
1406                    effective_multiplier: self.effective_multiplier,
1407                    bare: None,
1408                    weighted,
1409                    evaluation_skipped: true,
1410                },
1411            );
1412        };
1413
1414        let bare = evaluate_pass_two(pass_one);
1415        let weighted = &bare * &Complex::new_re(self.effective_multiplier.clone());
1416        (
1417            weighted.clone(),
1418            GenericThresholdCountertermComponentWeight {
1419                component_id: self.component_id,
1420                occurrence: self.occurrence,
1421                multiplier_values: self.multiplier_values,
1422                effective_multiplier: self.effective_multiplier,
1423                bare: Some(bare),
1424                weighted,
1425                evaluation_skipped: false,
1426            },
1427        )
1428    }
1429}
1430
1431#[derive(Clone)]
1432pub struct LUCTKinematicPoint<T: FloatLike> {
1433    pub unrescaled_sample: MomentumSample<T>,
1434    pub dualized_momentum_sample_cache: Vec<MomentumSample<T>>,
1435    pub lu_cut_parameter_cache: Vec<LUParams<T>>,
1436    pub lu_cut_esurface_values: Vec<DualOrNot<F<T>>>,
1437}
1438
1439impl<T: FloatLike> LUCTKinematicPoint<T> {
1440    pub fn unrescaled_sample(&self) -> &MomentumSample<T> {
1441        &self.unrescaled_sample
1442    }
1443
1444    pub fn representative_sample(&self) -> &MomentumSample<T> {
1445        &self.dualized_momentum_sample_cache[0]
1446    }
1447
1448    pub fn sample_for_order(&self, order: usize) -> &MomentumSample<T> {
1449        &self.dualized_momentum_sample_cache[order]
1450    }
1451
1452    pub fn lmb_transform(&self, from: &LoopMomentumBasis, to: &LoopMomentumBasis) -> Self {
1453        let transformed_samples = self
1454            .dualized_momentum_sample_cache
1455            .iter()
1456            .map(|sample| sample.lmb_transform(from, to))
1457            .collect();
1458
1459        Self {
1460            unrescaled_sample: self.unrescaled_sample.clone(),
1461            dualized_momentum_sample_cache: transformed_samples,
1462            lu_cut_parameter_cache: self.lu_cut_parameter_cache.clone(),
1463            lu_cut_esurface_values: self.lu_cut_esurface_values.clone(),
1464        }
1465    }
1466
1467    pub fn non_dual_cut_params(&self) -> LUParams<T> {
1468        self.lu_cut_parameter_cache[0].clone()
1469    }
1470
1471    pub fn cut_params_for_order(&self, order: usize) -> &LUParams<T> {
1472        &self.lu_cut_parameter_cache[order]
1473    }
1474
1475    pub fn cut_params_for_cut_cff_index(&self, cut_cff_index: &CutCFFIndex) -> LUParams<T> {
1476        let lu_order = cut_cff_index
1477            .lu_cut_order
1478            .expect("LU cut parameter lookup requires lu_cut_order")
1479            - 1;
1480        let base_params = self.cut_params_for_order(lu_order).clone();
1481        let target_shape = shape_from_cut_cff_index(cut_cff_index).map(HyperDual::new);
1482        let t_variable = variable_indices_from_cut_cff_index(cut_cff_index).lu_cut;
1483
1484        LUParams {
1485            tstar: embed_real_dual_or_not_in_target_shape(
1486                &base_params.tstar,
1487                &target_shape,
1488                t_variable,
1489            ),
1490            h_function: embed_real_dual_or_not_in_target_shape(
1491                &base_params.h_function,
1492                &target_shape,
1493                t_variable,
1494            ),
1495        }
1496    }
1497
1498    pub fn cut_esurface_for_order(&self, order: usize) -> &DualOrNot<F<T>> {
1499        &self.lu_cut_esurface_values[order]
1500    }
1501
1502    pub fn non_dual_cut_esurface_value(&self) -> F<T> {
1503        match self.cut_esurface_for_order(0) {
1504            DualOrNot::NonDual(value) => value.clone(),
1505            DualOrNot::Dual(_) => {
1506                unreachable!("representative LU cut e-surface value must be non-dual")
1507            }
1508        }
1509    }
1510
1511    pub fn new(unrescaled_sample: MomentumSample<T>) -> Self {
1512        Self {
1513            unrescaled_sample,
1514            dualized_momentum_sample_cache: Vec::new(),
1515            lu_cut_parameter_cache: Vec::new(),
1516            lu_cut_esurface_values: Vec::new(),
1517        }
1518    }
1519}
1520
1521impl LUCounterTerm {
1522    fn radial_root_identity(
1523        graph_name: &str,
1524        cut_group_id: CutGroupId,
1525        side: &str,
1526        overlap_group: usize,
1527        esurface_id: EsurfaceID,
1528        probe_rotation: &Rotation,
1529    ) -> RadialRootIdentity {
1530        RadialRootIdentity::new(format!(
1531            "LU graph '{graph_name}' cut group {} {side} overlap group {overlap_group} E-surface {} probe rotation {}",
1532            cut_group_id.0, esurface_id.0, probe_rotation.method,
1533        ))
1534    }
1535
1536    pub(crate) fn cut_group_is_active(&self, cut_group_id: CutGroupId) -> bool {
1537        self.active_cut_groups[cut_group_id]
1538    }
1539
1540    pub(crate) fn compile(
1541        &mut self,
1542        path: impl AsRef<Path>,
1543        frozen_mode: &FrozenCompilationMode,
1544    ) -> color_eyre::Result<()> {
1545        for (cut_group_id, evaluators) in self.evaluators.iter_mut_enumerated() {
1546            evaluators.compile(path.as_ref(), cut_group_id, frozen_mode)?;
1547        }
1548        Ok(())
1549    }
1550
1551    pub(crate) fn for_each_generic_evaluator_mut(
1552        &mut self,
1553        mut f: impl FnMut(&mut GenericEvaluator) -> color_eyre::Result<()>,
1554    ) -> color_eyre::Result<()> {
1555        for evaluators in self.evaluators.iter_mut() {
1556            evaluators.for_each_generic_evaluator_mut(&mut f)?;
1557        }
1558        Ok(())
1559    }
1560
1561    fn ensure_active_cut_group(&self, cut_group_id: CutGroupId) -> Result<()> {
1562        if self.cut_group_is_active(cut_group_id) {
1563            return Ok(());
1564        }
1565
1566        Err(eyre!(
1567            "Cut group {} was reached at runtime even though generation marked it inactive for the selected orientation subset",
1568            cut_group_id.0
1569        ))
1570    }
1571
1572    fn ensure_active_left_threshold(
1573        &self,
1574        cut_group_id: CutGroupId,
1575        threshold_id: LeftThresholdId,
1576    ) -> Result<()> {
1577        if self.active_left_thresholds[cut_group_id][threshold_id] {
1578            return Ok(());
1579        }
1580
1581        Err(eyre!(
1582            "Left threshold evaluator {} for cut group {} was reached at runtime even though generation marked it inactive for the selected orientation subset",
1583            threshold_id.0,
1584            cut_group_id.0
1585        ))
1586    }
1587
1588    fn ensure_active_right_threshold(
1589        &self,
1590        cut_group_id: CutGroupId,
1591        threshold_id: RightThresholdId,
1592    ) -> Result<()> {
1593        if self.active_right_thresholds[cut_group_id][threshold_id] {
1594            return Ok(());
1595        }
1596
1597        Err(eyre!(
1598            "Right threshold evaluator {} for cut group {} was reached at runtime even though generation marked it inactive for the selected orientation subset",
1599            threshold_id.0,
1600            cut_group_id.0
1601        ))
1602    }
1603
1604    fn ensure_active_iterated_threshold(
1605        &self,
1606        cut_group_id: CutGroupId,
1607        iterated_index: (LeftThresholdId, RightThresholdId),
1608    ) -> Result<()> {
1609        if self.active_iterated_thresholds[cut_group_id][iterated_index] {
1610            return Ok(());
1611        }
1612
1613        Err(eyre!(
1614            "Iterated threshold evaluator ({}, {}) for cut group {} was reached at runtime even though generation marked it inactive for the selected orientation subset",
1615            iterated_index.0.0,
1616            iterated_index.1.0,
1617            cut_group_id.0
1618        ))
1619    }
1620
1621    /// Collect all solved cuts before constructing independent physical-cycle groups.
1622    /// Local threshold IDs are retained only for residue dispatch; each center's
1623    /// multichannel complements continue to refer to the complete solve group.
1624    /// Only representative cut samples enter geometry preparation; the caller
1625    /// retains its raised LU packets for physical residue evaluation. Additional
1626    /// packets may carry different momenta and derivatives; none of those values
1627    /// may alter common centers or the foreign-cut data retained by a group.
1628    /// Canonical preparation chooses identity-frame centers once; native adoption
1629    /// retains those decisions and rebuilds only each member's actual fixed data.
1630    #[allow(clippy::too_many_arguments)]
1631    pub(crate) fn prepare_shared_overlaps<T: FloatLike>(
1632        &self,
1633        cut_samples: &[(CutGroupId, &MomentumSample<T>)],
1634        graph: &Graph,
1635        masses: &EdgeVec<F<T>>,
1636        reversed_edges: &TiVec<CutGroupId, Vec<EdgeIndex>>,
1637        all_lmbs: &TiVec<LmbIndex, LoopMomentumBasis>,
1638        settings: &RuntimeSettings,
1639        probe_rotation: &Rotation,
1640        canonical: Option<&TiVec<CutGroupId, Option<LUSharedOverlaps<ArbPrec>>>>,
1641    ) -> Result<TiVec<CutGroupId, Option<LUSharedOverlaps<T>>>> {
1642        if canonical.is_none() && !probe_rotation.is_identity() {
1643            return Err(eyre!(
1644                "physical overlap centers must be prepared in the canonical identity frame"
1645            ));
1646        }
1647        // Local dispatch can refer to one complete group more than once. Retain
1648        // its canonical occurrence identity, never compare or hash center values.
1649        let mut canonical_groups = canonical.map(|locals| {
1650            locals
1651                .iter()
1652                .flatten()
1653                .flat_map(|local| local.left_groups.iter().chain(&local.right_groups))
1654                .map(|(group, _)| group)
1655                .unique_by(|group| Arc::as_ptr(group))
1656                .collect_vec()
1657        });
1658        if let Some(canonical) = canonical
1659            && (canonical.len() != self.thresholds.len()
1660                || canonical.iter_enumerated().any(|(cut, overlap)| {
1661                    overlap.is_some() != cut_samples.iter().any(|(id, _)| *id == cut)
1662                }))
1663        {
1664            return Err(SamplingEvaluationError::UncertainGeometry {
1665                detail: format!(
1666                    "physical selector/cut membership differs from canonical graph '{}'",
1667                    graph.name
1668                ),
1669            }
1670            .into());
1671        }
1672        let mut partitions = BTreeMap::new();
1673        let e_cm = F::from_f64(settings.kinematics.e_cm);
1674        let existence_threshold = F::from_f64(settings.subtraction.esurface_existence_threshold);
1675        for &(cut_group_id, cut_sample) in cut_samples {
1676            let variants = self
1677                .variant_subspaces
1678                .as_ref()
1679                .map(|data| &data[cut_group_id]);
1680            for side in [
1681                ThresholdCountertermSide::Left,
1682                ThresholdCountertermSide::Right,
1683            ] {
1684                let (thresholds, legacy, subspaces, variant_ids, active) = match side {
1685                    ThresholdCountertermSide::Left => (
1686                        self.thresholds[cut_group_id]
1687                            .0
1688                            .iter()
1689                            .cloned()
1690                            .collect_vec(),
1691                        &self.subspaces[cut_group_id].0,
1692                        variants.map(|data| data.left.raw.as_slice()),
1693                        variants.map(|data| data.left_variant_ids.raw.as_slice()),
1694                        self.active_left_thresholds[cut_group_id].raw.as_slice(),
1695                    ),
1696                    ThresholdCountertermSide::Right => (
1697                        self.thresholds[cut_group_id]
1698                            .1
1699                            .iter()
1700                            .cloned()
1701                            .collect_vec(),
1702                        &self.subspaces[cut_group_id].1,
1703                        variants.map(|data| data.right.raw.as_slice()),
1704                        variants.map(|data| data.right_variant_ids.raw.as_slice()),
1705                        self.active_right_thresholds[cut_group_id].raw.as_slice(),
1706                    ),
1707                    ThresholdCountertermSide::Amplitude => unreachable!(),
1708                };
1709                for (local_id, surface) in thresholds.into_iter().enumerate() {
1710                    if !active[local_id] {
1711                        continue;
1712                    }
1713                    let subspace = subspaces.map_or(legacy, |data| &data[local_id]);
1714                    let native = cut_sample
1715                        .lmb_transform(&graph.loop_momentum_basis, subspace.get_lmb(all_lmbs));
1716                    if !surface
1717                        .classify_existence_subspace(
1718                            native.loop_moms(),
1719                            native.external_moms(),
1720                            subspace,
1721                            all_lmbs,
1722                            graph,
1723                            masses,
1724                            &reversed_edges[cut_group_id],
1725                            &e_cm,
1726                            &existence_threshold,
1727                        )
1728                        .is_existing()
1729                    {
1730                        continue;
1731                    }
1732                    let variant_id = variant_ids.map(|ids| ids[local_id]);
1733                    let group_id = variant_id.and_then(|id| {
1734                        self.metadata_registry
1735                            .as_ref()
1736                            .and_then(|registry| registry.variants.get(id.0))
1737                            .and_then(|variant| variant.group_id)
1738                    });
1739                    let record = GlobalOverlapRecord {
1740                        cut_group_id,
1741                        side,
1742                        local_threshold_id: local_id,
1743                        variant_id,
1744                    };
1745                    partitions
1746                        .entry((group_id, subspace.solve_signature(all_lmbs)))
1747                        .or_insert_with(Vec::new)
1748                        .push((record, surface, subspace.clone(), native));
1749                }
1750            }
1751        }
1752        let mut groups = Vec::new();
1753        for ((group_id, signature), entries) in partitions {
1754            let subspace = entries[0].2.clone();
1755            let mut thresholds = EsurfaceCollection::new();
1756            let mut kinematics = Vec::new();
1757            let mut records = Vec::new();
1758            let mut prefactor_power = 2;
1759            for (record, surface, native_subspace, native) in entries {
1760                let order = match record.side {
1761                    ThresholdCountertermSide::Left => self.evaluators[record.cut_group_id]
1762                        .left_thresholds_evaluator
1763                        [LeftThresholdId::from(record.local_threshold_id)]
1764                    .keys()
1765                    .filter_map(|index| index.left_threshold_order)
1766                    .max()
1767                    .unwrap_or(1),
1768                    ThresholdCountertermSide::Right => self.evaluators[record.cut_group_id]
1769                        .right_thresholds_evaluator
1770                        [RightThresholdId::from(record.local_threshold_id)]
1771                    .keys()
1772                    .filter_map(|index| index.right_threshold_order)
1773                    .max()
1774                    .unwrap_or(1),
1775                    ThresholdCountertermSide::Amplitude => unreachable!(),
1776                };
1777                let num_right = self.thresholds[record.cut_group_id].1.len();
1778                let iterated_order = self.evaluators[record.cut_group_id]
1779                    .iterated_evaluator
1780                    .iter()
1781                    .enumerate()
1782                    .filter(|(index, _)| match record.side {
1783                        ThresholdCountertermSide::Left => {
1784                            index / num_right == record.local_threshold_id
1785                        }
1786                        ThresholdCountertermSide::Right => {
1787                            index % num_right == record.local_threshold_id
1788                        }
1789                        ThresholdCountertermSide::Amplitude => false,
1790                    })
1791                    .flat_map(|(_, evaluator)| evaluator.keys())
1792                    .filter_map(|index| match record.side {
1793                        ThresholdCountertermSide::Left => index.left_threshold_order,
1794                        ThresholdCountertermSide::Right => index.right_threshold_order,
1795                        ThresholdCountertermSide::Amplitude => None,
1796                    })
1797                    .max()
1798                    .unwrap_or(1);
1799                let order = order.max(iterated_order);
1800                // An even power strictly above every pole order suppresses every
1801                // omitted threshold, including the derivatives of raised residues.
1802                prefactor_power = prefactor_power.max(2 * (order / 2 + 1));
1803                thresholds.push(surface);
1804                kinematics.push(native.lmb_transform(
1805                    native_subspace.get_lmb(all_lmbs),
1806                    subspace.get_lmb(all_lmbs),
1807                ));
1808                records.push(record);
1809            }
1810            let overlap = if let Some(groups) = &mut canonical_groups {
1811                let index = groups.iter().position(|group| {
1812                    group.records == records
1813                        && group.subspace.solve_signature(all_lmbs) == signature
1814                        && group.subspace.get_lmb(all_lmbs).loop_edges
1815                            == subspace.get_lmb(all_lmbs).loop_edges
1816                        && group.prefactor_power == prefactor_power
1817                }).ok_or_else(|| SamplingEvaluationError::UncertainGeometry {
1818                    detail: format!("physical overlap membership differs from canonical graph '{}' group {:?}, records {:?}", graph.name, group_id, records),
1819                })?;
1820                groups.remove(index).overlap.clone()
1821            } else {
1822                let solver_kinematics = kinematics
1823                    .iter()
1824                    .map(|sample| OverlapKinematics {
1825                        loop_moms: sample.loop_moms().iter().map(|p| p.to_f64()).collect(),
1826                        external_momenta: sample
1827                            .external_moms()
1828                            .iter()
1829                            .map(|p| p.to_f64())
1830                            .collect(),
1831                        edge_masses: None,
1832                    })
1833                    .collect_vec();
1834                let existing: ExistingThresholds = thresholds.keys().collect();
1835                let input = OverlapInput {
1836                    graph,
1837                    settings,
1838                    subspace: &subspace,
1839                    threshold_subspaces: None,
1840                    lmbs: all_lmbs,
1841                    thresholds: &thresholds,
1842                    edge_masses: masses.iter().map(|(_, mass)| F(mass.to_f64())).collect(),
1843                    surface_kinematics: Some(&solver_kinematics),
1844                };
1845                let first = &solver_kinematics[0];
1846                overlap_subspace::find_maximal_overlap(
1847                    &input,
1848                    &existing,
1849                    &first.loop_moms,
1850                    &first.external_momenta,
1851                    probe_rotation,
1852                )?
1853            };
1854            crate::debug_tags!(#integration, #subtraction, #threshold, #inspect, #center;
1855                graph = %graph.name, group_id = ?group_id,
1856                file.solve_signature = ?signature,
1857                file.instances = ?records.iter().map(|record| (record.cut_group_id, record.side, record.local_threshold_id, record.variant_id)).collect_vec(),
1858                centers = overlap.overlap_groups.len(),
1859                "prepared complete threshold solve group"
1860            );
1861            groups.push(Arc::new(ThresholdSolveGroup {
1862                thresholds,
1863                subspace,
1864                kinematics,
1865                records,
1866                overlap,
1867                prefactor_power,
1868            }));
1869        }
1870        if canonical_groups
1871            .as_ref()
1872            .is_some_and(|groups| !groups.is_empty())
1873        {
1874            return Err(SamplingEvaluationError::UncertainGeometry {
1875                detail: format!(
1876                    "physical graph '{}' omitted a canonical threshold solve group",
1877                    graph.name
1878                ),
1879            }
1880            .into());
1881        }
1882        let mut result = ti_vec![None; self.thresholds.len()];
1883        for &(cut_group_id, _) in cut_samples {
1884            let mut local = LUSharedOverlaps {
1885                left: OverlapStructure::new_empty(),
1886                right: OverlapStructure::new_empty(),
1887                left_groups: Vec::new(),
1888                right_groups: Vec::new(),
1889            };
1890            for side in [
1891                ThresholdCountertermSide::Left,
1892                ThresholdCountertermSide::Right,
1893            ] {
1894                let (structure, contexts) = if side == ThresholdCountertermSide::Left {
1895                    (&mut local.left, &mut local.left_groups)
1896                } else {
1897                    (&mut local.right, &mut local.right_groups)
1898                };
1899                structure.existing_esurfaces = groups
1900                    .iter()
1901                    .flat_map(|group| &group.records)
1902                    .filter(|record| record.cut_group_id == cut_group_id && record.side == side)
1903                    .map(|record| EsurfaceID::from(record.local_threshold_id))
1904                    .sorted()
1905                    .collect();
1906                for group in &groups {
1907                    let local_id = |id: ExistingEsurfaceId| {
1908                        let record = &group.records[group.overlap.existing_esurfaces[id].0];
1909                        if record.cut_group_id != cut_group_id || record.side != side {
1910                            return None;
1911                        }
1912                        structure
1913                            .existing_esurfaces
1914                            .iter_enumerated()
1915                            .find_map(|(id, surface)| {
1916                                (surface.0 == record.local_threshold_id).then_some(id)
1917                            })
1918                    };
1919                    for (center_index, center) in group.overlap.overlap_groups.iter().enumerate() {
1920                        let members = center
1921                            .existing_esurfaces
1922                            .iter()
1923                            .filter_map(|&id| local_id(id))
1924                            .collect_vec();
1925                        if members.is_empty() {
1926                            continue;
1927                        }
1928                        let complement = center
1929                            .complement
1930                            .iter()
1931                            .filter_map(|&id| local_id(id))
1932                            .collect();
1933                        structure.overlap_groups.push(OverlapGroup {
1934                            existing_esurfaces: members,
1935                            complement,
1936                            center: center.center.clone(),
1937                        });
1938                        contexts.push((Arc::clone(group), center_index));
1939                    }
1940                }
1941            }
1942            // Some(empty) retains an accepted physical cut with no existing
1943            // thresholds; None denotes a cut excluded before overlap preparation.
1944            result[cut_group_id] = Some(local);
1945        }
1946        Ok(result)
1947    }
1948
1949    #[allow(clippy::too_many_arguments)]
1950    pub(crate) fn evaluate<T: FloatLike>(
1951        &mut self,
1952        kinematic_point: &LUCTKinematicPoint<T>,
1953        cut_group_id: CutGroupId,
1954        _reversed_edges: &[EdgeIndex],
1955        all_lmbs: &TiVec<LmbIndex, LoopMomentumBasis>,
1956        graph: &Graph,
1957        masses: &EdgeVec<F<T>>,
1958        probe_rotation: &Rotation,
1959        settings: &RuntimeSettings,
1960        param_builder: &mut ParamBuilder<f64>,
1961        orientations: SingleOrAllOrientations<'_, OrientationID>,
1962        evaluation_meta_data: &mut EvaluationMetaData,
1963        record_components: bool,
1964        shared_overlaps: Option<&LUSharedOverlaps<T>>,
1965    ) -> Result<LUCountertermEvaluation<T>> {
1966        self.ensure_active_cut_group(cut_group_id)?;
1967
1968        // Metadata alone does not request runtime allocations. Detailed storage is enabled only
1969        // for an accepted event whose caller explicitly asked for additional weights.
1970        let record_components = record_components && self.metadata_registry.is_some();
1971        let additional_params = settings.additional_params::<T>();
1972
1973        let (left_thresholds_typed, right_thresholds_typed) = &self.thresholds[cut_group_id];
1974
1975        let left_thresholds = TiVec::from_ref(&left_thresholds_typed.raw);
1976        let right_thresholds = TiVec::from_ref(&right_thresholds_typed.raw);
1977
1978        if left_thresholds.is_empty() && right_thresholds.is_empty() {
1979            return Ok(LUCountertermEvaluation {
1980                total: Complex::new_re(kinematic_point.representative_sample().zero()),
1981                components: record_components.then(Vec::new),
1982            });
1983        }
1984
1985        let (legacy_left_subspace, legacy_right_subspace) = &self.subspaces[cut_group_id];
1986        let variant_subspaces = self
1987            .variant_subspaces
1988            .as_ref()
1989            .map(|subspaces| &subspaces[cut_group_id]);
1990        let threshold_helper_outputs = if variant_subspaces.is_some() {
1991            LUThresholdHelperOutputs::Pieces
1992        } else if self.metadata_registry.is_some() {
1993            LUThresholdHelperOutputs::LegacyAndPieces
1994        } else {
1995            LUThresholdHelperOutputs::Legacy
1996        };
1997        let left_subspace = legacy_left_subspace;
1998        let right_subspace = legacy_right_subspace;
1999        let left_threshold_subspaces =
2000            variant_subspaces.map(|subspaces| subspaces.left.raw.as_slice());
2001        let right_threshold_subspaces =
2002            variant_subspaces.map(|subspaces| subspaces.right.raw.as_slice());
2003        let detailed_variant_ids = if record_components {
2004            let registry = self
2005                .metadata_registry
2006                .as_ref()
2007                .expect("record_components requires threshold metadata");
2008            let (left, right) = if let Some(subspaces) = variant_subspaces {
2009                (
2010                    subspaces.left_variant_ids.iter().copied().collect_vec(),
2011                    subspaces.right_variant_ids.iter().copied().collect_vec(),
2012                )
2013            } else {
2014                (
2015                    registry.lu_variant_ids(cut_group_id, ThresholdCountertermSide::Left)?,
2016                    registry.lu_variant_ids(cut_group_id, ThresholdCountertermSide::Right)?,
2017                )
2018            };
2019            if left.len() != left_thresholds.len() || right.len() != right_thresholds.len() {
2020                return Err(eyre!(
2021                    "graph '{}' cut group {} threshold metadata maps {} left/{} right variants onto {} left/{} right runtime thresholds",
2022                    graph.name,
2023                    cut_group_id.0,
2024                    left.len(),
2025                    right.len(),
2026                    left_thresholds.len(),
2027                    right_thresholds.len(),
2028                ));
2029            }
2030            Some((left, right))
2031        } else {
2032            None
2033        };
2034        let Some(shared) = shared_overlaps else {
2035            return Ok(LUCountertermEvaluation {
2036                total: Complex::new_re(kinematic_point.representative_sample().zero()),
2037                components: record_components.then(Vec::new),
2038            });
2039        };
2040        let left_overlap = &shared.left;
2041        let right_overlap = &shared.right;
2042        debug!("left overlap structure: {}", left_overlap);
2043        debug!("right overlap structure: {}", right_overlap);
2044        let mut radial_root_failed = false;
2045        let left_counterterm_builder = CounterTermBuilder::new(
2046            graph,
2047            settings,
2048            left_thresholds,
2049            kinematic_point.clone(),
2050            left_overlap,
2051            masses,
2052            all_lmbs,
2053            left_subspace,
2054            left_threshold_subspaces,
2055            probe_rotation,
2056            cut_group_id,
2057            &shared.left_groups,
2058        );
2059        let left_overlap_builders = left_overlap
2060            .overlap_groups
2061            .iter()
2062            .enumerate()
2063            .map(|(group_index, group)| {
2064                group
2065                    .existing_esurfaces
2066                    .iter()
2067                    .map(|id| {
2068                        left_counterterm_builder
2069                            .new_overlap_builder(group_index, left_overlap.existing_esurfaces[*id])
2070                    })
2071                    .collect_vec()
2072            })
2073            .collect_vec();
2074        let mut left_overlap_solutions = Vec::with_capacity(left_overlap.overlap_groups.len());
2075        for (overlap_group_index, (group, builders)) in left_overlap
2076            .overlap_groups
2077            .iter()
2078            .zip(&left_overlap_builders)
2079            .enumerate()
2080        {
2081            let mut solutions = Vec::with_capacity(builders.len());
2082            for (&id, builder) in group.existing_esurfaces.iter().zip(builders) {
2083                let esurface_id = left_overlap.existing_esurfaces[id];
2084                let identity = Self::radial_root_identity(
2085                    &graph.name,
2086                    cut_group_id,
2087                    "left",
2088                    overlap_group_index,
2089                    esurface_id,
2090                    probe_rotation,
2091                );
2092                match builder.new_esurface_builder(id).solve_rstar(
2093                    &mut self.rstar_dependence_calculator[cut_group_id],
2094                    &identity,
2095                    evaluation_meta_data,
2096                ) {
2097                    Some(solution) => solutions.push(solution),
2098                    None => {
2099                        evaluation_meta_data.record_threshold_counterterm_error(format!(
2100                            "LU graph '{}' cut group {} left threshold {} center {} failed radial-root validation",
2101                            graph.name, cut_group_id.0, esurface_id.0, overlap_group_index));
2102                        radial_root_failed = true;
2103                    }
2104                }
2105            }
2106            left_overlap_solutions.push(solutions);
2107        }
2108        let right_counterterm_builder = CounterTermBuilder::new(
2109            graph,
2110            settings,
2111            right_thresholds,
2112            kinematic_point.clone(),
2113            right_overlap,
2114            masses,
2115            all_lmbs,
2116            right_subspace,
2117            right_threshold_subspaces,
2118            probe_rotation,
2119            cut_group_id,
2120            &shared.right_groups,
2121        );
2122        let right_overlap_builders = right_overlap
2123            .overlap_groups
2124            .iter()
2125            .enumerate()
2126            .map(|(group_index, group)| {
2127                group
2128                    .existing_esurfaces
2129                    .iter()
2130                    .map(|id| {
2131                        right_counterterm_builder
2132                            .new_overlap_builder(group_index, right_overlap.existing_esurfaces[*id])
2133                    })
2134                    .collect_vec()
2135            })
2136            .collect_vec();
2137        let mut right_overlap_solutions = Vec::with_capacity(right_overlap.overlap_groups.len());
2138        for (overlap_group_index, (group, builders)) in right_overlap
2139            .overlap_groups
2140            .iter()
2141            .zip(&right_overlap_builders)
2142            .enumerate()
2143        {
2144            let mut solutions = Vec::with_capacity(builders.len());
2145            for (&id, builder) in group.existing_esurfaces.iter().zip(builders) {
2146                let esurface_id = right_overlap.existing_esurfaces[id];
2147                let identity = Self::radial_root_identity(
2148                    &graph.name,
2149                    cut_group_id,
2150                    "right",
2151                    overlap_group_index,
2152                    esurface_id,
2153                    probe_rotation,
2154                );
2155                match builder.new_esurface_builder(id).solve_rstar(
2156                    &mut self.rstar_dependence_calculator[cut_group_id],
2157                    &identity,
2158                    evaluation_meta_data,
2159                ) {
2160                    Some(solution) => solutions.push(solution),
2161                    None => {
2162                        evaluation_meta_data.record_threshold_counterterm_error(format!(
2163                            "LU graph '{}' cut group {} right threshold {} center {} failed radial-root validation",
2164                            graph.name, cut_group_id.0, esurface_id.0, overlap_group_index));
2165                        radial_root_failed = true;
2166                    }
2167                }
2168            }
2169            right_overlap_solutions.push(solutions);
2170        }
2171        if radial_root_failed {
2172            return Ok(LUCountertermEvaluation {
2173                total: Complex::new_re(F::from_f64(f64::NAN)),
2174                components: None,
2175            });
2176        }
2177
2178        let zero = kinematic_point.representative_sample().zero();
2179        let mut total_result = Complex::new_re(zero.clone());
2180        let mut completed_components = record_components.then(Vec::new);
2181        let mut threshold_multiplier_workspace = self.evaluators[cut_group_id]
2182            .threshold_multipliers
2183            .as_ref()
2184            .map(|collection| {
2185                ThresholdMultiplierInputWorkspace::new(collection.layout(), zero.clone())
2186            });
2187        if threshold_multiplier_workspace.is_some() && variant_subspaces.is_none() {
2188            return Err(eyre!(
2189                "graph '{}' cut group {} has threshold multipliers but no variant/subspace registry",
2190                graph.name,
2191                cut_group_id.0,
2192            ));
2193        }
2194
2195        for order in 0..kinematic_point.lu_cut_parameter_cache.len() {
2196            let mut pending_components = record_components.then(Vec::new);
2197            let mut left_evaluations = zero_dual_or_not_complex(order, &zero);
2198
2199            for (overlap_group_index, solutions_group) in left_overlap_solutions.iter().enumerate()
2200            {
2201                for solution in solutions_group {
2202                    let representative_sample = solution.rstar_sample_for_order(order);
2203                    let left_threshold_id =
2204                        LeftThresholdId::from(representative_sample.get_esurface_id().0);
2205                    self.ensure_active_left_threshold(cut_group_id, left_threshold_id)?;
2206
2207                    let matching_cut_indices = self.evaluators[cut_group_id]
2208                        .left_thresholds_evaluator[left_threshold_id]
2209                        .keys()
2210                        .filter(|cut_cff_index| {
2211                            cut_cff_index.lu_cut_order == Some(order + 1)
2212                                && cut_cff_index.right_threshold_order.is_none()
2213                        })
2214                        .copied()
2215                        .collect_vec();
2216
2217                    for cut_cff_index in matching_cut_indices {
2218                        let variable_indices = variable_indices_from_cut_cff_index(&cut_cff_index);
2219                        let lu_cut_params =
2220                            kinematic_point.cut_params_for_cut_cff_index(&cut_cff_index);
2221                        let sample = solution.rstar_sample_for_cut_cff_index(
2222                            &cut_cff_index,
2223                            variable_indices.left_threshold,
2224                        );
2225                        debug!("left threshold parameters");
2226
2227                        let left_threshold_params: ThresholdParams<T> =
2228                            sample.extract_threshold_parameters(true);
2229                        debug!(
2230                            "LU left evaluator input: cut_group_id={}, left_threshold_id={}, cut_cff_index={:?}, lu_order={}, r={}, rstar={}",
2231                            cut_group_id.0,
2232                            left_threshold_id.0,
2233                            cut_cff_index,
2234                            order + 1,
2235                            left_threshold_params.radius,
2236                            left_threshold_params.radius_star,
2237                        );
2238                        let inverse_transformed_sample = sample.get_inverse_transformed_sample();
2239                        let detailed_variant_id = detailed_variant_ids
2240                            .as_ref()
2241                            .map(|(left, _)| left[left_threshold_id.0]);
2242                        let multiplier_coefficients = if let Some(workspace) =
2243                            threshold_multiplier_workspace.as_mut()
2244                        {
2245                            let variant_id = variant_subspaces
2246                                .expect("multiplier registry requires variant subspaces")
2247                                .left_variant_ids[left_threshold_id];
2248                            let collection = self.evaluators[cut_group_id]
2249                                .threshold_multipliers
2250                                .as_mut()
2251                                .expect("multiplier workspace requires evaluator collection");
2252                            let (effective, star) = single_multiplier_context(
2253                                SingleMultiplierComponent::Local,
2254                                kinematic_point.representative_sample(),
2255                                &inverse_transformed_sample,
2256                            );
2257                            let local = evaluate_single_multiplier_context(
2258                                collection,
2259                                workspace,
2260                                variant_id,
2261                                &graph.loop_momentum_basis,
2262                                masses,
2263                                param_builder.model_values(),
2264                                &additional_params,
2265                                effective,
2266                                star,
2267                                evaluation_meta_data,
2268                            )
2269                            .with_context(|| {
2270                                format!(
2271                                    "Failed to evaluate local multiplier for graph '{}' cut group {} left variant {}",
2272                                    graph.name, cut_group_id.0, variant_id.0,
2273                                )
2274                            })?;
2275                            let (effective, star) = single_multiplier_context(
2276                                SingleMultiplierComponent::Integrated,
2277                                kinematic_point.representative_sample(),
2278                                &inverse_transformed_sample,
2279                            );
2280                            let integrated = evaluate_single_multiplier_context(
2281                                collection,
2282                                workspace,
2283                                variant_id,
2284                                &graph.loop_momentum_basis,
2285                                masses,
2286                                param_builder.model_values(),
2287                                &additional_params,
2288                                effective,
2289                                star,
2290                                evaluation_meta_data,
2291                            )
2292                            .with_context(|| {
2293                                format!(
2294                                    "Failed to evaluate integrated multiplier for graph '{}' cut group {} left variant {}",
2295                                    graph.name, cut_group_id.0, variant_id.0,
2296                                )
2297                            })?;
2298                            let coefficients = SingleMultiplierCoefficients { local, integrated };
2299                            Some(coefficients)
2300                        } else if record_components {
2301                            let one = kinematic_point.representative_sample().one();
2302                            Some(SingleMultiplierCoefficients {
2303                                local: one.clone(),
2304                                integrated: one,
2305                            })
2306                        } else {
2307                            None
2308                        };
2309
2310                        if multiplier_coefficients
2311                            .as_ref()
2312                            .is_some_and(SingleMultiplierCoefficients::all_zero)
2313                        {
2314                            if let Some(pending) = pending_components.as_mut() {
2315                                let coefficients = multiplier_coefficients.as_ref().unwrap();
2316                                let variant_id = detailed_variant_id.expect(
2317                                    "detailed zero multiplier requires a stable left variant ID",
2318                                );
2319                                let registry = self.metadata_registry.as_ref().unwrap();
2320                                for (kind, coefficient) in [
2321                                    (
2322                                        ThresholdCountertermComponentKind::Local,
2323                                        &coefficients.local,
2324                                    ),
2325                                    (
2326                                        ThresholdCountertermComponentKind::Integrated,
2327                                        &coefficients.integrated,
2328                                    ),
2329                                ] {
2330                                    pending.push(PendingLUCountertermComponent {
2331                                        component_id: registry.component_id(
2332                                            Some(cut_group_id),
2333                                            kind,
2334                                            &[variant_id],
2335                                        )?,
2336                                        occurrence: ThresholdCountertermComponentOccurrence::LocalUnitarity {
2337                                            overlap_groups: SmallVec::from_slice(&[
2338                                                overlap_group_index,
2339                                            ]),
2340                                            left_threshold_order: cut_cff_index.left_threshold_order,
2341                                            right_threshold_order: None,
2342                                            lu_cut_order: cut_cff_index.lu_cut_order,
2343                                        },
2344                                        multiplier_values: smallvec![coefficient.clone()],
2345                                        effective_multiplier: coefficient.clone(),
2346                                        pass_one: None,
2347                                    });
2348                                }
2349                            }
2350                            continue;
2351                        }
2352
2353                        let params = T::get_parameters(
2354                            param_builder,
2355                            (false, false),
2356                            graph,
2357                            &inverse_transformed_sample,
2358                            settings.kinematics.externals.get_helicities(),
2359                            &additional_params,
2360                            Some(&left_threshold_params),
2361                            None,
2362                            Some(&lu_cut_params),
2363                        );
2364
2365                        let result_of_this_ct = self.evaluators[cut_group_id]
2366                            .left_thresholds_evaluator[left_threshold_id]
2367                            .get_mut(&cut_cff_index)
2368                            .unwrap()
2369                            .evaluate(params, orientations, settings, evaluation_meta_data)
2370                            .unwrap()
2371                            .pop()
2372                            .unwrap();
2373
2374                        // E-surface channel weights are part of the residue and
2375                        // retain radial derivatives. Opaque user multipliers are
2376                        // applied to the completed helper pieces below.
2377                        let result_of_this_ct = multiply_dual_or_not_complex(
2378                            result_of_this_ct,
2379                            &sample.value_of_multi_channeling_factor,
2380                        );
2381
2382                        debug!(
2383                            "result of left threshold evaluator {:?}: {}",
2384                            cut_cff_index, result_of_this_ct
2385                        );
2386
2387                        let helper_evaluation = evaluate_threshold_helper_single(
2388                            ThresholdHelperEvaluationContext {
2389                                helper: self.evaluators[cut_group_id]
2390                                    .threshold_helpers
2391                                    .left_thresholds[left_threshold_id]
2392                                    .get_mut(&cut_cff_index)
2393                                    .unwrap(),
2394                                cut_cff_index: &cut_cff_index,
2395                                threshold_result: &result_of_this_ct,
2396                                outputs: threshold_helper_outputs,
2397                                evaluation_meta_data,
2398                            },
2399                            &left_threshold_params,
2400                        );
2401                        if let Some(pending) = pending_components.as_mut() {
2402                            let helper_completed_pieces = helper_evaluation.into_pieces();
2403                            let coefficients = multiplier_coefficients
2404                                .as_ref()
2405                                .expect("detailed components always materialize identity factors");
2406                            let variant_id = detailed_variant_id
2407                                .expect("detailed left component requires a stable variant ID");
2408                            let registry = self.metadata_registry.as_ref().unwrap();
2409                            for (kind, piece, coefficient) in [
2410                                (
2411                                    ThresholdCountertermComponentKind::Local,
2412                                    helper_completed_pieces.local,
2413                                    coefficients.local.clone(),
2414                                ),
2415                                (
2416                                    ThresholdCountertermComponentKind::Integrated,
2417                                    helper_completed_pieces.integrated,
2418                                    coefficients.integrated.clone(),
2419                                ),
2420                            ] {
2421                                let pass_one = (!coefficient.is_zero())
2422                                    .then(|| negate_dual_or_not_complex(piece));
2423                                pending.push(PendingLUCountertermComponent {
2424                                    component_id: registry.component_id(
2425                                        Some(cut_group_id),
2426                                        kind,
2427                                        &[variant_id],
2428                                    )?,
2429                                    occurrence:
2430                                        ThresholdCountertermComponentOccurrence::LocalUnitarity {
2431                                            overlap_groups: SmallVec::from_slice(&[
2432                                                overlap_group_index,
2433                                            ]),
2434                                            left_threshold_order: cut_cff_index
2435                                                .left_threshold_order,
2436                                            right_threshold_order: None,
2437                                            lu_cut_order: cut_cff_index.lu_cut_order,
2438                                        },
2439                                    multiplier_values: smallvec![coefficient.clone()],
2440                                    effective_multiplier: coefficient,
2441                                    pass_one,
2442                                });
2443                            }
2444                        } else {
2445                            let helper_completed_result = if let Some(coefficients) =
2446                                &multiplier_coefficients
2447                            {
2448                                weight_single_multiplier_pieces(
2449                                    helper_evaluation.into_pieces(),
2450                                    coefficients,
2451                                    order,
2452                                    &zero,
2453                                )
2454                            } else if threshold_helper_outputs == LUThresholdHelperOutputs::Pieces {
2455                                let helper_completed_pieces = helper_evaluation.into_pieces();
2456                                let mut result = helper_completed_pieces.local;
2457                                result += helper_completed_pieces.integrated;
2458                                result
2459                            } else {
2460                                helper_evaluation.into_legacy()
2461                            };
2462                            left_evaluations += helper_completed_result;
2463                        }
2464                    }
2465                }
2466            }
2467
2468            let mut right_evaluations = zero_dual_or_not_complex(order, &zero);
2469
2470            for (overlap_group_index, solutions_group) in right_overlap_solutions.iter().enumerate()
2471            {
2472                for solution in solutions_group {
2473                    let representative_sample = solution.rstar_sample_for_order(order);
2474                    let right_threshold_id =
2475                        RightThresholdId::from(representative_sample.get_esurface_id().0);
2476                    self.ensure_active_right_threshold(cut_group_id, right_threshold_id)?;
2477
2478                    let matching_cut_indices = self.evaluators[cut_group_id]
2479                        .right_thresholds_evaluator[right_threshold_id]
2480                        .keys()
2481                        .filter(|cut_cff_index| {
2482                            cut_cff_index.lu_cut_order == Some(order + 1)
2483                                && cut_cff_index.left_threshold_order.is_none()
2484                        })
2485                        .copied()
2486                        .collect_vec();
2487
2488                    for cut_cff_index in matching_cut_indices {
2489                        let variable_indices = variable_indices_from_cut_cff_index(&cut_cff_index);
2490                        let lu_cut_params =
2491                            kinematic_point.cut_params_for_cut_cff_index(&cut_cff_index);
2492                        let sample = solution.rstar_sample_for_cut_cff_index(
2493                            &cut_cff_index,
2494                            variable_indices.right_threshold,
2495                        );
2496                        debug!("right threshold parameters");
2497                        let right_threshold_params: ThresholdParams<T> =
2498                            sample.extract_threshold_parameters(true);
2499                        debug!(
2500                            "LU right evaluator input: cut_group_id={}, right_threshold_id={}, cut_cff_index={:?}, lu_order={}, r={}, rstar={}",
2501                            cut_group_id.0,
2502                            right_threshold_id.0,
2503                            cut_cff_index,
2504                            order + 1,
2505                            right_threshold_params.radius,
2506                            right_threshold_params.radius_star,
2507                        );
2508                        let inverse_transformed_sample = sample.get_inverse_transformed_sample();
2509                        let detailed_variant_id = detailed_variant_ids
2510                            .as_ref()
2511                            .map(|(_, right)| right[right_threshold_id.0]);
2512                        let multiplier_coefficients = if let Some(workspace) =
2513                            threshold_multiplier_workspace.as_mut()
2514                        {
2515                            let variant_id = variant_subspaces
2516                                .expect("multiplier registry requires variant subspaces")
2517                                .right_variant_ids[right_threshold_id];
2518                            let collection = self.evaluators[cut_group_id]
2519                                .threshold_multipliers
2520                                .as_mut()
2521                                .expect("multiplier workspace requires evaluator collection");
2522                            let (effective, star) = single_multiplier_context(
2523                                SingleMultiplierComponent::Local,
2524                                kinematic_point.representative_sample(),
2525                                &inverse_transformed_sample,
2526                            );
2527                            let local = evaluate_single_multiplier_context(
2528                                collection,
2529                                workspace,
2530                                variant_id,
2531                                &graph.loop_momentum_basis,
2532                                masses,
2533                                param_builder.model_values(),
2534                                &additional_params,
2535                                effective,
2536                                star,
2537                                evaluation_meta_data,
2538                            )
2539                            .with_context(|| {
2540                                format!(
2541                                    "Failed to evaluate local multiplier for graph '{}' cut group {} right variant {}",
2542                                    graph.name, cut_group_id.0, variant_id.0,
2543                                )
2544                            })?;
2545                            let (effective, star) = single_multiplier_context(
2546                                SingleMultiplierComponent::Integrated,
2547                                kinematic_point.representative_sample(),
2548                                &inverse_transformed_sample,
2549                            );
2550                            let integrated = evaluate_single_multiplier_context(
2551                                collection,
2552                                workspace,
2553                                variant_id,
2554                                &graph.loop_momentum_basis,
2555                                masses,
2556                                param_builder.model_values(),
2557                                &additional_params,
2558                                effective,
2559                                star,
2560                                evaluation_meta_data,
2561                            )
2562                            .with_context(|| {
2563                                format!(
2564                                    "Failed to evaluate integrated multiplier for graph '{}' cut group {} right variant {}",
2565                                    graph.name, cut_group_id.0, variant_id.0,
2566                                )
2567                            })?;
2568                            let coefficients = SingleMultiplierCoefficients { local, integrated };
2569                            Some(coefficients)
2570                        } else if record_components {
2571                            let one = kinematic_point.representative_sample().one();
2572                            Some(SingleMultiplierCoefficients {
2573                                local: one.clone(),
2574                                integrated: one,
2575                            })
2576                        } else {
2577                            None
2578                        };
2579
2580                        if multiplier_coefficients
2581                            .as_ref()
2582                            .is_some_and(SingleMultiplierCoefficients::all_zero)
2583                        {
2584                            if let Some(pending) = pending_components.as_mut() {
2585                                let coefficients = multiplier_coefficients.as_ref().unwrap();
2586                                let variant_id = detailed_variant_id.expect(
2587                                    "detailed zero multiplier requires a stable right variant ID",
2588                                );
2589                                let registry = self.metadata_registry.as_ref().unwrap();
2590                                for (kind, coefficient) in [
2591                                    (
2592                                        ThresholdCountertermComponentKind::Local,
2593                                        &coefficients.local,
2594                                    ),
2595                                    (
2596                                        ThresholdCountertermComponentKind::Integrated,
2597                                        &coefficients.integrated,
2598                                    ),
2599                                ] {
2600                                    pending.push(PendingLUCountertermComponent {
2601                                        component_id: registry.component_id(
2602                                            Some(cut_group_id),
2603                                            kind,
2604                                            &[variant_id],
2605                                        )?,
2606                                        occurrence: ThresholdCountertermComponentOccurrence::LocalUnitarity {
2607                                            overlap_groups: SmallVec::from_slice(&[
2608                                                overlap_group_index,
2609                                            ]),
2610                                            left_threshold_order: None,
2611                                            right_threshold_order: cut_cff_index.right_threshold_order,
2612                                            lu_cut_order: cut_cff_index.lu_cut_order,
2613                                        },
2614                                        multiplier_values: smallvec![coefficient.clone()],
2615                                        effective_multiplier: coefficient.clone(),
2616                                        pass_one: None,
2617                                    });
2618                                }
2619                            }
2620                            continue;
2621                        }
2622
2623                        let params = T::get_parameters(
2624                            param_builder,
2625                            (false, false),
2626                            graph,
2627                            &inverse_transformed_sample,
2628                            settings.kinematics.externals.get_helicities(),
2629                            &additional_params,
2630                            None,
2631                            Some(&right_threshold_params),
2632                            Some(&lu_cut_params),
2633                        );
2634
2635                        let result_of_this_ct = self.evaluators[cut_group_id]
2636                            .right_thresholds_evaluator[right_threshold_id]
2637                            .get_mut(&cut_cff_index)
2638                            .unwrap()
2639                            .evaluate(params, orientations, settings, evaluation_meta_data)
2640                            .unwrap()
2641                            .pop()
2642                            .unwrap();
2643
2644                        // E-surface channel weights are part of the residue and
2645                        // retain radial derivatives. Opaque user multipliers are
2646                        // applied to the completed helper pieces below.
2647                        let result_of_this_ct = multiply_dual_or_not_complex(
2648                            result_of_this_ct,
2649                            &sample.value_of_multi_channeling_factor,
2650                        );
2651
2652                        debug!(
2653                            "result of right threshold evaluator {:?}: {}",
2654                            cut_cff_index, result_of_this_ct
2655                        );
2656
2657                        let helper_evaluation = evaluate_threshold_helper_single(
2658                            ThresholdHelperEvaluationContext {
2659                                helper: self.evaluators[cut_group_id]
2660                                    .threshold_helpers
2661                                    .right_thresholds[right_threshold_id]
2662                                    .get_mut(&cut_cff_index)
2663                                    .unwrap(),
2664                                cut_cff_index: &cut_cff_index,
2665                                threshold_result: &result_of_this_ct,
2666                                outputs: threshold_helper_outputs,
2667                                evaluation_meta_data,
2668                            },
2669                            &right_threshold_params,
2670                        );
2671                        if let Some(pending) = pending_components.as_mut() {
2672                            let helper_completed_pieces = helper_evaluation.into_pieces();
2673                            let coefficients = multiplier_coefficients
2674                                .as_ref()
2675                                .expect("detailed components always materialize identity factors");
2676                            let variant_id = detailed_variant_id
2677                                .expect("detailed right component requires a stable variant ID");
2678                            let registry = self.metadata_registry.as_ref().unwrap();
2679                            for (kind, piece, coefficient) in [
2680                                (
2681                                    ThresholdCountertermComponentKind::Local,
2682                                    helper_completed_pieces.local,
2683                                    coefficients.local.clone(),
2684                                ),
2685                                (
2686                                    ThresholdCountertermComponentKind::Integrated,
2687                                    helper_completed_pieces.integrated,
2688                                    coefficients.integrated.clone(),
2689                                ),
2690                            ] {
2691                                let pass_one = (!coefficient.is_zero())
2692                                    .then(|| negate_dual_or_not_complex(piece));
2693                                pending.push(PendingLUCountertermComponent {
2694                                    component_id: registry.component_id(
2695                                        Some(cut_group_id),
2696                                        kind,
2697                                        &[variant_id],
2698                                    )?,
2699                                    occurrence:
2700                                        ThresholdCountertermComponentOccurrence::LocalUnitarity {
2701                                            overlap_groups: SmallVec::from_slice(&[
2702                                                overlap_group_index,
2703                                            ]),
2704                                            left_threshold_order: None,
2705                                            right_threshold_order: cut_cff_index
2706                                                .right_threshold_order,
2707                                            lu_cut_order: cut_cff_index.lu_cut_order,
2708                                        },
2709                                    multiplier_values: smallvec![coefficient.clone()],
2710                                    effective_multiplier: coefficient,
2711                                    pass_one,
2712                                });
2713                            }
2714                        } else {
2715                            let helper_completed_result = if let Some(coefficients) =
2716                                &multiplier_coefficients
2717                            {
2718                                weight_single_multiplier_pieces(
2719                                    helper_evaluation.into_pieces(),
2720                                    coefficients,
2721                                    order,
2722                                    &zero,
2723                                )
2724                            } else if threshold_helper_outputs == LUThresholdHelperOutputs::Pieces {
2725                                let helper_completed_pieces = helper_evaluation.into_pieces();
2726                                let mut result = helper_completed_pieces.local;
2727                                result += helper_completed_pieces.integrated;
2728                                result
2729                            } else {
2730                                helper_evaluation.into_legacy()
2731                            };
2732                            right_evaluations += helper_completed_result;
2733                        }
2734                    }
2735                }
2736            }
2737
2738            let mut cartesian_product_result = zero_dual_or_not_complex(order, &zero);
2739
2740            for (left_overlap_group_index, left_solutions_group) in
2741                left_overlap_solutions.iter().enumerate()
2742            {
2743                for left_solution in left_solutions_group {
2744                    let left_sample_representative = left_solution.rstar_sample_for_order(order);
2745                    for (right_overlap_group_index, right_solutions_group) in
2746                        right_overlap_solutions.iter().enumerate()
2747                    {
2748                        for right_solution in right_solutions_group {
2749                            let right_sample_representative =
2750                                right_solution.rstar_sample_for_order(order);
2751                            let iterated_index = (
2752                                LeftThresholdId::from(
2753                                    left_sample_representative.get_esurface_id().0,
2754                                ),
2755                                RightThresholdId::from(
2756                                    right_sample_representative.get_esurface_id().0,
2757                                ),
2758                            );
2759                            self.ensure_active_iterated_threshold(cut_group_id, iterated_index)?;
2760
2761                            let matching_cut_indices = self.evaluators[cut_group_id]
2762                                .iterated_evaluator[iterated_index]
2763                                .keys()
2764                                .filter(|cut_cff_index| {
2765                                    cut_cff_index.lu_cut_order == Some(order + 1)
2766                                })
2767                                .copied()
2768                                .collect_vec();
2769
2770                            for cut_cff_index in matching_cut_indices {
2771                                let variable_indices =
2772                                    variable_indices_from_cut_cff_index(&cut_cff_index);
2773                                let lu_cut_params =
2774                                    kinematic_point.cut_params_for_cut_cff_index(&cut_cff_index);
2775                                let sample_left = left_solution.rstar_sample_for_cut_cff_index(
2776                                    &cut_cff_index,
2777                                    variable_indices.left_threshold,
2778                                );
2779                                let sample_right = right_solution.rstar_sample_for_cut_cff_index(
2780                                    &cut_cff_index,
2781                                    variable_indices.right_threshold,
2782                                );
2783
2784                                let left_threshold_params: ThresholdParams<T> =
2785                                    sample_left.extract_threshold_parameters(false);
2786                                let right_threshold_params: ThresholdParams<T> =
2787                                    sample_right.extract_threshold_parameters(false);
2788                                debug!(
2789                                    "LU iterated evaluator input: cut_group_id={}, left_threshold_id={}, right_threshold_id={}, cut_cff_index={:?}, lu_order={}, left_r={}, left_rstar={}, right_r={}, right_rstar={}",
2790                                    cut_group_id.0,
2791                                    iterated_index.0.0,
2792                                    iterated_index.1.0,
2793                                    cut_cff_index,
2794                                    order + 1,
2795                                    left_threshold_params.radius,
2796                                    left_threshold_params.radius_star,
2797                                    right_threshold_params.radius,
2798                                    right_threshold_params.radius_star,
2799                                );
2800                                let inverse_transformed_momentum_sample =
2801                                    merge_and_inverse_transform(
2802                                        &sample_left,
2803                                        &sample_right,
2804                                        &cut_cff_index,
2805                                    );
2806                                let detailed_pair_variant_ids =
2807                                    detailed_variant_ids.as_ref().map(|(left, right)| {
2808                                        (left[iterated_index.0.0], right[iterated_index.1.0])
2809                                    });
2810                                let multiplier_coefficients = if let Some(workspace) =
2811                                    threshold_multiplier_workspace.as_mut()
2812                                {
2813                                    let variant_subspaces = variant_subspaces
2814                                        .expect("multiplier registry requires variant subspaces");
2815                                    let left_variant_id =
2816                                        variant_subspaces.left_variant_ids[iterated_index.0];
2817                                    let right_variant_id =
2818                                        variant_subspaces.right_variant_ids[iterated_index.1];
2819                                    let left_root = sample_left.get_inverse_transformed_sample();
2820                                    let right_root = sample_right.get_inverse_transformed_sample();
2821                                    let collection = self.evaluators[cut_group_id]
2822                                        .threshold_multipliers
2823                                        .as_mut()
2824                                        .expect(
2825                                            "multiplier workspace requires evaluator collection",
2826                                        );
2827
2828                                    let (effective, star) = iterated_multiplier_context(
2829                                        IteratedMultiplierComponent::LocalLocal,
2830                                        kinematic_point.representative_sample(),
2831                                        &left_root,
2832                                        &right_root,
2833                                        &inverse_transformed_momentum_sample,
2834                                    );
2835                                    let (left, right) = evaluate_pair_multiplier_context(
2836                                        collection,
2837                                        workspace,
2838                                        left_variant_id,
2839                                        right_variant_id,
2840                                        &graph.loop_momentum_basis,
2841                                        masses,
2842                                        param_builder.model_values(),
2843                                        &additional_params,
2844                                        effective,
2845                                        star,
2846                                        evaluation_meta_data,
2847                                    )
2848                                    .with_context(|| {
2849                                        format!(
2850                                            "Failed to evaluate LL multipliers for graph '{}' cut group {} variants ({}, {})",
2851                                            graph.name,
2852                                            cut_group_id.0,
2853                                            left_variant_id.0,
2854                                            right_variant_id.0,
2855                                        )
2856                                    })?;
2857                                    let local_local = PairMultiplierCoefficient { left, right };
2858
2859                                    let (effective, star) = iterated_multiplier_context(
2860                                        IteratedMultiplierComponent::IntegratedLocal,
2861                                        kinematic_point.representative_sample(),
2862                                        &left_root,
2863                                        &right_root,
2864                                        &inverse_transformed_momentum_sample,
2865                                    );
2866                                    let (left, right) = evaluate_pair_multiplier_context(
2867                                        collection,
2868                                        workspace,
2869                                        left_variant_id,
2870                                        right_variant_id,
2871                                        &graph.loop_momentum_basis,
2872                                        masses,
2873                                        param_builder.model_values(),
2874                                        &additional_params,
2875                                        effective,
2876                                        star,
2877                                        evaluation_meta_data,
2878                                    )
2879                                    .with_context(|| {
2880                                        format!(
2881                                            "Failed to evaluate IL multipliers for graph '{}' cut group {} variants ({}, {})",
2882                                            graph.name,
2883                                            cut_group_id.0,
2884                                            left_variant_id.0,
2885                                            right_variant_id.0,
2886                                        )
2887                                    })?;
2888                                    let integrated_local =
2889                                        PairMultiplierCoefficient { left, right };
2890
2891                                    let (effective, star) = iterated_multiplier_context(
2892                                        IteratedMultiplierComponent::LocalIntegrated,
2893                                        kinematic_point.representative_sample(),
2894                                        &left_root,
2895                                        &right_root,
2896                                        &inverse_transformed_momentum_sample,
2897                                    );
2898                                    let (left, right) = evaluate_pair_multiplier_context(
2899                                        collection,
2900                                        workspace,
2901                                        left_variant_id,
2902                                        right_variant_id,
2903                                        &graph.loop_momentum_basis,
2904                                        masses,
2905                                        param_builder.model_values(),
2906                                        &additional_params,
2907                                        effective,
2908                                        star,
2909                                        evaluation_meta_data,
2910                                    )
2911                                    .with_context(|| {
2912                                        format!(
2913                                            "Failed to evaluate LI multipliers for graph '{}' cut group {} variants ({}, {})",
2914                                            graph.name,
2915                                            cut_group_id.0,
2916                                            left_variant_id.0,
2917                                            right_variant_id.0,
2918                                        )
2919                                    })?;
2920                                    let local_integrated =
2921                                        PairMultiplierCoefficient { left, right };
2922
2923                                    let (effective, star) = iterated_multiplier_context(
2924                                        IteratedMultiplierComponent::IntegratedIntegrated,
2925                                        kinematic_point.representative_sample(),
2926                                        &left_root,
2927                                        &right_root,
2928                                        &inverse_transformed_momentum_sample,
2929                                    );
2930                                    let (left, right) = evaluate_pair_multiplier_context(
2931                                        collection,
2932                                        workspace,
2933                                        left_variant_id,
2934                                        right_variant_id,
2935                                        &graph.loop_momentum_basis,
2936                                        masses,
2937                                        param_builder.model_values(),
2938                                        &additional_params,
2939                                        effective,
2940                                        star,
2941                                        evaluation_meta_data,
2942                                    )
2943                                    .with_context(|| {
2944                                        format!(
2945                                            "Failed to evaluate II multipliers for graph '{}' cut group {} variants ({}, {})",
2946                                            graph.name,
2947                                            cut_group_id.0,
2948                                            left_variant_id.0,
2949                                            right_variant_id.0,
2950                                        )
2951                                    })?;
2952                                    let integrated_integrated =
2953                                        PairMultiplierCoefficient { left, right };
2954
2955                                    let coefficients = IteratedMultiplierCoefficients {
2956                                        local_local,
2957                                        local_integrated,
2958                                        integrated_local,
2959                                        integrated_integrated,
2960                                    };
2961                                    Some(coefficients)
2962                                } else if record_components {
2963                                    let one = kinematic_point.representative_sample().one();
2964                                    let identity = || PairMultiplierCoefficient {
2965                                        left: one.clone(),
2966                                        right: one.clone(),
2967                                    };
2968                                    Some(IteratedMultiplierCoefficients {
2969                                        local_local: identity(),
2970                                        local_integrated: identity(),
2971                                        integrated_local: identity(),
2972                                        integrated_integrated: identity(),
2973                                    })
2974                                } else {
2975                                    None
2976                                };
2977
2978                                if multiplier_coefficients
2979                                    .as_ref()
2980                                    .is_some_and(IteratedMultiplierCoefficients::all_zero)
2981                                {
2982                                    if let Some(pending) = pending_components.as_mut() {
2983                                        let coefficients =
2984                                            multiplier_coefficients.as_ref().unwrap();
2985                                        let (left_variant_id, right_variant_id) =
2986                                            detailed_pair_variant_ids.expect(
2987                                                "detailed zero multipliers require stable pair variant IDs",
2988                                            );
2989                                        let registry = self.metadata_registry.as_ref().unwrap();
2990                                        for (kind, coefficient) in [
2991                                            (
2992                                                ThresholdCountertermComponentKind::LocalLocal,
2993                                                &coefficients.local_local,
2994                                            ),
2995                                            (
2996                                                ThresholdCountertermComponentKind::LocalIntegrated,
2997                                                &coefficients.local_integrated,
2998                                            ),
2999                                            (
3000                                                ThresholdCountertermComponentKind::IntegratedLocal,
3001                                                &coefficients.integrated_local,
3002                                            ),
3003                                            (
3004                                                ThresholdCountertermComponentKind::IntegratedIntegrated,
3005                                                &coefficients.integrated_integrated,
3006                                            ),
3007                                        ] {
3008                                            pending.push(PendingLUCountertermComponent {
3009                                                component_id: registry.component_id(
3010                                                    Some(cut_group_id),
3011                                                    kind,
3012                                                    &[left_variant_id, right_variant_id],
3013                                                )?,
3014                                                occurrence: ThresholdCountertermComponentOccurrence::LocalUnitarity {
3015                                                    overlap_groups: SmallVec::from_slice(&[
3016                                                        left_overlap_group_index,
3017                                                        right_overlap_group_index,
3018                                                    ]),
3019                                                    left_threshold_order: cut_cff_index.left_threshold_order,
3020                                                    right_threshold_order: cut_cff_index.right_threshold_order,
3021                                                    lu_cut_order: cut_cff_index.lu_cut_order,
3022                                                },
3023                                                multiplier_values: smallvec![
3024                                                    coefficient.left.clone(),
3025                                                    coefficient.right.clone(),
3026                                                ],
3027                                                effective_multiplier: coefficient.effective(),
3028                                                pass_one: None,
3029                                            });
3030                                        }
3031                                    }
3032                                    continue;
3033                                }
3034
3035                                let multi_channeling_factor = multiply_dual_or_not_complex(
3036                                    sample_left.value_of_multi_channeling_factor.clone(),
3037                                    &sample_right.value_of_multi_channeling_factor,
3038                                );
3039
3040                                let params = T::get_parameters(
3041                                    param_builder,
3042                                    (false, false),
3043                                    graph,
3044                                    &inverse_transformed_momentum_sample,
3045                                    settings.kinematics.externals.get_helicities(),
3046                                    &additional_params,
3047                                    Some(&left_threshold_params),
3048                                    Some(&right_threshold_params),
3049                                    Some(&lu_cut_params),
3050                                );
3051
3052                                let result_of_this_ct = self.evaluators[cut_group_id]
3053                                    .iterated_evaluator[iterated_index]
3054                                    .get_mut(&cut_cff_index)
3055                                    .unwrap()
3056                                    .evaluate(params, orientations, settings, evaluation_meta_data)
3057                                    .unwrap()
3058                                    .pop()
3059                                    .unwrap();
3060
3061                                let result_of_this_ct = multiply_dual_or_not_complex(
3062                                    result_of_this_ct,
3063                                    &multi_channeling_factor,
3064                                );
3065                                let helper_evaluation = evaluate_threshold_helper_iterated(
3066                                    ThresholdHelperEvaluationContext {
3067                                        helper: self.evaluators[cut_group_id]
3068                                            .threshold_helpers
3069                                            .iterated[iterated_index]
3070                                            .get_mut(&cut_cff_index)
3071                                            .unwrap(),
3072                                        cut_cff_index: &cut_cff_index,
3073                                        threshold_result: &result_of_this_ct,
3074                                        outputs: threshold_helper_outputs,
3075                                        evaluation_meta_data,
3076                                    },
3077                                    &left_threshold_params,
3078                                    &right_threshold_params,
3079                                );
3080                                if let Some(pending) = pending_components.as_mut() {
3081                                    let helper_completed_pieces = helper_evaluation.into_pieces();
3082                                    let coefficients = multiplier_coefficients.as_ref().expect(
3083                                        "detailed components always materialize identity factors",
3084                                    );
3085                                    let (left_variant_id, right_variant_id) =
3086                                        detailed_pair_variant_ids.expect(
3087                                            "detailed pair component requires stable variant IDs",
3088                                        );
3089                                    let registry = self.metadata_registry.as_ref().unwrap();
3090                                    for (kind, piece, coefficient) in [
3091                                        (
3092                                            ThresholdCountertermComponentKind::LocalLocal,
3093                                            helper_completed_pieces.local_local,
3094                                            &coefficients.local_local,
3095                                        ),
3096                                        (
3097                                            ThresholdCountertermComponentKind::LocalIntegrated,
3098                                            helper_completed_pieces.local_integrated,
3099                                            &coefficients.local_integrated,
3100                                        ),
3101                                        (
3102                                            ThresholdCountertermComponentKind::IntegratedLocal,
3103                                            helper_completed_pieces.integrated_local,
3104                                            &coefficients.integrated_local,
3105                                        ),
3106                                        (
3107                                            ThresholdCountertermComponentKind::IntegratedIntegrated,
3108                                            helper_completed_pieces.integrated_integrated,
3109                                            &coefficients.integrated_integrated,
3110                                        ),
3111                                    ] {
3112                                        let pass_one = (!coefficient.is_zero()).then_some(piece);
3113                                        pending.push(PendingLUCountertermComponent {
3114                                            component_id: registry.component_id(
3115                                                Some(cut_group_id),
3116                                                kind,
3117                                                &[left_variant_id, right_variant_id],
3118                                            )?,
3119                                            occurrence: ThresholdCountertermComponentOccurrence::LocalUnitarity {
3120                                                overlap_groups: SmallVec::from_slice(&[
3121                                                    left_overlap_group_index,
3122                                                    right_overlap_group_index,
3123                                                ]),
3124                                                left_threshold_order: cut_cff_index.left_threshold_order,
3125                                                right_threshold_order: cut_cff_index.right_threshold_order,
3126                                                lu_cut_order: cut_cff_index.lu_cut_order,
3127                                            },
3128                                            multiplier_values: smallvec![
3129                                                coefficient.left.clone(),
3130                                                coefficient.right.clone(),
3131                                            ],
3132                                            effective_multiplier: coefficient.effective(),
3133                                            pass_one,
3134                                        });
3135                                    }
3136                                } else {
3137                                    let helper_completed_result =
3138                                        if let Some(coefficients) = &multiplier_coefficients {
3139                                            weight_iterated_multiplier_pieces(
3140                                                helper_evaluation.into_pieces(),
3141                                                coefficients,
3142                                                order,
3143                                                &zero,
3144                                            )
3145                                        } else if threshold_helper_outputs
3146                                            == LUThresholdHelperOutputs::Pieces
3147                                        {
3148                                            let helper_completed_pieces =
3149                                                helper_evaluation.into_pieces();
3150                                            let mut result = helper_completed_pieces.local_local;
3151                                            result += helper_completed_pieces.local_integrated;
3152                                            result += helper_completed_pieces.integrated_local;
3153                                            result += helper_completed_pieces.integrated_integrated;
3154                                            result
3155                                        } else {
3156                                            helper_evaluation.into_legacy()
3157                                        };
3158
3159                                    cartesian_product_result += helper_completed_result;
3160                                }
3161                            }
3162                        }
3163                    }
3164                }
3165            }
3166
3167            let cut_esurface = kinematic_point.cut_esurface_for_order(order);
3168            if let Some(pending_components) = pending_components {
3169                let completed_components = completed_components
3170                    .as_mut()
3171                    .expect("detailed pending components require completed storage");
3172                for component in pending_components {
3173                    let (weighted, completed) = component.finish(&zero, |pass_one| {
3174                        self.evaluators[cut_group_id].evaluate_residue_from_esurface(
3175                            cut_group_id,
3176                            order,
3177                            pass_one,
3178                            cut_esurface,
3179                            evaluation_meta_data,
3180                        )
3181                    });
3182                    total_result += weighted;
3183                    completed_components.push(completed);
3184                }
3185            } else {
3186                let mut pass_one_result = left_evaluations;
3187                pass_one_result += right_evaluations;
3188                pass_one_result += negate_dual_or_not_complex(cartesian_product_result);
3189                let pass_one_result = negate_dual_or_not_complex(pass_one_result);
3190                total_result += self.evaluators[cut_group_id].evaluate_residue_from_esurface(
3191                    cut_group_id,
3192                    order,
3193                    pass_one_result,
3194                    cut_esurface,
3195                    evaluation_meta_data,
3196                );
3197            }
3198        }
3199
3200        Ok(LUCountertermEvaluation {
3201            total: total_result,
3202            components: completed_components,
3203        })
3204    }
3205}
3206
3207struct CounterTermBuilder<'a, T: FloatLike> {
3208    overlap_structure: &'a OverlapStructure,
3209    real_mass_vector: &'a EdgeVec<F<T>>,
3210    e_cm: F<T>,
3211    graph: &'a Graph,
3212    subspace: &'a SubspaceData,
3213    threshold_subspaces: Option<&'a [SubspaceData]>,
3214    all_lmbs: &'a TiVec<LmbIndex, LoopMomentumBasis>,
3215    settings: &'a RuntimeSettings,
3216    esurface_collection: &'a EsurfaceCollection,
3217    kinematic_point: LUCTKinematicPoint<T>,
3218    probe_rotation: &'a Rotation,
3219    cut_group_id: CutGroupId,
3220    shared_groups: &'a [(Arc<ThresholdSolveGroup<T>>, usize)],
3221}
3222
3223impl<'a, T: FloatLike> CounterTermBuilder<'a, T> {
3224    #[allow(clippy::too_many_arguments)]
3225    fn new(
3226        graph: &'a Graph,
3227        settings: &'a RuntimeSettings,
3228        esurface_collection: &'a EsurfaceCollection,
3229        kinematic_point: LUCTKinematicPoint<T>,
3230        overlap_structure: &'a OverlapStructure,
3231        masses: &'a EdgeVec<F<T>>,
3232        all_lmbs: &'a TiVec<LmbIndex, LoopMomentumBasis>,
3233        subspace: &'a SubspaceData,
3234        threshold_subspaces: Option<&'a [SubspaceData]>,
3235        probe_rotation: &'a Rotation,
3236        cut_group_id: CutGroupId,
3237        shared_groups: &'a [(Arc<ThresholdSolveGroup<T>>, usize)],
3238    ) -> Self {
3239        Self {
3240            real_mass_vector: masses,
3241            e_cm: F::from_f64(settings.kinematics.e_cm),
3242            graph,
3243            settings,
3244            esurface_collection,
3245            overlap_structure,
3246            kinematic_point,
3247            all_lmbs,
3248            subspace,
3249            threshold_subspaces,
3250            probe_rotation,
3251            cut_group_id,
3252            shared_groups,
3253        }
3254    }
3255
3256    fn threshold_subspace(&self, esurface_id: EsurfaceID) -> &SubspaceData {
3257        self.threshold_subspaces
3258            .map_or(self.subspace, |subspaces| &subspaces[esurface_id.0])
3259    }
3260
3261    fn new_overlap_builder(
3262        &'a self,
3263        group_index: usize,
3264        selected_esurface: EsurfaceID,
3265    ) -> OverlapBuilder<'a, T> {
3266        let subspace = self.threshold_subspace(selected_esurface);
3267        let (shared_group, shared_center) = &self.shared_groups[group_index];
3268        let transformed_kinematic_point = self.kinematic_point.lmb_transform(
3269            &self.graph.loop_momentum_basis,
3270            subspace.get_lmb(self.all_lmbs),
3271        );
3272        // A center has only active coordinates. Copy by defining edge instead of
3273        // transforming its artificial zero complement as a physical sample.
3274        let mut center = transformed_kinematic_point
3275            .representative_sample()
3276            .loop_moms()
3277            .clone();
3278        for momentum in center.0.iter_mut() {
3279            *momentum = ThreeMomentum::new(self.e_cm.zero(), self.e_cm.zero(), self.e_cm.zero());
3280        }
3281        let shared_lmb = shared_group.subspace.get_lmb(self.all_lmbs);
3282        for target_index in subspace.iter_lmb_indices() {
3283            let edge = subspace.get_lmb(self.all_lmbs).loop_edges[target_index];
3284            let source_index = shared_lmb
3285                .loop_edges
3286                .iter_enumerated()
3287                .find_map(|(index, &candidate)| (candidate == edge).then_some(index))
3288                .expect("compatible solve group has the same defining edges");
3289            center[target_index] = shared_group.overlap.overlap_groups[*shared_center].center
3290                [source_index]
3291                .map(&|value| F(T::from_f64_exact_binary(value.0)))
3292                .rotate(self.probe_rotation);
3293        }
3294        let shifted_loop_momenta = transformed_kinematic_point
3295            .representative_sample()
3296            .loop_moms()
3297            - &center;
3298        let radius = shifted_loop_momenta
3299            .hyper_radius_squared(subspace.as_subspace_simple())
3300            .sqrt();
3301        OverlapBuilder {
3302            counterterm_builder: self,
3303            subspace,
3304            center,
3305            shifted_loop_momenta,
3306            radius,
3307            transformed_kinematic_point,
3308            shared_group,
3309            shared_center: *shared_center,
3310        }
3311    }
3312}
3313
3314struct OverlapBuilder<'a, T: FloatLike> {
3315    counterterm_builder: &'a CounterTermBuilder<'a, T>,
3316    subspace: &'a SubspaceData,
3317    /// Canonical identity-frame centers are promoted exactly and rotated natively
3318    /// once; only active coordinates move to this threshold's native parent LMB.
3319    center: LoopMomenta<F<T>>,
3320    shifted_loop_momenta: LoopMomenta<F<T>>,
3321    radius: F<T>,
3322    transformed_kinematic_point: LUCTKinematicPoint<T>,
3323    shared_group: &'a ThresholdSolveGroup<T>,
3324    shared_center: usize,
3325}
3326
3327impl<'a, T: FloatLike> OverlapBuilder<'a, T> {
3328    fn new_esurface_builder(
3329        &'a self,
3330        existing_esurface_id: ExistingEsurfaceId,
3331    ) -> EsurfaceCTBuilder<'a, T> {
3332        let esurface_id = self
3333            .counterterm_builder
3334            .overlap_structure
3335            .existing_esurfaces[existing_esurface_id];
3336
3337        EsurfaceCTBuilder {
3338            overlap_builder: self,
3339            _existing_esurface_id: existing_esurface_id,
3340            esurface: &self.counterterm_builder.esurface_collection[esurface_id],
3341            esurface_id,
3342        }
3343    }
3344}
3345
3346const MAX_ITERATIONS: usize = 40;
3347
3348struct EsurfaceCTBuilder<'a, T: FloatLike> {
3349    overlap_builder: &'a OverlapBuilder<'a, T>,
3350    _existing_esurface_id: ExistingEsurfaceId,
3351    esurface: &'a Esurface,
3352    esurface_id: EsurfaceID,
3353}
3354
3355impl<'a, T: FloatLike> EsurfaceCTBuilder<'a, T> {
3356    fn solve_rstar(
3357        self,
3358        rstar_t_dependence_evaluator: &mut RstarTDependenceEvaluator,
3359        radial_root_identity: &RadialRootIdentity,
3360        evaluation_metadata: &mut EvaluationMetaData,
3361    ) -> Option<RstarSolution<'a, T>> {
3362        let subspace = self.overlap_builder.subspace;
3363        let graph = self.overlap_builder.counterterm_builder.graph;
3364        let lmbs = self.overlap_builder.counterterm_builder.all_lmbs;
3365        let masses = self.overlap_builder.counterterm_builder.real_mass_vector;
3366
3367        debug!("subspace: {:?}", subspace);
3368
3369        let radius = &self.overlap_builder.radius;
3370        if radius.is_nan() || radius.is_infinite() || radius <= &radius.zero() {
3371            warn!(%radius, "threshold projection requires a finite positive active displacement");
3372            return None;
3373        }
3374        let representative_sample = self
3375            .overlap_builder
3376            .transformed_kinematic_point
3377            .representative_sample();
3378        let mut center_with_fixed_complement = representative_sample.loop_moms().clone();
3379        for loop_index in subspace.iter_lmb_indices() {
3380            center_with_fixed_complement[loop_index] =
3381                self.overlap_builder.center[loop_index].clone();
3382        }
3383
3384        let shared = self.overlap_builder.shared_group;
3385        let shared_lmb = shared.subspace.get_lmb(lmbs);
3386        let shared_center = &shared.overlap.overlap_groups[self.overlap_builder.shared_center];
3387        // Validate every member with its own solved-cut data, including members
3388        // whose counterterm is dispatched by another cut.
3389        let center_surface_values = shared_center
3390            .existing_esurfaces
3391            .iter()
3392            .map(|&id| {
3393                let surface_id = shared.overlap.existing_esurfaces[id];
3394                let sample = &shared.kinematics[surface_id.0];
3395                let mut momenta = sample.loop_moms().clone();
3396                for index in shared.subspace.iter_lmb_indices() {
3397                    momenta[index] = shared_center.center[index]
3398                        .map(&|value| F(T::from_f64_exact_binary(value.0)))
3399                        .rotate(self.overlap_builder.counterterm_builder.probe_rotation);
3400                }
3401                let value = shared.thresholds[surface_id].compute_from_momenta(
3402                    shared_lmb,
3403                    masses,
3404                    &momenta,
3405                    sample.external_moms(),
3406                );
3407                let valid = esurface_value_is_strictly_inside(
3408                    &value,
3409                    &self.overlap_builder.counterterm_builder.e_cm,
3410                );
3411                (surface_id, value, valid)
3412            })
3413            .collect_vec();
3414        let all_center_values_valid = center_surface_values
3415            .iter()
3416            .all(|(_, _, is_valid)| *is_valid);
3417
3418        crate::debug_tags!(#integration, #subtraction, #threshold, #inspect, #center;
3419            stage = "lu_threshold_center_values",
3420            graph = %graph.name,
3421            selected_esurface_id = self.esurface_id.0,
3422            rotation_id = %self.overlap_builder.counterterm_builder.probe_rotation.method,
3423            center_provenance = "canonical_identity_center_promoted_and_rotated_natively",
3424            subspace_loop_indices = ?subspace.iter_lmb_indices().collect_vec(),
3425            file.active_center = %format!("{}", self.overlap_builder.center),
3426            file.center_with_fixed_complement = %format!("{}", center_with_fixed_complement),
3427            file.surface_values = %center_surface_values
3428                .iter()
3429                .map(|(esurface_id, value, is_valid)| format!(
3430                    "local={} value={:+16e} inside={}",
3431                    esurface_id.0,
3432                    value,
3433                    is_valid,
3434                ))
3435                .join("\n"),
3436            "LU threshold center values"
3437        );
3438
3439        if !all_center_values_valid {
3440            warn!(
3441                graph = %graph.name,
3442                selected_esurface_id = self.esurface_id.0,
3443                rotation_id = %self.overlap_builder.counterterm_builder.probe_rotation.method,
3444                center_provenance = "canonical_identity_center_promoted_and_rotated_natively",
3445                center = %center_with_fixed_complement,
3446                surface_values = %center_surface_values
3447                    .iter()
3448                    .map(|(esurface_id, value, is_valid)| format!(
3449                        "local={} value={:+16e} inside={}",
3450                        esurface_id.0,
3451                        value,
3452                        is_valid,
3453                    ))
3454                    .join("; "),
3455                "refusing to evaluate an LU threshold counterterm with an invalid probe-frame overlap center"
3456            );
3457            return None;
3458        }
3459
3460        let (raw_alpha_guess, _) = self.esurface.get_radius_guess_subspace(
3461            &self.overlap_builder.shifted_loop_momenta,
3462            self.overlap_builder
3463                .transformed_kinematic_point
3464                .representative_sample()
3465                .external_moms(),
3466            subspace,
3467            lmbs,
3468            graph,
3469            masses,
3470        );
3471
3472        let function = |alpha: &_| {
3473            self.esurface.compute_self_and_r_derivative_subspace(
3474                alpha,
3475                &self.overlap_builder.shifted_loop_momenta,
3476                &self.overlap_builder.center,
3477                self.overlap_builder
3478                    .transformed_kinematic_point
3479                    .representative_sample()
3480                    .external_moms(),
3481                self.overlap_builder.counterterm_builder.real_mass_vector,
3482                subspace,
3483                lmbs,
3484                graph,
3485            )
3486        };
3487
3488        let zero = raw_alpha_guess.zero();
3489        let mut alpha_guess = raw_alpha_guess.clone();
3490        if alpha_guess.is_nan() || alpha_guess.is_infinite() || alpha_guess <= zero {
3491            // The existing ray guess now uses the unnormalized displacement,
3492            // so its root coordinate and this fallback are dimensionless.
3493            alpha_guess = zero.one();
3494        }
3495
3496        debug!("initial alpha guess: {:?}", alpha_guess);
3497
3498        // Some residual is expected when Newton stagnates at the representable value nearest the
3499        // root. Direct convergence uses the active precision; the shared diagnostics may also
3500        // certify a residual-limited higher-precision result against the preceding precision.
3501        let tolerance = F::from_f64(
3502            self.overlap_builder
3503                .counterterm_builder
3504                .settings
3505                .subtraction
3506                .radial_root_residual_tolerance,
3507        );
3508        let mut solution = match evaluation_metadata.radial_root_diagnostics.solve(
3509            radial_root_identity,
3510            &zero,
3511            &alpha_guess,
3512            function,
3513            &tolerance,
3514            MAX_ITERATIONS,
3515            64,
3516            &self.overlap_builder.counterterm_builder.e_cm,
3517        ) {
3518            Ok(solution) => solution,
3519            Err(error) => {
3520                warn!(
3521                    graph = %graph.name,
3522                    esurface_id = self.esurface_id.0,
3523                    rotation_id = %self.overlap_builder.counterterm_builder.probe_rotation.method,
3524                    center_provenance = "canonical_identity_center_promoted_and_rotated_natively",
3525                    raw_alpha_guess = %raw_alpha_guess,
3526                    alpha_guess = %alpha_guess,
3527                    error = %error,
3528                    "refusing to evaluate a threshold counterterm with an invalid alpha solution"
3529                );
3530                return None;
3531            }
3532        };
3533
3534        debug!("alpha solution: {:?}", solution);
3535        let alpha = solution.solution.clone();
3536        // Diagnostics retain the dimensionless solve and its energy residual.
3537        // Physical residue interfaces still consume r* and d eta / d r.
3538        solution.solution *= radius;
3539        solution.derivative_at_solution /= radius;
3540        if [&solution.solution, &solution.derivative_at_solution]
3541            .iter()
3542            .any(|value| value.is_nan() || value.is_infinite() || **value <= zero)
3543        {
3544            warn!(%alpha, %radius, "threshold radial solution is not representable");
3545            return None;
3546        }
3547
3548        let alpha_t_dependence = if rstar_t_dependence_evaluator.supports_t_derivatives() {
3549            let t_star = match &self
3550                .overlap_builder
3551                .transformed_kinematic_point
3552                .non_dual_cut_params()
3553                .tstar
3554            {
3555                DualOrNot::NonDual(t_star) => t_star.clone(),
3556                DualOrNot::Dual(_) => {
3557                    unreachable!("representative LU cut parameters must stay non-dual")
3558                }
3559            };
3560
3561            Some(
3562                rstar_t_dependence_evaluator.evaluate_alpha(
3563                    RstarTDependenceInput {
3564                        t_star: &t_star,
3565                        alpha: &alpha,
3566                        overlap_center: &self.overlap_builder.center,
3567                        subspace,
3568                        unrescaled_momentum_sample: self
3569                            .overlap_builder
3570                            .transformed_kinematic_point
3571                            .unrescaled_sample(),
3572                        representative_sample,
3573                        masses,
3574                        threshold_esurface: self.esurface,
3575                        lmb: &self
3576                            .overlap_builder
3577                            .counterterm_builder
3578                            .graph
3579                            .loop_momentum_basis,
3580                        all_lmbs: lmbs,
3581                    },
3582                    evaluation_metadata,
3583                ),
3584            )
3585        } else {
3586            None
3587        };
3588
3589        Some(RstarSolution {
3590            esurface_ct_builder: self,
3591            solution,
3592            alpha,
3593            alpha_t_dependence,
3594        })
3595    }
3596}
3597
3598struct RstarSolution<'a, T: FloatLike> {
3599    esurface_ct_builder: EsurfaceCTBuilder<'a, T>,
3600    solution: NewtonIterationResult<T>,
3601    /// Authoritative dimensionless projection; physical radii are derived from it.
3602    alpha: F<T>,
3603    alpha_t_dependence: Option<HyperDual<F<T>>>,
3604}
3605
3606struct DualRstarGeometry<T: FloatLike> {
3607    radius: HyperDual<F<T>>,
3608    radius_star: HyperDual<F<T>>,
3609    esurface_derivative: HyperDual<F<T>>,
3610    rstar_loop_momenta: LoopMomenta<HyperDual<F<T>>>,
3611    external_moms: ExternalFourMomenta<HyperDual<F<T>>>,
3612}
3613
3614impl<'a, T: FloatLike> RstarSolution<'a, T> {
3615    fn subspace(&self) -> &SubspaceData {
3616        self.esurface_ct_builder.overlap_builder.subspace
3617    }
3618
3619    fn max_supported_order(&self) -> usize {
3620        self.alpha_t_dependence
3621            .as_ref()
3622            .map(|dual| dual.values.len().saturating_sub(1))
3623            .unwrap_or(0)
3624    }
3625
3626    fn base_rstar_loop_momenta(&self) -> LoopMomenta<F<T>> {
3627        let subspace = self.subspace();
3628
3629        &self
3630            .esurface_ct_builder
3631            .overlap_builder
3632            .shifted_loop_momenta
3633            .rescale(&self.alpha, subspace.as_subspace_simple())
3634            + &self.esurface_ct_builder.overlap_builder.center
3635    }
3636
3637    fn truncated_alpha_solution(&self, order: usize) -> HyperDual<F<T>> {
3638        let dual_solution = self
3639            .alpha_t_dependence
3640            .as_ref()
3641            .expect("higher-order LU threshold evaluation requires cached alpha(t)");
3642        HyperDual::from_values(
3643            simple_n_deriv_shape(order),
3644            dual_solution.values[..=order].to_vec(),
3645        )
3646    }
3647
3648    fn embedded_truncated_alpha_solution(&self, cut_cff_index: &CutCFFIndex) -> HyperDual<F<T>> {
3649        let lu_order = cut_cff_index
3650            .lu_cut_order
3651            .expect("mixed LU geometry requires lu_cut_order")
3652            - 1;
3653        let target_shape = shape_from_cut_cff_index(cut_cff_index)
3654            .map(HyperDual::new)
3655            .expect("mixed LU geometry requires a dual shape");
3656        let t_variable = variable_indices_from_cut_cff_index(cut_cff_index).lu_cut;
3657        let t_dual = if lu_order == 0 {
3658            HyperDual::from_values(simple_n_deriv_shape(0), vec![self.alpha.clone()])
3659        } else {
3660            self.truncated_alpha_solution(lu_order)
3661        };
3662
3663        embed_t_dual_in_target_shape(&t_dual, &target_shape, t_variable)
3664    }
3665
3666    fn dual_geometry_for_order(&self, order: usize) -> DualRstarGeometry<T> {
3667        let source_sample = self
3668            .esurface_ct_builder
3669            .overlap_builder
3670            .transformed_kinematic_point
3671            .sample_for_order(order);
3672        let dual_loop_momenta = source_sample
3673            .sample
3674            .dual_loop_moms
3675            .as_ref()
3676            .expect("higher-order LU threshold evaluation requires dual loop momenta")
3677            .clone();
3678
3679        self.dual_geometry(
3680            dual_loop_momenta,
3681            self.truncated_alpha_solution(order),
3682            source_sample.external_moms(),
3683            None,
3684        )
3685    }
3686
3687    fn dual_geometry_for_cut_cff_index(
3688        &self,
3689        cut_cff_index: &CutCFFIndex,
3690        threshold_variable: Option<usize>,
3691    ) -> DualRstarGeometry<T> {
3692        let lu_order = cut_cff_index
3693            .lu_cut_order
3694            .expect("mixed LU geometry requires lu_cut_order")
3695            - 1;
3696        let source_sample = self
3697            .esurface_ct_builder
3698            .overlap_builder
3699            .transformed_kinematic_point
3700            .sample_for_order(lu_order);
3701        let target_shape = shape_from_cut_cff_index(cut_cff_index)
3702            .map(HyperDual::new)
3703            .expect("mixed LU geometry requires a dual shape");
3704        let t_variable = variable_indices_from_cut_cff_index(cut_cff_index).lu_cut;
3705        let dual_loop_momenta = embedded_dual_loop_momenta_for_cut_cff_index(
3706            source_sample,
3707            &Some(target_shape.clone()),
3708            t_variable,
3709        )
3710        .expect("mixed LU geometry requires dual loop momenta");
3711
3712        self.dual_geometry(
3713            dual_loop_momenta,
3714            self.embedded_truncated_alpha_solution(cut_cff_index),
3715            source_sample.external_moms(),
3716            threshold_variable,
3717        )
3718    }
3719
3720    fn dual_geometry(
3721        &self,
3722        mut dual_loop_momenta: LoopMomenta<HyperDual<F<T>>>,
3723        mut alpha: HyperDual<F<T>>,
3724        source_externals: &ExternalFourMomenta<F<T>>,
3725        threshold_variable: Option<usize>,
3726    ) -> DualRstarGeometry<T> {
3727        let representative = self
3728            .esurface_ct_builder
3729            .overlap_builder
3730            .transformed_kinematic_point
3731            .representative_sample();
3732        // Higher-order packets retain their derivatives at the exact physical
3733        // base already used by the alpha solve and implicit-function evaluator.
3734        for (momentum, base) in dual_loop_momenta
3735            .0
3736            .iter_mut()
3737            .zip(representative.loop_moms().iter())
3738        {
3739            momentum.px.values[0] = base.px.clone();
3740            momentum.py.values[0] = base.py.clone();
3741            momentum.pz.values[0] = base.pz.clone();
3742        }
3743        let center = dualize_loop_momenta(&alpha, &self.esurface_ct_builder.overlap_builder.center);
3744        let external_moms = dualize_external_momenta(&alpha, source_externals);
3745        let shifted_momenta = dual_loop_momenta
3746            .iter()
3747            .zip(center.iter())
3748            .map(|(momentum, center)| momentum.clone() - center.clone())
3749            .collect::<LoopMomenta<_>>();
3750        let radius = dual_shifted_radius(&shifted_momenta, self.subspace());
3751        let mut radius_star = alpha.clone() * &radius;
3752        // Threshold derivatives remain derivatives in physical r*, not alpha.
3753        // This zero-based variation preserves the authoritative projection base.
3754        if threshold_variable.is_some() {
3755            let mut radial_variation = new_constant(&alpha, &self.alpha.zero());
3756            activate_threshold_variable_in_target_shape(&mut radial_variation, threshold_variable);
3757            radius_star += &radial_variation;
3758            alpha += radial_variation / &radius;
3759        }
3760        let rstar_loop_momenta = shifted_momenta
3761            .rescale(&alpha, self.subspace().as_subspace_simple())
3762            .iter()
3763            .zip(center.iter())
3764            .map(|(momentum, center)| momentum.clone() + center.clone())
3765            .collect();
3766        let (_, alpha_derivative) = compute_self_and_r_derivative_subspace_dual(
3767            self.esurface_ct_builder.esurface,
3768            &alpha,
3769            &shifted_momenta,
3770            &center,
3771            &external_moms,
3772            self.esurface_ct_builder
3773                .overlap_builder
3774                .counterterm_builder
3775                .real_mass_vector,
3776            self.subspace(),
3777            self.esurface_ct_builder
3778                .overlap_builder
3779                .counterterm_builder
3780                .all_lmbs,
3781            self.esurface_ct_builder
3782                .overlap_builder
3783                .counterterm_builder
3784                .graph,
3785        );
3786        let esurface_derivative = alpha_derivative / &radius;
3787
3788        DualRstarGeometry {
3789            radius,
3790            radius_star,
3791            esurface_derivative,
3792            rstar_loop_momenta,
3793            external_moms,
3794        }
3795    }
3796
3797    fn non_dual_multichanneling_factor(&self, rstar_sample: &MomentumSample<T>) -> Complex<F<T>> {
3798        let builder = self.esurface_ct_builder.overlap_builder;
3799        let group = builder.shared_group;
3800        let native_lmb = self
3801            .subspace()
3802            .get_lmb(builder.counterterm_builder.all_lmbs);
3803        let group_lmb = group.subspace.get_lmb(builder.counterterm_builder.all_lmbs);
3804        let active_sample = rstar_sample.lmb_transform(native_lmb, group_lmb);
3805        let values: Vec<_> = group
3806            .thresholds
3807            .iter_enumerated()
3808            .map(|(id, surface)| {
3809                let mut momenta = group.kinematics[id.0].loop_moms().clone();
3810                for index in group.subspace.iter_lmb_indices() {
3811                    momenta[index] = active_sample.loop_moms()[index].clone();
3812                }
3813                surface
3814                    .compute_from_momenta(
3815                        group_lmb,
3816                        builder.counterterm_builder.real_mass_vector,
3817                        &momenta,
3818                        group.kinematics[id.0].external_moms(),
3819                    )
3820                    .powi(group.prefactor_power as i32)
3821            })
3822            .collect();
3823        let weights = group
3824            .overlap
3825            .overlap_groups
3826            .iter()
3827            .map(|center| {
3828                center
3829                    .complement
3830                    .iter()
3831                    .map(|id| &values[group.overlap.existing_esurfaces[*id].0])
3832                    .fold(rstar_sample.one(), |weight, value| weight * value)
3833            })
3834            .collect_vec();
3835        let denominator = weights
3836            .iter()
3837            .fold(rstar_sample.zero(), |sum, value| sum + value);
3838        Complex::new_re(weights[builder.shared_center].clone() / denominator)
3839    }
3840
3841    fn dual_multichanneling_factor(&self, geometry: &DualRstarGeometry<T>) -> HyperDual<F<T>> {
3842        let builder = self.esurface_ct_builder.overlap_builder;
3843        let group = builder.shared_group;
3844        let native_lmb = self
3845            .subspace()
3846            .get_lmb(builder.counterterm_builder.all_lmbs);
3847        let group_lmb = group.subspace.get_lmb(builder.counterterm_builder.all_lmbs);
3848        let external_spatial = geometry
3849            .external_moms
3850            .iter()
3851            .map(|p| p.spatial.clone())
3852            .collect();
3853        let current =
3854            geometry
3855                .rstar_loop_momenta
3856                .lmb_transform(native_lmb, group_lmb, &external_spatial);
3857        let values = group
3858            .thresholds
3859            .iter_enumerated()
3860            .map(|(id, surface)| {
3861                let sample = &group.kinematics[id.0];
3862                // The target cut's complement carries its live LU-t derivatives.
3863                // Other cuts' already-solved data are constants for this residue;
3864                // their threshold-r dependence enters through the shared coordinates.
3865                let (mut momenta, externals) = if group.records[id.0].cut_group_id
3866                    == builder.counterterm_builder.cut_group_id
3867                {
3868                    (current.clone(), geometry.external_moms.clone())
3869                } else {
3870                    (
3871                        dualize_loop_momenta(&geometry.radius, sample.loop_moms()),
3872                        dualize_external_momenta(&geometry.radius, sample.external_moms()),
3873                    )
3874                };
3875                for index in group.subspace.iter_lmb_indices() {
3876                    momenta[index] = current[index].clone();
3877                }
3878                let value = surface.compute_from_dual_momenta(
3879                    group_lmb,
3880                    builder.counterterm_builder.real_mass_vector,
3881                    &momenta,
3882                    &externals,
3883                );
3884                (0..group.prefactor_power).fold(
3885                    new_constant(&value, &value.values[0].one()),
3886                    |product, _| product * &value,
3887                )
3888            })
3889            .collect_vec();
3890        let one = new_constant(&geometry.radius, &geometry.radius.values[0].one());
3891        let zero = new_constant(&geometry.radius, &geometry.radius.values[0].zero());
3892        let weights = group
3893            .overlap
3894            .overlap_groups
3895            .iter()
3896            .map(|center| {
3897                center
3898                    .complement
3899                    .iter()
3900                    .map(|id| &values[group.overlap.existing_esurfaces[*id].0])
3901                    .fold(one.clone(), |weight, value| weight * value)
3902            })
3903            .collect_vec();
3904        let denominator = weights.iter().fold(zero, |sum, value| sum + value);
3905        weights[builder.shared_center].clone() / denominator
3906    }
3907
3908    fn rstar_sample_for_order<'solution>(
3909        &'solution self,
3910        order: usize,
3911    ) -> RstarSample<'solution, 'a, T> {
3912        let base_rstar_loop_momenta = self.base_rstar_loop_momenta();
3913
3914        if order == 0 {
3915            let mut rstar_sample = self
3916                .esurface_ct_builder
3917                .overlap_builder
3918                .transformed_kinematic_point
3919                .representative_sample()
3920                .clone();
3921            rstar_sample.sample.loop_moms = base_rstar_loop_momenta;
3922            rstar_sample.sample.dual_loop_moms = None;
3923
3924            let radius = self.esurface_ct_builder.overlap_builder.radius.clone();
3925            let radius_star = self.solution.solution.clone();
3926            let esurface_derivative = self.solution.derivative_at_solution.clone();
3927            let e_cm = &self
3928                .esurface_ct_builder
3929                .overlap_builder
3930                .counterterm_builder
3931                .e_cm;
3932            let uv_localisation_settings = &self
3933                .esurface_ct_builder
3934                .overlap_builder
3935                .counterterm_builder
3936                .settings
3937                .subtraction
3938                .local_ct_settings
3939                .uv_localisation;
3940
3941            let threshold_params = ThresholdParams {
3942                radius: DualOrNot::NonDual(radius.clone()),
3943                radius_star: DualOrNot::NonDual(radius_star.clone()),
3944                esurface_derivative: DualOrNot::NonDual(esurface_derivative.clone()),
3945                uv_damp_plus: DualOrNot::NonDual(evaluate_uv_damper(
3946                    &radius,
3947                    &radius_star,
3948                    e_cm,
3949                    uv_localisation_settings,
3950                )),
3951                uv_damp_minus: DualOrNot::NonDual(evaluate_uv_damper(
3952                    &-radius.clone(),
3953                    &radius_star,
3954                    e_cm,
3955                    uv_localisation_settings,
3956                )),
3957                h_function: DualOrNot::NonDual(evaluate_integrated_ct_normalisation(
3958                    &radius,
3959                    &radius_star,
3960                    e_cm,
3961                    &self
3962                        .esurface_ct_builder
3963                        .overlap_builder
3964                        .counterterm_builder
3965                        .settings
3966                        .subtraction
3967                        .integrated_ct_settings,
3968                )),
3969            };
3970
3971            let value_of_multi_channeling_factor =
3972                DualOrNot::NonDual(self.non_dual_multichanneling_factor(&rstar_sample));
3973
3974            return RstarSample {
3975                rstar_solution: self,
3976                rstar_sample,
3977                threshold_params,
3978                value_of_multi_channeling_factor,
3979            };
3980        }
3981
3982        assert!(
3983            order <= self.max_supported_order(),
3984            "requested LU threshold derivative order {} but only {} orders are cached",
3985            order,
3986            self.max_supported_order()
3987        );
3988
3989        let geometry = self.dual_geometry_for_order(order);
3990        let mut rstar_sample = self
3991            .esurface_ct_builder
3992            .overlap_builder
3993            .transformed_kinematic_point
3994            .sample_for_order(order)
3995            .clone();
3996        rstar_sample.sample.loop_moms = base_rstar_loop_momenta;
3997        rstar_sample.sample.dual_loop_moms = Some(geometry.rstar_loop_momenta.clone());
3998
3999        let e_cm = &self
4000            .esurface_ct_builder
4001            .overlap_builder
4002            .counterterm_builder
4003            .e_cm;
4004        let uv_localisation_settings = &self
4005            .esurface_ct_builder
4006            .overlap_builder
4007            .counterterm_builder
4008            .settings
4009            .subtraction
4010            .local_ct_settings
4011            .uv_localisation;
4012
4013        let threshold_params = ThresholdParams {
4014            radius: DualOrNot::Dual(geometry.radius.clone()),
4015            radius_star: DualOrNot::Dual(geometry.radius_star.clone()),
4016            esurface_derivative: DualOrNot::Dual(geometry.esurface_derivative.clone()),
4017            uv_damp_plus: DualOrNot::Dual(evaluate_uv_damper_dual(
4018                &geometry.radius,
4019                &geometry.radius_star,
4020                e_cm,
4021                uv_localisation_settings,
4022            )),
4023            uv_damp_minus: DualOrNot::Dual(evaluate_uv_damper_dual(
4024                &-geometry.radius.clone(),
4025                &geometry.radius_star,
4026                e_cm,
4027                uv_localisation_settings,
4028            )),
4029            h_function: DualOrNot::Dual(evaluate_integrated_ct_normalisation_dual(
4030                &geometry.radius,
4031                &geometry.radius_star,
4032                e_cm,
4033                &self
4034                    .esurface_ct_builder
4035                    .overlap_builder
4036                    .counterterm_builder
4037                    .settings
4038                    .subtraction
4039                    .integrated_ct_settings,
4040            )),
4041        };
4042
4043        let value_of_multi_channeling_factor = DualOrNot::Dual(real_dual_to_complex(
4044            self.dual_multichanneling_factor(&geometry),
4045        ));
4046
4047        RstarSample {
4048            rstar_solution: self,
4049            rstar_sample,
4050            threshold_params,
4051            value_of_multi_channeling_factor,
4052        }
4053    }
4054
4055    fn rstar_sample_for_cut_cff_index<'solution>(
4056        &'solution self,
4057        cut_cff_index: &CutCFFIndex,
4058        threshold_variable: Option<usize>,
4059    ) -> RstarSample<'solution, 'a, T> {
4060        let lu_order = cut_cff_index
4061            .lu_cut_order
4062            .expect("LU threshold sampling requires lu_cut_order")
4063            - 1;
4064
4065        if shape_from_cut_cff_index(cut_cff_index).is_none() {
4066            return self.rstar_sample_for_order(lu_order);
4067        }
4068
4069        let base_rstar_loop_momenta = self.base_rstar_loop_momenta();
4070        let geometry = self.dual_geometry_for_cut_cff_index(cut_cff_index, threshold_variable);
4071        let mut rstar_sample = self
4072            .esurface_ct_builder
4073            .overlap_builder
4074            .transformed_kinematic_point
4075            .sample_for_order(lu_order)
4076            .clone();
4077        rstar_sample.sample.loop_moms = base_rstar_loop_momenta;
4078        rstar_sample.sample.dual_loop_moms = Some(geometry.rstar_loop_momenta.clone());
4079
4080        let e_cm = &self
4081            .esurface_ct_builder
4082            .overlap_builder
4083            .counterterm_builder
4084            .e_cm;
4085        let uv_localisation_settings = &self
4086            .esurface_ct_builder
4087            .overlap_builder
4088            .counterterm_builder
4089            .settings
4090            .subtraction
4091            .local_ct_settings
4092            .uv_localisation;
4093
4094        let threshold_params = ThresholdParams {
4095            radius: DualOrNot::Dual(geometry.radius.clone()),
4096            radius_star: DualOrNot::Dual(geometry.radius_star.clone()),
4097            esurface_derivative: DualOrNot::Dual(geometry.esurface_derivative.clone()),
4098            uv_damp_plus: DualOrNot::Dual(evaluate_uv_damper_dual(
4099                &geometry.radius,
4100                &geometry.radius_star,
4101                e_cm,
4102                uv_localisation_settings,
4103            )),
4104            uv_damp_minus: DualOrNot::Dual(evaluate_uv_damper_dual(
4105                &-geometry.radius.clone(),
4106                &geometry.radius_star,
4107                e_cm,
4108                uv_localisation_settings,
4109            )),
4110            h_function: DualOrNot::Dual(evaluate_integrated_ct_normalisation_dual(
4111                &geometry.radius,
4112                &geometry.radius_star,
4113                e_cm,
4114                &self
4115                    .esurface_ct_builder
4116                    .overlap_builder
4117                    .counterterm_builder
4118                    .settings
4119                    .subtraction
4120                    .integrated_ct_settings,
4121            )),
4122        };
4123
4124        let value_of_multi_channeling_factor = DualOrNot::Dual(real_dual_to_complex(
4125            self.dual_multichanneling_factor(&geometry),
4126        ));
4127
4128        RstarSample {
4129            rstar_solution: self,
4130            rstar_sample,
4131            threshold_params,
4132            value_of_multi_channeling_factor,
4133        }
4134    }
4135}
4136
4137struct RstarSample<'solution, 'a, T: FloatLike> {
4138    rstar_solution: &'solution RstarSolution<'a, T>,
4139    rstar_sample: MomentumSample<T>,
4140    threshold_params: ThresholdParams<T>,
4141    value_of_multi_channeling_factor: DualOrNot<Complex<F<T>>>,
4142}
4143
4144impl<'solution, 'a, T: FloatLike> RstarSample<'solution, 'a, T> {
4145    fn extract_threshold_parameters(&self, is_first_call: bool) -> ThresholdParams<T> {
4146        if is_first_call {
4147            let edges_in_esurface = self
4148                .rstar_solution
4149                .esurface_ct_builder
4150                .esurface
4151                .energies
4152                .iter()
4153                .map(|i| {
4154                    self.rstar_solution
4155                        .esurface_ct_builder
4156                        .overlap_builder
4157                        .counterterm_builder
4158                        .graph[i]
4159                        .0
4160                        .name
4161                        .clone()
4162                })
4163                .collect_vec();
4164
4165            debug!("esurface_id: {}", self.get_esurface_id().0);
4166            debug!("edges in esurface: {:?}", edges_in_esurface);
4167            debug!("radius: {}", self.threshold_params.radius);
4168            debug!("radius_star: {}", self.threshold_params.radius_star);
4169            debug!(
4170                "esurface_derivative: {}",
4171                self.threshold_params.esurface_derivative
4172            );
4173            debug!("uv_damp_plus: {}", self.threshold_params.uv_damp_plus);
4174            debug!("uv_damp_minus: {}", self.threshold_params.uv_damp_minus);
4175            debug!("h_function: {}", self.threshold_params.h_function);
4176            debug!(
4177                "value of multi-channeling factor: {}",
4178                self.value_of_multi_channeling_factor
4179            );
4180        }
4181
4182        self.threshold_params.clone()
4183    }
4184
4185    fn get_inverse_transformed_sample(&self) -> MomentumSample<T> {
4186        let subspace = self.rstar_solution.subspace();
4187        let current_lmb = subspace.get_lmb(
4188            self.rstar_solution
4189                .esurface_ct_builder
4190                .overlap_builder
4191                .counterterm_builder
4192                .all_lmbs,
4193        );
4194        let target_lmb = &self
4195            .rstar_solution
4196            .esurface_ct_builder
4197            .overlap_builder
4198            .counterterm_builder
4199            .graph
4200            .loop_momentum_basis;
4201
4202        self.rstar_sample.lmb_transform(current_lmb, target_lmb)
4203    }
4204
4205    fn get_esurface_id(&self) -> EsurfaceID {
4206        self.rstar_solution.esurface_ct_builder.esurface_id
4207    }
4208}
4209
4210fn merge_and_inverse_transform<T: FloatLike>(
4211    left_sample: &RstarSample<'_, '_, T>,
4212    right_sample: &RstarSample<'_, '_, T>,
4213    cut_cff_index: &CutCFFIndex,
4214) -> MomentumSample<T> {
4215    let left_builder = left_sample
4216        .rstar_solution
4217        .esurface_ct_builder
4218        .overlap_builder;
4219    let lmbs = left_builder.counterterm_builder.all_lmbs;
4220    let left_subspace = left_sample.rstar_solution.subspace();
4221    let right_subspace = right_sample.rstar_solution.subspace();
4222    assert!(
4223        left_subspace.is_mergable_with(right_subspace, lmbs),
4224        "incompatible physical subspaces for merging samples"
4225    );
4226    let graph_lmb = &left_builder.counterterm_builder.graph.loop_momentum_basis;
4227    let mut merged = left_sample.get_inverse_transformed_sample();
4228    let right = right_sample.get_inverse_transformed_sample();
4229    let base = left_builder
4230        .transformed_kinematic_point
4231        .representative_sample()
4232        .lmb_transform(left_subspace.get_lmb(lmbs), graph_lmb);
4233    // Add the two physical displacements to their common solved-cut sample.
4234    // This is the same Cartesian projection even when native parent slots differ.
4235    for index in (0..merged.loop_moms().0.len()).map(LoopIndex::from) {
4236        merged.sample.loop_moms[index] +=
4237            &right.sample.loop_moms[index] - &base.sample.loop_moms[index];
4238    }
4239    match (
4240        merged.sample.dual_loop_moms.as_mut(),
4241        right.sample.dual_loop_moms.as_ref(),
4242    ) {
4243        (Some(merged_duals), Some(right_duals)) => {
4244            let source = &left_builder.transformed_kinematic_point;
4245            let order = cut_cff_index.lu_cut_order.expect("iterated LU order") - 1;
4246            let base_sample = source.sample_for_order(order);
4247            let target_shape = shape_from_cut_cff_index(cut_cff_index).map(HyperDual::new);
4248            let native_base = embedded_dual_loop_momenta_for_cut_cff_index(
4249                base_sample,
4250                &target_shape,
4251                variable_indices_from_cut_cff_index(cut_cff_index).lu_cut,
4252            )
4253            .expect("iterated dual base matches the residue shape");
4254            let external = dualize_external_momenta(
4255                &merged_duals[LoopIndex::from(0)].px,
4256                base_sample.external_moms(),
4257            );
4258            let base_duals = native_base.lmb_transform(
4259                left_subspace.get_lmb(lmbs),
4260                graph_lmb,
4261                &external.iter().map(|p| p.spatial.clone()).collect(),
4262            );
4263            for index in (0..merged_duals.0.len()).map(LoopIndex::from) {
4264                merged_duals[index] = merged_duals[index].clone() + right_duals[index].clone()
4265                    - base_duals[index].clone();
4266            }
4267        }
4268        (None, None) => {}
4269        _ => unreachable!("iterated LU samples must carry matching dual shapes"),
4270    }
4271    merged
4272}
4273
4274#[cfg(test)]
4275#[path = "lu_counterterm_group_tests.rs"]
4276mod group_tests;
4277
4278#[cfg(test)]
4279mod tests {
4280    use super::{
4281        IteratedMultiplierCoefficients, IteratedMultiplierComponent, LUCounterTerm,
4282        PairMultiplierCoefficient, PendingLUCountertermComponent, SingleMultiplierCoefficients,
4283        SingleMultiplierComponent, extend_extracted_eta_params,
4284        extract_zero_threshold_coefficient_t_dual, iterated_multiplier_context,
4285        single_multiplier_context, weight_iterated_multiplier_pieces,
4286        weight_single_multiplier_pieces,
4287    };
4288    use crate::cff::CutCFFIndex;
4289    use crate::observables::events::ThresholdCountertermComponentOccurrence;
4290    use crate::processes::{CutGroupId, IteratedThresholdPieces, SingleThresholdPieces};
4291    use crate::utils::{
4292        ArbPrec, F, FloatLike, f128,
4293        hyperdual_utils::{DualOrNot, new_from_values, shape_from_cut_cff_index},
4294    };
4295    use spenso::algebra::complex::Complex;
4296    use symbolica::domains::dual::HyperDual;
4297    use typed_index_collections::{TiVec, ti_vec};
4298
4299    #[test]
4300    fn inactive_cut_group_guard_reports_runtime_access() {
4301        let counterterm = LUCounterTerm {
4302            evaluators: TiVec::new(),
4303            thresholds: TiVec::new(),
4304            subspaces: TiVec::new(),
4305            variant_subspaces: None,
4306            metadata_registry: None,
4307            rstar_dependence_calculator: TiVec::new(),
4308            active_cut_groups: ti_vec![false],
4309            active_left_thresholds: ti_vec![TiVec::new()],
4310            active_right_thresholds: ti_vec![TiVec::new()],
4311            active_iterated_thresholds: TiVec::new(),
4312        };
4313
4314        let error = counterterm
4315            .ensure_active_cut_group(CutGroupId(0))
4316            .unwrap_err();
4317        assert!(error.to_string().contains("generation marked it inactive"));
4318    }
4319
4320    #[test]
4321    fn zero_threshold_coefficient_lookup_is_explicit() {
4322        let shape = HyperDual::new(
4323            shape_from_cut_cff_index(&CutCFFIndex {
4324                left_threshold_order: Some(2),
4325                right_threshold_order: Some(2),
4326                lu_cut_order: Some(2),
4327            })
4328            .unwrap(),
4329        );
4330        let mixed_dual = new_from_values(
4331            &shape,
4332            &[
4333                F(10.0_f64),
4334                F(11.0_f64),
4335                F(20.0_f64),
4336                F(30.0_f64),
4337                F(40.0_f64),
4338                F(21.0_f64),
4339                F(31.0_f64),
4340                F(41.0_f64),
4341            ],
4342        );
4343
4344        let zero_threshold = extract_zero_threshold_coefficient_t_dual(&mixed_dual, 0);
4345
4346        assert_eq!(zero_threshold.values, vec![F(10.0_f64), F(11.0_f64)]);
4347    }
4348
4349    #[test]
4350    fn iterated_eta_parameters_project_onto_their_own_threshold_axis() {
4351        let cut_cff_index = CutCFFIndex {
4352            left_threshold_order: Some(1),
4353            right_threshold_order: Some(2),
4354            lu_cut_order: Some(1),
4355        };
4356        let shape = HyperDual::new(shape_from_cut_cff_index(&cut_cff_index).unwrap());
4357        let mixed_eta = DualOrNot::Dual(new_from_values(&shape, &[F(10.0_f64), F(20.0_f64)]));
4358
4359        let mut left_params = Vec::new();
4360        extend_extracted_eta_params(&mut left_params, &mixed_eta, None, None);
4361        assert_eq!(
4362            left_params,
4363            vec![Complex::new_re(F(10.0_f64))],
4364            "an order-one left eta must take the zero coefficient of the right threshold axis",
4365        );
4366
4367        let mut right_params = Vec::new();
4368        extend_extracted_eta_params(&mut right_params, &mixed_eta, None, Some(0));
4369        assert_eq!(
4370            right_params,
4371            vec![Complex::new_re(F(10.0_f64)), Complex::new_re(F(20.0_f64)),],
4372            "the right eta must retain derivatives along its own threshold axis",
4373        );
4374    }
4375
4376    #[test]
4377    fn multiplier_contexts_follow_expanded_term_semantics() {
4378        let base = "base";
4379        let left_root = "left_root";
4380        let right_root = "right_root";
4381        let merged_root = "merged_root";
4382
4383        assert_eq!(
4384            single_multiplier_context(SingleMultiplierComponent::Local, &base, &left_root),
4385            (&base, &left_root),
4386        );
4387        assert_eq!(
4388            single_multiplier_context(SingleMultiplierComponent::Integrated, &base, &left_root),
4389            (&left_root, &left_root),
4390        );
4391        assert_eq!(
4392            iterated_multiplier_context(
4393                IteratedMultiplierComponent::LocalLocal,
4394                &base,
4395                &left_root,
4396                &right_root,
4397                &merged_root,
4398            ),
4399            (&base, &merged_root),
4400        );
4401        assert_eq!(
4402            iterated_multiplier_context(
4403                IteratedMultiplierComponent::IntegratedLocal,
4404                &base,
4405                &left_root,
4406                &right_root,
4407                &merged_root,
4408            ),
4409            (&left_root, &merged_root),
4410        );
4411        assert_eq!(
4412            iterated_multiplier_context(
4413                IteratedMultiplierComponent::LocalIntegrated,
4414                &base,
4415                &left_root,
4416                &right_root,
4417                &merged_root,
4418            ),
4419            (&right_root, &merged_root),
4420        );
4421        assert_eq!(
4422            iterated_multiplier_context(
4423                IteratedMultiplierComponent::IntegratedIntegrated,
4424                &base,
4425                &left_root,
4426                &right_root,
4427                &merged_root,
4428            ),
4429            (&merged_root, &merged_root),
4430        );
4431
4432        // One original term, two single-sided (local/integrated) terms on each side, and four
4433        // paired terms exhaust the 3x3 expansion used by the GL638 two-sided regression.
4434        let left_only_terms = 2;
4435        let right_only_terms = 2;
4436        let paired_terms = 4;
4437        assert_eq!(1 + left_only_terms + right_only_terms + paired_terms, 9);
4438        assert_eq!((1 + left_only_terms) * (1 + right_only_terms), 9);
4439    }
4440
4441    #[test]
4442    fn zero_multiplier_coefficients_skip_unused_pieces() {
4443        let zero = F(0.0_f64);
4444        let single = weight_single_multiplier_pieces(
4445            SingleThresholdPieces {
4446                local: DualOrNot::NonDual(Complex::new_re(F(f64::NAN))),
4447                integrated: DualOrNot::NonDual(Complex::new_re(F(3.0))),
4448            },
4449            &SingleMultiplierCoefficients {
4450                local: F(0.0),
4451                integrated: F(5.0),
4452            },
4453            0,
4454            &zero,
4455        )
4456        .unwrap_real();
4457        assert_eq!(single, Complex::new_re(F(15.0)));
4458
4459        let iterated = weight_iterated_multiplier_pieces(
4460            IteratedThresholdPieces {
4461                local_local: DualOrNot::NonDual(Complex::new_re(F(1.0))),
4462                local_integrated: DualOrNot::NonDual(Complex::new_re(F(f64::NAN))),
4463                integrated_local: DualOrNot::NonDual(Complex::new_re(F(100.0))),
4464                integrated_integrated: DualOrNot::NonDual(Complex::new_re(F(1000.0))),
4465            },
4466            &IteratedMultiplierCoefficients {
4467                local_local: PairMultiplierCoefficient {
4468                    left: F(2.0),
4469                    right: F(1.0),
4470                },
4471                local_integrated: PairMultiplierCoefficient {
4472                    left: F(0.0),
4473                    right: F(1.0),
4474                },
4475                integrated_local: PairMultiplierCoefficient {
4476                    left: F(5.0),
4477                    right: F(1.0),
4478                },
4479                integrated_integrated: PairMultiplierCoefficient {
4480                    left: F(7.0),
4481                    right: F(1.0),
4482                },
4483            },
4484            0,
4485            &zero,
4486        )
4487        .unwrap_real();
4488        assert_eq!(iterated, Complex::new_re(F(7502.0)));
4489
4490        assert!(
4491            SingleMultiplierCoefficients {
4492                local: zero,
4493                integrated: zero,
4494            }
4495            .all_zero()
4496        );
4497        assert!(
4498            IteratedMultiplierCoefficients {
4499                local_local: PairMultiplierCoefficient {
4500                    left: zero,
4501                    right: F(1.0),
4502                },
4503                local_integrated: PairMultiplierCoefficient {
4504                    left: zero,
4505                    right: F(1.0),
4506                },
4507                integrated_local: PairMultiplierCoefficient {
4508                    left: zero,
4509                    right: F(1.0),
4510                },
4511                integrated_integrated: PairMultiplierCoefficient {
4512                    left: zero,
4513                    right: F(1.0),
4514                },
4515            }
4516            .all_zero()
4517        );
4518    }
4519
4520    #[test]
4521    fn detailed_component_finishing_skips_exact_zero_and_reconciles_weights() {
4522        let occurrence = ThresholdCountertermComponentOccurrence::LocalUnitarity {
4523            overlap_groups: smallvec::smallvec![2],
4524            left_threshold_order: Some(1),
4525            right_threshold_order: None,
4526            lu_cut_order: Some(2),
4527        };
4528        let nonzero = PendingLUCountertermComponent {
4529            component_id: 4,
4530            occurrence: occurrence.clone(),
4531            multiplier_values: smallvec::smallvec![F(2.0_f64)],
4532            effective_multiplier: F(2.0),
4533            // Single-sided signs are already carried by the pass-one component.
4534            pass_one: Some(DualOrNot::NonDual(Complex::new_re(F(-3.0)))),
4535        };
4536        let mut pass_two_calls = 0;
4537        let (weighted, completed) = nonzero.finish(&F(0.0), |pass_one| {
4538            pass_two_calls += 1;
4539            pass_one.unwrap_real() * Complex::new_re(F(4.0))
4540        });
4541        assert_eq!(pass_two_calls, 1);
4542        assert_eq!(completed.bare, Some(Complex::new_re(F(-12.0))));
4543        assert_eq!(weighted, Complex::new_re(F(-24.0)));
4544        assert_eq!(completed.weighted, weighted);
4545
4546        let skipped = PendingLUCountertermComponent {
4547            component_id: 5,
4548            occurrence,
4549            multiplier_values: smallvec::smallvec![F(0.0_f64)],
4550            effective_multiplier: F(0.0),
4551            pass_one: None,
4552        };
4553        let (skipped_weight, skipped) = skipped.finish(&F(0.0), |_| {
4554            panic!("exact-zero component must not reach pass two")
4555        });
4556        assert!(skipped.evaluation_skipped);
4557        assert!(skipped.bare.is_none());
4558        assert_eq!(skipped_weight, Complex::new_re(F(0.0)));
4559
4560        let decomposition = crate::observables::events::GenericThresholdCountertermEventInfo {
4561            original: Complex::new_re(F(30.0)),
4562            components: vec![completed, skipped],
4563        };
4564        assert_eq!(decomposition.total(), Complex::new_re(F(6.0)));
4565    }
4566
4567    fn assert_detailed_component_precision<T: FloatLike>() {
4568        let value = |value| F(T::from_f64(value));
4569        let component = PendingLUCountertermComponent {
4570            component_id: 0,
4571            occurrence: ThresholdCountertermComponentOccurrence::LocalUnitarity {
4572                overlap_groups: smallvec::smallvec![0],
4573                left_threshold_order: Some(1),
4574                right_threshold_order: None,
4575                lu_cut_order: Some(1),
4576            },
4577            multiplier_values: smallvec::smallvec![value(2.0)],
4578            effective_multiplier: value(2.0),
4579            pass_one: Some(DualOrNot::NonDual(Complex::new_re(value(-3.0)))),
4580        };
4581        let (_, completed) = component.finish(&value(0.0), |pass_one| {
4582            pass_one.unwrap_real() * Complex::new_re(value(4.0))
4583        });
4584
4585        assert_eq!(completed.bare.unwrap().re.0.into_f64(), -12.0);
4586        assert_eq!(completed.weighted.re.0.into_f64(), -24.0);
4587        assert_eq!(completed.effective_multiplier.0.into_f64(), 2.0);
4588    }
4589
4590    #[test]
4591    fn detailed_components_preserve_all_runtime_precision_lanes() {
4592        assert_detailed_component_precision::<f64>();
4593        assert_detailed_component_precision::<f128>();
4594        assert_detailed_component_precision::<ArbPrec>();
4595    }
4596}