1use std::array;
2
3use nalgebra::{matrix, vector};
4use nalgebra::{SMatrix, SVector};
5use num::traits::ConstOne;
6use num::traits::FloatConst;
7use serde::{Deserialize, Serialize};
8
9use laddu_core::{
10 amplitudes::{Amplitude, AmplitudeID, ParameterLike},
11 data::Event,
12 resources::{Cache, ComplexVectorID, MatrixID, ParameterID, Parameters, Resources},
13 utils::{
14 functions::{blatt_weisskopf, chi_plus, rho},
15 variables::{Mass, Variable},
16 },
17 Complex, DVector, Float, LadduError,
18};
19
20#[cfg(feature = "python")]
21use laddu_python::{
22 amplitudes::{PyAmplitude, PyParameterLike},
23 utils::variables::PyMass,
24};
25#[cfg(feature = "python")]
26use pyo3::prelude::*;
27
28#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
30pub struct AdlerZero {
31 pub s_0: Float,
33 pub s_norm: Float,
35}
36
37#[derive(Clone, Debug, Serialize, Deserialize)]
39pub struct FixedKMatrix<const CHANNELS: usize, const RESONANCES: usize> {
40 g: SMatrix<Float, CHANNELS, RESONANCES>,
41 c: SMatrix<Float, CHANNELS, CHANNELS>,
42 m1s: SVector<Float, CHANNELS>,
43 m2s: SVector<Float, CHANNELS>,
44 mrs: SVector<Float, RESONANCES>,
45 adler_zero: Option<AdlerZero>,
46 l: usize,
47}
48impl<const CHANNELS: usize, const RESONANCES: usize> FixedKMatrix<CHANNELS, RESONANCES> {
49 fn c_mat(&self, s: Float) -> SMatrix<Complex<Float>, CHANNELS, CHANNELS> {
50 SMatrix::from_diagonal(&SVector::from_fn(|i, _| {
51 let m1 = self.m1s[i];
52 let m2 = self.m2s[i];
53 ((rho(s, m1, m2)
54 * Complex::ln(
55 (chi_plus(s, m1, m2) + rho(s, m1, m2)) / (chi_plus(s, m1, m2) - rho(s, m1, m2)),
56 ))
57 - (chi_plus(s, m1, m2) * ((m2 - m1) / (m1 + m2)) * Float::ln(m2 / m1)))
58 / Float::PI()
59 }))
60 }
61 fn barrier_mat(&self, s: Float) -> SMatrix<Float, CHANNELS, RESONANCES> {
62 let m0 = Float::sqrt(s);
63 SMatrix::from_fn(|i, a| {
64 let m1 = self.m1s[i];
65 let m2 = self.m2s[i];
66 let mr = self.mrs[a];
67 blatt_weisskopf(m0, m1, m2, self.l) / blatt_weisskopf(mr, m1, m2, self.l)
68 })
69 }
70 fn product_of_poles(&self, s: Float) -> Float {
71 self.mrs.map(|m| m.powi(2) - s).product()
72 }
73 fn product_of_poles_except_one(&self, s: Float, a_i: usize) -> Float {
74 self.mrs
75 .iter()
76 .enumerate()
77 .filter_map(|(a_j, m_j)| {
78 if a_j != a_i {
79 Some(m_j.powi(2) - s)
80 } else {
81 None
82 }
83 })
84 .product()
85 }
86
87 fn k_mat(&self, s: Float) -> SMatrix<Complex<Float>, CHANNELS, CHANNELS> {
88 let bf = self.barrier_mat(s);
89 SMatrix::from_fn(|i, j| {
90 self.adler_zero
91 .map_or(Float::ONE, |az| (s - az.s_0) / az.s_norm)
92 * (0..RESONANCES)
93 .map(|a| {
94 Complex::from(
95 bf[(i, a)] * bf[(j, a)] * self.g[(i, a)] * self.g[(j, a)]
96 + (self.c[(i, j)] * (self.mrs[a].powi(2) - s)),
97 ) * self.product_of_poles_except_one(s, a)
98 })
99 .sum::<Complex<Float>>()
100 })
101 }
102
103 fn ikc_inv_vec(&self, s: Float, channel: usize) -> SVector<Complex<Float>, CHANNELS> {
104 let i_mat: SMatrix<Complex<Float>, CHANNELS, CHANNELS> = SMatrix::identity();
105 let k_mat = self.k_mat(s);
106 let c_mat = self.c_mat(s);
107 let ikc_mat = i_mat.scale(self.product_of_poles(s)) + k_mat * c_mat;
108 let ikc_inv_mat = ikc_mat.try_inverse().expect("Matrix inverse failed!");
109 ikc_inv_mat.row(channel).transpose()
110 }
111
112 fn p_vec_constants(&self, s: Float) -> SMatrix<Float, CHANNELS, RESONANCES> {
113 let barrier_mat = self.barrier_mat(s);
114 SMatrix::from_fn(|i, a| {
115 barrier_mat[(i, a)] * self.g[(i, a)] * self.product_of_poles_except_one(s, a)
116 })
117 }
118
119 fn compute(
120 betas: &SVector<Complex<Float>, RESONANCES>,
121 ikc_inv_vec: &SVector<Complex<Float>, CHANNELS>,
122 p_vec_constants: &SMatrix<Float, CHANNELS, RESONANCES>,
123 ) -> Complex<Float> {
124 let p_vec: SVector<Complex<Float>, CHANNELS> = SVector::from_fn(|j, _| {
125 (0..RESONANCES)
126 .map(|a| betas[a] * p_vec_constants[(j, a)])
127 .sum()
128 });
129 ikc_inv_vec.dot(&p_vec)
130 }
131
132 fn compute_gradient(
133 ikc_inv_vec: &SVector<Complex<Float>, CHANNELS>,
134 p_vec_constants: &SMatrix<Float, CHANNELS, RESONANCES>,
135 ) -> DVector<Complex<Float>> {
136 DVector::from_fn(RESONANCES, |a, _| {
137 (0..RESONANCES)
138 .map(|j| ikc_inv_vec[j] * p_vec_constants[(j, a)])
139 .sum()
140 })
141 }
142}
143
144#[derive(Clone, Serialize, Deserialize)]
149pub struct KopfKMatrixF0 {
150 name: String,
151 channel: usize,
152 mass: Mass,
153 constants: FixedKMatrix<5, 5>,
154 couplings_real: [ParameterLike; 5],
155 couplings_imag: [ParameterLike; 5],
156 couplings_indices_real: [ParameterID; 5],
157 couplings_indices_imag: [ParameterID; 5],
158 ikc_cache_index: ComplexVectorID<5>,
159 p_vec_cache_index: MatrixID<5, 5>,
160}
161
162impl KopfKMatrixF0 {
163 pub fn new(
182 name: &str,
183 couplings: [[ParameterLike; 2]; 5],
184 channel: usize,
185 mass: &Mass,
186 ) -> Box<Self> {
187 let mut couplings_real: [ParameterLike; 5] = array::from_fn(|_| ParameterLike::default());
188 let mut couplings_imag: [ParameterLike; 5] = array::from_fn(|_| ParameterLike::default());
189 for i in 0..5 {
190 couplings_real[i] = couplings[i][0].clone();
191 couplings_imag[i] = couplings[i][1].clone();
192 }
193 Self {
194 name: name.to_string(),
195 channel,
196 mass: mass.clone(),
197 constants: FixedKMatrix {
198 g: matrix![
199 0.74987, 0.06401, -0.23417, 0.01270, -0.14242;
200 -0.01257, 0.00204, -0.01032, 0.26700, 0.22780;
201 0.27536, 0.77413, 0.72283, 0.09214, 0.15981;
202 -0.15102, 0.50999, 0.11934, 0.02742, 0.16272;
203 0.36103, 0.13112, 0.36792, -0.04025, -0.17397
204
205 ],
206 c: matrix![
207 0.03728, 0.00000, -0.01398, -0.02203, 0.01397;
208 0.00000, 0.00000, 0.00000, 0.00000, 0.00000;
209 -0.01398, 0.00000, 0.02349, 0.03101, -0.04003;
210 -0.02203, 0.00000, 0.03101, -0.13769, -0.06722;
211 0.01397, 0.00000, -0.04003, -0.06722, -0.28401
212 ],
213 m1s: vector![0.1349768, 2.0 * 0.1349768, 0.493677, 0.547862, 0.547862],
214 m2s: vector![0.1349768, 2.0 * 0.1349768, 0.497611, 0.547862, 0.95778],
215 mrs: vector![0.51461, 0.90630, 1.23089, 1.46104, 1.69611],
216 adler_zero: Some(AdlerZero {
217 s_0: 0.0091125,
218 s_norm: 1.0,
219 }),
220 l: 0,
221 },
222 couplings_real,
223 couplings_imag,
224 couplings_indices_real: [ParameterID::default(); 5],
225 couplings_indices_imag: [ParameterID::default(); 5],
226 ikc_cache_index: ComplexVectorID::default(),
227 p_vec_cache_index: MatrixID::default(),
228 }
229 .into()
230 }
231}
232
233#[typetag::serde]
234impl Amplitude for KopfKMatrixF0 {
235 fn register(&mut self, resources: &mut Resources) -> Result<AmplitudeID, LadduError> {
236 for i in 0..self.couplings_indices_real.len() {
237 self.couplings_indices_real[i] = resources.register_parameter(&self.couplings_real[i]);
238 self.couplings_indices_imag[i] = resources.register_parameter(&self.couplings_imag[i]);
239 }
240 self.ikc_cache_index = resources
241 .register_complex_vector(Some(&format!("KopfKMatrixF0<{}> ikc_vec", self.name)));
242 self.p_vec_cache_index =
243 resources.register_matrix(Some(&format!("KopfKMatrixF0<{}> p_vec", self.name)));
244 resources.register_amplitude(&self.name)
245 }
246
247 fn precompute(&self, event: &Event, cache: &mut Cache) {
248 let s = self.mass.value(event).powi(2);
249 cache.store_complex_vector(
250 self.ikc_cache_index,
251 self.constants.ikc_inv_vec(s, self.channel),
252 );
253 cache.store_matrix(self.p_vec_cache_index, self.constants.p_vec_constants(s));
254 }
255
256 fn compute(&self, parameters: &Parameters, _event: &Event, cache: &Cache) -> Complex<Float> {
257 let betas = SVector::from_fn(|i, _| {
258 Complex::new(
259 parameters.get(self.couplings_indices_real[i]),
260 parameters.get(self.couplings_indices_imag[i]),
261 )
262 });
263 let ikc_inv_vec = cache.get_complex_vector(self.ikc_cache_index);
264 let p_vec_constants = cache.get_matrix(self.p_vec_cache_index);
265 FixedKMatrix::compute(&betas, &ikc_inv_vec, &p_vec_constants)
266 }
267
268 fn compute_gradient(
269 &self,
270 _parameters: &Parameters,
271 _event: &Event,
272 cache: &Cache,
273 gradient: &mut DVector<Complex<Float>>,
274 ) {
275 let ikc_inv_vec = cache.get_complex_vector(self.ikc_cache_index);
276 let p_vec_constants = cache.get_matrix(self.p_vec_cache_index);
277 let internal_gradient = FixedKMatrix::compute_gradient(&ikc_inv_vec, &p_vec_constants);
278 for i in 0..5 {
279 if let ParameterID::Parameter(index) = self.couplings_indices_real[i] {
280 gradient[index] = internal_gradient[i];
281 }
282 if let ParameterID::Parameter(index) = self.couplings_indices_imag[i] {
283 gradient[index] = Complex::<Float>::I * internal_gradient[i];
284 }
285 }
286 }
287}
288
289#[cfg(feature = "python")]
347#[pyfunction(name = "KopfKMatrixF0")]
348pub fn py_kopf_kmatrix_f0(
349 name: &str,
350 couplings: [[PyParameterLike; 2]; 5],
351 channel: usize,
352 mass: PyMass,
353) -> PyAmplitude {
354 PyAmplitude(KopfKMatrixF0::new(
355 name,
356 array::from_fn(|i| array::from_fn(|j| couplings[i][j].clone().0)),
357 channel,
358 &mass.0,
359 ))
360}
361
362#[derive(Clone, Serialize, Deserialize)]
367pub struct KopfKMatrixF2 {
368 name: String,
369 channel: usize,
370 mass: Mass,
371 constants: FixedKMatrix<4, 4>,
372 couplings_real: [ParameterLike; 4],
373 couplings_imag: [ParameterLike; 4],
374 couplings_indices_real: [ParameterID; 4],
375 couplings_indices_imag: [ParameterID; 4],
376 ikc_cache_index: ComplexVectorID<4>,
377 p_vec_cache_index: MatrixID<4, 4>,
378}
379
380impl KopfKMatrixF2 {
381 pub fn new(
398 name: &str,
399 couplings: [[ParameterLike; 2]; 4],
400 channel: usize,
401 mass: &Mass,
402 ) -> Box<Self> {
403 let mut couplings_real: [ParameterLike; 4] = array::from_fn(|_| ParameterLike::default());
404 let mut couplings_imag: [ParameterLike; 4] = array::from_fn(|_| ParameterLike::default());
405 for i in 0..4 {
406 couplings_real[i] = couplings[i][0].clone();
407 couplings_imag[i] = couplings[i][1].clone();
408 }
409 Self {
410 name: name.to_string(),
411 channel,
412 mass: mass.clone(),
413 constants: FixedKMatrix {
414 g: matrix![
415 0.40033, 0.01820, -0.06709, -0.49924;
416 0.15479, 0.17300, 0.22941, 0.19295;
417 -0.08900, 0.32393, -0.43133, 0.27975;
418 -0.00113, 0.15256, 0.23721, -0.03987
419 ],
420 c: matrix![
421 -0.04319, 0.00000, 0.00984, 0.01028;
422 0.00000, 0.00000, 0.00000, 0.00000;
423 0.00984, 0.00000, -0.07344, 0.05533;
424 0.01028, 0.00000, 0.05533, -0.05183
425 ],
426 m1s: vector![0.1349768, 2.0 * 0.1349768, 0.493677, 0.547862],
427 m2s: vector![0.1349768, 2.0 * 0.1349768, 0.497611, 0.547862],
428 mrs: vector![1.15299, 1.48359, 1.72923, 1.96700],
429 adler_zero: None,
430 l: 2,
431 },
432 couplings_real,
433 couplings_imag,
434 couplings_indices_real: [ParameterID::default(); 4],
435 couplings_indices_imag: [ParameterID::default(); 4],
436 ikc_cache_index: ComplexVectorID::default(),
437 p_vec_cache_index: MatrixID::default(),
438 }
439 .into()
440 }
441}
442
443#[typetag::serde]
444impl Amplitude for KopfKMatrixF2 {
445 fn register(&mut self, resources: &mut Resources) -> Result<AmplitudeID, LadduError> {
446 for i in 0..self.couplings_indices_real.len() {
447 self.couplings_indices_real[i] = resources.register_parameter(&self.couplings_real[i]);
448 self.couplings_indices_imag[i] = resources.register_parameter(&self.couplings_imag[i]);
449 }
450 self.ikc_cache_index = resources
451 .register_complex_vector(Some(&format!("KopfKMatrixF2<{}> ikc_vec", self.name)));
452 self.p_vec_cache_index =
453 resources.register_matrix(Some(&format!("KopfKMatrixF2<{}> p_vec", self.name)));
454 resources.register_amplitude(&self.name)
455 }
456
457 fn precompute(&self, event: &Event, cache: &mut Cache) {
458 let s = self.mass.value(event).powi(2);
459 cache.store_complex_vector(
460 self.ikc_cache_index,
461 self.constants.ikc_inv_vec(s, self.channel),
462 );
463 cache.store_matrix(self.p_vec_cache_index, self.constants.p_vec_constants(s));
464 }
465
466 fn compute(&self, parameters: &Parameters, _event: &Event, cache: &Cache) -> Complex<Float> {
467 let betas = SVector::from_fn(|i, _| {
468 Complex::new(
469 parameters.get(self.couplings_indices_real[i]),
470 parameters.get(self.couplings_indices_imag[i]),
471 )
472 });
473 let ikc_inv_vec = cache.get_complex_vector(self.ikc_cache_index);
474 let p_vec_constants = cache.get_matrix(self.p_vec_cache_index);
475 FixedKMatrix::compute(&betas, &ikc_inv_vec, &p_vec_constants)
476 }
477
478 fn compute_gradient(
479 &self,
480 _parameters: &Parameters,
481 _event: &Event,
482 cache: &Cache,
483 gradient: &mut DVector<Complex<Float>>,
484 ) {
485 let ikc_inv_vec = cache.get_complex_vector(self.ikc_cache_index);
486 let p_vec_constants = cache.get_matrix(self.p_vec_cache_index);
487 let internal_gradient = FixedKMatrix::compute_gradient(&ikc_inv_vec, &p_vec_constants);
488 for i in 0..4 {
489 if let ParameterID::Parameter(index) = self.couplings_indices_real[i] {
490 gradient[index] = internal_gradient[i];
491 }
492 if let ParameterID::Parameter(index) = self.couplings_indices_imag[i] {
493 gradient[index] = Complex::<Float>::I * internal_gradient[i];
494 }
495 }
496 }
497}
498
499#[cfg(feature = "python")]
551#[pyfunction(name = "KopfKMatrixF2")]
552pub fn py_kopf_kmatrix_f2(
553 name: &str,
554 couplings: [[PyParameterLike; 2]; 4],
555 channel: usize,
556 mass: PyMass,
557) -> PyAmplitude {
558 PyAmplitude(KopfKMatrixF2::new(
559 name,
560 array::from_fn(|i| array::from_fn(|j| couplings[i][j].clone().0)),
561 channel,
562 &mass.0,
563 ))
564}
565
566#[derive(Clone, Serialize, Deserialize)]
571pub struct KopfKMatrixA0 {
572 name: String,
573 channel: usize,
574 mass: Mass,
575 constants: FixedKMatrix<2, 2>,
576 couplings_real: [ParameterLike; 2],
577 couplings_imag: [ParameterLike; 2],
578 couplings_indices_real: [ParameterID; 2],
579 couplings_indices_imag: [ParameterID; 2],
580 ikc_cache_index: ComplexVectorID<2>,
581 p_vec_cache_index: MatrixID<2, 2>,
582}
583
584impl KopfKMatrixA0 {
585 pub fn new(
598 name: &str,
599 couplings: [[ParameterLike; 2]; 2],
600 channel: usize,
601 mass: &Mass,
602 ) -> Box<Self> {
603 let mut couplings_real: [ParameterLike; 2] = array::from_fn(|_| ParameterLike::default());
604 let mut couplings_imag: [ParameterLike; 2] = array::from_fn(|_| ParameterLike::default());
605 for i in 0..2 {
606 couplings_real[i] = couplings[i][0].clone();
607 couplings_imag[i] = couplings[i][1].clone();
608 }
609 Self {
610 name: name.to_string(),
611 channel,
612 mass: mass.clone(),
613 constants: FixedKMatrix {
614 g: matrix![
615 0.43215, 0.19000;
616 -0.28825, 0.43372
617 ],
618 c: matrix![
619 0.00000, 0.00000;
620 0.00000, 0.00000
621 ],
622 m1s: vector![0.1349768, 0.493677],
623 m2s: vector![0.547862, 0.497611],
624 mrs: vector![0.95395, 1.26767],
625 adler_zero: None,
626 l: 0,
627 },
628 couplings_real,
629 couplings_imag,
630 couplings_indices_real: [ParameterID::default(); 2],
631 couplings_indices_imag: [ParameterID::default(); 2],
632 ikc_cache_index: ComplexVectorID::default(),
633 p_vec_cache_index: MatrixID::default(),
634 }
635 .into()
636 }
637}
638
639#[typetag::serde]
640impl Amplitude for KopfKMatrixA0 {
641 fn register(&mut self, resources: &mut Resources) -> Result<AmplitudeID, LadduError> {
642 for i in 0..self.couplings_indices_real.len() {
643 self.couplings_indices_real[i] = resources.register_parameter(&self.couplings_real[i]);
644 self.couplings_indices_imag[i] = resources.register_parameter(&self.couplings_imag[i]);
645 }
646 self.ikc_cache_index = resources
647 .register_complex_vector(Some(&format!("KopfKMatrixA0<{}> ikc_vec", self.name)));
648 self.p_vec_cache_index =
649 resources.register_matrix(Some(&format!("KopfKMatrixA0<{}> p_vec", self.name)));
650 resources.register_amplitude(&self.name)
651 }
652
653 fn precompute(&self, event: &Event, cache: &mut Cache) {
654 let s = self.mass.value(event).powi(2);
655 cache.store_complex_vector(
656 self.ikc_cache_index,
657 self.constants.ikc_inv_vec(s, self.channel),
658 );
659 cache.store_matrix(self.p_vec_cache_index, self.constants.p_vec_constants(s));
660 }
661
662 fn compute(&self, parameters: &Parameters, _event: &Event, cache: &Cache) -> Complex<Float> {
663 let betas = SVector::from_fn(|i, _| {
664 Complex::new(
665 parameters.get(self.couplings_indices_real[i]),
666 parameters.get(self.couplings_indices_imag[i]),
667 )
668 });
669 let ikc_inv_vec = cache.get_complex_vector(self.ikc_cache_index);
670 let p_vec_constants = cache.get_matrix(self.p_vec_cache_index);
671 FixedKMatrix::compute(&betas, &ikc_inv_vec, &p_vec_constants)
672 }
673
674 fn compute_gradient(
675 &self,
676 _parameters: &Parameters,
677 _event: &Event,
678 cache: &Cache,
679 gradient: &mut DVector<Complex<Float>>,
680 ) {
681 let ikc_inv_vec = cache.get_complex_vector(self.ikc_cache_index);
682 let p_vec_constants = cache.get_matrix(self.p_vec_cache_index);
683 let internal_gradient = FixedKMatrix::compute_gradient(&ikc_inv_vec, &p_vec_constants);
684 for i in 0..2 {
685 if let ParameterID::Parameter(index) = self.couplings_indices_real[i] {
686 gradient[index] = internal_gradient[i];
687 }
688 if let ParameterID::Parameter(index) = self.couplings_indices_imag[i] {
689 gradient[index] = Complex::<Float>::I * internal_gradient[i];
690 }
691 }
692 }
693}
694
695#[cfg(feature = "python")]
739#[pyfunction(name = "KopfKMatrixA0")]
740pub fn py_kopf_kmatrix_a0(
741 name: &str,
742 couplings: [[PyParameterLike; 2]; 2],
743 channel: usize,
744 mass: PyMass,
745) -> PyAmplitude {
746 PyAmplitude(KopfKMatrixA0::new(
747 name,
748 array::from_fn(|i| array::from_fn(|j| couplings[i][j].clone().0)),
749 channel,
750 &mass.0,
751 ))
752}
753
754#[derive(Clone, Serialize, Deserialize)]
759pub struct KopfKMatrixA2 {
760 name: String,
761 channel: usize,
762 mass: Mass,
763 constants: FixedKMatrix<3, 2>,
764 couplings_real: [ParameterLike; 2],
765 couplings_imag: [ParameterLike; 2],
766 couplings_indices_real: [ParameterID; 2],
767 couplings_indices_imag: [ParameterID; 2],
768 ikc_cache_index: ComplexVectorID<3>,
769 p_vec_cache_index: MatrixID<3, 2>,
770}
771
772impl KopfKMatrixA2 {
773 pub fn new(
787 name: &str,
788 couplings: [[ParameterLike; 2]; 2],
789 channel: usize,
790 mass: &Mass,
791 ) -> Box<Self> {
792 let mut couplings_real: [ParameterLike; 2] = array::from_fn(|_| ParameterLike::default());
793 let mut couplings_imag: [ParameterLike; 2] = array::from_fn(|_| ParameterLike::default());
794 for i in 0..2 {
795 couplings_real[i] = couplings[i][0].clone();
796 couplings_imag[i] = couplings[i][1].clone();
797 }
798 Self {
799 name: name.to_string(),
800 channel,
801 mass: mass.clone(),
802 constants: FixedKMatrix {
803 g: matrix![
804 0.30073, 0.68567;
805 0.21426, 0.12543;
806 -0.09162, 0.00184
807
808 ],
809 c: matrix![
810 -0.40184, 0.00033, -0.08707;
811 0.00033, -0.21416, -0.06193;
812 -0.08707, -0.06193, -0.17435
813 ],
814 m1s: vector![0.1349768, 0.493677, 0.1349768],
815 m2s: vector![0.547862, 0.497611, 0.95778],
816 mrs: vector![1.30080, 1.75351],
817 adler_zero: None,
818 l: 2,
819 },
820 couplings_real,
821 couplings_imag,
822 couplings_indices_real: [ParameterID::default(); 2],
823 couplings_indices_imag: [ParameterID::default(); 2],
824 ikc_cache_index: ComplexVectorID::default(),
825 p_vec_cache_index: MatrixID::default(),
826 }
827 .into()
828 }
829}
830
831#[typetag::serde]
832impl Amplitude for KopfKMatrixA2 {
833 fn register(&mut self, resources: &mut Resources) -> Result<AmplitudeID, LadduError> {
834 for i in 0..self.couplings_indices_real.len() {
835 self.couplings_indices_real[i] = resources.register_parameter(&self.couplings_real[i]);
836 self.couplings_indices_imag[i] = resources.register_parameter(&self.couplings_imag[i]);
837 }
838 self.ikc_cache_index = resources
839 .register_complex_vector(Some(&format!("KopfKMatrixA2<{}> ikc_vec", self.name)));
840 self.p_vec_cache_index =
841 resources.register_matrix(Some(&format!("KopfKMatrixA2<{}> p_vec", self.name)));
842 resources.register_amplitude(&self.name)
843 }
844
845 fn precompute(&self, event: &Event, cache: &mut Cache) {
846 let s = self.mass.value(event).powi(2);
847 cache.store_complex_vector(
848 self.ikc_cache_index,
849 self.constants.ikc_inv_vec(s, self.channel),
850 );
851 cache.store_matrix(self.p_vec_cache_index, self.constants.p_vec_constants(s));
852 }
853
854 fn compute(&self, parameters: &Parameters, _event: &Event, cache: &Cache) -> Complex<Float> {
855 let betas = SVector::from_fn(|i, _| {
856 Complex::new(
857 parameters.get(self.couplings_indices_real[i]),
858 parameters.get(self.couplings_indices_imag[i]),
859 )
860 });
861 let ikc_inv_vec = cache.get_complex_vector(self.ikc_cache_index);
862 let p_vec_constants = cache.get_matrix(self.p_vec_cache_index);
863 FixedKMatrix::compute(&betas, &ikc_inv_vec, &p_vec_constants)
864 }
865
866 fn compute_gradient(
867 &self,
868 _parameters: &Parameters,
869 _event: &Event,
870 cache: &Cache,
871 gradient: &mut DVector<Complex<Float>>,
872 ) {
873 let ikc_inv_vec = cache.get_complex_vector(self.ikc_cache_index);
874 let p_vec_constants = cache.get_matrix(self.p_vec_cache_index);
875 let internal_gradient = FixedKMatrix::compute_gradient(&ikc_inv_vec, &p_vec_constants);
876 for i in 0..2 {
877 if let ParameterID::Parameter(index) = self.couplings_indices_real[i] {
878 gradient[index] = internal_gradient[i];
879 }
880 if let ParameterID::Parameter(index) = self.couplings_indices_imag[i] {
881 gradient[index] = Complex::<Float>::I * internal_gradient[i];
882 }
883 }
884 }
885}
886
887#[cfg(feature = "python")]
933#[pyfunction(name = "KopfKMatrixA2")]
934pub fn py_kopf_kmatrix_a2(
935 name: &str,
936 couplings: [[PyParameterLike; 2]; 2],
937 channel: usize,
938 mass: PyMass,
939) -> PyAmplitude {
940 PyAmplitude(KopfKMatrixA2::new(
941 name,
942 array::from_fn(|i| array::from_fn(|j| couplings[i][j].clone().0)),
943 channel,
944 &mass.0,
945 ))
946}
947
948#[derive(Clone, Serialize, Deserialize)]
953pub struct KopfKMatrixRho {
954 name: String,
955 channel: usize,
956 mass: Mass,
957 constants: FixedKMatrix<3, 2>,
958 couplings_real: [ParameterLike; 2],
959 couplings_imag: [ParameterLike; 2],
960 couplings_indices_real: [ParameterID; 2],
961 couplings_indices_imag: [ParameterID; 2],
962 ikc_cache_index: ComplexVectorID<3>,
963 p_vec_cache_index: MatrixID<3, 2>,
964}
965
966impl KopfKMatrixRho {
967 pub fn new(
981 name: &str,
982 couplings: [[ParameterLike; 2]; 2],
983 channel: usize,
984 mass: &Mass,
985 ) -> Box<Self> {
986 let mut couplings_real: [ParameterLike; 2] = array::from_fn(|_| ParameterLike::default());
987 let mut couplings_imag: [ParameterLike; 2] = array::from_fn(|_| ParameterLike::default());
988 for i in 0..2 {
989 couplings_real[i] = couplings[i][0].clone();
990 couplings_imag[i] = couplings[i][1].clone();
991 }
992 Self {
993 name: name.to_string(),
994 channel,
995 mass: mass.clone(),
996 constants: FixedKMatrix {
997 g: matrix![
998 0.28023, 0.16318;
999 0.01806, 0.53879;
1000 0.06501, 0.00495
1001 ],
1002 c: matrix![
1003 -0.06948, 0.00000, 0.07958;
1004 0.00000, 0.00000, 0.00000;
1005 0.07958, 0.00000, -0.60000
1006 ],
1007 m1s: vector![0.1349768, 2.0 * 0.1349768, 0.493677],
1008 m2s: vector![0.1349768, 2.0 * 0.1349768, 0.497611],
1009 mrs: vector![0.71093, 1.58660],
1010 adler_zero: None,
1011 l: 1,
1012 },
1013 couplings_real,
1014 couplings_imag,
1015 couplings_indices_real: [ParameterID::default(); 2],
1016 couplings_indices_imag: [ParameterID::default(); 2],
1017 ikc_cache_index: ComplexVectorID::default(),
1018 p_vec_cache_index: MatrixID::default(),
1019 }
1020 .into()
1021 }
1022}
1023
1024#[typetag::serde]
1025impl Amplitude for KopfKMatrixRho {
1026 fn register(&mut self, resources: &mut Resources) -> Result<AmplitudeID, LadduError> {
1027 for i in 0..self.couplings_indices_real.len() {
1028 self.couplings_indices_real[i] = resources.register_parameter(&self.couplings_real[i]);
1029 self.couplings_indices_imag[i] = resources.register_parameter(&self.couplings_imag[i]);
1030 }
1031 self.ikc_cache_index = resources
1032 .register_complex_vector(Some(&format!("KopfKMatrixRho<{}> ikc_vec", self.name)));
1033 self.p_vec_cache_index =
1034 resources.register_matrix(Some(&format!("KopfKMatrixRho<{}> p_vec", self.name)));
1035 resources.register_amplitude(&self.name)
1036 }
1037
1038 fn precompute(&self, event: &Event, cache: &mut Cache) {
1039 let s = self.mass.value(event).powi(2);
1040 cache.store_complex_vector(
1041 self.ikc_cache_index,
1042 self.constants.ikc_inv_vec(s, self.channel),
1043 );
1044 cache.store_matrix(self.p_vec_cache_index, self.constants.p_vec_constants(s));
1045 }
1046
1047 fn compute(&self, parameters: &Parameters, _event: &Event, cache: &Cache) -> Complex<Float> {
1048 let betas = SVector::from_fn(|i, _| {
1049 Complex::new(
1050 parameters.get(self.couplings_indices_real[i]),
1051 parameters.get(self.couplings_indices_imag[i]),
1052 )
1053 });
1054 let ikc_inv_vec = cache.get_complex_vector(self.ikc_cache_index);
1055 let p_vec_constants = cache.get_matrix(self.p_vec_cache_index);
1056 FixedKMatrix::compute(&betas, &ikc_inv_vec, &p_vec_constants)
1057 }
1058
1059 fn compute_gradient(
1060 &self,
1061 _parameters: &Parameters,
1062 _event: &Event,
1063 cache: &Cache,
1064 gradient: &mut DVector<Complex<Float>>,
1065 ) {
1066 let ikc_inv_vec = cache.get_complex_vector(self.ikc_cache_index);
1067 let p_vec_constants = cache.get_matrix(self.p_vec_cache_index);
1068 let internal_gradient = FixedKMatrix::compute_gradient(&ikc_inv_vec, &p_vec_constants);
1069 for i in 0..2 {
1070 if let ParameterID::Parameter(index) = self.couplings_indices_real[i] {
1071 gradient[index] = internal_gradient[i];
1072 }
1073 if let ParameterID::Parameter(index) = self.couplings_indices_imag[i] {
1074 gradient[index] = Complex::<Float>::I * internal_gradient[i];
1075 }
1076 }
1077 }
1078}
1079
1080#[cfg(feature = "python")]
1126#[pyfunction(name = "KopfKMatrixRho")]
1127pub fn py_kopf_kmatrix_rho(
1128 name: &str,
1129 couplings: [[PyParameterLike; 2]; 2],
1130 channel: usize,
1131 mass: PyMass,
1132) -> PyAmplitude {
1133 PyAmplitude(KopfKMatrixRho::new(
1134 name,
1135 array::from_fn(|i| array::from_fn(|j| couplings[i][j].clone().0)),
1136 channel,
1137 &mass.0,
1138 ))
1139}
1140
1141#[derive(Clone, Serialize, Deserialize)]
1146pub struct KopfKMatrixPi1 {
1147 name: String,
1148 channel: usize,
1149 mass: Mass,
1150 constants: FixedKMatrix<2, 1>,
1151 couplings_real: [ParameterLike; 1],
1152 couplings_imag: [ParameterLike; 1],
1153 couplings_indices_real: [ParameterID; 1],
1154 couplings_indices_imag: [ParameterID; 1],
1155 ikc_cache_index: ComplexVectorID<2>,
1156 p_vec_cache_index: MatrixID<2, 1>,
1157}
1158
1159impl KopfKMatrixPi1 {
1160 pub fn new(
1172 name: &str,
1173 couplings: [[ParameterLike; 2]; 1],
1174 channel: usize,
1175 mass: &Mass,
1176 ) -> Box<Self> {
1177 let mut couplings_real: [ParameterLike; 1] = array::from_fn(|_| ParameterLike::default());
1178 let mut couplings_imag: [ParameterLike; 1] = array::from_fn(|_| ParameterLike::default());
1179 for i in 0..1 {
1180 couplings_real[i] = couplings[i][0].clone();
1181 couplings_imag[i] = couplings[i][1].clone();
1182 }
1183 Self {
1184 name: name.to_string(),
1185 channel,
1186 mass: mass.clone(),
1187 constants: FixedKMatrix {
1188 g: matrix![
1189 0.80564;
1190 1.04595
1191 ],
1192 c: matrix![
1193 1.05000, 0.15163;
1194 0.15163, -0.24611
1195 ],
1196 m1s: vector![0.1349768, 0.1349768],
1197 m2s: vector![0.547862, 0.95778],
1198 mrs: vector![1.38552],
1199 adler_zero: None,
1200 l: 1,
1201 },
1202 couplings_real,
1203 couplings_imag,
1204 couplings_indices_real: [ParameterID::default(); 1],
1205 couplings_indices_imag: [ParameterID::default(); 1],
1206 ikc_cache_index: ComplexVectorID::default(),
1207 p_vec_cache_index: MatrixID::default(),
1208 }
1209 .into()
1210 }
1211}
1212
1213#[typetag::serde]
1214impl Amplitude for KopfKMatrixPi1 {
1215 fn register(&mut self, resources: &mut Resources) -> Result<AmplitudeID, LadduError> {
1216 for i in 0..self.couplings_indices_real.len() {
1217 self.couplings_indices_real[i] = resources.register_parameter(&self.couplings_real[i]);
1218 self.couplings_indices_imag[i] = resources.register_parameter(&self.couplings_imag[i]);
1219 }
1220 self.ikc_cache_index = resources
1221 .register_complex_vector(Some(&format!("KopfKMatrixPi1<{}> ikc_vec", self.name)));
1222 self.p_vec_cache_index =
1223 resources.register_matrix(Some(&format!("KopfKMatrixPi1<{}> p_vec", self.name)));
1224 resources.register_amplitude(&self.name)
1225 }
1226
1227 fn precompute(&self, event: &Event, cache: &mut Cache) {
1228 let s = self.mass.value(event).powi(2);
1229 cache.store_complex_vector(
1230 self.ikc_cache_index,
1231 self.constants.ikc_inv_vec(s, self.channel),
1232 );
1233 cache.store_matrix(self.p_vec_cache_index, self.constants.p_vec_constants(s));
1234 }
1235
1236 fn compute(&self, parameters: &Parameters, _event: &Event, cache: &Cache) -> Complex<Float> {
1237 let betas = SVector::from_fn(|i, _| {
1238 Complex::new(
1239 parameters.get(self.couplings_indices_real[i]),
1240 parameters.get(self.couplings_indices_imag[i]),
1241 )
1242 });
1243 let ikc_inv_vec = cache.get_complex_vector(self.ikc_cache_index);
1244 let p_vec_constants = cache.get_matrix(self.p_vec_cache_index);
1245 FixedKMatrix::compute(&betas, &ikc_inv_vec, &p_vec_constants)
1246 }
1247
1248 fn compute_gradient(
1249 &self,
1250 _parameters: &Parameters,
1251 _event: &Event,
1252 cache: &Cache,
1253 gradient: &mut DVector<Complex<Float>>,
1254 ) {
1255 let ikc_inv_vec = cache.get_complex_vector(self.ikc_cache_index);
1256 let p_vec_constants = cache.get_matrix(self.p_vec_cache_index);
1257 let internal_gradient = FixedKMatrix::compute_gradient(&ikc_inv_vec, &p_vec_constants);
1258 if let ParameterID::Parameter(index) = self.couplings_indices_real[0] {
1259 gradient[index] = internal_gradient[0];
1260 }
1261 if let ParameterID::Parameter(index) = self.couplings_indices_imag[0] {
1262 gradient[index] = Complex::<Float>::I * internal_gradient[0];
1263 }
1264 }
1265}
1266
1267#[cfg(feature = "python")]
1309#[pyfunction(name = "KopfKMatrixPi1")]
1310pub fn py_kopf_kmatrix_pi1(
1311 name: &str,
1312 couplings: [[PyParameterLike; 2]; 1],
1313 channel: usize,
1314 mass: PyMass,
1315) -> PyAmplitude {
1316 PyAmplitude(KopfKMatrixPi1::new(
1317 name,
1318 array::from_fn(|i| array::from_fn(|j| couplings[i][j].clone().0)),
1319 channel,
1320 &mass.0,
1321 ))
1322}
1323
1324#[cfg(test)]
1325mod tests {
1326 use std::sync::Arc;
1328
1329 use super::*;
1330 use approx::assert_relative_eq;
1331 use laddu_core::{data::test_dataset, parameter, Manager, Mass};
1332
1333 #[test]
1334 fn test_f0_evaluation() {
1335 let mut manager = Manager::default();
1336 let res_mass = Mass::new([2, 3]);
1337 let amp = KopfKMatrixF0::new(
1338 "f0",
1339 [
1340 [parameter("p0"), parameter("p1")],
1341 [parameter("p2"), parameter("p3")],
1342 [parameter("p4"), parameter("p5")],
1343 [parameter("p6"), parameter("p7")],
1344 [parameter("p8"), parameter("p9")],
1345 ],
1346 1,
1347 &res_mass,
1348 );
1349 let aid = manager.register(amp).unwrap();
1350
1351 let dataset = Arc::new(test_dataset());
1352 let expr = aid.into();
1353 let model = manager.model(&expr);
1354 let evaluator = model.load(&dataset);
1355
1356 let result = evaluator.evaluate(&[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]);
1357
1358 assert_relative_eq!(result[0].re, 0.26749455, epsilon = Float::EPSILON.sqrt());
1359 assert_relative_eq!(result[0].im, 0.72894511, epsilon = Float::EPSILON.sqrt());
1360 }
1361
1362 #[test]
1363 fn test_f0_gradient() {
1364 let mut manager = Manager::default();
1365 let res_mass = Mass::new([2, 3]);
1366 let amp = KopfKMatrixF0::new(
1367 "f0",
1368 [
1369 [parameter("p0"), parameter("p1")],
1370 [parameter("p2"), parameter("p3")],
1371 [parameter("p4"), parameter("p5")],
1372 [parameter("p6"), parameter("p7")],
1373 [parameter("p8"), parameter("p9")],
1374 ],
1375 1,
1376 &res_mass,
1377 );
1378 let aid = manager.register(amp).unwrap();
1379
1380 let dataset = Arc::new(test_dataset());
1381 let expr = aid.into();
1382 let model = manager.model(&expr);
1383 let evaluator = model.load(&dataset);
1384
1385 let result =
1386 evaluator.evaluate_gradient(&[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]);
1387
1388 assert_relative_eq!(result[0][0].re, -0.0324912, epsilon = Float::EPSILON.cbrt());
1389 assert_relative_eq!(result[0][0].im, -0.0110734, epsilon = Float::EPSILON.cbrt());
1390 assert_relative_eq!(result[0][1].re, -result[0][0].im);
1391 assert_relative_eq!(result[0][1].im, result[0][0].re);
1392 assert_relative_eq!(result[0][2].re, 0.0241053, epsilon = Float::EPSILON.cbrt());
1393 assert_relative_eq!(result[0][2].im, 0.0079184, epsilon = Float::EPSILON.cbrt());
1394 assert_relative_eq!(result[0][3].re, -result[0][2].im);
1395 assert_relative_eq!(result[0][3].im, result[0][2].re);
1396 assert_relative_eq!(result[0][4].re, -0.0316345, epsilon = Float::EPSILON.cbrt());
1397 assert_relative_eq!(result[0][4].im, 0.0149155, epsilon = Float::EPSILON.cbrt());
1398 assert_relative_eq!(result[0][5].re, -result[0][4].im);
1399 assert_relative_eq!(result[0][5].im, result[0][4].re);
1400 assert_relative_eq!(result[0][6].re, 0.5838982, epsilon = Float::EPSILON.cbrt());
1401 assert_relative_eq!(result[0][6].im, 0.2071617, epsilon = Float::EPSILON.cbrt());
1402 assert_relative_eq!(result[0][7].re, -result[0][6].im);
1403 assert_relative_eq!(result[0][7].im, result[0][6].re);
1404 assert_relative_eq!(result[0][8].re, 0.0914546, epsilon = Float::EPSILON.cbrt());
1405 assert_relative_eq!(result[0][8].im, 0.0360771, epsilon = Float::EPSILON.cbrt());
1406 assert_relative_eq!(result[0][9].re, -result[0][8].im);
1407 assert_relative_eq!(result[0][9].im, result[0][8].re);
1408 }
1409
1410 #[test]
1411 fn test_f2_evaluation() {
1412 let mut manager = Manager::default();
1413 let res_mass = Mass::new([2, 3]);
1414 let amp = KopfKMatrixF2::new(
1415 "f2",
1416 [
1417 [parameter("p0"), parameter("p1")],
1418 [parameter("p2"), parameter("p3")],
1419 [parameter("p4"), parameter("p5")],
1420 [parameter("p6"), parameter("p7")],
1421 ],
1422 1,
1423 &res_mass,
1424 );
1425 let aid = manager.register(amp).unwrap();
1426
1427 let dataset = Arc::new(test_dataset());
1428 let expr = aid.into();
1429 let model = manager.model(&expr);
1430 let evaluator = model.load(&dataset);
1431
1432 let result = evaluator.evaluate(&[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]);
1433
1434 assert_relative_eq!(result[0].re, 0.02523304, epsilon = Float::EPSILON.sqrt());
1435 assert_relative_eq!(result[0].im, 0.39712393, epsilon = Float::EPSILON.sqrt());
1436 }
1437
1438 #[test]
1439 fn test_f2_gradient() {
1440 let mut manager = Manager::default();
1441 let res_mass = Mass::new([2, 3]);
1442 let amp = KopfKMatrixF2::new(
1443 "f2",
1444 [
1445 [parameter("p0"), parameter("p1")],
1446 [parameter("p2"), parameter("p3")],
1447 [parameter("p4"), parameter("p5")],
1448 [parameter("p6"), parameter("p7")],
1449 ],
1450 1,
1451 &res_mass,
1452 );
1453 let aid = manager.register(amp).unwrap();
1454
1455 let dataset = Arc::new(test_dataset());
1456 let expr = aid.into();
1457 let model = manager.model(&expr);
1458 let evaluator = model.load(&dataset);
1459
1460 let result = evaluator.evaluate_gradient(&[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]);
1461
1462 assert_relative_eq!(result[0][0].re, -0.3078948, epsilon = Float::EPSILON.cbrt());
1463 assert_relative_eq!(result[0][0].im, 0.3808689, epsilon = Float::EPSILON.cbrt());
1464 assert_relative_eq!(result[0][1].re, -result[0][0].im);
1465 assert_relative_eq!(result[0][1].im, result[0][0].re);
1466 assert_relative_eq!(result[0][2].re, 0.4290085, epsilon = Float::EPSILON.cbrt());
1467 assert_relative_eq!(result[0][2].im, 0.0799660, epsilon = Float::EPSILON.cbrt());
1468 assert_relative_eq!(result[0][3].re, -result[0][2].im);
1469 assert_relative_eq!(result[0][3].im, result[0][2].re);
1470 assert_relative_eq!(result[0][4].re, 0.1657487, epsilon = Float::EPSILON.cbrt());
1471 assert_relative_eq!(result[0][4].im, -0.0041382, epsilon = Float::EPSILON.cbrt());
1472 assert_relative_eq!(result[0][5].re, -result[0][4].im);
1473 assert_relative_eq!(result[0][5].im, result[0][4].re);
1474 assert_relative_eq!(result[0][6].re, 0.0594691, epsilon = Float::EPSILON.cbrt());
1475 assert_relative_eq!(result[0][6].im, 0.1143819, epsilon = Float::EPSILON.cbrt());
1476 assert_relative_eq!(result[0][7].re, -result[0][6].im);
1477 assert_relative_eq!(result[0][7].im, result[0][6].re);
1478 }
1479
1480 #[test]
1481 fn test_a0_evaluation() {
1482 let mut manager = Manager::default();
1483 let res_mass = Mass::new([2, 3]);
1484 let amp = KopfKMatrixA0::new(
1485 "a0",
1486 [
1487 [parameter("p0"), parameter("p1")],
1488 [parameter("p2"), parameter("p3")],
1489 ],
1490 1,
1491 &res_mass,
1492 );
1493 let aid = manager.register(amp).unwrap();
1494
1495 let dataset = Arc::new(test_dataset());
1496 let expr = aid.into();
1497 let model = manager.model(&expr);
1498 let evaluator = model.load(&dataset);
1499
1500 let result = evaluator.evaluate(&[0.1, 0.2, 0.3, 0.4]);
1501
1502 assert_relative_eq!(result[0].re, -0.80027591, epsilon = Float::EPSILON.sqrt());
1503 assert_relative_eq!(result[0].im, -0.13593066, epsilon = Float::EPSILON.sqrt());
1504 }
1505
1506 #[test]
1507 fn test_a0_gradient() {
1508 let mut manager = Manager::default();
1509 let res_mass = Mass::new([2, 3]);
1510 let amp = KopfKMatrixA0::new(
1511 "a0",
1512 [
1513 [parameter("p0"), parameter("p1")],
1514 [parameter("p2"), parameter("p3")],
1515 ],
1516 1,
1517 &res_mass,
1518 );
1519 let aid = manager.register(amp).unwrap();
1520
1521 let dataset = Arc::new(test_dataset());
1522 let expr = aid.into();
1523 let model = manager.model(&expr);
1524 let evaluator = model.load(&dataset);
1525
1526 let result = evaluator.evaluate_gradient(&[0.1, 0.2, 0.3, 0.4]);
1527
1528 assert_relative_eq!(result[0][0].re, 0.2906192, epsilon = Float::EPSILON.cbrt());
1529 assert_relative_eq!(result[0][0].im, -0.0998906, epsilon = Float::EPSILON.cbrt());
1530 assert_relative_eq!(result[0][1].re, -result[0][0].im);
1531 assert_relative_eq!(result[0][1].im, result[0][0].re);
1532 assert_relative_eq!(result[0][2].re, -1.3136838, epsilon = Float::EPSILON.cbrt());
1533 assert_relative_eq!(result[0][2].im, 1.1380269, epsilon = Float::EPSILON.cbrt());
1534 assert_relative_eq!(result[0][3].re, -result[0][2].im);
1535 assert_relative_eq!(result[0][3].im, result[0][2].re);
1536 }
1537
1538 #[test]
1539 fn test_a2_evaluation() {
1540 let mut manager = Manager::default();
1541 let res_mass = Mass::new([2, 3]);
1542 let amp = KopfKMatrixA2::new(
1543 "a2",
1544 [
1545 [parameter("p0"), parameter("p1")],
1546 [parameter("p2"), parameter("p3")],
1547 ],
1548 1,
1549 &res_mass,
1550 );
1551 let aid = manager.register(amp).unwrap();
1552
1553 let dataset = Arc::new(test_dataset());
1554 let expr = aid.into();
1555 let model = manager.model(&expr);
1556 let evaluator = model.load(&dataset);
1557
1558 let result = evaluator.evaluate(&[0.1, 0.2, 0.3, 0.4]);
1559
1560 assert_relative_eq!(result[0].re, -0.20926617, epsilon = Float::EPSILON.sqrt());
1561 assert_relative_eq!(result[0].im, -0.0985062, epsilon = Float::EPSILON.sqrt());
1562 }
1563
1564 #[test]
1565 fn test_a2_gradient() {
1566 let mut manager = Manager::default();
1567 let res_mass = Mass::new([2, 3]);
1568 let amp = KopfKMatrixA2::new(
1569 "a2",
1570 [
1571 [parameter("p0"), parameter("p1")],
1572 [parameter("p2"), parameter("p3")],
1573 ],
1574 1,
1575 &res_mass,
1576 );
1577 let aid = manager.register(amp).unwrap();
1578
1579 let dataset = Arc::new(test_dataset());
1580 let expr = aid.into();
1581 let model = manager.model(&expr);
1582 let evaluator = model.load(&dataset);
1583
1584 let result = evaluator.evaluate_gradient(&[0.1, 0.2, 0.3, 0.4]);
1585
1586 assert_relative_eq!(result[0][0].re, -0.5756896, epsilon = Float::EPSILON.cbrt());
1587 assert_relative_eq!(result[0][0].im, 0.9398863, epsilon = Float::EPSILON.cbrt());
1588 assert_relative_eq!(result[0][1].re, -result[0][0].im);
1589 assert_relative_eq!(result[0][1].im, result[0][0].re);
1590 assert_relative_eq!(result[0][2].re, -0.0811143, epsilon = Float::EPSILON.cbrt());
1591 assert_relative_eq!(result[0][2].im, -0.1522787, epsilon = Float::EPSILON.cbrt());
1592 assert_relative_eq!(result[0][3].re, -result[0][2].im);
1593 assert_relative_eq!(result[0][3].im, result[0][2].re);
1594 }
1595
1596 #[test]
1597 fn test_rho_evaluation() {
1598 let mut manager = Manager::default();
1599 let res_mass = Mass::new([2, 3]);
1600 let amp = KopfKMatrixRho::new(
1601 "rho",
1602 [
1603 [parameter("p0"), parameter("p1")],
1604 [parameter("p2"), parameter("p3")],
1605 ],
1606 1,
1607 &res_mass,
1608 );
1609 let aid = manager.register(amp).unwrap();
1610
1611 let dataset = Arc::new(test_dataset());
1612 let expr = aid.into();
1613 let model = manager.model(&expr);
1614 let evaluator = model.load(&dataset);
1615
1616 let result = evaluator.evaluate(&[0.1, 0.2, 0.3, 0.4]);
1617
1618 assert_relative_eq!(result[0].re, 0.09483558, epsilon = Float::EPSILON.sqrt());
1619 assert_relative_eq!(result[0].im, 0.26091837, epsilon = Float::EPSILON.sqrt());
1620 }
1621
1622 #[test]
1623 fn test_rho_gradient() {
1624 let mut manager = Manager::default();
1625 let res_mass = Mass::new([2, 3]);
1626 let amp = KopfKMatrixRho::new(
1627 "rho",
1628 [
1629 [parameter("p0"), parameter("p1")],
1630 [parameter("p2"), parameter("p3")],
1631 ],
1632 1,
1633 &res_mass,
1634 );
1635 let aid = manager.register(amp).unwrap();
1636
1637 let dataset = Arc::new(test_dataset());
1638 let expr = aid.into();
1639 let model = manager.model(&expr);
1640 let evaluator = model.load(&dataset);
1641
1642 let result = evaluator.evaluate_gradient(&[0.1, 0.2, 0.3, 0.4]);
1643
1644 assert_relative_eq!(result[0][0].re, 0.0265203, epsilon = Float::EPSILON.cbrt());
1645 assert_relative_eq!(result[0][0].im, -0.0266026, epsilon = Float::EPSILON.cbrt());
1646 assert_relative_eq!(result[0][1].re, -result[0][0].im);
1647 assert_relative_eq!(result[0][1].im, result[0][0].re);
1648 assert_relative_eq!(result[0][2].re, 0.5172379, epsilon = Float::EPSILON.cbrt());
1649 assert_relative_eq!(result[0][2].im, 0.1707373, epsilon = Float::EPSILON.cbrt());
1650 assert_relative_eq!(result[0][3].re, -result[0][2].im);
1651 assert_relative_eq!(result[0][3].im, result[0][2].re);
1652 }
1653
1654 #[test]
1655 fn test_pi1_evaluation() {
1656 let mut manager = Manager::default();
1657 let res_mass = Mass::new([2, 3]);
1658 let amp = KopfKMatrixPi1::new("pi1", [[parameter("p0"), parameter("p1")]], 1, &res_mass);
1659 let aid = manager.register(amp).unwrap();
1660
1661 let dataset = Arc::new(test_dataset());
1662 let expr = aid.into();
1663 let model = manager.model(&expr);
1664 let evaluator = model.load(&dataset);
1665
1666 let result = evaluator.evaluate(&[0.1, 0.2]);
1667
1668 assert_relative_eq!(result[0].re, -0.11017586, epsilon = Float::EPSILON.sqrt());
1669 assert_relative_eq!(result[0].im, 0.26387172, epsilon = Float::EPSILON.sqrt());
1670 }
1671
1672 #[test]
1673 fn test_pi1_gradient() {
1674 let mut manager = Manager::default();
1675 let res_mass = Mass::new([2, 3]);
1676 let amp = KopfKMatrixPi1::new("pi1", [[parameter("p0"), parameter("p1")]], 1, &res_mass);
1677 let aid = manager.register(amp).unwrap();
1678
1679 let dataset = Arc::new(test_dataset());
1680 let expr = aid.into();
1681 let model = manager.model(&expr);
1682 let evaluator = model.load(&dataset);
1683
1684 let result = evaluator.evaluate_gradient(&[0.1, 0.2]);
1685
1686 assert_relative_eq!(
1687 result[0][0].re,
1688 -14.7987174,
1689 epsilon = Float::EPSILON.cbrt()
1690 );
1691 assert_relative_eq!(result[0][0].im, -5.8430094, epsilon = Float::EPSILON.cbrt());
1692 assert_relative_eq!(result[0][1].re, -result[0][0].im);
1693 assert_relative_eq!(result[0][1].im, result[0][0].re);
1694 }
1695}