1#![allow(dead_code)]
2
3pub mod sample;
4pub mod signature;
5
6use std::{
7 borrow::Borrow,
8 fmt::{Display, LowerExp},
9 ops::{Add, AddAssign, Index, Mul, MulAssign, Neg, Sub, SubAssign},
10 str::FromStr,
11};
12
13use bincode_trait_derive::{Decode, Encode};
14use eyre::Context;
15use linnet::half_edge::involution::EdgeIndex;
16pub use linnet::num_traits::{Pow, Sign, SignError, SignOrZero};
17use momtrop::vector::Vector;
18use schemars::JsonSchema;
19use serde::{Deserialize, Serialize};
20use spenso::{
21 algebra::{algebraic_traits::RefZero, complex::Complex, upgrading_arithmetic::FallibleAdd},
22 contraction::Contract,
23 iterators::IteratableTensor,
24 shadowing::Shadowable,
25 structure::{
26 CastStructure, IndexLess, NamedStructure, OrderedStructure, PermutedStructure,
27 TensorStructure, ToSymbolic,
28 abstract_index::AbstractIndex,
29 representation::{BaseRepName, Euclidean, LibraryRep, Minkowski, RepName},
30 slot::{DualSlotTo, Slot},
31 },
32 tensors::{
33 complex::RealOrComplexTensor,
34 data::{
35 DataIterator, DataTensor, DenseTensor, HasTensorData, SetTensorData, SparseTensor,
36 StorageTensor,
37 },
38 parametric::{
39 EvalTensor, FlatCoefficent, MixedTensor, ParamOrConcrete, atomcore::TensorAtomOps,
40 },
41 },
42};
43use symbolica::{
44 domains::{
45 dual::HyperDual,
46 float::{Complex as SymComplex, FloatLike as SymFloatLike},
47 rational::RationalField,
48 },
49 prelude::*,
50};
51use symbolica_utils::NoArgs;
52use thiserror::Error;
53
54use crate::{
55 GammaLoopContext,
56 settings::runtime::RotationSetting,
57 utils::{
58 ApproxEq, F, FloatLike, RefDefault, hyperdual_utils::new_constant, representations::GR,
59 },
60};
61
62#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Encode, Decode)]
63pub struct Energy<T> {
64 pub value: T,
65}
66
67impl<T> Energy<T> {
68 pub(crate) fn map_ref<U>(&self, f: &impl Fn(&T) -> U) -> Energy<U> {
69 Energy {
70 value: f(&self.value),
71 }
72 }
73
74 pub(crate) fn map<U>(self, f: &impl Fn(T) -> U) -> Energy<U> {
75 Energy {
76 value: f(self.value),
77 }
78 }
79}
80
81impl<T: FloatLike> ApproxEq<Energy<F<T>>, F<T>> for Energy<F<T>> {
82 fn approx_eq(&self, other: &Energy<F<T>>, threshold: &F<T>) -> bool {
83 self.value.approx_eq(&other.value, threshold)
84 }
85}
86
87impl<T: FloatLike> Energy<F<T>> {
88 pub(crate) fn higher(&self) -> Energy<F<T::Higher>>
89 where
90 T::Higher: FloatLike,
91 {
92 Energy {
93 value: self.value.higher(),
94 }
95 }
96
97 pub(crate) fn lower(&self) -> Energy<F<T::Lower>>
98 where
99 T::Lower: FloatLike,
100 {
101 Energy {
102 value: self.value.lower(),
103 }
104 }
105
106 pub(crate) fn from_ff64(energy: Energy<F<f64>>) -> Self {
107 Energy {
108 value: F::from_ff64(energy.value),
109 }
110 }
111}
112
113impl<T: FloatLike> From<Energy<T>> for Energy<F<T>> {
114 fn from(value: Energy<T>) -> Self {
115 Energy {
116 value: F(value.value),
117 }
118 }
119}
120
121impl<T: Real> Energy<T> {
122 pub(crate) fn zero(&self) -> Self {
123 Energy {
124 value: self.value.zero(),
125 }
126 }
127}
128
129impl<T> Add<Energy<T>> for Energy<T>
130where
131 T: Add<T, Output = T>,
132{
133 type Output = Energy<T>;
134 fn add(self, rhs: Energy<T>) -> Self::Output {
135 Energy {
136 value: self.value + rhs.value,
137 }
138 }
139}
140
141impl<T> Add<&Energy<T>> for Energy<T>
142where
143 T: for<'a> Add<&'a T, Output = T>,
144{
145 type Output = Energy<T>;
146 fn add(self, rhs: &Energy<T>) -> Self::Output {
147 Energy {
148 value: self.value + &rhs.value,
149 }
150 }
151}
152
153impl<'b, T> Add<&Energy<T>> for &'b Energy<T>
154where
155 &'b T: for<'a> Add<&'a T, Output = T>,
156{
157 type Output = Energy<T>;
158 fn add(self, rhs: &Energy<T>) -> Self::Output {
159 Energy {
160 value: &self.value + &rhs.value,
161 }
162 }
163}
164
165impl<T> Add<Energy<T>> for &Energy<T>
166where
167 T: for<'a> Add<&'a T, Output = T>,
168{
169 type Output = Energy<T>;
170 fn add(self, rhs: Energy<T>) -> Self::Output {
171 rhs + self
172 }
173}
174
175impl<T> AddAssign<Energy<T>> for Energy<T>
176where
177 T: AddAssign<T>,
178{
179 fn add_assign(&mut self, rhs: Energy<T>) {
180 self.value += rhs.value;
181 }
182}
183
184impl<'a, T> AddAssign<&'a Energy<T>> for Energy<T>
185where
186 T: AddAssign<&'a T>,
187{
188 fn add_assign(&mut self, rhs: &'a Energy<T>) {
189 self.value += &rhs.value;
190 }
191}
192
193impl<T> Sub<Energy<T>> for Energy<T>
194where
195 T: Sub<T, Output = T>,
196{
197 type Output = Energy<T>;
198 fn sub(self, rhs: Energy<T>) -> Self::Output {
199 Energy {
200 value: self.value - rhs.value,
201 }
202 }
203}
204
205impl<T> SubAssign<Energy<T>> for Energy<T>
206where
207 T: SubAssign<T>,
208{
209 fn sub_assign(&mut self, rhs: Energy<T>) {
210 self.value -= rhs.value;
211 }
212}
213
214impl<'a, T> SubAssign<&'a Energy<T>> for Energy<T>
215where
216 T: SubAssign<&'a T>,
217{
218 fn sub_assign(&mut self, rhs: &'a Energy<T>) {
219 self.value -= &rhs.value;
220 }
221}
222impl<T> Mul<Energy<T>> for Energy<T>
223where
224 T: Mul<T, Output = T>,
225{
226 type Output = T;
227 fn mul(self, rhs: Energy<T>) -> Self::Output {
228 self.value * rhs.value
229 }
230}
231
232impl<T> MulAssign<Energy<T>> for Energy<T>
233where
234 T: MulAssign<T>,
235{
236 fn mul_assign(&mut self, rhs: Energy<T>) {
237 self.value *= rhs.value;
238 }
239}
240
241impl<T> Neg for Energy<T>
242where
243 T: Neg<Output = T>,
244{
245 type Output = Energy<T>;
246 fn neg(self) -> Self::Output {
247 Energy { value: -self.value }
248 }
249}
250
251impl<T> Energy<T> {
252 pub(crate) fn new(value: T) -> Self {
253 Energy { value }
254 }
255
256 }
268
269impl<U, T: RefZero<U>> RefZero<Energy<U>> for Energy<T>
276where
277 Energy<T>: Borrow<Energy<U>>,
278{
279 fn ref_zero(&self) -> Energy<U> {
280 Energy {
281 value: self.value.ref_zero(),
282 }
283 }
284}
285
286impl Energy<Atom> {
287 pub(crate) fn new_parametric(id: usize) -> Self {
288 let value = parse!(&format!("E_{}", id));
289 Energy { value }
290 }
291}
292
293impl<T: Display> Display for Energy<T> {
294 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
295 write!(f, "E: {}", self.value)
296 }
297}
298
299impl<T: LowerExp> LowerExp for Energy<T> {
300 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
301 write!(f, "E: {:e}", self.value)
302 }
303}
304
305impl<T: Default> Default for Energy<T> {
306 fn default() -> Self {
307 Energy {
308 value: T::default(),
309 }
310 }
311}
312
313impl<T> From<T> for Energy<T> {
314 fn from(value: T) -> Self {
315 Energy { value }
316 }
317}
318
319#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Encode, Decode)]
320pub struct ThreeMomentum<T> {
321 pub px: T,
322 pub py: T,
323 pub pz: T,
324}
325
326impl<T> ThreeMomentum<T> {
327 pub(crate) fn map_ref<U>(&self, f: &impl Fn(&T) -> U) -> ThreeMomentum<U> {
328 ThreeMomentum {
329 px: f(&self.px),
330 py: f(&self.py),
331 pz: f(&self.pz),
332 }
333 }
334
335 pub(crate) fn map<U>(self, f: &impl Fn(T) -> U) -> ThreeMomentum<U> {
336 ThreeMomentum {
337 px: f(self.px),
338 py: f(self.py),
339 pz: f(self.pz),
340 }
341 }
342}
343
344impl<T: FloatLike> ApproxEq<ThreeMomentum<F<T>>, F<T>> for ThreeMomentum<F<T>> {
345 fn approx_eq(&self, other: &ThreeMomentum<F<T>>, threshold: &F<T>) -> bool {
346 F::approx_eq_iterator(
347 [&self.px, &self.py, &self.pz],
348 [&other.px, &other.py, &other.pz],
349 threshold,
350 )
351 }
352}
353
354pub struct ThreeRotation<T> {
355 pub map: fn(ThreeMomentum<T>) -> ThreeMomentum<T>,
356 pub inv_map: fn(ThreeMomentum<T>) -> ThreeMomentum<T>,
357}
358
359impl<T: Neg<Output = T>> ThreeRotation<T> {
360 pub(crate) fn half_pi_x() -> Self {
361 let map = |mut momentum: ThreeMomentum<T>| -> ThreeMomentum<T> {
362 std::mem::swap(&mut momentum.py, &mut momentum.pz);
363 momentum.pz = -momentum.pz;
364 momentum
365 };
366
367 let inv_map = |mut momentum: ThreeMomentum<T>| -> ThreeMomentum<T> {
368 momentum.pz = -momentum.pz;
369 std::mem::swap(&mut momentum.py, &mut momentum.pz);
370 momentum
371 };
372
373 ThreeRotation { map, inv_map }
374 }
375
376 pub(crate) fn half_pi_y() -> Self {
377 let map = |mut momentum: ThreeMomentum<T>| -> ThreeMomentum<T> {
378 std::mem::swap(&mut momentum.px, &mut momentum.pz);
379 momentum.px = -momentum.px;
380 momentum
381 };
382
383 let inv_map = |mut momentum: ThreeMomentum<T>| -> ThreeMomentum<T> {
384 momentum.px = -momentum.px;
385 std::mem::swap(&mut momentum.px, &mut momentum.pz);
386 momentum
387 };
388
389 ThreeRotation { map, inv_map }
390 }
391
392 pub(crate) fn half_pi_z() -> Self {
393 let map = |mut momentum: ThreeMomentum<T>| -> ThreeMomentum<T> {
394 std::mem::swap(&mut momentum.px, &mut momentum.py);
395 momentum.py = -momentum.py;
396 momentum
397 };
398
399 let inv_map = |mut momentum: ThreeMomentum<T>| -> ThreeMomentum<T> {
400 momentum.py = -momentum.py;
401 std::mem::swap(&mut momentum.px, &mut momentum.py);
402 momentum
403 };
404
405 ThreeRotation { map, inv_map }
406 }
407}
408
409impl<T: FloatLike> ThreeMomentum<F<T>> {
410 pub(crate) fn higher(&self) -> ThreeMomentum<F<T::Higher>>
411 where
412 T::Higher: FloatLike,
413 {
414 ThreeMomentum {
415 px: self.px.higher(),
416 py: self.py.higher(),
417 pz: self.pz.higher(),
418 }
419 }
420
421 pub(crate) fn lower(&self) -> ThreeMomentum<F<T::Lower>>
422 where
423 T::Lower: FloatLike,
424 {
425 ThreeMomentum {
426 px: self.px.lower(),
427 py: self.py.lower(),
428 pz: self.pz.lower(),
429 }
430 }
431
432 pub(crate) fn from_ff64(three_mom: ThreeMomentum<F<f64>>) -> Self {
433 ThreeMomentum {
434 px: F::from_ff64(three_mom.px),
435 py: F::from_ff64(three_mom.py),
436 pz: F::from_ff64(three_mom.pz),
437 }
438 }
439}
440
441impl<T: FloatLike> From<ThreeMomentum<T>> for ThreeMomentum<F<T>> {
442 fn from(value: ThreeMomentum<T>) -> Self {
443 ThreeMomentum {
444 px: F(value.px),
445 py: F(value.py),
446 pz: F(value.pz),
447 }
448 }
449}
450
451impl<T> IntoIterator for ThreeMomentum<T> {
452 type Item = T;
453 type IntoIter = std::array::IntoIter<T, 3>;
454
455 fn into_iter(self) -> Self::IntoIter {
456 let [px, py, pz] = [self.px, self.py, self.pz];
457 [px, py, pz].into_iter()
458 }
459}
460
461impl<'a, T> IntoIterator for &'a ThreeMomentum<T> {
462 type Item = &'a T;
463 type IntoIter = std::array::IntoIter<&'a T, 3>;
464
465 fn into_iter(self) -> Self::IntoIter {
466 let [px, py, pz] = [&self.px, &self.py, &self.pz];
467 [px, py, pz].into_iter()
468 }
469}
470
471impl<T: Real> RefDefault for ThreeMomentum<T> {
472 fn default(&self) -> Self {
473 let zero = self.px.zero();
474 ThreeMomentum {
475 px: zero.clone(),
476 py: zero.clone(),
477 pz: zero.clone(),
478 }
479 }
480}
481
482impl<T: Real> ThreeMomentum<T> {
483 pub(crate) fn zero(&self) -> Self {
484 let zero = self.px.zero();
485 ThreeMomentum {
486 px: zero.clone(),
487 py: zero.clone(),
488 pz: zero.clone(),
489 }
490 }
491}
492
493impl<T: RefZero> RefZero for ThreeMomentum<T> {
494 fn ref_zero(&self) -> Self {
495 ThreeMomentum {
496 px: self.px.ref_zero(),
497 py: self.py.ref_zero(),
498 pz: self.pz.ref_zero(),
499 }
500 }
501}
502
503impl<T> ThreeMomentum<T> {
504 pub(crate) fn new(px: T, py: T, pz: T) -> Self {
505 ThreeMomentum { px, py, pz }
506 }
507
508 pub(crate) fn into_dense(self, index: AbstractIndex) -> DenseTensor<T, OrderedStructure>
509 where
510 T: Clone,
511 {
512 let structure =
513 PermutedStructure::from_iter(vec![Euclidean {}.new_slot(3, index)]).structure;
514 DenseTensor::from_data(vec![self.px, self.py, self.pz], structure).unwrap()
515 }
516
517 pub(crate) fn into_dense_param(self, index: AbstractIndex) -> DenseTensor<T, OrderedStructure>
518 where
519 T: Clone,
520 {
521 let structure =
522 PermutedStructure::from_iter(vec![Euclidean {}.new_slot(3, index)]).structure;
523 DenseTensor::from_data(vec![self.px, self.py, self.pz], structure).unwrap()
524 }
525}
526
527impl<T: FloatLike> ThreeMomentum<F<T>> {
528 pub(crate) fn to_f64(&self) -> ThreeMomentum<F<f64>> {
529 ThreeMomentum {
530 px: F(self.px.to_f64()),
531 py: F(self.py.to_f64()),
532 pz: F(self.pz.to_f64()),
533 }
534 }
535 pub(crate) fn getdelphi(&self, p2: &ThreeMomentum<F<T>>) -> F<T> {
537 let pt1 = self.pt();
538 let pt2 = p2.pt();
539 if pt1.is_zero() {
540 return pt1.max_value();
541 }
542 if pt2.is_zero() {
543 return pt2.max_value();
544 }
545
546 let mut tmp = self.px.clone() * &p2.px + self.py.clone() * &p2.py;
547
548 tmp /= pt1 * pt2;
549 if tmp.norm() > tmp.one() + tmp.epsilon() {
550 panic!("Cosine larger than 1. in phase-space cuts.")
551 }
552 if tmp.norm() > tmp.one() {
553 (tmp.clone() / tmp.norm()).acos()
554 } else {
555 tmp.acos()
556 }
557 }
558
559 #[inline]
561 pub(crate) fn delta_r(&self, p2: &ThreeMomentum<F<T>>) -> F<T>
562 where
563 T: Real,
564 {
565 let delta_eta = self.pseudo_rap() - p2.pseudo_rap();
566 let delta_phi = self.getdelphi(p2);
567 (delta_eta.square() + delta_phi.square()).sqrt()
568 }
569
570 pub(crate) fn rotate_mut(&mut self, alpha: &F<T>, beta: &F<T>, gamma: &F<T>) {
571 let sin_alpha = alpha.sin();
572 let cos_alpha = alpha.cos();
573 let sin_beta = beta.sin();
574 let cos_beta = beta.cos();
575 let sin_gamma = gamma.sin();
576 let cos_gamma = gamma.cos();
577
578 let px = self.px.clone();
579 let py = self.py.clone();
580 let pz = self.pz.clone();
581
582 self.px = cos_gamma.clone() * &cos_beta * &px
583 + (-(cos_alpha.clone()) * &sin_gamma + sin_alpha.clone() * &sin_beta * &cos_gamma)
584 * &py
585 + (sin_alpha.clone() * &sin_gamma + cos_alpha.clone() * &sin_beta * &cos_gamma) * &pz;
586
587 self.py = sin_gamma.clone() * &cos_beta * &px
588 + (cos_alpha.clone() * &cos_gamma + sin_alpha.clone() * &sin_beta * &sin_gamma) * &py
589 + (-sin_alpha.clone() * &cos_gamma + cos_alpha.clone() * &sin_beta * &sin_gamma) * &pz;
590
591 self.pz =
592 -sin_beta * &px + cos_beta.clone() * &sin_alpha * &py + cos_alpha * &cos_beta * &pz;
593 }
594
595 #[inline]
597 pub(crate) fn pt(&self) -> F<T> {
598 (self.px.square() + self.py.square()).sqrt()
599 }
600
601 #[inline]
603 pub(crate) fn pseudo_rap(&self) -> F<T> {
604 let pt = self.pt();
605 if pt.less_than_epsilon() && self.pz.norm().less_than_epsilon() {
606 if self.pz.positive() {
607 return pt.max_value();
608 } else {
609 return pt.min_value();
610 }
611 }
612 let th = pt.atan2(&self.pz);
613 let two = pt.from_i64(2);
614 -(th / two).tan().ln()
615 }
616
617 pub(crate) fn cross_product(&self, rhs: &ThreeMomentum<F<T>>) -> ThreeMomentum<F<T>> {
618 ThreeMomentum {
619 px: &self.py * &rhs.pz - &self.pz * &rhs.py,
620 py: &self.pz * &rhs.px - &self.px * &rhs.pz,
621 pz: &self.px * &rhs.py - &self.py * &rhs.px,
622 }
623 }
624
625 pub(crate) fn axis_angle_rotation(
627 &self,
628 cos_theta: &F<T>,
629 axis: &ThreeMomentum<F<T>>,
630 ) -> ThreeMomentum<F<T>> {
631 let axis = axis * &axis.norm().inv();
633 let sin_theta = if cos_theta >= &cos_theta.one() {
634 cos_theta.zero()
635 } else {
636 (cos_theta.one() - cos_theta.square()).sqrt()
637 };
638 let k_cross_v = axis.cross_product(self);
639 let k_dot_v = self * axis.clone();
640
641 self * cos_theta + k_cross_v * &sin_theta + &axis * &k_dot_v * &(self.px.one() - cos_theta)
642 }
643
644 pub(crate) fn get_cos_theta_with(&self, rhs: &ThreeMomentum<F<T>>) -> F<T> {
645 let self_norm = self.norm();
646 let rhs_norm = rhs.norm();
647 if self_norm < self_norm.epsilon() || rhs_norm < rhs_norm.epsilon() {
648 return self_norm.one();
649 }
650
651 let dot = self * rhs.clone();
652 dot / (self.norm() * rhs.norm())
653 }
654}
655
656impl<T: Neg<Output = T> + Clone> ThreeMomentum<T> {
657 pub(crate) fn perform_pi2_rotation_x_mut(&mut self) {
658 self.pz = -self.pz.clone();
659 std::mem::swap(&mut self.pz, &mut self.py);
660 }
661
662 pub(crate) fn perform_pi2_rotation_x(&self) -> Self {
663 Self {
665 px: self.px.clone(),
666 py: -self.pz.clone(),
667 pz: self.py.clone(),
668 }
669 }
670
671 pub(crate) fn perform_pi2_rotation_y_mut(&mut self) {
672 self.px = -self.px.clone();
673 std::mem::swap(&mut self.px, &mut self.pz);
674 }
675
676 pub(crate) fn perform_pi2_rotation_y(&self) -> Self {
677 Self {
679 px: self.pz.clone(),
680 py: self.py.clone(),
681 pz: -self.px.clone(),
682 }
683 }
684
685 pub(crate) fn perform_pi2_rotation_z_mut(&mut self) {
686 self.py = -self.py.clone();
687 std::mem::swap(&mut self.px, &mut self.py);
688 }
689
690 pub(crate) fn perform_pi2_rotation_z(&self) -> Self {
691 Self {
693 px: -self.py.clone(),
694 py: self.px.clone(),
695 pz: self.pz.clone(),
696 }
697 }
698}
699
700impl<T> Add<ThreeMomentum<T>> for ThreeMomentum<T>
701where
702 T: Add<T, Output = T>,
703{
704 type Output = ThreeMomentum<T>;
705 fn add(self, rhs: ThreeMomentum<T>) -> Self::Output {
706 ThreeMomentum {
707 px: self.px + rhs.px,
708 py: self.py + rhs.py,
709 pz: self.pz + rhs.pz,
710 }
711 }
712}
713
714impl<T> Add<&ThreeMomentum<T>> for ThreeMomentum<T>
715where
716 T: for<'a> Add<&'a T, Output = T>,
717{
718 type Output = ThreeMomentum<T>;
719 fn add(self, rhs: &ThreeMomentum<T>) -> Self::Output {
720 ThreeMomentum {
721 px: self.px + &rhs.px,
722 py: self.py + &rhs.py,
723 pz: self.pz + &rhs.pz,
724 }
725 }
726}
727
728impl<'b, T> Add<&ThreeMomentum<T>> for &'b ThreeMomentum<T>
729where
730 &'b T: for<'a> Add<&'a T, Output = T>,
731{
732 type Output = ThreeMomentum<T>;
733 fn add(self, rhs: &ThreeMomentum<T>) -> Self::Output {
734 ThreeMomentum {
735 px: &self.px + &rhs.px,
736 py: &self.py + &rhs.py,
737 pz: &self.pz + &rhs.pz,
738 }
739 }
740}
741
742impl<T> Add<ThreeMomentum<T>> for &ThreeMomentum<T>
743where
744 T: for<'a> Add<&'a T, Output = T>,
745{
746 type Output = ThreeMomentum<T>;
747 fn add(self, rhs: ThreeMomentum<T>) -> Self::Output {
748 rhs + self
749 }
750}
751
752impl<T> AddAssign<ThreeMomentum<T>> for ThreeMomentum<T>
753where
754 T: AddAssign<T>,
755{
756 fn add_assign(&mut self, rhs: ThreeMomentum<T>) {
757 self.px += rhs.px;
758 self.py += rhs.py;
759 self.pz += rhs.pz;
760 }
761}
762
763impl<'a, T> AddAssign<&'a ThreeMomentum<T>> for ThreeMomentum<T>
764where
765 T: AddAssign<&'a T>,
766{
767 fn add_assign(&mut self, rhs: &'a ThreeMomentum<T>) {
768 self.px += &rhs.px;
769 self.py += &rhs.py;
770 self.pz += &rhs.pz;
771 }
772}
773
774impl<T> Sub<ThreeMomentum<T>> for ThreeMomentum<T>
775where
776 T: Sub<T, Output = T>,
777{
778 type Output = ThreeMomentum<T>;
779 fn sub(self, rhs: ThreeMomentum<T>) -> Self::Output {
780 ThreeMomentum {
781 px: self.px - rhs.px,
782 py: self.py - rhs.py,
783 pz: self.pz - rhs.pz,
784 }
785 }
786}
787
788impl<T> Sub<&ThreeMomentum<T>> for &ThreeMomentum<T>
789where
790 for<'a> &'a T: Sub<&'a T, Output = T>,
791{
792 type Output = ThreeMomentum<T>;
793 fn sub(self, rhs: &ThreeMomentum<T>) -> Self::Output {
794 ThreeMomentum {
795 px: &self.px - &rhs.px,
796 py: &self.py - &rhs.py,
797 pz: &self.pz - &rhs.pz,
798 }
799 }
800}
801
802impl<T> SubAssign<ThreeMomentum<T>> for ThreeMomentum<T>
803where
804 T: SubAssign<T>,
805{
806 fn sub_assign(&mut self, rhs: ThreeMomentum<T>) {
807 self.px -= rhs.px;
808 self.py -= rhs.py;
809 self.pz -= rhs.pz;
810 }
811}
812
813impl<'a, T> SubAssign<&'a ThreeMomentum<T>> for ThreeMomentum<T>
814where
815 T: SubAssign<&'a T>,
816{
817 fn sub_assign(&mut self, rhs: &'a ThreeMomentum<T>) {
818 self.px -= &rhs.px;
819 self.py -= &rhs.py;
820 self.pz -= &rhs.pz;
821 }
822}
823
824impl<T> Mul<ThreeMomentum<T>> for ThreeMomentum<T>
825where
826 T: Mul<T, Output = T> + Add<T, Output = T>,
827{
828 type Output = T;
829 fn mul(self, rhs: ThreeMomentum<T>) -> Self::Output {
830 self.px * rhs.px + self.py * rhs.py + self.pz * rhs.pz
831 }
832}
833
834impl<T> Mul<&ThreeMomentum<T>> for ThreeMomentum<T>
835where
836 T: for<'a> Mul<&'a T, Output = T> + Add<T, Output = T>,
837{
838 type Output = T;
839 fn mul(self, rhs: &ThreeMomentum<T>) -> Self::Output {
840 self.px * &rhs.px + self.py * &rhs.py + self.pz * &rhs.pz
841 }
842}
843
844impl<T> Mul<ThreeMomentum<T>> for &ThreeMomentum<T>
845where
846 T: for<'b> Mul<&'b T, Output = T> + Add<T, Output = T>,
847{
848 type Output = T;
849 fn mul(self, rhs: ThreeMomentum<T>) -> Self::Output {
850 rhs * self
851 }
852}
853
854impl<T> Mul<T> for ThreeMomentum<T>
855where
856 T: Mul<T, Output = T> + Clone,
857{
858 type Output = ThreeMomentum<T>;
859 fn mul(self, rhs: T) -> Self::Output {
860 ThreeMomentum {
861 px: self.px * rhs.clone(),
862 py: self.py * rhs.clone(),
863 pz: self.pz * rhs,
864 }
865 }
866}
867
868impl<T> Mul<&T> for ThreeMomentum<T>
869where
870 T: for<'a> Mul<&'a T, Output = T> + Clone,
871{
872 type Output = ThreeMomentum<T>;
873 fn mul(self, rhs: &T) -> Self::Output {
874 ThreeMomentum {
875 px: self.px * rhs,
876 py: self.py * rhs,
877 pz: self.pz * rhs,
878 }
879 }
880}
881
882impl<T> Mul<T> for &ThreeMomentum<T>
883where
884 T: Mul<T, Output = T> + Clone,
885{
886 type Output = ThreeMomentum<T>;
887 fn mul(self, rhs: T) -> Self::Output {
888 ThreeMomentum {
889 px: self.px.clone() * rhs.clone(),
890 py: self.py.clone() * rhs.clone(),
891 pz: self.pz.clone() * rhs,
892 }
893 }
894}
895
896impl<T> Mul<&T> for &ThreeMomentum<T>
897where
898 T: for<'b> Mul<&'b T, Output = T> + Clone,
899{
900 type Output = ThreeMomentum<T>;
901 fn mul(self, rhs: &T) -> Self::Output {
902 ThreeMomentum {
903 px: self.px.clone() * rhs,
904 py: self.py.clone() * rhs,
905 pz: self.pz.clone() * rhs,
906 }
907 }
908}
909
910impl<T> Neg for ThreeMomentum<T>
911where
912 T: Neg<Output = T>,
913{
914 type Output = ThreeMomentum<T>;
915 fn neg(self) -> Self::Output {
916 ThreeMomentum {
917 px: -self.px,
918 py: -self.py,
919 pz: -self.pz,
920 }
921 }
922}
923
924impl<T> Neg for &ThreeMomentum<T>
925where
926 T: Neg<Output = T> + Clone,
927{
928 type Output = ThreeMomentum<T>;
929 fn neg(self) -> Self::Output {
930 ThreeMomentum {
931 px: -self.px.clone(),
932 py: -self.py.clone(),
933 pz: -self.pz.clone(),
934 }
935 }
936}
937
938impl<T: Display> Display for ThreeMomentum<T> {
939 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
940 write!(f, "px: {}, py: {}, pz: {}", self.px, self.py, self.pz)
941 }
942}
943
944impl<T: LowerExp> LowerExp for ThreeMomentum<T> {
945 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
946 write!(f, "px: {:e}, py: {:e}, pz: {:e}", self.px, self.py, self.pz)
947 }
948}
949
950impl<T: Default> Default for ThreeMomentum<T> {
951 fn default() -> Self {
952 ThreeMomentum {
953 px: T::default(),
954 py: T::default(),
955 pz: T::default(),
956 }
957 }
958}
959
960impl<T> ThreeMomentum<T> {
961 pub(crate) fn norm_squared(&self) -> T
962 where
963 T: for<'a> Mul<&'a T, Output = T> + Add<T, Output = T> + Clone,
964 {
965 self.px.clone() * &self.px + self.py.clone() * &self.py + self.pz.clone() * &self.pz
966 }
967
968 pub(crate) fn on_shell_energy(&self, mass: Option<T>) -> Energy<T>
969 where
970 T: Mul<T, Output = T> + Add<T, Output = T> + Clone + std::ops::Add<Output = T> + Real,
971 {
972 let energy_squared = self.on_shell_energy_squared(mass);
973 Energy {
974 value: energy_squared.value.sqrt(),
975 }
976 }
977
978 pub(crate) fn on_shell_energy_squared(&self, mass: Option<T>) -> Energy<T>
979 where
980 T: for<'a> Mul<&'a T, Output = T>
981 + Add<T, Output = T>
982 + Clone
983 + std::ops::Add<Output = T>
984 + Display,
985 {
986 let p2 = self.norm_squared();
987 if let Some(mass) = mass {
988 Energy {
990 value: p2 + mass.clone() * &mass,
991 }
992 } else {
993 Energy { value: p2 }
994 }
995 }
996
997 pub(crate) fn norm(&self) -> T
998 where
999 T: for<'a> Mul<&'a T, Output = T> + Add<T> + Real,
1000 {
1001 self.norm_squared().sqrt()
1002 }
1003
1004 pub(crate) fn into_four_momentum_parametric(self, id: usize) -> FourMomentum<T, Atom> {
1005 let energy = Energy::new_parametric(id);
1006 FourMomentum {
1007 temporal: energy,
1008 spatial: self,
1009 }
1010 }
1011
1012 pub(crate) fn into_on_shell_four_momentum(self, mass: Option<T>) -> FourMomentum<T, T>
1013 where
1014 T: Mul<T> + Add<T> + std::ops::Add<Output = T> + Real,
1015 {
1016 FourMomentum::new_on_shell(self, mass)
1017 }
1018
1019 pub(crate) fn cast<U>(&self) -> ThreeMomentum<U>
1020 where
1021 T: Clone + Into<U>,
1022 {
1023 ThreeMomentum {
1024 px: (self.px.clone()).into(),
1025 py: (self.py.clone()).into(),
1026 pz: (self.pz.clone()).into(),
1027 }
1028 }
1029
1030 #[allow(clippy::wrong_self_convention)]
1031 pub(crate) fn into_f64(&self) -> ThreeMomentum<f64>
1032 where
1033 T: FloatLike,
1034 {
1035 ThreeMomentum {
1036 px: self.px.to_f64(),
1037 py: self.py.to_f64(),
1038 pz: self.pz.to_f64(),
1039 }
1040 }
1041}
1042
1043impl<T> From<[T; 3]> for ThreeMomentum<T> {
1044 fn from(data: [T; 3]) -> Self {
1045 let [px, py, pz] = data;
1046 ThreeMomentum { px, py, pz }
1047 }
1048}
1049
1050impl<T> From<ThreeMomentum<T>> for [T; 3] {
1051 fn from(data: ThreeMomentum<T>) -> Self {
1052 [data.px, data.py, data.pz]
1053 }
1054}
1055
1056impl<T> From<(T, T, T)> for ThreeMomentum<T> {
1057 fn from(data: (T, T, T)) -> Self {
1058 let (px, py, pz) = data;
1059 ThreeMomentum { px, py, pz }
1060 }
1061}
1062
1063impl<T> From<ThreeMomentum<T>> for (T, T, T) {
1064 fn from(data: ThreeMomentum<T>) -> Self {
1065 (data.px, data.py, data.pz)
1066 }
1067}
1068
1069#[derive(Default, Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Encode, Decode)]
1070pub struct FourMomentum<T, U = T> {
1071 pub temporal: Energy<U>,
1072 pub spatial: ThreeMomentum<T>,
1073}
1074
1075impl<T: FloatLike> Sub<&FourMomentum<F<T>>> for &FourMomentum<F<T>> {
1076 type Output = FourMomentum<F<T>>;
1077 fn sub(self, rhs: &FourMomentum<F<T>>) -> Self::Output {
1078 FourMomentum {
1079 temporal: Energy {
1080 value: &self.temporal.value - &rhs.temporal.value,
1081 },
1082 spatial: &self.spatial - &rhs.spatial,
1083 }
1084 }
1085}
1086
1087impl<T> FourMomentum<T> {
1088 pub(crate) fn map_ref<U>(&self, f: &impl Fn(&T) -> U) -> FourMomentum<U> {
1089 FourMomentum {
1090 temporal: self.temporal.map_ref(f),
1091 spatial: self.spatial.map_ref(f),
1092 }
1093 }
1094
1095 pub(crate) fn map<U>(self, f: &impl Fn(T) -> U) -> FourMomentum<U> {
1096 FourMomentum {
1097 temporal: self.temporal.map(f),
1098 spatial: self.spatial.map(f),
1099 }
1100 }
1101}
1102
1103impl<T: FloatLike> ApproxEq<FourMomentum<F<T>>, F<T>> for FourMomentum<F<T>> {
1104 fn approx_eq(&self, other: &FourMomentum<F<T>>, threshold: &F<T>) -> bool {
1105 self.temporal.approx_eq(&other.temporal, threshold)
1106 && self.spatial.approx_eq(&other.spatial, threshold)
1107 }
1108}
1109
1110impl<T: FloatLike> ApproxEq<Polarization<Complex<F<T>>>, F<T>> for FourMomentum<F<T>> {
1111 fn approx_eq(&self, other: &Polarization<Complex<F<T>>>, tolerance: &F<T>) -> bool {
1112 if other.tensor.size().unwrap() != 4 {
1113 false
1114 } else {
1115 self.into_iter()
1116 .zip(other.tensor.iter_flat())
1117 .all(|(a, (_, b))| a.approx_eq(b, tolerance))
1118 }
1119 }
1120
1121 fn approx_eq_res(
1122 &self,
1123 other: &Polarization<Complex<F<T>>>,
1124 tolerance: &F<T>,
1125 ) -> color_eyre::Result<()> {
1126 if other.tensor.size().unwrap() != 4 {
1127 Err(eyre::eyre!("Polarization tensor has wrong size."))
1128 } else {
1129 self.into_iter()
1130 .zip(other.tensor.iter_flat())
1131 .try_for_each(|(a, (i, b))| {
1132 a.approx_eq_res(b, tolerance).wrap_err(format!(
1133 "Polarization tensor element {} does not match. ",
1134 i
1135 ))
1136 })
1137 }
1138 }
1139}
1140
1141impl<T: FloatLike> FourMomentum<F<T>> {
1142 pub(crate) fn from_ff64(four_momentum: &FourMomentum<F<f64>>) -> Self {
1143 let temporal = Energy::from_ff64(four_momentum.temporal);
1144 let spatial = ThreeMomentum::from_ff64(four_momentum.spatial);
1145 FourMomentum { temporal, spatial }
1146 }
1147
1148 pub(crate) fn rescale_spatial(&self, x: &F<T>) -> Self {
1149 FourMomentum {
1150 temporal: self.temporal.clone(),
1151 spatial: ThreeMomentum {
1152 px: &self.spatial.px * x,
1153 py: &self.spatial.py * x,
1154 pz: &self.spatial.pz * x,
1155 },
1156 }
1157 }
1158
1159 pub(crate) fn set_energy_on_shell(&self, mass: Option<F<T>>) -> Self {
1160 let energy = self.spatial.on_shell_energy(mass);
1161 FourMomentum {
1162 temporal: energy,
1163 spatial: self.spatial.clone(),
1164 }
1165 }
1166
1167 pub(crate) fn rapidity(&self) -> F<T> {
1168 let pt = self.spatial.pt();
1169 self.rapidity_with_pt2(&pt.square())
1170 }
1171
1172 pub(crate) fn rapidity_with_pt2(&self, pt2: &F<T>) -> F<T> {
1173 const MAX_RAPIDITY_SENTINEL: usize = 100_000;
1174
1175 let zero = self.temporal.value.zero();
1176 let pz = self.spatial.pz.clone();
1177 let abs_pz = pz.norm();
1178 let energy = self.temporal.value.clone();
1179
1180 if *pt2 == zero.clone() && energy == abs_pz {
1181 let max_rapidity_here = abs_pz.from_usize(MAX_RAPIDITY_SENTINEL) + abs_pz;
1182 if pz >= zero {
1183 max_rapidity_here
1184 } else {
1185 -max_rapidity_here
1186 }
1187 } else {
1188 let mass2 = energy.square()
1189 - (self.spatial.px.square() + self.spatial.py.square() + self.spatial.pz.square());
1190 let effective_mass2 = if mass2 < zero { zero.clone() } else { mass2 };
1191 let e_plus_abs_pz = energy + abs_pz;
1192 let half = e_plus_abs_pz.one() / e_plus_abs_pz.from_usize(2);
1193 let mut rapidity =
1194 ((pt2.clone() + effective_mass2) / (e_plus_abs_pz.clone() * e_plus_abs_pz)).ln()
1195 * half;
1196 if pz > zero {
1197 rapidity = -rapidity;
1198 }
1199 rapidity
1200 }
1201 }
1202}
1203
1204#[derive(
1205 Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Encode, Decode, Hash, JsonSchema,
1206)]
1207#[serde(untagged)]
1209pub enum ExternalMomenta<T> {
1210 Dependent(Dep),
1211 Independent([T; 4]),
1212}
1213
1214impl<T: FloatLike> Rotatable for ExternalMomenta<F<T>> {
1215 fn rotate(&self, rotation: &Rotation) -> Self {
1216 match self {
1217 ExternalMomenta::Dependent(_) => ExternalMomenta::Dependent(Dep::Dep),
1218 ExternalMomenta::Independent(data) => {
1219 let fm = FourMomentum::from(data.clone());
1220 fm.rotate(rotation).into()
1221 }
1222 }
1223 }
1224}
1225
1226#[derive(
1227 Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Encode, Decode, Hash, JsonSchema,
1228)]
1229pub enum Dep {
1230 #[serde(rename = "dependent")]
1231 Dep,
1232}
1233
1234#[derive(Error, Debug)]
1235pub enum ExternalMomentaError {
1236 #[error("Dependent momenta cannot be converted to FourMomentum")]
1237 Dependent,
1238}
1239
1240impl<T> TryFrom<ExternalMomenta<T>> for FourMomentum<T> {
1241 type Error = ExternalMomentaError;
1242 fn try_from(value: ExternalMomenta<T>) -> Result<Self, Self::Error> {
1243 match value {
1244 ExternalMomenta::Dependent(_) => Err(ExternalMomentaError::Dependent),
1245 ExternalMomenta::Independent(data) => Ok(FourMomentum::from(data)),
1246 }
1247 }
1248}
1249
1250impl<T> From<FourMomentum<T>> for ExternalMomenta<T> {
1251 fn from(value: FourMomentum<T>) -> Self {
1252 ExternalMomenta::Independent(value.into())
1253 }
1254}
1255
1256impl<T> From<[T; 4]> for ExternalMomenta<T> {
1257 fn from(data: [T; 4]) -> Self {
1258 ExternalMomenta::Independent(data)
1259 }
1260}
1261
1262impl<T: RefZero, U: RefZero> RefZero for FourMomentum<T, U> {
1263 fn ref_zero(&self) -> Self {
1264 FourMomentum {
1265 temporal: self.temporal.ref_zero(),
1266 spatial: self.spatial.ref_zero(),
1267 }
1268 }
1269}
1270
1271impl<T: RefZero, U: RefZero> RefZero<FourMomentum<T, U>> for &FourMomentum<T, U> {
1272 fn ref_zero(&self) -> FourMomentum<T, U> {
1273 FourMomentum {
1274 temporal: self.temporal.ref_zero(),
1275 spatial: self.spatial.ref_zero(),
1276 }
1277 }
1278}
1279
1280#[derive(
1281 Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Encode, Decode, Eq, PartialOrd, Ord,
1282)]
1283pub struct PolDef {
1284 pub pol_type: PolType,
1285 pub eid: EdgeIndex,
1286}
1287
1288#[derive(
1289 Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Encode, Decode, Eq, PartialOrd, Ord,
1290)]
1291pub enum PolType {
1292 U,
1293 V,
1294 UBar,
1295 VBar,
1296 Scalar,
1297 Epsilon,
1298 EpsilonBar,
1299}
1300
1301impl PolType {
1302 pub(crate) fn bar(self) -> Self {
1303 match self {
1304 Self::Epsilon => Self::EpsilonBar,
1305 Self::Scalar => Self::Scalar,
1306 Self::U => Self::UBar,
1307 Self::UBar => Self::U,
1308 Self::EpsilonBar => Self::Epsilon,
1309 Self::VBar => Self::V,
1310 Self::V => Self::VBar,
1311 }
1312 }
1313}
1314
1315impl Display for PolType {
1316 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1317 match self {
1318 Self::U => write!(f, "u"),
1319 Self::V => write!(f, "v"),
1320 Self::VBar => write!(f, "vbar"),
1321 Self::UBar => write!(f, "ubar"),
1322 Self::Scalar => write!(f, ""),
1323 Self::Epsilon => write!(f, "ϵ"),
1324 Self::EpsilonBar => write!(f, "ϵbar"),
1325 }
1326 }
1327}
1328
1329#[derive(Debug, PartialEq, Clone, bincode_trait_derive::Encode, bincode_trait_derive::Decode)]
1330#[trait_decode(trait = symbolica::state::HasStateMap)]
1331pub struct Polarization<T> {
1332 pub tensor: DenseTensor<T, IndexLess<LibraryRep>>,
1333 pol_type: PolType,
1334}
1335
1336impl<T: FloatLike> ApproxEq<Polarization<Complex<F<T>>>, F<T>> for Polarization<Complex<F<T>>> {
1337 fn approx_eq(&self, other: &Polarization<Complex<F<T>>>, tolerance: &F<T>) -> bool {
1338 self.pol_type == other.pol_type
1339 && self
1340 .tensor
1341 .flat_iter()
1342 .zip(other.tensor.flat_iter())
1343 .all(|((_, a), (_, b))| a.approx_eq(b, tolerance))
1344 }
1345
1346 fn approx_eq_res(
1347 &self,
1348 other: &Polarization<Complex<F<T>>>,
1349 tolerance: &F<T>,
1350 ) -> color_eyre::Result<()> {
1351 if self.pol_type != other.pol_type {
1352 Err(eyre::eyre!(
1353 "Polarization types do not match. self: {}, other: {}",
1354 self,
1355 other
1356 ))
1357 } else {
1358 self.tensor
1359 .flat_iter()
1360 .zip(other.tensor.flat_iter())
1361 .try_for_each(|((i, a), (_, b))| {
1362 a.approx_eq_res(b, tolerance).wrap_err(format!(
1363 "Polarization tensor element {} does not match. self: {}, other: {}",
1364 i, self, other
1365 ))
1366 })
1367 }
1368 }
1369}
1370
1371impl<T: for<'a> std::ops::AddAssign<&'a T>> AddAssign<Polarization<T>> for Polarization<T> {
1372 fn add_assign(&mut self, rhs: Polarization<T>) {
1373 self.tensor += rhs.tensor;
1374 }
1375}
1376
1377impl<T: for<'a> SubAssign<&'a T>> SubAssign<Polarization<T>> for Polarization<T> {
1378 fn sub_assign(&mut self, rhs: Polarization<T>) {
1379 self.tensor -= rhs.tensor;
1380 }
1381}
1382
1383impl<T: FloatLike> Display for Polarization<F<T>> {
1384 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1385 write!(f, "Pol {}: {}", self.pol_type, self.tensor)
1386 }
1387}
1388
1389impl<T: FloatLike> Display for Polarization<Complex<F<T>>> {
1390 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1391 write!(f, "Pol {}: {}", self.pol_type, self.tensor)
1392 }
1393}
1394
1395impl<T: FloatLike> LowerExp for Polarization<Complex<F<T>>> {
1396 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1397 write!(f, "Pol {}: {:+e}", self.pol_type, self.tensor)
1398 }
1399}
1400
1401impl<T> Polarization<T> {
1402 pub(crate) fn map<U>(&self, f: impl Fn(&T) -> U) -> Polarization<U> {
1403 Polarization {
1404 tensor: self.tensor.map_data_ref(f),
1405 pol_type: self.pol_type,
1406 }
1407 }
1408}
1409
1410impl<T: Clone> Polarization<T> {
1411 pub(crate) fn is_scalar(&self) -> bool {
1412 self.pol_type == PolType::Scalar
1413 }
1414 pub(crate) fn scalar(value: T) -> Self {
1415 let structure = IndexLess::new(vec![]);
1416 Polarization {
1417 tensor: DenseTensor {
1418 data: vec![value],
1419 structure,
1420 },
1421 pol_type: PolType::Scalar,
1422 }
1423 }
1424
1425 pub(crate) fn lorentz(value: [T; 4]) -> Self {
1426 let structure = IndexLess::new(vec![Minkowski {}.new_rep(4).cast()]);
1427 Polarization {
1428 tensor: DenseTensor {
1429 data: value.to_vec(),
1430 structure,
1431 },
1432 pol_type: PolType::Epsilon,
1433 }
1434 }
1435
1436 pub(crate) fn bispinor_u(value: [T; 4]) -> Self {
1437 let structure = IndexLess::new(vec![GR.bis.new_rep(4)]);
1438
1439 Polarization {
1440 tensor: DenseTensor {
1441 data: value.to_vec(),
1442 structure,
1443 },
1444 pol_type: PolType::U,
1445 }
1446 }
1447
1448 pub(crate) fn bispinor_v(value: [T; 4]) -> Self {
1449 let structure = IndexLess::new(vec![GR.bis.new_rep(4)]);
1450
1451 Polarization {
1452 tensor: DenseTensor {
1453 data: value.to_vec(),
1454 structure,
1455 },
1456 pol_type: PolType::V,
1457 }
1458 }
1459
1460 pub(crate) fn shadow(&self) -> DenseTensor<Atom, IndexLess<LibraryRep>> {
1461 self.tensor
1462 .structure
1463 .clone()
1464 .to_dense_labeled(|_, i| FlatCoefficent::<NoArgs> {
1465 index: i,
1466 name: Some(symbol!(self.pol_type.to_string())),
1467 args: None,
1468 })
1469 .unwrap()
1470 }
1471}
1472
1473impl<T> Index<usize> for Polarization<T> {
1474 type Output = T;
1475 fn index(&self, index: usize) -> &Self::Output {
1476 &self.tensor[index.into()]
1477 }
1478}
1479
1480impl<T> RefZero for Polarization<T>
1481where
1482 T: RefZero + Clone,
1483{
1484 fn ref_zero(&self) -> Self {
1485 Polarization {
1486 tensor: <DenseTensor<_, _> as RefZero>::ref_zero(&self.tensor),
1487 pol_type: self.pol_type,
1488 }
1489 }
1490}
1491
1492impl<'a, T, U> Mul<&'a U> for Polarization<T>
1493where
1494 T: Clone + MulAssign<&'a U>,
1495{
1496 type Output = Polarization<T>;
1497 fn mul(mut self, rhs: &'a U) -> Self::Output {
1498 for i in self.tensor.data.iter_mut() {
1499 *i *= rhs;
1500 }
1501 self
1502 }
1503}
1504
1505impl<'a, T> MulAssign<&'a T> for Polarization<T>
1506where
1507 T: Clone + MulAssign<&'a T>,
1508{
1509 fn mul_assign(&mut self, rhs: &'a T) {
1510 for i in self.tensor.data.iter_mut() {
1511 *i *= rhs;
1512 }
1513 }
1514}
1515
1516impl<T, U, Out> FallibleAdd<Polarization<U>> for Polarization<T>
1517where
1518 T: FallibleAdd<U, Output = Out>,
1519{
1520 type Output = Polarization<Out>;
1521 fn add_fallible(&self, rhs: &Polarization<U>) -> Option<Self::Output> {
1522 Some(Polarization {
1523 tensor: self.tensor.add_fallible(&rhs.tensor)?,
1524 pol_type: self.pol_type,
1525 })
1526 }
1527}
1528
1529impl<T> Polarization<T> {
1530 pub(crate) fn cast<U>(&self) -> Polarization<U>
1531 where
1532 T: Clone,
1533 U: Clone + From<T>,
1534 {
1535 Polarization {
1536 tensor: self.tensor.cast(),
1537 pol_type: self.pol_type,
1538 }
1539 }
1540}
1541
1542impl<T> Polarization<Complex<T>> {
1543 pub(crate) fn complex_cast<U>(&self) -> Polarization<Complex<U>>
1544 where
1545 T: Clone,
1546 U: Clone + From<T>,
1547 {
1548 Polarization {
1549 tensor: self
1550 .tensor
1551 .map_data_ref(|d| d.map_ref(|r| r.clone().into())),
1552 pol_type: self.pol_type,
1553 }
1554 }
1555}
1556
1557impl<T: FloatLike> Polarization<Complex<F<T>>> {
1558 pub(crate) fn higher(&self) -> Polarization<Complex<F<T::Higher>>>
1559 where
1560 T::Higher: FloatLike,
1561 {
1562 Polarization {
1563 tensor: self.tensor.map_data_ref(|t| t.map_ref(|r| r.higher())),
1564 pol_type: self.pol_type,
1565 }
1566 }
1567
1568 pub(crate) fn lower(&self) -> Polarization<Complex<F<T::Lower>>>
1569 where
1570 T::Lower: FloatLike,
1571 {
1572 Polarization {
1573 tensor: self.tensor.map_data_ref(|t| t.map_ref(|r| r.lower())),
1574 pol_type: self.pol_type,
1575 }
1576 }
1577}
1578
1579impl<T: FloatLike, U: FloatLike> From<FourMomentum<T, U>> for FourMomentum<F<T>, F<U>> {
1580 fn from(value: FourMomentum<T, U>) -> Self {
1581 FourMomentum {
1582 temporal: value.temporal.into(),
1583 spatial: value.spatial.into(),
1584 }
1585 }
1586}
1587
1588impl<T: FloatLike, U: FloatLike> FourMomentum<F<T>, F<U>> {
1589 pub(crate) fn higher(&self) -> FourMomentum<F<T::Higher>, F<U::Higher>>
1590 where
1591 T::Higher: FloatLike,
1592 U::Higher: FloatLike,
1593 {
1594 FourMomentum {
1595 temporal: self.temporal.higher(),
1596 spatial: self.spatial.higher(),
1597 }
1598 }
1599
1600 pub(crate) fn lower(&self) -> FourMomentum<F<T::Lower>, F<U::Lower>>
1601 where
1602 T::Lower: FloatLike,
1603 U::Lower: FloatLike,
1604 {
1605 FourMomentum {
1606 temporal: self.temporal.lower(),
1607 spatial: self.spatial.lower(),
1608 }
1609 }
1610}
1611
1612impl<T, U> FourMomentum<T, U> {
1613 pub(crate) fn new(energy: Energy<U>, three_momentum: ThreeMomentum<T>) -> Self {
1614 FourMomentum {
1615 temporal: energy,
1616 spatial: three_momentum,
1617 }
1618 }
1619}
1620
1621impl<T: Real> RefDefault for FourMomentum<T, T> {
1622 fn default(&self) -> Self {
1623 let zero = self.temporal.value.zero();
1624 FourMomentum {
1625 temporal: Energy::new(zero.clone()),
1626 spatial: ThreeMomentum::new(zero.clone(), zero.clone(), zero.clone()),
1627 }
1628 }
1629}
1630
1631impl<T> Mul<T> for &FourMomentum<T, T>
1632where
1633 T: Mul<T, Output = T> + Clone,
1634{
1635 type Output = FourMomentum<T, T>;
1636 fn mul(self, rhs: T) -> Self::Output {
1637 FourMomentum {
1638 temporal: Energy {
1639 value: self.temporal.value.clone() * rhs.clone(),
1640 },
1641 spatial: self.spatial.clone() * rhs,
1642 }
1643 }
1644}
1645
1646impl<T> Mul<&T> for &FourMomentum<T, T>
1647where
1648 T: for<'b> Mul<&'b T, Output = T> + Clone,
1649{
1650 type Output = FourMomentum<T, T>;
1651 fn mul(self, rhs: &T) -> Self::Output {
1652 FourMomentum {
1653 temporal: Energy {
1654 value: self.temporal.value.clone() * rhs,
1655 },
1656 spatial: self.spatial.clone() * rhs,
1657 }
1658 }
1659}
1660
1661impl<T> FourMomentum<T, T> {
1662 pub(crate) fn zero(&self) -> Self
1663 where
1664 T: Real,
1665 {
1666 let zero = self.temporal.value.zero();
1667 FourMomentum {
1668 temporal: Energy::new(zero.clone()),
1669 spatial: ThreeMomentum::new(zero.clone(), zero.clone(), zero.clone()),
1670 }
1671 }
1672
1673 pub(crate) fn square(&self) -> T
1674 where
1675 T: for<'a> Mul<&'a T, Output = T> + Add<T, Output = T> + Clone + Sub<T, Output = T>,
1676 {
1677 let temporal = self.temporal.value.clone();
1678 let spatial = self.spatial.norm_squared();
1679 temporal * &self.temporal.value - spatial
1680 }
1681
1682 pub(crate) fn norm(&self) -> T
1683 where
1684 T: Real,
1685 {
1686 self.square().sqrt()
1687 }
1688
1689 pub(crate) fn from_args(energy: T, px: T, py: T, pz: T) -> Self {
1690 let energy = Energy::new(energy);
1691 let three_momentum = ThreeMomentum { px, py, pz };
1692 FourMomentum {
1693 temporal: energy,
1694 spatial: three_momentum,
1695 }
1696 }
1697 pub(crate) fn new_on_shell(three_momentum: ThreeMomentum<T>, mass: Option<T>) -> Self
1698 where
1699 T: Mul<T> + Add<T> + std::ops::Add<Output = T> + Real,
1700 {
1701 let energy = three_momentum.on_shell_energy(mass);
1702 FourMomentum {
1704 temporal: energy,
1705 spatial: three_momentum,
1706 }
1707 }
1708
1709 pub(crate) fn into_dense(self, index: AbstractIndex) -> DenseTensor<T, OrderedStructure>
1710 where
1711 T: Clone,
1712 {
1713 let structure =
1714 PermutedStructure::from_iter([LibraryRep::new_slot(Minkowski {}.into(), 4, index)])
1715 .structure;
1716 DenseTensor::from_data(
1717 vec![
1718 self.temporal.value,
1719 self.spatial.px,
1720 self.spatial.py,
1721 self.spatial.pz,
1722 ],
1723 structure,
1724 )
1725 .unwrap()
1726 }
1727
1728 pub(crate) fn into_dense_named(
1729 self,
1730 index: AbstractIndex,
1731 name: Symbol,
1732 num: usize,
1733 ) -> DenseTensor<T, NamedStructure<Symbol, usize>>
1734 where
1735 T: Clone,
1736 {
1737 let structure = PermutedStructure::<OrderedStructure>::from_iter([LibraryRep::new_slot(
1738 Minkowski {}.into(),
1739 4,
1740 index,
1741 )])
1742 .structure
1743 .to_named(name, Some(num));
1744 DenseTensor::from_data(
1745 vec![
1746 self.temporal.value,
1747 self.spatial.px,
1748 self.spatial.py,
1749 self.spatial.pz,
1750 ],
1751 structure,
1752 )
1753 .unwrap()
1754 }
1755
1756 pub(crate) fn cast<U>(&self) -> FourMomentum<U, U>
1757 where
1758 T: Clone + Into<U>,
1759 {
1760 FourMomentum {
1761 temporal: Energy::new((self.temporal.value.clone()).into()),
1762 spatial: ThreeMomentum::cast(&self.spatial),
1763 }
1764 }
1765
1766 pub(crate) fn boost(&self, boost_vector: &ThreeMomentum<T>) -> FourMomentum<T>
1767 where
1768 T: Real + SingleFloat + PartialOrd,
1769 {
1770 let b2 = boost_vector.norm_squared();
1771 let one = b2.one();
1772 let zero = one.zero();
1773 let gamma = (one.clone() - &b2).sqrt().inv();
1774
1775 let bp = self.spatial.clone() * boost_vector;
1776 let gamma2 = if b2 > zero {
1777 (gamma.clone() - &one) / b2
1778 } else {
1779 zero
1780 };
1781 let factor = gamma2 * &bp + gamma.clone() * &self.temporal.value;
1782
1783 FourMomentum::from_args(
1784 (bp + &self.temporal.value) * &gamma,
1785 self.spatial.px.mul_add(&factor, &boost_vector.px),
1786 self.spatial.py.mul_add(&factor, &boost_vector.py),
1787 self.spatial.pz.mul_add(&factor, &boost_vector.pz),
1788 )
1789 }
1790}
1791
1792impl<T: FloatLike> FourMomentum<F<T>, F<T>> {
1793 pub(crate) fn dot(&self, p2: &FourMomentum<F<T>>) -> F<T> {
1794 &self.temporal.value * &p2.temporal.value
1795 - (&self.spatial.px * &p2.spatial.px
1796 + &self.spatial.py * &p2.spatial.py
1797 + &self.spatial.pz * &p2.spatial.pz)
1798 }
1799 pub(crate) fn getdelphi(&self, p2: &FourMomentum<F<T>>) -> F<T>
1801 where
1802 T: Real,
1803 {
1804 self.spatial.getdelphi(&p2.spatial)
1805 }
1806
1807 #[inline]
1809 pub(crate) fn delta_r(&self, p2: &FourMomentum<F<T>>) -> F<T>
1810 where
1811 T: Real,
1812 {
1813 self.spatial.delta_r(&p2.spatial)
1814 }
1815
1816 pub(crate) fn pt(&self) -> F<T>
1817 where
1818 T: Real,
1819 {
1820 self.spatial.pt()
1821 }
1822
1823 pub(crate) fn to_f64(&self) -> FourMomentum<F<f64>, F<f64>> {
1824 FourMomentum {
1825 temporal: Energy {
1826 value: F(self.temporal.value.to_f64()),
1827 },
1828 spatial: self.spatial.to_f64().cast(),
1829 }
1830 }
1831
1832 pub(crate) fn pol_one(&self) -> [F<T>; 4]
1833 where
1834 T: FloatLike,
1835 {
1836 let pt = self.pt();
1841 let p = self.spatial.norm();
1842
1843 let (e1, e2, e3) = if pt.is_zero() {
1844 (pt.one(), pt.zero(), pt.zero())
1845 } else {
1846 (
1847 &self.spatial.px * &self.spatial.pz / (&pt * &p),
1848 &self.spatial.py * &self.spatial.pz / (&pt * &p),
1849 -(&pt / &p),
1850 )
1851 };
1852
1853 [pt.zero(), e1, e2, e3]
1861
1862 }
1864
1865 pub(crate) fn pol_two(&self) -> [F<T>; 4]
1866 where
1867 T: FloatLike,
1868 {
1869 let pt = self.pt();
1871 let (e1, e2, e3) = if pt.is_zero() {
1872 if self.spatial.pz.positive() {
1873 (pt.zero(), pt.one(), pt.zero())
1874 } else {
1875 (pt.zero(), -pt.one(), pt.zero())
1876 }
1877 } else {
1878 (-(&self.spatial.py / &pt), &self.spatial.px / &pt, pt.zero())
1879 };
1880 [pt.zero(), e1, e2, e3]
1881 }
1882
1883 pub(crate) fn pol_three(&self) -> Polarization<F<T>>
1884 where
1885 T: FloatLike,
1886 {
1887 let m = self.norm();
1889 let p = self.spatial.norm();
1890 let emp = &self.temporal.value / (&m * &p);
1891 let e0 = p / &m;
1892 let e1 = &self.spatial.px * &emp;
1893 let e2 = &self.spatial.py * &emp;
1894 let e3 = &self.spatial.pz * &emp;
1895
1896 Polarization::lorentz([e0, e1, e2, e3])
1897 }
1898
1899 pub(crate) fn eps_pol<Hel: Into<Helicity>>(&self, lambda: Hel) -> Polarization<Complex<F<T>>> {
1900 match lambda.into() {
1901 Helicity::Signed(lambda) => {
1902 if lambda.is_zero() {
1903 self.pol_three().cast()
1904 } else {
1905 let one = self.temporal.value.one();
1906 let sqrt_2_inv: F<T> = (&one + &one).sqrt().inv();
1907
1908 let [eone0, eone1, eone2, eone3] = self.pol_one();
1909
1910 let [etwo0, etwo1, etwo2, etwo3] = self.pol_two();
1911
1912 let components = [
1913 Complex {
1914 re: -lambda * eone0 * &sqrt_2_inv,
1915 im: -etwo0 * &sqrt_2_inv, },
1917 Complex {
1918 re: -lambda * eone1 * &sqrt_2_inv,
1919 im: -etwo1 * &sqrt_2_inv,
1920 },
1921 Complex {
1922 re: -lambda * eone2 * &sqrt_2_inv,
1923 im: -etwo2 * &sqrt_2_inv,
1924 },
1925 Complex {
1926 re: -lambda * eone3 * &sqrt_2_inv,
1927 im: -etwo3 * &sqrt_2_inv,
1928 },
1929 ];
1930
1931 Polarization::lorentz(components)
1932 }
1933 }
1934 _ => {
1935 let zero = self.temporal.value.zero();
1936 let cmpz = Complex::new_re(zero);
1937 Polarization::lorentz([cmpz.clone(), cmpz.clone(), cmpz.clone(), cmpz.clone()])
1938 }
1939 }
1940 }
1941
1942 pub(crate) fn omega(&self, lambda: Sign) -> Complex<F<T>> {
1943 match lambda {
1944 Sign::Positive => (&self.temporal.value + self.spatial.norm()).complex_sqrt(),
1945 Sign::Negative => (&self.temporal.value - self.spatial.norm()).complex_sqrt(),
1946 }
1947 }
1948
1949 pub(crate) fn u(&self, lambda: Sign) -> Polarization<Complex<F<T>>> {
1950 let xi = self.xi(lambda);
1951 Polarization::bispinor_u([
1952 self.omega(-lambda) * &xi[0],
1953 self.omega(-lambda) * &xi[1],
1954 self.omega(lambda) * &xi[0],
1955 self.omega(lambda) * &xi[1],
1956 ])
1957 }
1958
1959 pub(crate) fn v(&self, lambda: Sign) -> Polarization<Complex<F<T>>> {
1960 let xi = self.xi(-lambda);
1961 Polarization::bispinor_v([
1962 (-lambda) * self.omega(lambda) * &xi[0],
1963 (-lambda) * self.omega(lambda) * &xi[1],
1964 lambda * self.omega(-lambda) * &xi[0],
1965 lambda * self.omega(-lambda) * &xi[1],
1966 ])
1967 }
1968
1969 pub(crate) fn xi(&self, lambda: Sign) -> [Complex<F<T>>; 2] {
1970 if (self.spatial.pz < self.spatial.pz.zero()
1971 && self.spatial.py.is_zero()
1972 && self.spatial.px.is_zero())
1973 || (self.spatial.pz == -self.spatial.norm())
1974 {
1975 let zero: Complex<F<T>> = self.temporal.value.zero().into();
1976 let one = zero.one();
1977 match lambda {
1979 Sign::Positive => [zero, -one],
1980 Sign::Negative => [one, zero],
1981 }
1982 } else {
1983 let prefactor: F<T> = ((F::from_f64(2.)
1984 * self.spatial.norm()
1985 * (self.spatial.norm() + &self.spatial.pz))
1986 .sqrt())
1987 .inv();
1988 let mut xi: [Complex<F<T>>; 2] = [
1989 Complex::new_re(&prefactor * (self.spatial.norm() + &self.spatial.pz)),
1990 Complex::new(self.spatial.px.clone(), self.spatial.py.clone()) * &prefactor,
1991 ]; if matches!(lambda, Sign::Negative) {
1994 xi.swap(0, 1);
1995 xi[0].re = -xi[0].re.clone();
1996 }
1997 xi
1998 }
1999 }
2000}
2001
2002impl<T: FloatLike> Polarization<Complex<F<T>>> {
2003 pub(crate) fn bar(&self) -> Self {
2004 let mut tensor = self.tensor.map_data_ref(Complex::conj);
2005
2006 if matches!(
2007 self.pol_type,
2008 PolType::U | PolType::V | PolType::UBar | PolType::VBar
2009 ) {
2010 tensor.data.swap(0, 2);
2011 tensor.data.swap(1, 3);
2012 }
2013 Polarization {
2014 tensor,
2015 pol_type: self.pol_type.bar(),
2016 }
2017 }
2018}
2019
2020impl<T> IntoIterator for Polarization<T> {
2021 type IntoIter = std::vec::IntoIter<Self::Item>;
2022 type Item = T;
2023
2024 fn into_iter(self) -> Self::IntoIter {
2025 self.tensor.data.into_iter()
2026 }
2027}
2028
2029impl<'a, T> IntoIterator for &'a Polarization<T> {
2030 type IntoIter = std::slice::Iter<'a, T>;
2031 type Item = &'a T;
2032
2033 fn into_iter(self) -> Self::IntoIter {
2034 self.tensor.data.iter()
2035 }
2036}
2037
2038#[derive(
2039 Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash, bincode::Encode, bincode::Decode,
2040)]
2041pub enum Helicity {
2042 Signed(SignOrZero),
2043 Summed,
2044 SummedAveraged,
2045}
2046
2047impl Display for Helicity {
2048 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2049 match self {
2050 Helicity::Signed(s) => write!(f, "{}", s),
2051 Helicity::Summed => write!(f, "summed"),
2052 Helicity::SummedAveraged => write!(f, "summed_averaged"),
2053 }
2054 }
2055}
2056
2057impl Serialize for Helicity {
2058 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2059 where
2060 S: serde::Serializer,
2061 {
2062 match self {
2063 Helicity::Signed(SignOrZero::Plus) => serializer.serialize_i8(1),
2064 Helicity::Signed(SignOrZero::Minus) => serializer.serialize_i8(-1),
2065 Helicity::Signed(SignOrZero::Zero) => serializer.serialize_i8(0),
2066 Helicity::Summed => serializer.serialize_str("summed"),
2067 Helicity::SummedAveraged => serializer.serialize_str("summed_averaged"),
2068 }
2069 }
2070}
2071
2072#[derive(Deserialize)]
2073#[serde(untagged)]
2074enum HelicitySerde {
2075 Numeric(i8),
2076 Text(String),
2077}
2078
2079impl<'de> Deserialize<'de> for Helicity {
2080 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2081 where
2082 D: serde::Deserializer<'de>,
2083 {
2084 let value = HelicitySerde::deserialize(deserializer)?;
2085 match value {
2086 HelicitySerde::Numeric(value) => {
2087 Helicity::try_from(value).map_err(serde::de::Error::custom)
2088 }
2089 HelicitySerde::Text(value) => {
2090 Helicity::from_str(&value).map_err(serde::de::Error::custom)
2091 }
2092 }
2093 }
2094}
2095
2096impl JsonSchema for Helicity {
2097 fn schema_name() -> std::borrow::Cow<'static, str> {
2098 "Helicity".into()
2099 }
2100
2101 fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
2102 schemars::json_schema!({
2103 "description": "Helicity input. Use -1, 0, or 1 for fixed helicities; or use a string for summed modes.",
2104 "oneOf": [
2105 {
2106 "type": "integer",
2107 "enum": [-1, 0, 1]
2108 },
2109 {
2110 "type": "string",
2111 "enum": [
2112 "+",
2113 "-",
2114 "0",
2115 "plus",
2116 "minus",
2117 "zero",
2118 "summed",
2119 "summed_averaged",
2120 "summed-averaged"
2121 ]
2122 }
2123 ]
2124 })
2125 }
2126}
2127
2128impl TryFrom<i8> for Helicity {
2129 type Error = SignError;
2130 fn try_from(value: i8) -> Result<Self, Self::Error> {
2131 Ok(Helicity::Signed(SignOrZero::try_from(value)?))
2132 }
2133}
2134
2135impl From<Sign> for Helicity {
2136 fn from(sign: Sign) -> Self {
2137 Self::Signed(sign.into())
2138 }
2139}
2140
2141impl From<SignOrZero> for Helicity {
2142 fn from(sign: SignOrZero) -> Self {
2143 Self::Signed(sign)
2144 }
2145}
2146
2147impl TryFrom<Helicity> for SignOrZero {
2148 type Error = SignError;
2149
2150 fn try_from(value: Helicity) -> Result<Self, Self::Error> {
2151 match value {
2152 Helicity::Signed(sign) => Ok(sign),
2153 Helicity::Summed | Helicity::SummedAveraged => Err(SignError::InvalidValue),
2154 }
2155 }
2156}
2157
2158impl TryFrom<Helicity> for Sign {
2159 type Error = SignError;
2160
2161 fn try_from(value: Helicity) -> Result<Self, Self::Error> {
2162 match value {
2163 Helicity::Signed(sign) => sign.try_into(),
2164 Helicity::Summed | Helicity::SummedAveraged => Err(SignError::InvalidValue),
2165 }
2166 }
2167}
2168
2169#[allow(non_upper_case_globals)]
2170impl Helicity {
2171 pub const ZERO: Self = Self::Signed(SignOrZero::Zero);
2172 pub const PLUS: Self = Self::Signed(SignOrZero::Plus);
2173 pub const MINUS: Self = Self::Signed(SignOrZero::Minus);
2174 pub const Zero: Self = Self::ZERO;
2175 pub const Plus: Self = Self::PLUS;
2176 pub const Minus: Self = Self::MINUS;
2177}
2178
2179impl FromStr for Helicity {
2180 type Err = String;
2181
2182 fn from_str(value: &str) -> Result<Self, Self::Err> {
2183 match value.trim().to_ascii_lowercase().as_str() {
2184 "+" | "plus" => Ok(Self::PLUS),
2185 "-" | "minus" => Ok(Self::MINUS),
2186 "0" | "zero" => Ok(Self::ZERO),
2187 "summed" => Ok(Self::Summed),
2188 "summed_averaged" | "summed-averaged" => Ok(Self::SummedAveraged),
2189 _ => Err(format!(
2190 "Invalid helicity '{value}'. Expected one of -1, 0, 1, plus, minus, zero, summed, summed_averaged"
2191 )),
2192 }
2193 }
2194}
2195
2196#[derive(
2197 Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Encode, Decode, PartialOrd, Ord, Hash,
2198)]
2199pub struct Signature(pub Vec<SignOrZero>);
2200
2201impl Display for Signature {
2202 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2203 for sign in &self.0 {
2204 write!(f, "{}", sign)?;
2205 }
2206 Ok(())
2207 }
2208}
2209
2210impl FromIterator<SignOrZero> for Signature {
2211 fn from_iter<I: IntoIterator<Item = SignOrZero>>(iter: I) -> Self {
2212 Signature(iter.into_iter().collect())
2213 }
2214}
2215
2216impl FromIterator<i8> for Signature {
2217 fn from_iter<I: IntoIterator<Item = i8>>(iter: I) -> Self {
2218 Signature(
2219 iter.into_iter()
2220 .map(|x| match x {
2221 0 => SignOrZero::Zero,
2222 1 => SignOrZero::Plus,
2223 -1 => SignOrZero::Minus,
2224 _ => panic!("Invalid value for Signature"),
2225 })
2226 .collect(),
2227 )
2228 }
2229}
2230
2231impl FromIterator<isize> for Signature {
2232 fn from_iter<I: IntoIterator<Item = isize>>(iter: I) -> Self {
2233 Signature(
2234 iter.into_iter()
2235 .map(|x| match x {
2236 0 => SignOrZero::Zero,
2237 1 => SignOrZero::Plus,
2238 -1 => SignOrZero::Minus,
2239 _ => panic!("Invalid value for Signature"),
2240 })
2241 .collect(),
2242 )
2243 }
2244}
2245
2246impl From<Vec<i8>> for Signature {
2247 fn from(value: Vec<i8>) -> Self {
2248 Signature::from_iter(value)
2249 }
2250}
2251
2252impl IntoIterator for Signature {
2253 type Item = SignOrZero;
2254 type IntoIter = std::vec::IntoIter<Self::Item>;
2255
2256 fn into_iter(self) -> Self::IntoIter {
2257 self.0.into_iter()
2258 }
2259}
2260
2261impl<'a> IntoIterator for &'a Signature {
2262 type Item = SignOrZero;
2263 type IntoIter = std::iter::Copied<std::slice::Iter<'a, Self::Item>>;
2264
2265 fn into_iter(self) -> Self::IntoIter {
2266 self.0.iter().copied()
2267 }
2268}
2269
2270impl Index<usize> for Signature {
2271 type Output = SignOrZero;
2272 fn index(&self, index: usize) -> &Self::Output {
2273 &self.0[index]
2274 }
2275}
2276
2277impl Signature {
2278 pub(crate) fn validate_basis<T>(&self, basis: &[T]) -> bool {
2279 self.len() == basis.len()
2280 }
2281
2282 pub(crate) fn sum(&mut self, other: &Self) {
2283 for (i, sign) in other.iter().enumerate() {
2284 match (self[i], sign) {
2285 (SignOrZero::Zero, SignOrZero::Zero) => self.0[i] = SignOrZero::Zero,
2286 (SignOrZero::Zero, SignOrZero::Plus) => self.0[i] = SignOrZero::Plus,
2287 (SignOrZero::Zero, SignOrZero::Minus) => self.0[i] = SignOrZero::Minus,
2288 (SignOrZero::Plus, SignOrZero::Zero) => self.0[i] = SignOrZero::Plus,
2289 (SignOrZero::Plus, SignOrZero::Plus) => panic!("cannot add two positive signs"),
2290 (SignOrZero::Plus, SignOrZero::Minus) => self.0[i] = SignOrZero::Zero,
2291 (SignOrZero::Minus, SignOrZero::Zero) => self.0[i] = SignOrZero::Minus,
2292 (SignOrZero::Minus, SignOrZero::Plus) => self.0[i] = SignOrZero::Zero,
2293 (SignOrZero::Minus, SignOrZero::Minus) => panic!("cannot add two negative signs"),
2294 }
2295 }
2296 }
2297
2298 pub(crate) fn panic_validate_basis<T>(&self, basis: &[T]) {
2299 if !self.validate_basis(basis) {
2300 panic!(
2301 "Invalid basis for Signature, expected length {}, got length {}",
2302 self.len(),
2303 basis.len()
2304 );
2305 }
2306 }
2307
2308 pub(crate) fn to_momtrop_format(&self) -> Vec<isize> {
2309 self.0
2310 .iter()
2311 .map(|x| match x {
2312 SignOrZero::Zero => 0,
2313 SignOrZero::Plus => 1,
2314 SignOrZero::Minus => -1,
2315 })
2316 .collect()
2317 }
2318 pub(crate) fn len(&self) -> usize {
2319 self.0.len()
2320 }
2321
2322 pub(crate) fn iter(&'_ self) -> std::slice::Iter<'_, SignOrZero> {
2323 self.0.iter()
2324 }
2325
2326 pub(crate) fn is_empty(&self) -> bool {
2327 self.0.is_empty()
2328 }
2329 pub(crate) fn apply<T>(&self, basis: &[T]) -> T
2330 where
2331 T: RefZero + Clone + Neg<Output = T> + AddAssign<T>,
2332 {
2333 let mut result = basis[0].ref_zero();
2335 for (&sign, t) in self.0.iter().zip(basis.iter().cloned()) {
2336 result += sign * t;
2337 }
2338 result
2339 }
2340
2341 pub(crate) fn apply_iter<I, T>(&self, basis: I) -> Option<T>
2342 where
2343 I: IntoIterator,
2344 I::Item: RefZero<T>,
2345 T: Clone + SubAssign<I::Item> + AddAssign<I::Item>,
2346 {
2347 let mut basis_iter = basis.into_iter();
2348 let mut signature_iter = self.into_iter();
2349
2350 while let (Some(sign), Some(item)) = (signature_iter.next(), basis_iter.next()) {
2351 if sign.is_sign() {
2352 let mut result = item.ref_zero();
2354 match sign {
2355 SignOrZero::Zero => {
2356 panic!("unreachable");
2357 }
2359 SignOrZero::Plus => {
2360 result += item;
2361 }
2362 SignOrZero::Minus => {
2363 result -= item;
2364 }
2365 }
2366
2367 while let (Some(sign), Some(item)) = (signature_iter.next(), basis_iter.next()) {
2369 match sign {
2370 SignOrZero::Zero => {}
2371 SignOrZero::Plus => {
2372 result += item;
2373 }
2374 SignOrZero::Minus => {
2375 result -= item;
2376 }
2377 }
2378 }
2379
2380 return Some(result);
2381 }
2382 }
2383
2384 None
2386 }
2387
2388 pub(crate) fn label_with(&self, label: &str) -> String {
2389 let mut result = String::new();
2390 let mut first = true;
2391 for (i, sign) in self.0.iter().enumerate() {
2392 if !first {
2393 result.push_str(&sign.to_string());
2394 } else {
2395 first = false;
2396 }
2397 if sign.is_sign() {
2398 result.push_str(&format!("{}_{}", label, i));
2399 }
2400 }
2401 result
2402 }
2403}
2404
2405#[test]
2406fn test_signature() {
2407 let sig = Signature(vec![SignOrZero::Plus, SignOrZero::Minus]);
2408 let basis: Vec<i32> = vec![1, 2];
2409 assert_eq!(sig.apply(&basis), 1 - 2);
2410 assert_eq!(sig.apply_iter(basis.iter()), Some(-1));
2411
2412 let basis: [FourMomentum<i32>; 4] = [
2413 FourMomentum::from_args(1, 1, 0, 0),
2414 FourMomentum::from_args(1, 0, 1, 0),
2415 FourMomentum::from_args(1, 0, 0, 1),
2416 FourMomentum::from_args(1, 1, 1, 1),
2417 ];
2418
2419 let sig = Signature(vec![
2420 SignOrZero::Plus,
2421 SignOrZero::Minus,
2422 SignOrZero::Zero,
2423 SignOrZero::Plus,
2424 ]);
2425
2426 assert_eq!(sig.apply(&basis), FourMomentum::from_args(1, 2, 0, 1));
2427 let sig = Signature(vec![
2428 SignOrZero::Zero,
2429 SignOrZero::Zero,
2430 SignOrZero::Zero,
2431 SignOrZero::Zero,
2432 ]);
2433 assert_eq!(sig.apply_iter(basis.iter()), None);
2434 let sig = Signature(vec![
2435 SignOrZero::Zero,
2436 SignOrZero::Zero,
2437 SignOrZero::Zero,
2438 SignOrZero::Minus,
2439 ]);
2440 assert_eq!(sig.apply_iter(basis.iter()), Some(-basis[3]));
2441}
2442
2443impl<T> IntoIterator for FourMomentum<T> {
2473 type Item = T;
2474 type IntoIter = std::array::IntoIter<T, 4>;
2475
2476 fn into_iter(self) -> Self::IntoIter {
2477 let [e, px, py, pz] = [
2478 self.temporal.value,
2479 self.spatial.px,
2480 self.spatial.py,
2481 self.spatial.pz,
2482 ];
2483 [e, px, py, pz].into_iter()
2484 }
2485}
2486
2487impl<'a, T> IntoIterator for &'a FourMomentum<T> {
2488 type Item = &'a T;
2489 type IntoIter = std::array::IntoIter<&'a T, 4>;
2490
2491 fn into_iter(self) -> Self::IntoIter {
2492 let [e, px, py, pz] = [
2493 &self.temporal.value,
2494 &self.spatial.px,
2495 &self.spatial.py,
2496 &self.spatial.pz,
2497 ];
2498 [e, px, py, pz].into_iter()
2499 }
2500}
2501
2502impl<T> From<[T; 4]> for FourMomentum<T, T> {
2503 fn from(data: [T; 4]) -> Self {
2504 let [t, px, py, pz] = data;
2505 FourMomentum {
2506 temporal: Energy::new(t),
2507 spatial: ThreeMomentum { px, py, pz },
2508 }
2509 }
2510}
2511
2512impl<T> From<FourMomentum<T, T>> for [T; 4] {
2513 fn from(data: FourMomentum<T, T>) -> Self {
2514 [
2515 data.temporal.value,
2516 data.spatial.px,
2517 data.spatial.py,
2518 data.spatial.pz,
2519 ]
2520 }
2521}
2522
2523impl<T> From<(T, T, T, T)> for FourMomentum<T, T> {
2524 fn from(data: (T, T, T, T)) -> Self {
2525 let (t, px, py, pz) = data;
2526 FourMomentum {
2527 temporal: Energy::new(t),
2528 spatial: ThreeMomentum { px, py, pz },
2529 }
2530 }
2531}
2532
2533impl<T> From<FourMomentum<T, T>> for (T, T, T, T) {
2534 fn from(data: FourMomentum<T, T>) -> Self {
2535 (
2536 data.temporal.value,
2537 data.spatial.px,
2538 data.spatial.py,
2539 data.spatial.pz,
2540 )
2541 }
2542}
2543
2544impl<T> FourMomentum<T, Atom> {
2545 pub(crate) fn into_dense_param(
2546 self,
2547 index: AbstractIndex,
2548 ) -> DenseTensor<MultivariatePolynomial<RationalField, T>, OrderedStructure>
2549 where
2550 T: Clone + Into<Coefficient> + Exponent,
2551 {
2552 let structure =
2553 PermutedStructure::from_iter([LibraryRep::new_slot(Minkowski {}.into(), 4, index)])
2554 .structure;
2555 let energy = self
2556 .temporal
2557 .value
2558 .to_polynomial(&RationalField::new(IntegerRing {}), None);
2559
2560 let px: MultivariatePolynomial<RationalField, _> =
2561 Atom::num(self.spatial.px).to_polynomial(&RationalField::new(IntegerRing {}), None);
2562 let py =
2563 Atom::num(self.spatial.py).to_polynomial(&RationalField::new(IntegerRing {}), None);
2564 let pz =
2565 Atom::num(self.spatial.pz).to_polynomial(&RationalField::new(IntegerRing {}), None);
2566
2567 DenseTensor::from_data(vec![energy, px, py, pz], structure).unwrap()
2568 }
2569}
2570
2571impl<T, U> Add<FourMomentum<T, U>> for FourMomentum<T, U>
2572where
2573 T: Add<T, Output = T>,
2574 U: Add<U, Output = U>,
2575{
2576 type Output = FourMomentum<T, U>;
2577 fn add(self, rhs: FourMomentum<T, U>) -> Self::Output {
2578 FourMomentum {
2579 temporal: self.temporal + rhs.temporal,
2580 spatial: self.spatial + rhs.spatial,
2581 }
2582 }
2583}
2584
2585impl<T, U> AddAssign<FourMomentum<T, U>> for FourMomentum<T, U>
2586where
2587 T: AddAssign<T>,
2588 U: AddAssign<U>,
2589{
2590 fn add_assign(&mut self, rhs: FourMomentum<T, U>) {
2591 self.temporal += rhs.temporal;
2592 self.spatial += rhs.spatial;
2593 }
2594}
2595
2596impl<'a, T, U> AddAssign<&'a FourMomentum<T, U>> for FourMomentum<T, U>
2597where
2598 T: AddAssign<&'a T>,
2599 U: AddAssign<&'a U>,
2600{
2601 fn add_assign(&mut self, rhs: &'a FourMomentum<T, U>) {
2602 self.temporal += &rhs.temporal;
2603 self.spatial += &rhs.spatial;
2604 }
2605}
2606
2607impl<T, U> Sub<FourMomentum<T, U>> for FourMomentum<T, U>
2608where
2609 T: Sub<T, Output = T>,
2610 U: Sub<U, Output = U>,
2611{
2612 type Output = FourMomentum<T, U>;
2613 fn sub(self, rhs: FourMomentum<T, U>) -> Self::Output {
2614 FourMomentum {
2615 temporal: self.temporal - rhs.temporal,
2616 spatial: self.spatial - rhs.spatial,
2617 }
2618 }
2619}
2620
2621impl<T, U> Neg for FourMomentum<T, U>
2622where
2623 T: Neg<Output = T>,
2624 U: Neg<Output = U>,
2625{
2626 type Output = FourMomentum<T, U>;
2627 fn neg(self) -> Self::Output {
2628 FourMomentum {
2629 temporal: -self.temporal,
2630 spatial: -self.spatial,
2631 }
2632 }
2633}
2634
2635impl<T, U> Neg for &FourMomentum<T, U>
2636where
2637 T: Neg<Output = T> + Clone,
2638 U: Neg<Output = U> + Clone,
2639{
2640 type Output = FourMomentum<T, U>;
2641 fn neg(self) -> Self::Output {
2642 FourMomentum {
2643 temporal: -self.temporal.clone(),
2644 spatial: -self.spatial.clone(),
2645 }
2646 }
2647}
2648
2649impl<T, U> SubAssign<FourMomentum<T, U>> for FourMomentum<T, U>
2650where
2651 T: SubAssign<T>,
2652 U: SubAssign<U>,
2653{
2654 fn sub_assign(&mut self, rhs: FourMomentum<T, U>) {
2655 self.temporal -= rhs.temporal;
2656 self.spatial -= rhs.spatial;
2657 }
2658}
2659
2660impl<'a, T, U> SubAssign<&'a FourMomentum<T, U>> for FourMomentum<T, U>
2661where
2662 T: SubAssign<&'a T>,
2663 U: SubAssign<&'a U>,
2664{
2665 fn sub_assign(&mut self, rhs: &'a FourMomentum<T, U>) {
2666 self.temporal -= &rhs.temporal;
2667 self.spatial -= &rhs.spatial;
2668 }
2669}
2670
2671impl<T: Display, U: Display> Display for FourMomentum<T, U> {
2672 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2673 write!(f, "{}, {}", self.temporal, self.spatial)
2674 }
2675}
2676
2677impl<T: LowerExp, U: LowerExp> LowerExp for FourMomentum<T, U> {
2678 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2679 write!(f, "{:e}, {:e}", self.temporal, self.spatial)
2680 }
2681}
2682
2683impl<T: FloatLike> From<Vector<F<T>, 3>> for ThreeMomentum<F<T>> {
2684 fn from(value: Vector<F<T>, 3>) -> Self {
2685 ThreeMomentum::new(value[0].clone(), value[1].clone(), value[2].clone())
2686 }
2687}
2688
2689pub enum LorentzTransformation<T> {
2690 Boost(ThreeMomentum<T>),
2691 Rotation(Rotation),
2692 General {
2693 boost: ThreeMomentum<T>,
2694 rotation: Rotation,
2695 },
2696}
2697
2698pub trait LorentzTransformable<T>: Rotatable {
2699 fn lorentz_transform(&self, transformation: &LorentzTransformation<T>) -> Self;
2700}
2701
2702pub enum LorentzRep {
2730 Scalar,
2733 Vector,
2734 Bispinor,
2735}
2736
2737#[derive(Clone, Copy, Debug, Encode, Decode)]
2738pub enum RotationMethod {
2739 EulerAngles(f64, f64, f64),
2740 Pi2X,
2741 Pi2Y,
2742 Pi2Z,
2743 Identity,
2744}
2745
2746impl Display for RotationMethod {
2747 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2748 match self {
2749 RotationMethod::EulerAngles(alpha, beta, gamma) => {
2750 write!(f, "EulerAngles({}, {}, {})", alpha, beta, gamma)
2751 }
2752 RotationMethod::Pi2X => write!(f, "Pi/2 rotation around x-axis"),
2753 RotationMethod::Pi2Y => write!(f, "Pi/2 rotation around y-axis"),
2754 RotationMethod::Pi2Z => write!(f, "Pi/2 rotation around z-axis"),
2755 RotationMethod::Identity => write!(f, "Identity rotation"),
2756 }
2757 }
2758}
2759
2760impl From<RotationMethod> for Rotation {
2761 fn from(method: RotationMethod) -> Self {
2762 Rotation::new(method)
2763 }
2764}
2765
2766#[derive(Clone, Encode, Decode)]
2767#[trait_decode(trait = GammaLoopContext)]
2768pub struct Rotation {
2769 pub method: RotationMethod,
2770 pub lorentz_rotation: EvalTensor<ExpressionEvaluator<Complex<F<f64>>>, OrderedStructure>,
2771 pub bispinor_rotation: EvalTensor<ExpressionEvaluator<Complex<F<f64>>>, OrderedStructure>,
2772 }
2774
2775impl Rotation {
2776 pub(crate) fn is_identity(&self) -> bool {
2777 matches!(self.method, RotationMethod::Identity)
2778 }
2779
2780 pub(crate) fn inverse_rotate_three<T: FloatLike>(
2781 &self,
2782 momentum: &ThreeMomentum<F<T>>,
2783 ) -> ThreeMomentum<F<T>> {
2784 match self.method {
2785 RotationMethod::Identity => momentum.clone(),
2786 RotationMethod::Pi2X => ThreeMomentum {
2787 px: momentum.px.clone(),
2788 py: momentum.pz.clone(),
2789 pz: -momentum.py.clone(),
2790 },
2791 RotationMethod::Pi2Y => ThreeMomentum {
2792 px: -momentum.pz.clone(),
2793 py: momentum.py.clone(),
2794 pz: momentum.px.clone(),
2795 },
2796 RotationMethod::Pi2Z => ThreeMomentum {
2797 px: momentum.py.clone(),
2798 py: -momentum.px.clone(),
2799 pz: momentum.pz.clone(),
2800 },
2801 RotationMethod::EulerAngles(alpha, beta, gamma) => {
2802 let alpha = F::<T>::from_f64(alpha);
2805 let beta = F::<T>::from_f64(beta);
2806 let gamma = F::<T>::from_f64(gamma);
2807 let sin_alpha = alpha.sin();
2808 let cos_alpha = alpha.cos();
2809 let sin_beta = beta.sin();
2810 let cos_beta = beta.cos();
2811 let sin_gamma = gamma.sin();
2812 let cos_gamma = gamma.cos();
2813
2814 let px = momentum.px.clone();
2815 let py = momentum.py.clone();
2816 let pz = momentum.pz.clone();
2817
2818 ThreeMomentum {
2819 px: cos_gamma.clone() * &cos_beta * &px + sin_gamma.clone() * &cos_beta * &py
2820 - sin_beta.clone() * &pz,
2821 py: (-(cos_alpha.clone()) * &sin_gamma
2822 + sin_alpha.clone() * &sin_beta * &cos_gamma)
2823 * &px
2824 + (cos_alpha.clone() * &cos_gamma
2825 + sin_alpha.clone() * &sin_beta * &sin_gamma)
2826 * &py
2827 + cos_beta.clone() * &sin_alpha * &pz,
2828 pz: (sin_alpha.clone() * &sin_gamma
2829 + cos_alpha.clone() * &sin_beta * &cos_gamma)
2830 * &px
2831 + (-sin_alpha.clone() * &cos_gamma
2832 + cos_alpha.clone() * &sin_beta * &sin_gamma)
2833 * &py
2834 + cos_alpha * &cos_beta * &pz,
2835 }
2836 }
2837 }
2838 }
2839
2840 pub(crate) fn inverse_rotate_four<T: FloatLike>(
2841 &self,
2842 momentum: &FourMomentum<F<T>>,
2843 ) -> FourMomentum<F<T>> {
2844 FourMomentum {
2845 temporal: momentum.temporal.clone(),
2846 spatial: self.inverse_rotate_three(&momentum.spatial),
2847 }
2848 }
2849
2850 pub(crate) fn setting(&self) -> RotationSetting {
2851 match self.method {
2852 RotationMethod::EulerAngles(alpha, beta, gamma) => {
2853 RotationSetting::EulerAngles { alpha, beta, gamma }
2854 }
2855 RotationMethod::Pi2X => RotationSetting::Pi2X {},
2856 RotationMethod::Pi2Y => RotationSetting::Pi2Y {},
2857 RotationMethod::Pi2Z => RotationSetting::Pi2Z {},
2858 RotationMethod::Identity => RotationSetting::None {},
2859 }
2860 }
2861 pub(crate) fn new(method: RotationMethod) -> Self {
2862 let mu = Minkowski::slot(4, 1);
2863
2864 let al = Minkowski::slot(4, 3);
2865 let mud = mu.dual();
2866
2867 let shadow: NamedStructure<String, ()> =
2868 PermutedStructure::<OrderedStructure>::from_iter([mu])
2869 .structure
2870 .to_named("eps".to_string(), None);
2871 let shadow_t: MixedTensor<_, OrderedStructure> =
2872 ParamOrConcrete::param(shadow.to_shell().expanded_shadow().unwrap().into())
2873 .cast_structure();
2874
2875 let rotation: MixedTensor<f64, _> = method
2876 .lorentz_tensor(mud, al)
2877 .try_into_dense()
2878 .unwrap()
2879 .into();
2880
2881 let fn_map = FunctionMap::new();
2882
2883 let lorentz_eval: EvalTensor<ExpressionEvaluator<SymComplex<Rational>>, OrderedStructure> =
2884 shadow_t
2885 .contract(&rotation)
2886 .unwrap()
2887 .try_into_parametric()
2888 .unwrap()
2889 .to_evaluation_tree(
2890 &fn_map,
2891 &shadow_t.try_into_parametric().unwrap().tensor.data(),
2892 )
2893 .unwrap()
2894 .linearize(
2895 &OptimizationSettings::new()
2896 .cpe_iterations(Some(1))
2897 .verbose(false),
2898 );
2899
2900 let i = GR.bis.new_slot(4, 1);
2901
2902 let j = GR.bis.new_slot(4, 3);
2903
2904 let shadow: NamedStructure<String, ()> =
2905 PermutedStructure::from_iter([i.cast::<LibraryRep>()])
2906 .structure
2907 .to_named("u".to_string(), None);
2908 let shadow_t: MixedTensor<_, OrderedStructure> =
2909 ParamOrConcrete::param(shadow.to_shell().expanded_shadow().unwrap().into())
2910 .cast_structure();
2911
2912 let rotation: MixedTensor<f64, _> =
2913 ParamOrConcrete::Concrete(RealOrComplexTensor::Complex(method.bispinor_tensor(i, j)));
2914
2915 let res = shadow_t
2916 .contract(&rotation)
2917 .unwrap()
2918 .try_into_parametric()
2919 .unwrap();
2920
2921 let fn_map = FunctionMap::new();
2922 let mut params = shadow_t.try_into_parametric().unwrap().tensor.data();
2923 params.push(Atom::i());
2924
2925 let spinor_eval: EvalTensor<ExpressionEvaluator<SymComplex<Rational>>, OrderedStructure> =
2926 res.to_evaluation_tree(&fn_map, ¶ms).unwrap().linearize(
2927 &OptimizationSettings::new()
2928 .cpe_iterations(Some(1))
2929 .verbose(false),
2930 );
2931
2932 Self {
2933 method,
2934 lorentz_rotation: lorentz_eval.map_coeff(&|f| {
2935 Complex::new(F::from_f64(f.re.to_f64()), F::from_f64(f.im.to_f64()))
2936 }),
2937 bispinor_rotation: spinor_eval.map_coeff(&|f| {
2938 Complex::new(F::from_f64(f.re.to_f64()), F::from_f64(f.im.to_f64()))
2939 }),
2940 }
2941 }
2942}
2943
2944impl RotationMethod {
2945 pub(crate) fn generator(
2946 &self,
2947 i: AbstractIndex,
2948 j: AbstractIndex,
2949 ) -> DataTensor<f64, OrderedStructure> {
2950 let structure = PermutedStructure::from_iter([
2951 LibraryRep::new_slot(Minkowski {}.into(), 4, i),
2952 LibraryRep::new_slot(Minkowski {}.into(), 4, j),
2953 ])
2954 .structure;
2955 let zero = 0.;
2956 match self {
2957 RotationMethod::Identity => {
2958 let omega = SparseTensor::empty(structure, zero);
2959 omega.into()
2960 }
2961 RotationMethod::Pi2X => {
2962 let mut omega = SparseTensor::empty(structure, zero);
2963 omega.set(&[2, 3], -zero.PIHALF()).unwrap();
2964 omega.set(&[3, 2], zero.PIHALF()).unwrap();
2965 omega.into()
2966 }
2967 RotationMethod::Pi2Y => {
2968 let mut omega = SparseTensor::empty(structure, zero);
2969 omega.set(&[1, 3], zero.PIHALF()).unwrap();
2970 omega.set(&[3, 1], -zero.PIHALF()).unwrap();
2971 omega.into()
2972 }
2973 RotationMethod::Pi2Z => {
2974 let mut omega = SparseTensor::empty(structure, zero);
2975 omega.set(&[1, 2], -zero.PIHALF()).unwrap();
2976 omega.set(&[2, 1], zero.PIHALF()).unwrap();
2977 omega.into()
2978 }
2979 RotationMethod::EulerAngles(alpha, beta, gamma) => DenseTensor::from_data(
2980 vec![
2981 zero, zero, zero, zero, zero, zero, -gamma, *beta, zero, *gamma, zero, -alpha, zero, -beta, *alpha, zero,
2986 ],
2987 structure,
2988 )
2989 .unwrap()
2990 .into(),
2991 }
2992 }
2993
2994 pub(crate) fn lorentz_tensor(
2995 &self,
2996 i: Slot<Minkowski>,
2997 j: Slot<Minkowski>,
2998 ) -> DataTensor<f64, OrderedStructure> {
2999 let structure = PermutedStructure::from_iter([i.cast::<LibraryRep>(), j.cast()]).structure;
3000
3001 match self {
3002 RotationMethod::Identity => {
3003 let rot = DenseTensor::from_data(
3004 vec![
3005 1., 0., 0., 0., 0., -1., 0., 0., 0., 0., -1., 0., 0., 0., 0., -1.,
3009 ],
3010 structure,
3011 )
3012 .unwrap();
3013 rot.into()
3014 }
3015 RotationMethod::Pi2X => {
3016 let rot = DenseTensor::from_data(
3017 vec![
3018 1., 0., 0., 0., 0., -1., 0., 0., 0., 0., 0., -1., 0., 0., 1., 0.,
3022 ],
3023 structure,
3024 )
3025 .unwrap();
3026 rot.into()
3027 }
3028 RotationMethod::Pi2Y => {
3029 let rot = DenseTensor::from_data(
3030 vec![
3031 1., 0., 0., 0., 0., 0., 0., 1., 0., 0., -1., 0., 0., -1., 0., 0.,
3035 ],
3036 structure,
3037 )
3038 .unwrap();
3039 rot.into()
3040 }
3041 RotationMethod::Pi2Z => {
3042 let rot = DenseTensor::from_data(
3043 vec![
3044 1., 0., 0., 0., 0., 0., -1., 0., 0., 1., 0., 0., 0., 0., 0., -1.,
3048 ],
3049 structure,
3050 )
3051 .unwrap();
3052 rot.into()
3053 }
3054 RotationMethod::EulerAngles(alpha, beta, gamma) => DenseTensor::from_data(
3055 vec![
3056 1.,
3058 0.,
3059 0.,
3060 0.,
3061 0.,
3063 -gamma.cos() * beta.cos(),
3064 alpha.sin() * beta.sin() * gamma.cos() - alpha.cos() * gamma.sin(),
3065 alpha.sin() * gamma.sin() + alpha.cos() * beta.sin() * gamma.cos(),
3066 0.,
3068 gamma.sin() * beta.cos(),
3069 -alpha.cos() * gamma.cos() - alpha.sin() * beta.sin() * gamma.sin(),
3070 -alpha.sin() * gamma.cos() + alpha.cos() * beta.sin() * gamma.sin(),
3071 0.,
3073 -beta.sin(),
3074 alpha.sin() * beta.cos(),
3075 -alpha.cos() * beta.cos(),
3076 ],
3077 structure,
3078 )
3079 .unwrap()
3080 .into(),
3081 }
3109 }
3110
3111 pub(crate) fn bispinor_tensor(
3112 &self,
3113 i: Slot<LibraryRep>,
3114 j: Slot<LibraryRep>,
3115 ) -> DataTensor<Complex<f64>, OrderedStructure> {
3116 let structure = PermutedStructure::from_iter([i.cast::<LibraryRep>(), j.cast()]).structure;
3117 let zero = 0.; let zeroc = Complex::new_re(zero);
3119
3120 match self {
3121 RotationMethod::Identity => {
3122 let mut rot = SparseTensor::empty(structure, zeroc);
3123 rot.set(&[0, 0], zeroc.one()).unwrap();
3124 rot.set(&[1, 1], zeroc.one()).unwrap();
3125 rot.set(&[2, 2], zeroc.one()).unwrap();
3126 rot.set(&[3, 3], zeroc.one()).unwrap();
3127 rot.into()
3128 }
3129 RotationMethod::Pi2X => {
3130 let mut rot = SparseTensor::empty(structure, zeroc);
3131 let sqrt2_half = zero.SQRT_2_HALF();
3132 let sqrt2_halfim = Complex::new_im(sqrt2_half);
3133 let sqrt2_halfre = Complex::new_re(sqrt2_half);
3134
3135 rot.set(&[0, 0], sqrt2_halfim).unwrap();
3136 rot.set(&[0, 1], sqrt2_halfim).unwrap();
3137 rot.set(&[1, 0], sqrt2_halfim).unwrap();
3138 rot.set(&[1, 1], sqrt2_halfre).unwrap();
3139 rot.set(&[2, 2], sqrt2_halfim).unwrap();
3140 rot.set(&[2, 3], sqrt2_halfim).unwrap();
3141 rot.set(&[3, 2], sqrt2_halfim).unwrap();
3142 rot.set(&[3, 3], sqrt2_halfre).unwrap();
3143 rot.into()
3144 }
3145 RotationMethod::Pi2Y => {
3146 let mut rot = SparseTensor::empty(structure, zeroc);
3147 let sqrt2_half = zero.SQRT_2_HALF();
3148 let sqrt2_halfim = Complex::new_im(sqrt2_half);
3149 let sqrt2_halfre = Complex::new_re(sqrt2_half);
3150 let nsqrt2_half = -sqrt2_halfre;
3151
3152 rot.set(&[0, 0], sqrt2_halfim).unwrap();
3153 rot.set(&[0, 1], nsqrt2_half).unwrap();
3154 rot.set(&[1, 0], nsqrt2_half).unwrap();
3155 rot.set(&[1, 1], sqrt2_halfre).unwrap();
3156 rot.set(&[2, 2], sqrt2_halfim).unwrap();
3157 rot.set(&[2, 3], nsqrt2_half).unwrap();
3158 rot.set(&[3, 2], nsqrt2_half).unwrap();
3159 rot.set(&[3, 3], sqrt2_halfre).unwrap();
3160 rot.into()
3161 }
3162 RotationMethod::Pi2Z => {
3163 let mut rot = SparseTensor::empty(structure, zeroc);
3164 let sqrt2_half = zero.SQRT_2_HALF();
3165 let sqrt2_halfc = Complex::new(sqrt2_half, sqrt2_half);
3166 let sqrt2_halfcc = sqrt2_halfc.conj();
3167
3168 rot.set(&[0, 0], sqrt2_halfc).unwrap();
3169 rot.set(&[1, 1], sqrt2_halfcc).unwrap();
3170 rot.set(&[2, 2], sqrt2_halfc).unwrap();
3171 rot.set(&[3, 3], sqrt2_halfcc).unwrap();
3172 rot.into()
3173 }
3174 RotationMethod::EulerAngles(alpha, beta, gamma) => {
3175 let norm = alpha.square() + beta.square() + gamma.square();
3176
3177 if alpha.is_zero() && beta.is_zero() {
3178 let normhalf = &norm / 2.; let cos_phihalf = normhalf.cos();
3180 let sin_phihalf = normhalf.sin();
3181
3182 let e = Complex::new(cos_phihalf, sin_phihalf);
3183 let econj = e.conj();
3184 return DenseTensor::from_data(
3185 vec![
3186 e, zeroc, zeroc, zeroc, zeroc, econj, zeroc, zeroc, zeroc, zeroc, e, zeroc, zeroc, zeroc, zeroc, econj,
3191 ],
3192 structure,
3193 )
3194 .unwrap()
3195 .into();
3196 }
3197
3198 let complex_phi = Complex::new(*alpha, *beta);
3199
3200 let normhalf = &norm / 2.; let cos_phihalf = normhalf.cos();
3202 let sin_phihalf = normhalf.sin();
3203
3204 let a_00 = Complex::new(gamma / norm * cos_phihalf, sin_phihalf);
3205 let a_01 =
3206 Complex::new_im((norm - gamma.square() / norm) * sin_phihalf) / complex_phi;
3207 let a_10 = complex_phi * Complex::new_im(sin_phihalf / norm);
3208 let a_11 = Complex::new(cos_phihalf, -gamma / norm * sin_phihalf);
3209
3210 DenseTensor::from_data(
3211 vec![
3212 a_00, a_01, zeroc, zeroc, a_10, a_11, zeroc, zeroc, zeroc, zeroc, a_00, a_01, zeroc, zeroc, a_10, a_11,
3217 ],
3218 structure,
3219 )
3220 .unwrap()
3221 .into()
3222 }
3223 }
3224 }
3225}
3226
3227pub trait Rotatable {
3228 fn rotate(&self, rotation: &Rotation) -> Self;
3229}
3230
3231impl<T: FloatLike> Rotatable for ThreeMomentum<F<T>> {
3232 fn rotate(&self, rotation: &Rotation) -> Self {
3233 match rotation.method {
3234 RotationMethod::Identity => self.clone(),
3235 RotationMethod::Pi2X => ThreeMomentum::perform_pi2_rotation_x(self),
3236 RotationMethod::Pi2Y => ThreeMomentum::perform_pi2_rotation_y(self),
3237 RotationMethod::Pi2Z => ThreeMomentum::perform_pi2_rotation_z(self),
3238 RotationMethod::EulerAngles(alpha, beta, gamma) => {
3239 let mut result = self.clone();
3240 result.rotate_mut(&F::from_f64(alpha), &F::from_f64(beta), &F::from_f64(gamma));
3241 result
3242 }
3243 }
3244 }
3245}
3246
3247impl<T: FloatLike> Rotatable for ThreeMomentum<HyperDual<F<T>>> {
3248 fn rotate(&self, rotation: &Rotation) -> Self {
3249 match rotation.method {
3250 RotationMethod::Identity => self.clone(),
3251 RotationMethod::Pi2X => ThreeMomentum::perform_pi2_rotation_x(self),
3252 RotationMethod::Pi2Y => ThreeMomentum::perform_pi2_rotation_y(self),
3253 RotationMethod::Pi2Z => ThreeMomentum::perform_pi2_rotation_z(self),
3254 RotationMethod::EulerAngles(alpha, beta, gamma) => {
3255 let mut result = self.clone();
3256 result.rotate_mut(
3257 &new_constant(&self.px, &F::from_f64(alpha)),
3258 &new_constant(&self.py, &F::from_f64(beta)),
3259 &new_constant(&self.pz, &F::from_f64(gamma)),
3260 );
3261 result
3262 }
3263 }
3264 }
3265}
3266
3267impl<T: FloatLike> ThreeMomentum<HyperDual<F<T>>> {
3268 fn rotate_mut(
3269 &mut self,
3270 alpha: &HyperDual<F<T>>,
3271 beta: &HyperDual<F<T>>,
3272 gamma: &HyperDual<F<T>>,
3273 ) {
3274 let sin_alpha = alpha.sin();
3275 let cos_alpha = alpha.cos();
3276 let sin_beta = beta.sin();
3277 let cos_beta = beta.cos();
3278 let sin_gamma = gamma.sin();
3279 let cos_gamma = gamma.cos();
3280
3281 let px = self.px.clone();
3282 let py = self.py.clone();
3283 let pz = self.pz.clone();
3284
3285 self.px = cos_gamma.clone() * &cos_beta * &px
3286 + (-(cos_alpha.clone()) * &sin_gamma + sin_alpha.clone() * &sin_beta * &cos_gamma)
3287 * &py
3288 + (sin_alpha.clone() * &sin_gamma + cos_alpha.clone() * &sin_beta * &cos_gamma) * &pz;
3289
3290 self.py = sin_gamma.clone() * &cos_beta * &px
3291 + (cos_alpha.clone() * &cos_gamma + sin_alpha.clone() * &sin_beta * &sin_gamma) * &py
3292 + (-sin_alpha.clone() * &cos_gamma + cos_alpha.clone() * &sin_beta * &sin_gamma) * &pz;
3293
3294 self.pz =
3295 -sin_beta * &px + cos_beta.clone() * &sin_alpha * &py + cos_alpha * &cos_beta * &pz;
3296 }
3297}
3298
3299impl<T: FloatLike> Rotatable for FourMomentum<F<T>> {
3300 fn rotate(&self, rotation: &Rotation) -> Self {
3301 Self {
3302 temporal: self.temporal.clone(),
3303 spatial: self.spatial.rotate(rotation),
3304 }
3305 }
3306}
3307
3308impl<T: FloatLike + EvaluationDomain> Rotatable for Polarization<Complex<F<T>>> {
3309 fn rotate(&self, rotation: &Rotation) -> Self {
3310 let rotated = match self.pol_type {
3311 PolType::Epsilon | PolType::EpsilonBar => rotation
3312 .lorentz_rotation
3313 .clone()
3314 .map_coeff(&|t| t.map(|f| F::from_ff64(f)))
3315 .evaluate(&self.tensor.data)
3316 .try_into_dense()
3317 .unwrap()
3318 .cast_structure(),
3319 PolType::U | PolType::UBar | PolType::V | PolType::VBar | PolType::Scalar => {
3320 self.tensor.clone()
3321 }
3322 };
3323
3324 Polarization {
3325 tensor: rotated,
3326 pol_type: self.pol_type,
3327 }
3328 }
3329}
3330
3331#[cfg(test)]
3332mod tests {
3333
3334 use core::f64;
3335
3336 use eyre::Context;
3337 use schemars::schema_for;
3338
3339 use crate::utils::F;
3340
3341 use super::*;
3342 use crate::utils::ApproxEq;
3343
3344 #[test]
3345 fn polarization() {
3346 let mom = FourMomentum::from_args(F(1.), F(1.), F(0.), F(0.));
3347
3348 let pol: Polarization<F<f64>> = Polarization::lorentz(mom.pol_one());
3349 println!("{}", pol)
3350
3351 }
3365
3366 #[test]
3367 fn serialization() {
3368 #[derive(Debug, Serialize, Deserialize, PartialEq)]
3369 struct HelicityToml {
3370 helicities: Vec<Helicity>,
3371 }
3372
3373 let hels = vec![
3374 Helicity::PLUS,
3375 Helicity::MINUS,
3376 Helicity::ZERO,
3377 Helicity::Summed,
3378 Helicity::SummedAveraged,
3379 ];
3380 let serialized = serde_json::to_string(&hels).unwrap();
3381 assert_eq!(serialized, r#"[1,-1,0,"summed","summed_averaged"]"#);
3382
3383 let deserialized: Vec<Helicity> =
3384 serde_json::from_str(r#"[1,"minus","0","summed","summed-averaged"]"#).unwrap();
3385 assert_eq!(deserialized, hels);
3386
3387 let toml_value = toml::from_str::<HelicityToml>(
3388 r#"
3389helicities = [1, "minus", "zero", "summed", "summed_averaged"]
3390"#,
3391 )
3392 .unwrap();
3393 assert_eq!(toml_value.helicities, hels);
3394
3395 let schema = serde_json::to_value(schema_for!(Helicity)).unwrap();
3396 let allowed_strings = &schema["enum"];
3397 assert!(allowed_strings.is_null());
3398 assert!(
3399 schema["oneOf"][0]["enum"]
3400 .as_array()
3401 .unwrap()
3402 .contains(&(-1).into())
3403 );
3404 assert!(
3405 schema["oneOf"][1]["enum"]
3406 .as_array()
3407 .unwrap()
3408 .contains(&"summed_averaged".into())
3409 );
3410 }
3411
3412 #[test]
3413 fn eps() {
3414 let mom = FourMomentum::from_args(
3415 F(156.2565490076973),
3416 F(-108.59017233120495),
3417 F(-100.2097685717689),
3418 F(50.81619686346704),
3419 );
3420
3421 let pol = mom.eps_pol(SignOrZero::Plus);
3422 println!("{}", pol);
3423
3424 let mom = FourMomentum::from_args(
3425 F(441.7831721921727),
3426 F(237.7632083614734),
3427 F(368.76330416225863),
3428 F(-51.523329523343534),
3429 );
3430
3431 let pol = mom.eps_pol(SignOrZero::Plus);
3432 println!("{}", pol);
3433
3434 let mom = FourMomentum::from_args(F(485.0355), F(0.), F(0.), F(485.0355));
3435
3436 let pol = mom.eps_pol(SignOrZero::Plus);
3437
3438 println!("{}", pol);
3439
3440 println!("pt{}", mom.pt());
3441
3442 let mom = FourMomentum::from_args(F(485.0355), F(-107.6044), F(-431.5174), F(193.5805));
3443
3444 let pol = mom.eps_pol(SignOrZero::Plus).bar();
3445
3446 println!("{}", pol);
3453 println!("pt{}", mom.pt());
3454 }
3455
3456 #[test]
3457 fn spinors_degenerate() {
3458 let mom = FourMomentum::from_args(F(2.), F(0.), F(0.), F(-2.));
3459
3460 assert_eq!(
3461 [Complex::new_zero(), Complex::new_re(F(-1.))],
3462 mom.xi(Sign::Positive)
3463 );
3464
3465 assert_eq!(
3466 [Complex::new_re(F(1.)), Complex::new_re(F(0.))],
3467 mom.xi(Sign::Negative)
3468 );
3469
3470 let u_p = mom.u(Sign::Positive);
3471 let u_p_target: Polarization<Complex<F<_>>> =
3472 Polarization::bispinor_u([F(0.), F(0.), F(0.), F(-2.)]).cast();
3473
3474 u_p.approx_eq_res(&u_p_target, &F(0.001))
3475 .wrap_err("u+((2,0,0,-2)) does not match target: (0,0,0,-2)")
3476 .unwrap();
3477
3478 let mut u_p_bar_target: Polarization<Complex<F<_>>> =
3479 Polarization::bispinor_u([F(0.), F(-2.), F(0.), F(0.)]).cast();
3480
3481 u_p_bar_target.pol_type = PolType::UBar;
3482
3483 u_p.bar()
3484 .approx_eq_res(&u_p_bar_target, &F(0.001))
3485 .wrap_err("u+bar((2,0,0,-2)) does not match target: (0,-2,0,0)")
3486 .unwrap();
3487
3488 let u_m = mom.u(Sign::Negative);
3489 let u_m_target: Polarization<Complex<F<_>>> =
3490 Polarization::bispinor_u([F(2.), F(0.), F(0.), F(0.)]).cast();
3491
3492 u_m.approx_eq_res(&u_m_target, &F(0.001))
3493 .wrap_err("u-((2,0,0,-2)) does not match target: (2,0,0,0)")
3494 .unwrap();
3495
3496 let mut u_m_bar_target: Polarization<Complex<F<_>>> =
3497 Polarization::bispinor_u([F(0.), F(0.), F(2.), F(0.)]).cast();
3498
3499 u_m_bar_target.pol_type = PolType::UBar;
3500
3501 u_m.bar()
3502 .approx_eq_res(&u_m_bar_target, &F(0.001))
3503 .wrap_err("u-bar((2,0,0,-2)) does not match target: (0,0,2,0)")
3504 .unwrap();
3505
3506 let v_p = mom.v(Sign::Positive);
3507 let v_p_target: Polarization<Complex<F<_>>> =
3508 Polarization::bispinor_v([F(-2.), F(0.), F(0.), F(0.)]).cast();
3509
3510 v_p.approx_eq_res(&v_p_target, &F(0.001))
3511 .wrap_err("v+((2,0,0,-2)) does not match target: (-2,0,0,0)")
3512 .unwrap();
3513
3514 let mut v_p_bar_target: Polarization<Complex<F<_>>> =
3515 Polarization::bispinor_v([F(0.), F(0.), F(-2.), F(0.)]).cast();
3516
3517 v_p_bar_target.pol_type = PolType::VBar;
3518
3519 v_p.bar()
3520 .approx_eq_res(&v_p_bar_target, &F(0.001))
3521 .wrap_err("v+bar((2,0,0,-2)) does not match target: (0,0,-2,0)")
3522 .unwrap();
3523
3524 let v_m = mom.v(Sign::Negative);
3525 let v_m_target: Polarization<Complex<F<_>>> =
3526 Polarization::bispinor_v([F(0.), F(0.), F(0.), F(2.)]).cast();
3527
3528 v_m.approx_eq_res(&v_m_target, &F(0.001))
3529 .wrap_err("v-((2,0,0,-2)) does not match target: (0,0,0,2)")
3530 .unwrap();
3531
3532 let mut v_m_bar_target: Polarization<Complex<F<_>>> =
3533 Polarization::bispinor_v([F(0.), F(2.), F(0.), F(0.)]).cast();
3534
3535 v_m_bar_target.pol_type = PolType::VBar;
3536
3537 v_m.bar()
3538 .approx_eq_res(&v_m_bar_target, &F(0.001))
3539 .wrap_err("v-bar((2,0,0,-2)) does not match target: (0,2,0,0)")
3540 .unwrap();
3541 }
3542
3543 #[test]
3544 fn spinors() {
3545 let mom = FourMomentum::from_args(F(4.), F(0.), F(4.), F(0.));
3546
3547 assert!(
3548 Complex::approx_eq_slice(
3549 &[
3550 Complex::new_re(F(f64::consts::FRAC_1_SQRT_2)),
3551 Complex::new_im(F(f64::consts::FRAC_1_SQRT_2)),
3552 ],
3553 &mom.xi(Sign::Positive),
3554 &F(0.001),
3555 ),
3556 "xi+((4,0,4,0)) does not match target: (1/sqrt(2),i/sqrt(2))"
3557 );
3558
3559 assert!(
3560 Complex::approx_eq_slice(
3561 &[
3562 Complex::new_im(F(f64::consts::FRAC_1_SQRT_2)),
3563 Complex::new_re(F(f64::consts::FRAC_1_SQRT_2)),
3564 ],
3565 &mom.xi(Sign::Negative),
3566 &F(0.001),
3567 ),
3568 "xi-((4,0,4,0)) does not match target: (i/sqrt(2),1/sqrt(2))"
3569 );
3570
3571 let zero: Complex<_> = F(0.).into();
3572 let u_p = mom.u(Sign::Positive);
3573 let u_p_target: Polarization<Complex<F<_>>> =
3574 Polarization::bispinor_u([zero, zero, Complex::new_re(F(2.)), Complex::new_im(F(2.))])
3575 .cast();
3576
3577 u_p.approx_eq_res(&u_p_target, &F(0.001))
3578 .wrap_err("u+((4,0,4,0)) does not match target: (0,0,2,i2)")
3579 .unwrap();
3580
3581 let mut u_p_bar_target: Polarization<Complex<F<_>>> =
3582 Polarization::bispinor_u([Complex::new_re(F(2.)), Complex::new_im(-F(2.)), zero, zero])
3583 .cast();
3584
3585 u_p_bar_target.pol_type = PolType::UBar;
3586
3587 u_p.bar()
3588 .approx_eq_res(&u_p_bar_target, &F(0.001))
3589 .wrap_err("u+bar((4,0,4,0)) does not match target: (2,-2i,0,0)")
3590 .unwrap();
3591
3592 let u_m = mom.u(Sign::Negative);
3593 let u_m_target: Polarization<Complex<F<_>>> =
3594 Polarization::bispinor_u([Complex::new_im(F(2.)), Complex::new_re(F(2.)), zero, zero])
3595 .cast();
3596
3597 u_m.approx_eq_res(&u_m_target, &F(0.001))
3598 .wrap_err("u-((4,0,4,0)) does not match target: (2i,2,0,0)")
3599 .unwrap();
3600
3601 let mut u_m_bar_target: Polarization<Complex<F<_>>> =
3602 Polarization::bispinor_u([zero, zero, Complex::new_im(-F(2.)), Complex::new_re(F(2.))])
3603 .cast();
3604
3605 u_m_bar_target.pol_type = PolType::UBar;
3606
3607 u_m.bar()
3608 .approx_eq_res(&u_m_bar_target, &F(0.001))
3609 .wrap_err("u-bar((4,0,4,0)) does not match target: (0,0,-2i,2)")
3610 .unwrap();
3611
3612 let v_p = mom.v(Sign::Positive);
3613 let v_p_target: Polarization<Complex<F<_>>> = Polarization::bispinor_v([
3614 Complex::new_im(-F(2.)),
3615 Complex::new_re(-F(2.)),
3616 zero,
3617 zero,
3618 ])
3619 .cast();
3620
3621 v_p.approx_eq_res(&v_p_target, &F(0.001))
3622 .wrap_err("v+((4,0,4,0)) does not match target: (-2i,-2,0,0)")
3623 .unwrap();
3624
3625 let mut v_p_bar_target: Polarization<Complex<F<_>>> =
3626 Polarization::bispinor_v([zero, zero, Complex::new_im(F(2.)), Complex::new_re(F(-2.))])
3627 .cast();
3628
3629 v_p_bar_target.pol_type = PolType::VBar;
3630
3631 v_p.bar()
3632 .approx_eq_res(&v_p_bar_target, &F(0.001))
3633 .wrap_err("v+bar((4,0,4,0)) does not match target: (0,0,2i,-2)")
3634 .unwrap();
3635
3636 let v_m = mom.v(Sign::Negative);
3637 let v_m_target: Polarization<Complex<F<_>>> = Polarization::bispinor_v([
3638 zero,
3639 zero,
3640 Complex::new_re(-F(2.)),
3641 Complex::new_im(-F(2.)),
3642 ])
3643 .cast();
3644
3645 v_m.approx_eq_res(&v_m_target, &F(0.001))
3646 .wrap_err("v-((4,0,4,0)) does not match target: (0,0,-2,-2i)")
3647 .unwrap();
3648
3649 let mut v_m_bar_target: Polarization<Complex<F<_>>> =
3650 Polarization::bispinor_v([Complex::new_re(-F(2.)), Complex::new_im(F(2.)), zero, zero])
3651 .cast();
3652
3653 v_m_bar_target.pol_type = PolType::VBar;
3654
3655 v_m.bar()
3656 .approx_eq_res(&v_m_bar_target, &F(0.001))
3657 .wrap_err("v-bar((4,0,4,0)) does not match target: (-2,2i,0,0)")
3658 .unwrap();
3659 }
3660
3661 #[test]
3662 fn vectors_degenerate() {
3663 let mom = FourMomentum::from_args(F(2.), F(0.), F(0.), F(-2.));
3664
3665 assert!(
3666 F::approx_eq_slice(&mom.pol_one(), &[0., 1., 0., 0.].map(F), &F(0.001)),
3667 "pol_one((2,0,0,-2)) does not match target: (0,1,0,0)"
3668 );
3669
3670 assert!(
3671 F::approx_eq_slice(&mom.pol_two(), &[0., 0., -1., 0.].map(F), &F(0.001)),
3672 "pol_two((2,0,0,-2)) does not match target: (0,0,-1,0)"
3673 );
3674
3675 let e_p = mom.eps_pol(SignOrZero::Plus);
3676
3677 let zero = F(0.).into();
3678 let e_p_target = Polarization::lorentz([
3679 zero,
3680 Complex::new_re(F(-f64::consts::FRAC_1_SQRT_2)),
3681 Complex::new_im(F(f64::consts::FRAC_1_SQRT_2)),
3682 zero,
3683 ]);
3684
3685 let mut e_p_bar_target = Polarization::lorentz([
3686 zero,
3687 Complex::new_re(F(-f64::consts::FRAC_1_SQRT_2)),
3688 Complex::new_im(F(-f64::consts::FRAC_1_SQRT_2)),
3689 zero,
3690 ]);
3691
3692 e_p_bar_target.pol_type = PolType::EpsilonBar;
3693
3694 e_p.approx_eq_res(&e_p_target, &F(0.0001))
3695 .wrap_err("e+((2,0,0,-2)) does not match target: (0,-1/sqrt(2),i/sqrt(2),0)")
3696 .unwrap();
3697
3698 e_p.bar()
3699 .approx_eq_res(&e_p_bar_target, &F(0.0001))
3700 .wrap_err("e+bar((2,0,0,-2)) does not match target: (0,-1/sqrt(2),-i/sqrt(2),0)")
3701 .unwrap();
3702
3703 let e_m = mom.eps_pol(SignOrZero::Minus);
3704
3705 let e_m_target = Polarization::lorentz([
3706 zero,
3707 Complex::new_re(F(f64::consts::FRAC_1_SQRT_2)),
3708 Complex::new_im(F(f64::consts::FRAC_1_SQRT_2)),
3709 zero,
3710 ]);
3711
3712 let mut e_m_bar_target = Polarization::lorentz([
3713 zero,
3714 Complex::new_re(F(f64::consts::FRAC_1_SQRT_2)),
3715 Complex::new_im(F(-f64::consts::FRAC_1_SQRT_2)),
3716 zero,
3717 ]);
3718
3719 e_m_bar_target.pol_type = PolType::EpsilonBar;
3720
3721 e_m.approx_eq_res(&e_m_target, &F(0.0001))
3722 .wrap_err("e-((2,0,0,-2)) does not match target: (0,1/sqrt(2),i/sqrt(2),0)")
3723 .unwrap();
3724
3725 e_m.bar()
3726 .approx_eq_res(&e_m_bar_target, &F(0.0001))
3727 .wrap_err("e-bar((2,0,0,-2)) does not match target: (0,1/sqrt(2),-i/sqrt(2),0)")
3728 .unwrap();
3729 }
3730
3731 #[test]
3732 fn vectors() {
3733 let mom = FourMomentum::from_args(F(2.), F(0.), F(0.), F(-2.));
3734
3735 assert!(
3736 F::approx_eq_slice(&mom.pol_one(), &[0., 1., 0., 0.].map(F), &F(0.001)),
3737 "pol_one((2,0,0,-2)) does not match target: (0,1,0,0)"
3738 );
3739
3740 assert!(
3741 F::approx_eq_slice(&mom.pol_two(), &[0., 0., -1., 0.].map(F), &F(0.001)),
3742 "pol_two((2,0,0,-2)) does not match target: (0,0,-1,0)"
3743 );
3744
3745 let e_p = mom.eps_pol(SignOrZero::Plus);
3746
3747 let zero = F(0.).into();
3748 let e_p_target = Polarization::lorentz([
3749 zero,
3750 Complex::new_re(F(-f64::consts::FRAC_1_SQRT_2)),
3751 Complex::new_im(F(f64::consts::FRAC_1_SQRT_2)),
3752 zero,
3753 ]);
3754
3755 let mut e_p_bar_target = Polarization::lorentz([
3756 zero,
3757 Complex::new_re(F(-f64::consts::FRAC_1_SQRT_2)),
3758 Complex::new_im(F(-f64::consts::FRAC_1_SQRT_2)),
3759 zero,
3760 ]);
3761
3762 e_p_bar_target.pol_type = PolType::EpsilonBar;
3763
3764 e_p.approx_eq_res(&e_p_target, &F(0.0001))
3765 .wrap_err("e+((2,0,0,-2)) does not match target: (0,-1/sqrt(2),i/sqrt(2),0)")
3766 .unwrap();
3767
3768 e_p.bar()
3769 .approx_eq_res(&e_p_bar_target, &F(0.0001))
3770 .wrap_err("e+bar((2,0,0,-2)) does not match target: (0,-1/sqrt(2),-i/sqrt(2),0)")
3771 .unwrap();
3772
3773 let e_m = mom.eps_pol(SignOrZero::Minus);
3774
3775 let e_m_target = Polarization::lorentz([
3776 zero,
3777 Complex::new_re(F(f64::consts::FRAC_1_SQRT_2)),
3778 Complex::new_im(F(f64::consts::FRAC_1_SQRT_2)),
3779 zero,
3780 ]);
3781
3782 let mut e_m_bar_target = Polarization::lorentz([
3783 zero,
3784 Complex::new_re(F(f64::consts::FRAC_1_SQRT_2)),
3785 Complex::new_im(F(-f64::consts::FRAC_1_SQRT_2)),
3786 zero,
3787 ]);
3788
3789 e_m_bar_target.pol_type = PolType::EpsilonBar;
3790
3791 e_m.approx_eq_res(&e_m_target, &F(0.0001))
3792 .wrap_err("e-((2,0,0,-2)) does not match target: (0,1/sqrt(2),i/sqrt(2),0)")
3793 .unwrap();
3794
3795 e_m.bar()
3796 .approx_eq_res(&e_m_bar_target, &F(0.0001))
3797 .wrap_err("e-bar((2,0,0,-2)) does not match target: (0,1/sqrt(2),-i/sqrt(2),0)")
3798 .unwrap();
3799 }
3800
3801 #[test]
3802 fn rotations() {
3803 let mom = FourMomentum::from_args(F(2.), F(3.), F(1.), F(2.));
3804
3805 let momc: [F<f64>; 4] = mom.into();
3806 let e = Polarization::lorentz(momc.map(Complex::new_re));
3807 let u = mom.u(Sign::Negative);
3808
3809 let rot: Rotation = RotationMethod::EulerAngles(std::f64::consts::PI / 2., 0., 0.).into();
3810 let rotx = RotationMethod::Pi2X.into();
3811
3812 let rotye: Rotation = RotationMethod::EulerAngles(0., std::f64::consts::PI / 2., 0.).into();
3813 let roty = RotationMethod::Pi2Y.into();
3814
3815 let rotze: Rotation = RotationMethod::EulerAngles(0., 0., std::f64::consts::PI / 2.).into();
3816 let rotz = RotationMethod::Pi2Z.into();
3817
3818 let rotid = RotationMethod::Identity.into();
3819 let mom_rot = mom.rotate(&rot);
3828 let mom_id = mom.rotate(&rotid);
3829 let other_mom_rot = mom.rotate(&rotx);
3830 let mom_rotye = mom.rotate(&rotye);
3831 let mom_roty = mom.rotate(&roty);
3832 let mom_rotze = mom.rotate(&rotze);
3833 let mom_rotz = mom.rotate(&rotz);
3834
3835 println!("id:\t{}", mom_id);
3836 mom_id
3837 .approx_eq_res(&mom, &F(0.001))
3838 .wrap_err("mom is not identical to mom transformed with identity")
3839 .unwrap();
3840 println!("orig:\t{}", mom);
3841
3842 println!("rotxe:\t{}", mom_rot);
3843 println!("pi2x:\t{}", other_mom_rot);
3844 mom_rot.approx_eq_res(&other_mom_rot, &F(0.001)).unwrap();
3845
3846 println!("rotye:\t{}", mom_rotye);
3847 println!("pi2y:\t{}", mom_roty);
3848
3849 mom_rotye.approx_eq_res(&mom_roty, &F(0.001)).unwrap();
3850 println!("rotze:\t{}", mom_rotze);
3851 println!("pi2z:\t{}", mom_rotz);
3852 mom_rotze.approx_eq_res(&mom_rotz, &F(0.001)).unwrap();
3853
3854 let e_rot = e.rotate(&rot);
3855 println!("rotxe:{}", e_rot);
3856 mom_rot.approx_eq_res(&e_rot, &F(0.001)).unwrap();
3857
3858 let e_id = e.rotate(&rotid);
3859 mom_id.approx_eq_res(&e_id, &F(0.001)).unwrap();
3860 let e_rotye = e.rotate(&rotye);
3861
3862 println!("rotye:{}", e_rotye);
3863 mom_rotye.approx_eq_res(&e_rotye, &F(0.001)).unwrap();
3864 let e_roty = e.rotate(&roty);
3865 mom_roty.approx_eq_res(&e_roty, &F(0.001)).unwrap();
3866 let e_rotze = e.rotate(&rotze);
3867 mom_rotze.approx_eq_res(&e_rotze, &F(0.001)).unwrap();
3868 let e_rotz = e.rotate(&rotz);
3869 mom_rotz.approx_eq_res(&e_rotz, &F(0.001)).unwrap();
3870 let u_rot = u.rotate(&rot);
3871 let u_id = u.rotate(&rotid);
3872
3873 let other_e_rot = e.rotate(&rotx);
3874
3875 let other_u_rot = u.rotate(&rotx);
3876
3877 println!("id:{}", e_id);
3878 println!("orig:{}", e);
3879 println!("euler:{}", e_rot);
3880 println!("Pi2X:{}", other_e_rot);
3881 println!("rotye:{}", e_rotye);
3882 println!("pi2y:{}", e_roty);
3883 println!("rotze:{}", e_rotze);
3884 println!("pi2z:{}", e_rotz);
3885
3886 println!("id:{}", u_id);
3887 println!("orig:{}", u);
3888 println!("euler:{}", u_rot);
3889 println!("Pi2X {}", other_u_rot);
3890 }
3891
3892 }