1pub mod evaluation;
2pub mod process;
3
4use crate::integrands::evaluation::{EvaluationResult, RawBatchEvaluationResult};
5use crate::integrands::process::{EvaluationTarget, ProcessIntegrand};
6use crate::integrands::process::{amplitude, cross_section};
7use crate::model::Model;
8use crate::observables::{
9 ObservableAccumulatorBundle, ObservableFileFormat, ObservableSnapshotBundle,
10};
11use crate::settings::runtime::IntegratorSettings;
12use crate::utils::F;
13#[cfg(test)]
14use crate::{is_interrupted, settings::RuntimeSettings};
15
16use color_eyre::Result;
17use enum_dispatch::enum_dispatch;
18use spenso::algebra::complex::Complex;
19#[cfg(test)]
20use symbolica::numerical_integration::ContinuousGrid;
21use symbolica::numerical_integration::{Grid, Sample};
22
23#[enum_dispatch]
24pub trait HasIntegrand {
25 fn create_grid(&self) -> Grid<F<f64>>;
27
28 fn name(&self) -> String;
30
31 fn evaluate_sample(
33 &mut self,
34 sample: &Sample<F<f64>>,
35 model: &Model,
36 wgt: F<f64>,
37 iter: usize,
38 use_arb_prec: bool,
39 max_eval: Complex<F<f64>>,
40 ) -> Result<EvaluationResult>;
41
42 fn get_n_dim(&self) -> usize;
44
45 fn get_integrator_settings(&self) -> IntegratorSettings {
47 IntegratorSettings::default()
48 }
49
50 fn merge_results<I: HasIntegrand>(&mut self, _other: &mut I, _iter: usize) {}
53
54 fn update_results(&mut self, _iter: usize) {}
57}
58
59#[derive(Clone)]
63#[allow(clippy::large_enum_variant)]
64pub enum Integrand {
65 ProcessIntegrand(Box<ProcessIntegrand>),
67 #[cfg(test)]
68 TestProbe(TestProbeIntegrand),
69}
70
71impl Integrand {
72 pub fn evaluate_samples_raw(
73 &mut self,
74 samples: &[Sample<F<f64>>],
75 target: EvaluationTarget<'_>,
76 iter: usize,
77 use_arb_prec: bool,
78 stop_on_interrupt: bool,
79 max_eval: Complex<F<f64>>,
80 ) -> Result<RawBatchEvaluationResult> {
81 match self {
82 Integrand::ProcessIntegrand(integrand) => integrand.evaluate_samples_raw(
83 target,
84 samples,
85 iter,
86 use_arb_prec,
87 stop_on_interrupt,
88 max_eval,
89 ),
90 #[cfg(test)]
91 Integrand::TestProbe(_) => {
92 let EvaluationTarget::Physical(model) = target else {
93 return Err(color_eyre::eyre::eyre!(
94 "reference overlays require a generated process"
95 ));
96 };
97 let mut results = Vec::with_capacity(samples.len());
98 for sample in samples {
99 if stop_on_interrupt && is_interrupted() {
100 break;
101 }
102 results.push(self.evaluate_sample(
103 sample,
104 model,
105 sample.get_weight(),
106 iter,
107 use_arb_prec,
108 max_eval,
109 )?);
110 }
111 Ok(RawBatchEvaluationResult {
112 statistics: evaluation::StatisticsCounter::from_evaluation_results(&results),
113 samples: results,
114 })
115 }
116 }
117 }
118
119 #[allow(clippy::too_many_arguments)]
120 #[allow(clippy::too_many_arguments)]
121 pub fn evaluate_samples_raw_with_estimate(
122 &mut self,
123 samples: &[Sample<F<f64>>],
124 target: EvaluationTarget<'_>,
125 iter: usize,
126 use_arb_prec: bool,
127 stop_on_interrupt: bool,
128 max_eval: Complex<F<f64>>,
129 integral_estimate: Option<(f64, f64)>,
130 ) -> Result<RawBatchEvaluationResult> {
131 match self {
132 Integrand::ProcessIntegrand(integrand) => integrand.evaluate_samples_raw_with_estimate(
133 target,
134 samples,
135 iter,
136 use_arb_prec,
137 stop_on_interrupt,
138 max_eval,
139 integral_estimate,
140 ),
141 #[cfg(test)]
142 Integrand::TestProbe(_) => self.evaluate_samples_raw(
143 samples,
144 target,
145 iter,
146 use_arb_prec,
147 stop_on_interrupt,
148 max_eval,
149 ),
150 }
151 }
152
153 pub fn process_evaluation_result(&mut self, result: &EvaluationResult) {
154 match self {
155 Integrand::ProcessIntegrand(integrand) => integrand.process_evaluation_result(result),
156 #[cfg(test)]
157 Integrand::TestProbe(_) => {}
158 }
159 }
160
161 pub fn merge_runtime_results(&mut self, other: &mut Integrand) -> Result<()> {
162 match (self, other) {
163 (Integrand::ProcessIntegrand(lhs), Integrand::ProcessIntegrand(rhs)) => {
164 lhs.merge_event_processing_runtime(rhs)
165 }
166 #[cfg(test)]
167 _ => Ok(()),
168 }
169 }
170
171 pub fn update_runtime_results(&mut self, iter: usize) {
172 match self {
173 Integrand::ProcessIntegrand(integrand) => {
174 integrand.update_event_processing_runtime(iter)
175 }
176 #[cfg(test)]
177 Integrand::TestProbe(_) => {}
178 }
179 }
180
181 pub fn observable_accumulator_bundle(&self) -> Option<ObservableAccumulatorBundle> {
182 match self {
183 Integrand::ProcessIntegrand(integrand) => integrand.observable_accumulator_bundle(),
184 #[cfg(test)]
185 Integrand::TestProbe(_) => None,
186 }
187 }
188
189 pub fn has_observables(&self) -> bool {
190 match self {
191 Integrand::ProcessIntegrand(integrand) => integrand.has_observables(),
192 #[cfg(test)]
193 Integrand::TestProbe(_) => false,
194 }
195 }
196
197 pub fn observable_snapshot_bundle(&self) -> Option<ObservableSnapshotBundle> {
198 match self {
199 Integrand::ProcessIntegrand(integrand) => integrand.observable_snapshot_bundle(),
200 #[cfg(test)]
201 Integrand::TestProbe(_) => None,
202 }
203 }
204
205 pub fn build_observable_snapshots_for_result(
206 &self,
207 result: &EvaluationResult,
208 ) -> Option<ObservableSnapshotBundle> {
209 match self {
210 Integrand::ProcessIntegrand(integrand) => {
211 integrand.build_observable_snapshots_for_result(result)
212 }
213 #[cfg(test)]
214 Integrand::TestProbe(_) => None,
215 }
216 }
217
218 pub fn write_observable_snapshots(
219 &self,
220 path: impl AsRef<std::path::Path>,
221 format: ObservableFileFormat,
222 ) -> Result<()> {
223 match self {
224 Integrand::ProcessIntegrand(integrand) => {
225 integrand.write_observable_snapshots(path, format)
226 }
227 #[cfg(test)]
228 Integrand::TestProbe(_) => Ok(()),
229 }
230 }
231}
232
233impl HasIntegrand for Integrand {
234 fn name(&self) -> String {
235 match self {
236 Integrand::ProcessIntegrand(i) => i.name(),
237 #[cfg(test)]
238 Integrand::TestProbe(integrand) => integrand.name(),
239 }
240 }
241
242 fn create_grid(&self) -> Grid<F<f64>> {
243 match self {
244 Integrand::ProcessIntegrand(integrand) => integrand.create_grid(),
245 #[cfg(test)]
246 Integrand::TestProbe(integrand) => integrand.create_grid(),
247 }
248 }
249
250 fn evaluate_sample(
251 &mut self,
252 sample: &Sample<F<f64>>,
253 model: &Model,
254 wgt: F<f64>,
255 iter: usize,
256 use_arb_prec: bool,
257 max_eval: Complex<F<f64>>,
258 ) -> Result<EvaluationResult> {
259 match self {
260 Integrand::ProcessIntegrand(integrand) => {
261 integrand.evaluate_sample(sample, model, wgt, iter, use_arb_prec, max_eval)
262 }
263 #[cfg(test)]
264 Integrand::TestProbe(integrand) => {
265 integrand.evaluate_sample(sample, model, wgt, iter, use_arb_prec, max_eval)
266 }
267 }
268 }
269
270 fn get_n_dim(&self) -> usize {
271 match self {
272 Integrand::ProcessIntegrand(integrand) => integrand.get_n_dim(),
273 #[cfg(test)]
274 Integrand::TestProbe(integrand) => integrand.get_n_dim(),
275 }
276 }
277
278 fn get_integrator_settings(&self) -> IntegratorSettings {
279 match self {
280 Integrand::ProcessIntegrand(integrand) => integrand.get_integrator_settings(),
281 #[cfg(test)]
282 Integrand::TestProbe(integrand) => integrand.get_integrator_settings(),
283 }
284 }
285}
286
287#[cfg(test)]
291#[derive(Clone)]
292pub struct TestProbeIntegrand {
293 settings: RuntimeSettings,
294 n_dim: usize,
295}
296
297#[cfg(test)]
298impl TestProbeIntegrand {
299 pub(crate) fn new(settings: RuntimeSettings, n_dim: usize) -> Self {
300 Self { settings, n_dim }
301 }
302}
303
304#[cfg(test)]
305impl HasIntegrand for TestProbeIntegrand {
306 fn create_grid(&self) -> Grid<F<f64>> {
307 Grid::Continuous(
308 ContinuousGrid::new(
309 self.n_dim,
310 self.settings.integrator.n_bins,
311 self.settings.integrator.min_samples_for_update,
312 self.settings.integrator.bin_number_evolution.clone(),
313 self.settings.integrator.train_on_avg,
314 )
315 .expect("test-probe integration requires valid continuous-grid settings"),
316 )
317 }
318
319 fn name(&self) -> String {
320 "TestProbeIntegrand".to_string()
321 }
322
323 fn evaluate_sample(
324 &mut self,
325 _sample: &Sample<F<f64>>,
326 _model: &Model,
327 wgt: F<f64>,
328 _iter: usize,
329 _use_arb_prec: bool,
330 _max_eval: Complex<F<f64>>,
331 ) -> Result<EvaluationResult> {
332 let mut result = EvaluationResult::zero();
333 result.integrand_result = Complex::new_re(F(1.0));
334 result.parameterization_jacobian = Some(F(1.0));
335 result.integrator_weight = wgt;
336 Ok(result)
337 }
338
339 fn get_n_dim(&self) -> usize {
340 self.n_dim
341 }
342}