laddu_python/utils/variables.rs
1use crate::data::{PyDataset, PyEvent};
2use laddu_core::{
3 data::{Dataset, Event},
4 traits::Variable,
5 utils::variables::{
6 Angles, CosTheta, Mandelstam, Mass, Phi, PolAngle, PolMagnitude, Polarization,
7 VariableExpression,
8 },
9 Float,
10};
11use numpy::PyArray1;
12use pyo3::prelude::*;
13use serde::{Deserialize, Serialize};
14use std::fmt::{Debug, Display};
15
16#[derive(FromPyObject, Clone, Serialize, Deserialize)]
17pub enum PyVariable {
18 #[pyo3(transparent)]
19 Mass(PyMass),
20 #[pyo3(transparent)]
21 CosTheta(PyCosTheta),
22 #[pyo3(transparent)]
23 Phi(PyPhi),
24 #[pyo3(transparent)]
25 PolAngle(PyPolAngle),
26 #[pyo3(transparent)]
27 PolMagnitude(PyPolMagnitude),
28 #[pyo3(transparent)]
29 Mandelstam(PyMandelstam),
30}
31
32impl Debug for PyVariable {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 match self {
35 Self::Mass(v) => write!(f, "{:?}", v.0),
36 Self::CosTheta(v) => write!(f, "{:?}", v.0),
37 Self::Phi(v) => write!(f, "{:?}", v.0),
38 Self::PolAngle(v) => write!(f, "{:?}", v.0),
39 Self::PolMagnitude(v) => write!(f, "{:?}", v.0),
40 Self::Mandelstam(v) => write!(f, "{:?}", v.0),
41 }
42 }
43}
44impl Display for PyVariable {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 match self {
47 Self::Mass(v) => write!(f, "{}", v.0),
48 Self::CosTheta(v) => write!(f, "{}", v.0),
49 Self::Phi(v) => write!(f, "{}", v.0),
50 Self::PolAngle(v) => write!(f, "{}", v.0),
51 Self::PolMagnitude(v) => write!(f, "{}", v.0),
52 Self::Mandelstam(v) => write!(f, "{}", v.0),
53 }
54 }
55}
56
57#[pyclass(name = "VariableExpression", module = "laddu")]
58pub struct PyVariableExpression(pub VariableExpression);
59
60#[pymethods]
61impl PyVariableExpression {
62 fn __and__(&self, rhs: &PyVariableExpression) -> PyVariableExpression {
63 PyVariableExpression(self.0.clone() & rhs.0.clone())
64 }
65 fn __or__(&self, rhs: &PyVariableExpression) -> PyVariableExpression {
66 PyVariableExpression(self.0.clone() | rhs.0.clone())
67 }
68 fn __invert__(&self) -> PyVariableExpression {
69 PyVariableExpression(!self.0.clone())
70 }
71 fn __str__(&self) -> String {
72 format!("{}", self.0)
73 }
74}
75
76/// The invariant mass of an arbitrary combination of constituent particles in an Event
77///
78/// This variable is calculated by summing up the 4-momenta of each particle listed by index in
79/// `constituents` and taking the invariant magnitude of the resulting 4-vector.
80///
81/// Parameters
82/// ----------
83/// constituents : list of int
84/// The indices of particles to combine to create the final 4-momentum
85///
86/// See Also
87/// --------
88/// laddu.utils.vectors.Vec4.m
89///
90#[pyclass(name = "Mass", module = "laddu")]
91#[derive(Clone, Serialize, Deserialize)]
92pub struct PyMass(pub Mass);
93
94#[pymethods]
95impl PyMass {
96 #[new]
97 fn new(constituents: Vec<usize>) -> Self {
98 Self(Mass::new(&constituents))
99 }
100 /// The value of this Variable for the given Event
101 ///
102 /// Parameters
103 /// ----------
104 /// event : Event
105 /// The Event upon which the Variable is calculated
106 ///
107 /// Returns
108 /// -------
109 /// value : float
110 /// The value of the Variable for the given `event`
111 ///
112 fn value(&self, event: &PyEvent) -> Float {
113 self.0.value(&event.0)
114 }
115 /// All values of this Variable on the given Dataset
116 ///
117 /// Parameters
118 /// ----------
119 /// dataset : Dataset
120 /// The Dataset upon which the Variable is calculated
121 ///
122 /// Returns
123 /// -------
124 /// values : array_like
125 /// The values of the Variable for each Event in the given `dataset`
126 ///
127 fn value_on<'py>(&self, py: Python<'py>, dataset: &PyDataset) -> Bound<'py, PyArray1<Float>> {
128 PyArray1::from_slice(py, &self.0.value_on(&dataset.0))
129 }
130 fn __eq__(&self, value: Float) -> PyVariableExpression {
131 PyVariableExpression(self.0.eq(value))
132 }
133 fn __lt__(&self, value: Float) -> PyVariableExpression {
134 PyVariableExpression(self.0.lt(value))
135 }
136 fn __gt__(&self, value: Float) -> PyVariableExpression {
137 PyVariableExpression(self.0.gt(value))
138 }
139 fn __le__(&self, value: Float) -> PyVariableExpression {
140 PyVariableExpression(self.0.le(value))
141 }
142 fn __ge__(&self, value: Float) -> PyVariableExpression {
143 PyVariableExpression(self.0.ge(value))
144 }
145 fn __repr__(&self) -> String {
146 format!("{:?}", self.0)
147 }
148 fn __str__(&self) -> String {
149 format!("{}", self.0)
150 }
151}
152
153/// The cosine of the polar decay angle in the rest frame of the given `resonance`
154///
155/// This Variable is calculated by forming the given frame (helicity or Gottfried-Jackson) and
156/// calculating the spherical angles according to one of the decaying `daughter` particles.
157///
158/// The helicity frame is defined in terms of the following Cartesian axes in the rest frame of
159/// the `resonance`:
160///
161/// .. math:: \hat{z} \propto -\vec{p}'_{\text{recoil}}
162/// .. math:: \hat{y} \propto \vec{p}_{\text{beam}} \times (-\vec{p}_{\text{recoil}})
163/// .. math:: \hat{x} = \hat{y} \times \hat{z}
164///
165/// where primed vectors are in the rest frame of the `resonance` and unprimed vectors are in
166/// the center-of-momentum frame.
167///
168/// The Gottfried-Jackson frame differs only in the definition of :math:`\hat{z}`:
169///
170/// .. math:: \hat{z} \propto \vec{p}'_{\text{beam}}
171///
172/// Parameters
173/// ----------
174/// beam : int
175/// The index of the `beam` particle
176/// recoil : list of int
177/// Indices of particles which are combined to form the recoiling particle (particles which
178/// are not `beam` or part of the `resonance`)
179/// daughter : list of int
180/// Indices of particles which are combined to form one of the decay products of the
181/// `resonance`
182/// resonance : list of int
183/// Indices of particles which are combined to form the `resonance`
184/// frame : {'Helicity', 'HX', 'HEL', 'GottfriedJackson', 'Gottfried Jackson', 'GJ', 'Gottfried-Jackson'}
185/// The frame to use in the calculation
186///
187/// Raises
188/// ------
189/// ValueError
190/// If `frame` is not one of the valid options
191///
192/// See Also
193/// --------
194/// laddu.utils.vectors.Vec3.costheta
195///
196#[pyclass(name = "CosTheta", module = "laddu")]
197#[derive(Clone, Serialize, Deserialize)]
198pub struct PyCosTheta(pub CosTheta);
199
200#[pymethods]
201impl PyCosTheta {
202 #[new]
203 #[pyo3(signature=(beam, recoil, daughter, resonance, frame="Helicity"))]
204 fn new(
205 beam: usize,
206 recoil: Vec<usize>,
207 daughter: Vec<usize>,
208 resonance: Vec<usize>,
209 frame: &str,
210 ) -> PyResult<Self> {
211 Ok(Self(CosTheta::new(
212 beam,
213 &recoil,
214 &daughter,
215 &resonance,
216 frame.parse()?,
217 )))
218 }
219 /// The value of this Variable for the given Event
220 ///
221 /// Parameters
222 /// ----------
223 /// event : Event
224 /// The Event upon which the Variable is calculated
225 ///
226 /// Returns
227 /// -------
228 /// value : float
229 /// The value of the Variable for the given `event`
230 ///
231 fn value(&self, event: &PyEvent) -> Float {
232 self.0.value(&event.0)
233 }
234 /// All values of this Variable on the given Dataset
235 ///
236 /// Parameters
237 /// ----------
238 /// dataset : Dataset
239 /// The Dataset upon which the Variable is calculated
240 ///
241 /// Returns
242 /// -------
243 /// values : array_like
244 /// The values of the Variable for each Event in the given `dataset`
245 ///
246 fn value_on<'py>(&self, py: Python<'py>, dataset: &PyDataset) -> Bound<'py, PyArray1<Float>> {
247 PyArray1::from_slice(py, &self.0.value_on(&dataset.0))
248 }
249 fn __eq__(&self, value: Float) -> PyVariableExpression {
250 PyVariableExpression(self.0.eq(value))
251 }
252 fn __lt__(&self, value: Float) -> PyVariableExpression {
253 PyVariableExpression(self.0.lt(value))
254 }
255 fn __gt__(&self, value: Float) -> PyVariableExpression {
256 PyVariableExpression(self.0.gt(value))
257 }
258 fn __le__(&self, value: Float) -> PyVariableExpression {
259 PyVariableExpression(self.0.le(value))
260 }
261 fn __ge__(&self, value: Float) -> PyVariableExpression {
262 PyVariableExpression(self.0.ge(value))
263 }
264 fn __repr__(&self) -> String {
265 format!("{:?}", self.0)
266 }
267 fn __str__(&self) -> String {
268 format!("{}", self.0)
269 }
270}
271
272/// The aziumuthal decay angle in the rest frame of the given `resonance`
273///
274/// This Variable is calculated by forming the given frame (helicity or Gottfried-Jackson) and
275/// calculating the spherical angles according to one of the decaying `daughter` particles.
276///
277/// The helicity frame is defined in terms of the following Cartesian axes in the rest frame of
278/// the `resonance`:
279///
280/// .. math:: \hat{z} \propto -\vec{p}'_{\text{recoil}}
281/// .. math:: \hat{y} \propto \vec{p}_{\text{beam}} \times (-\vec{p}_{\text{recoil}})
282/// .. math:: \hat{x} = \hat{y} \times \hat{z}
283///
284/// where primed vectors are in the rest frame of the `resonance` and unprimed vectors are in
285/// the center-of-momentum frame.
286///
287/// The Gottfried-Jackson frame differs only in the definition of :math:`\hat{z}`:
288///
289/// .. math:: \hat{z} \propto \vec{p}'_{\text{beam}}
290///
291/// Parameters
292/// ----------
293/// beam : int
294/// The index of the `beam` particle
295/// recoil : list of int
296/// Indices of particles which are combined to form the recoiling particle (particles which
297/// are not `beam` or part of the `resonance`)
298/// daughter : list of int
299/// Indices of particles which are combined to form one of the decay products of the
300/// `resonance`
301/// resonance : list of int
302/// Indices of particles which are combined to form the `resonance`
303/// frame : {'Helicity', 'HX', 'HEL', 'GottfriedJackson', 'Gottfried Jackson', 'GJ', 'Gottfried-Jackson'}
304/// The frame to use in the calculation
305///
306/// Raises
307/// ------
308/// ValueError
309/// If `frame` is not one of the valid options
310///
311///
312/// See Also
313/// --------
314/// laddu.utils.vectors.Vec3.phi
315///
316#[pyclass(name = "Phi", module = "laddu")]
317#[derive(Clone, Serialize, Deserialize)]
318pub struct PyPhi(pub Phi);
319
320#[pymethods]
321impl PyPhi {
322 #[new]
323 #[pyo3(signature=(beam, recoil, daughter, resonance, frame="Helicity"))]
324 fn new(
325 beam: usize,
326 recoil: Vec<usize>,
327 daughter: Vec<usize>,
328 resonance: Vec<usize>,
329 frame: &str,
330 ) -> PyResult<Self> {
331 Ok(Self(Phi::new(
332 beam,
333 &recoil,
334 &daughter,
335 &resonance,
336 frame.parse()?,
337 )))
338 }
339 /// The value of this Variable for the given Event
340 ///
341 /// Parameters
342 /// ----------
343 /// event : Event
344 /// The Event upon which the Variable is calculated
345 ///
346 /// Returns
347 /// -------
348 /// value : float
349 /// The value of the Variable for the given `event`
350 ///
351 fn value(&self, event: &PyEvent) -> Float {
352 self.0.value(&event.0)
353 }
354 /// All values of this Variable on the given Dataset
355 ///
356 /// Parameters
357 /// ----------
358 /// dataset : Dataset
359 /// The Dataset upon which the Variable is calculated
360 ///
361 /// Returns
362 /// -------
363 /// values : array_like
364 /// The values of the Variable for each Event in the given `dataset`
365 ///
366 fn value_on<'py>(&self, py: Python<'py>, dataset: &PyDataset) -> Bound<'py, PyArray1<Float>> {
367 PyArray1::from_slice(py, &self.0.value_on(&dataset.0))
368 }
369 fn __eq__(&self, value: Float) -> PyVariableExpression {
370 PyVariableExpression(self.0.eq(value))
371 }
372 fn __lt__(&self, value: Float) -> PyVariableExpression {
373 PyVariableExpression(self.0.lt(value))
374 }
375 fn __gt__(&self, value: Float) -> PyVariableExpression {
376 PyVariableExpression(self.0.gt(value))
377 }
378 fn __le__(&self, value: Float) -> PyVariableExpression {
379 PyVariableExpression(self.0.le(value))
380 }
381 fn __ge__(&self, value: Float) -> PyVariableExpression {
382 PyVariableExpression(self.0.ge(value))
383 }
384 fn __repr__(&self) -> String {
385 format!("{:?}", self.0)
386 }
387 fn __str__(&self) -> String {
388 format!("{}", self.0)
389 }
390}
391
392/// A Variable used to define both spherical decay angles in the given frame
393///
394/// This class combines ``laddu.CosTheta`` and ``laddu.Phi`` into a single
395/// object
396///
397/// Parameters
398/// ----------
399/// beam : int
400/// The index of the `beam` particle
401/// recoil : list of int
402/// Indices of particles which are combined to form the recoiling particle (particles which
403/// are not `beam` or part of the `resonance`)
404/// daughter : list of int
405/// Indices of particles which are combined to form one of the decay products of the
406/// `resonance`
407/// resonance : list of int
408/// Indices of particles which are combined to form the `resonance`
409/// frame : {'Helicity', 'HX', 'HEL', 'GottfriedJackson', 'Gottfried Jackson', 'GJ', 'Gottfried-Jackson'}
410/// The frame to use in the calculation
411///
412/// Raises
413/// ------
414/// ValueError
415/// If `frame` is not one of the valid options
416///
417/// See Also
418/// --------
419/// laddu.CosTheta
420/// laddu.Phi
421///
422#[pyclass(name = "Angles", module = "laddu")]
423#[derive(Clone)]
424pub struct PyAngles(pub Angles);
425#[pymethods]
426impl PyAngles {
427 #[new]
428 #[pyo3(signature=(beam, recoil, daughter, resonance, frame="Helicity"))]
429 fn new(
430 beam: usize,
431 recoil: Vec<usize>,
432 daughter: Vec<usize>,
433 resonance: Vec<usize>,
434 frame: &str,
435 ) -> PyResult<Self> {
436 Ok(Self(Angles::new(
437 beam,
438 &recoil,
439 &daughter,
440 &resonance,
441 frame.parse()?,
442 )))
443 }
444 /// The Variable representing the cosine of the polar spherical decay angle
445 ///
446 /// Returns
447 /// -------
448 /// CosTheta
449 ///
450 #[getter]
451 fn costheta(&self) -> PyCosTheta {
452 PyCosTheta(self.0.costheta.clone())
453 }
454 // The Variable representing the polar azimuthal decay angle
455 //
456 // Returns
457 // -------
458 // Phi
459 //
460 #[getter]
461 fn phi(&self) -> PyPhi {
462 PyPhi(self.0.phi.clone())
463 }
464 fn __repr__(&self) -> String {
465 format!("{:?}", self.0)
466 }
467 fn __str__(&self) -> String {
468 format!("{}", self.0)
469 }
470}
471
472/// The polar angle of the given polarization vector with respect to the production plane
473///
474/// The `beam` and `recoil` particles define the plane of production, and this Variable
475/// describes the polar angle of the `beam` relative to this plane
476///
477/// Parameters
478/// ----------
479/// beam : int
480/// The index of the `beam` particle
481/// recoil : list of int
482/// Indices of particles which are combined to form the recoiling particle (particles which
483/// are not `beam` or part of the `resonance`)
484/// beam_polarization : int
485/// The index of the auxiliary vector in storing the `beam` particle's polarization
486///
487#[pyclass(name = "PolAngle", module = "laddu")]
488#[derive(Clone, Serialize, Deserialize)]
489pub struct PyPolAngle(pub PolAngle);
490
491#[pymethods]
492impl PyPolAngle {
493 #[new]
494 fn new(beam: usize, recoil: Vec<usize>, beam_polarization: usize) -> Self {
495 Self(PolAngle::new(beam, &recoil, beam_polarization))
496 }
497 /// The value of this Variable for the given Event
498 ///
499 /// Parameters
500 /// ----------
501 /// event : Event
502 /// The Event upon which the Variable is calculated
503 ///
504 /// Returns
505 /// -------
506 /// value : float
507 /// The value of the Variable for the given `event`
508 ///
509 fn value(&self, event: &PyEvent) -> Float {
510 self.0.value(&event.0)
511 }
512 /// All values of this Variable on the given Dataset
513 ///
514 /// Parameters
515 /// ----------
516 /// dataset : Dataset
517 /// The Dataset upon which the Variable is calculated
518 ///
519 /// Returns
520 /// -------
521 /// values : array_like
522 /// The values of the Variable for each Event in the given `dataset`
523 ///
524 fn value_on<'py>(&self, py: Python<'py>, dataset: &PyDataset) -> Bound<'py, PyArray1<Float>> {
525 PyArray1::from_slice(py, &self.0.value_on(&dataset.0))
526 }
527 fn __eq__(&self, value: Float) -> PyVariableExpression {
528 PyVariableExpression(self.0.eq(value))
529 }
530 fn __lt__(&self, value: Float) -> PyVariableExpression {
531 PyVariableExpression(self.0.lt(value))
532 }
533 fn __gt__(&self, value: Float) -> PyVariableExpression {
534 PyVariableExpression(self.0.gt(value))
535 }
536 fn __le__(&self, value: Float) -> PyVariableExpression {
537 PyVariableExpression(self.0.le(value))
538 }
539 fn __ge__(&self, value: Float) -> PyVariableExpression {
540 PyVariableExpression(self.0.ge(value))
541 }
542 fn __repr__(&self) -> String {
543 format!("{:?}", self.0)
544 }
545 fn __str__(&self) -> String {
546 format!("{}", self.0)
547 }
548}
549
550/// The magnitude of the given particle's polarization vector
551///
552/// This Variable simply represents the magnitude of the polarization vector of the particle
553/// with the index `beam`
554///
555/// Parameters
556/// ----------
557/// beam_polarization : int
558/// The index of the auxiliary vector in storing the `beam` particle's polarization
559///
560/// See Also
561/// --------
562/// laddu.utils.vectors.Vec3.mag
563///
564#[pyclass(name = "PolMagnitude", module = "laddu")]
565#[derive(Clone, Serialize, Deserialize)]
566pub struct PyPolMagnitude(pub PolMagnitude);
567
568#[pymethods]
569impl PyPolMagnitude {
570 #[new]
571 fn new(beam_polarization: usize) -> Self {
572 Self(PolMagnitude::new(beam_polarization))
573 }
574 /// The value of this Variable for the given Event
575 ///
576 /// Parameters
577 /// ----------
578 /// event : Event
579 /// The Event upon which the Variable is calculated
580 ///
581 /// Returns
582 /// -------
583 /// value : float
584 /// The value of the Variable for the given `event`
585 ///
586 fn value(&self, event: &PyEvent) -> Float {
587 self.0.value(&event.0)
588 }
589 /// All values of this Variable on the given Dataset
590 ///
591 /// Parameters
592 /// ----------
593 /// dataset : Dataset
594 /// The Dataset upon which the Variable is calculated
595 ///
596 /// Returns
597 /// -------
598 /// values : array_like
599 /// The values of the Variable for each Event in the given `dataset`
600 ///
601 fn value_on<'py>(&self, py: Python<'py>, dataset: &PyDataset) -> Bound<'py, PyArray1<Float>> {
602 PyArray1::from_slice(py, &self.0.value_on(&dataset.0))
603 }
604 fn __eq__(&self, value: Float) -> PyVariableExpression {
605 PyVariableExpression(self.0.eq(value))
606 }
607 fn __lt__(&self, value: Float) -> PyVariableExpression {
608 PyVariableExpression(self.0.lt(value))
609 }
610 fn __gt__(&self, value: Float) -> PyVariableExpression {
611 PyVariableExpression(self.0.gt(value))
612 }
613 fn __le__(&self, value: Float) -> PyVariableExpression {
614 PyVariableExpression(self.0.le(value))
615 }
616 fn __ge__(&self, value: Float) -> PyVariableExpression {
617 PyVariableExpression(self.0.ge(value))
618 }
619 fn __repr__(&self) -> String {
620 format!("{:?}", self.0)
621 }
622 fn __str__(&self) -> String {
623 format!("{}", self.0)
624 }
625}
626
627/// A Variable used to define both the polarization angle and magnitude of the given particle``
628///
629/// This class combines ``laddu.PolAngle`` and ``laddu.PolMagnitude`` into a single
630/// object
631///
632/// Parameters
633/// ----------
634/// beam : int
635/// The index of the `beam` particle
636/// recoil : list of int
637/// Indices of particles which are combined to form the recoiling particle (particles which
638/// are not `beam` or part of the `resonance`)
639/// beam_polarization : int
640/// The index of the auxiliary vector in storing the `beam` particle's polarization
641///
642/// See Also
643/// --------
644/// laddu.PolAngle
645/// laddu.PolMagnitude
646///
647#[pyclass(name = "Polarization", module = "laddu")]
648#[derive(Clone)]
649pub struct PyPolarization(pub Polarization);
650#[pymethods]
651impl PyPolarization {
652 #[new]
653 fn new(beam: usize, recoil: Vec<usize>, beam_polarization: usize) -> Self {
654 PyPolarization(Polarization::new(beam, &recoil, beam_polarization))
655 }
656 /// The Variable representing the magnitude of the polarization vector
657 ///
658 /// Returns
659 /// -------
660 /// PolMagnitude
661 ///
662 #[getter]
663 fn pol_magnitude(&self) -> PyPolMagnitude {
664 PyPolMagnitude(self.0.pol_magnitude)
665 }
666 /// The Variable representing the polar angle of the polarization vector
667 ///
668 /// Returns
669 /// -------
670 /// PolAngle
671 ///
672 #[getter]
673 fn pol_angle(&self) -> PyPolAngle {
674 PyPolAngle(self.0.pol_angle.clone())
675 }
676 fn __repr__(&self) -> String {
677 format!("{:?}", self.0)
678 }
679 fn __str__(&self) -> String {
680 format!("{}", self.0)
681 }
682}
683
684/// Mandelstam variables s, t, and u
685///
686/// By convention, the metric is chosen to be :math:`(+---)` and the variables are defined as follows
687/// (ignoring factors of :math:`c`):
688///
689/// .. math:: s = (p_1 + p_2)^2 = (p_3 + p_4)^2
690///
691/// .. math:: t = (p_1 - p_3)^2 = (p_4 - p_2)^2
692///
693/// .. math:: u = (p_1 - p_4)^2 = (p_3 - p_2)^2
694///
695/// Parameters
696/// ----------
697/// p1: list of int
698/// The indices of particles to combine to create :math:`p_1` in the diagram
699/// p2: list of int
700/// The indices of particles to combine to create :math:`p_2` in the diagram
701/// p3: list of int
702/// The indices of particles to combine to create :math:`p_3` in the diagram
703/// p4: list of int
704/// The indices of particles to combine to create :math:`p_4` in the diagram
705/// channel: {'s', 't', 'u', 'S', 'T', 'U'}
706/// The Mandelstam channel to calculate
707///
708/// Raises
709/// ------
710/// Exception
711/// If more than one particle list is empty
712/// ValueError
713/// If `channel` is not one of the valid options
714///
715/// Notes
716/// -----
717/// At most one of the input particles may be omitted by using an empty list. This will cause
718/// the calculation to use whichever equality listed above does not contain that particle.
719///
720/// By default, the first equality is used if no particle lists are empty.
721///
722#[pyclass(name = "Mandelstam", module = "laddu")]
723#[derive(Clone, Serialize, Deserialize)]
724pub struct PyMandelstam(pub Mandelstam);
725
726#[pymethods]
727impl PyMandelstam {
728 #[new]
729 fn new(
730 p1: Vec<usize>,
731 p2: Vec<usize>,
732 p3: Vec<usize>,
733 p4: Vec<usize>,
734 channel: &str,
735 ) -> PyResult<Self> {
736 Ok(Self(Mandelstam::new(p1, p2, p3, p4, channel.parse()?)?))
737 }
738 /// The value of this Variable for the given Event
739 ///
740 /// Parameters
741 /// ----------
742 /// event : Event
743 /// The Event upon which the Variable is calculated
744 ///
745 /// Returns
746 /// -------
747 /// value : float
748 /// The value of the Variable for the given `event`
749 ///
750 fn value(&self, event: &PyEvent) -> Float {
751 self.0.value(&event.0)
752 }
753 /// All values of this Variable on the given Dataset
754 ///
755 /// Parameters
756 /// ----------
757 /// dataset : Dataset
758 /// The Dataset upon which the Variable is calculated
759 ///
760 /// Returns
761 /// -------
762 /// values : array_like
763 /// The values of the Variable for each Event in the given `dataset`
764 ///
765 fn value_on<'py>(&self, py: Python<'py>, dataset: &PyDataset) -> Bound<'py, PyArray1<Float>> {
766 PyArray1::from_slice(py, &self.0.value_on(&dataset.0))
767 }
768 fn __eq__(&self, value: Float) -> PyVariableExpression {
769 PyVariableExpression(self.0.eq(value))
770 }
771 fn __lt__(&self, value: Float) -> PyVariableExpression {
772 PyVariableExpression(self.0.lt(value))
773 }
774 fn __gt__(&self, value: Float) -> PyVariableExpression {
775 PyVariableExpression(self.0.gt(value))
776 }
777 fn __le__(&self, value: Float) -> PyVariableExpression {
778 PyVariableExpression(self.0.le(value))
779 }
780 fn __ge__(&self, value: Float) -> PyVariableExpression {
781 PyVariableExpression(self.0.ge(value))
782 }
783 fn __repr__(&self) -> String {
784 format!("{:?}", self.0)
785 }
786 fn __str__(&self) -> String {
787 format!("{}", self.0)
788 }
789}
790
791#[typetag::serde]
792impl Variable for PyVariable {
793 fn value_on(&self, dataset: &Dataset) -> Vec<Float> {
794 match self {
795 PyVariable::Mass(mass) => mass.0.value_on(dataset),
796 PyVariable::CosTheta(cos_theta) => cos_theta.0.value_on(dataset),
797 PyVariable::Phi(phi) => phi.0.value_on(dataset),
798 PyVariable::PolAngle(pol_angle) => pol_angle.0.value_on(dataset),
799 PyVariable::PolMagnitude(pol_magnitude) => pol_magnitude.0.value_on(dataset),
800 PyVariable::Mandelstam(mandelstam) => mandelstam.0.value_on(dataset),
801 }
802 }
803
804 fn value(&self, event: &Event) -> Float {
805 match self {
806 PyVariable::Mass(mass) => mass.0.value(event),
807 PyVariable::CosTheta(cos_theta) => cos_theta.0.value(event),
808 PyVariable::Phi(phi) => phi.0.value(event),
809 PyVariable::PolAngle(pol_angle) => pol_angle.0.value(event),
810 PyVariable::PolMagnitude(pol_magnitude) => pol_magnitude.0.value(event),
811 PyVariable::Mandelstam(mandelstam) => mandelstam.0.value(event),
812 }
813 }
814}