1use crate::integrands::process::GenericEvaluatorFloat;
2use crate::model::Model;
3use crate::momentum::sample::{
4 ExternalFourMomenta, ExternalIndex, ExternalThreeMomenta, LoopMomenta, SubspaceData,
5};
6use crate::momentum::signature::{ExternalSignature, LoopSignature};
7use crate::momentum::{FourMomentum, ThreeMomentum};
8use crate::numerator::aind::Aind;
9use crate::numerator::ufo::UFO;
10use crate::settings::runtime::ParameterizationSettings;
11use crate::settings::runtime::SamplingSettings;
12use crate::settings::runtime::kinematic::Externals;
13use crate::settings::runtime::{ParameterizationMapping, ParameterizationMode};
14use crate::utils::hyperdual_utils::new_constant;
15
16use bincode::{Decode, Encode};
17use colored::Colorize;
18use idenso::representations::initialize;
19use itertools::Itertools;
20use linnet::half_edge::involution::EdgeIndex;
21
22use rand::Rng;
23use ref_ops::{RefAdd, RefDiv, RefMul, RefNeg, RefRem, RefSub};
24use rug::float::{Constant, ParseFloatError};
25use rug::ops::{CompleteRound, Pow};
26use rug::{Assign, Float};
27use schemars::JsonSchema;
28use serde::{Deserialize, Deserializer, Serialize};
29use spenso::algebra::algebraic_traits::RefOne;
30use spenso::algebra::algebraic_traits::RefZero;
31use spenso::algebra::complex::Complex;
32use spenso::algebra::complex::R;
33use spenso::algebra::complex::SymbolicaComplex;
34use spenso::algebra::complex::symbolica_traits::ToFloat;
35use spenso::algebra::upgrading_arithmetic::TrySmallestUpgrade;
36use spenso::network::library::TensorLibraryData;
37use spenso::network::library::function_lib::{INBUILTS, Panic, PanicMissingConcrete, SymbolLib};
38use spenso::network::library::symbolic::{ExplicitKey, TensorLibrary};
39use spenso::network::parsing::ShadowedStructure;
40use spenso::structure::concrete_index::ExpandedIndex;
41use spenso::tensors::complex::RealOrComplexTensor;
42use spenso::tensors::data::StorageTensor;
43use spenso::tensors::parametric::to_param::ToAtom;
44use spenso::tensors::parametric::{MixedTensor, ParamTensor};
45use spenso_hep_lib::hep_lib_atom;
46use symbolica::{
47 domains::{
48 dual::HyperDual,
49 float::{FixedPrecision, Float as SymbolicaFloat, FloatLike as SymFloatLike},
50 },
51 prelude::*,
52};
53
54use statrs::function::gamma::{gamma, gamma_lr, gamma_ur};
55use std::cmp::{Ord, Ordering};
56use std::fmt::{Debug, Display};
57use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign};
58use std::str::FromStr;
59use std::sync::{LazyLock, OnceLock, RwLock};
60use std::time::Duration;
61
62use vakint::Vakint;
63use crate::MAX_LOOP;
70use ::tracing::debug;
71use typed_index_collections::TiVec;
72
73pub const GIT_VERSION: &str = env!("CARGO_PKG_VERSION");
74pub const VERSION: &str = "0.0.1";
75pub mod fitting;
76pub mod hyperdual_utils;
77pub mod progress;
78pub mod representations;
79pub mod serde_utils;
80pub use progress::{long_running_progress_style, long_running_progress_style_with_eta_warmup};
81#[macro_export]
83macro_rules! disable {
84 ($($tokens:tt)*) => {};
85}
86
87#[macro_export]
182macro_rules! debug_tags {
183 (@collect_tags [$($acc:tt)*] # $tag:ident, $($tail:tt)*) => {
184 $crate::debug_tags!(@collect_tags [$($acc)* $tag = true,] $($tail)*)
185 };
186 (@collect_tags [$($acc:tt)*] # $tag:ident; $($rest:tt)*) => {
187 $crate::debug_tags!(@collect_fields [$($acc)* $tag = true,] [] $($rest)*)
188 };
189 (@collect_fields [$($tags:tt)*] [$($fields:tt)*] log.$name:ident = $value:expr, $($tail:tt)*) => {
190 $crate::debug_tags!(@collect_fields
191 [$($tags)*]
192 [$($fields)*
193 display.$name = %$crate::LogMessage::log_display(&$value),
194 file.$name = %$crate::LogMessage::log_file(&$value),
195 ]
196 $($tail)*
197 )
198 };
199 (@collect_fields [$($tags:tt)*] [$($fields:tt)*] $prefix:ident.$name:ident = %$value:expr, $($tail:tt)*) => {
200 $crate::debug_tags!(@collect_fields [$($tags)*] [$($fields)* $prefix.$name = %$value,] $($tail)*)
201 };
202 (@collect_fields [$($tags:tt)*] [$($fields:tt)*] $prefix:ident.$name:ident = ?$value:expr, $($tail:tt)*) => {
203 $crate::debug_tags!(@collect_fields [$($tags)*] [$($fields)* $prefix.$name = ?$value,] $($tail)*)
204 };
205 (@collect_fields [$($tags:tt)*] [$($fields:tt)*] $prefix:ident.$name:ident = $value:expr, $($tail:tt)*) => {
206 $crate::debug_tags!(@collect_fields [$($tags)*] [$($fields)* $prefix.$name = $value,] $($tail)*)
207 };
208 (@collect_fields [$($tags:tt)*] [$($fields:tt)*] $name:ident = %$value:expr, $($tail:tt)*) => {
209 $crate::debug_tags!(@collect_fields [$($tags)*] [$($fields)* $name = %$value,] $($tail)*)
210 };
211 (@collect_fields [$($tags:tt)*] [$($fields:tt)*] $name:ident = ?$value:expr, $($tail:tt)*) => {
212 $crate::debug_tags!(@collect_fields [$($tags)*] [$($fields)* $name = ?$value,] $($tail)*)
213 };
214 (@collect_fields [$($tags:tt)*] [$($fields:tt)*] $name:ident = $value:expr, $($tail:tt)*) => {
215 $crate::debug_tags!(@collect_fields [$($tags)*] [$($fields)* $name = $value,] $($tail)*)
216 };
217 (@collect_fields [$($tags:tt)*] [$($fields:tt)*] $name:ident, $($tail:tt)*) => {
218 $crate::debug_tags!(@collect_fields [$($tags)*] [$($fields)* $name,] $($tail)*)
219 };
220 (@collect_fields [$($tags:tt)*] [$($fields:tt)*] $($rest:tt)+) => {
221 tracing::debug!($($tags)* $($fields)* $($rest)+)
222 };
223 ($($input:tt)*) => {
224 $crate::debug_tags!(@collect_tags [] $($input)*)
225 };
226}
227
228#[allow(unused)]
229const MAX_DIMENSION: usize = MAX_LOOP * 3;
230
231pub(crate) const ESURFACE_SHIFT_THRESHOLD: f64 = 1.0e-13;
232pub const DEFAULT_ESURFACE_EXISTENCE_THRESHOLD: f64 = 1.0e-7;
234
235pub const LEFT: usize = 0;
236pub const RIGHT: usize = 1;
237
238#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
239pub enum Side {
240 LEFT = 0,
241 RIGHT = 1,
242}
243
244impl From<Side> for usize {
245 fn from(val: Side) -> Self {
246 val as usize
247 }
248}
249
250pub mod linnet_ext;
251pub mod symbolica_ext;
252pub mod tracing;
253
254#[cfg(test)]
255mod tests {
256 #[test]
257 fn debug_tags_macro_supports_tags_and_regular_fields() {
258 crate::debug_tags!(#integration, #summary;
259 inspect = false,
260 answer = 42,
261 "macro compiles with tag shorthands and regular fields"
262 );
263 }
264}
265
266pub trait FloatConvertFrom<U> {
267 fn convert_from(x: &U) -> Self;
268}
269
270#[derive(Debug, Clone, PartialEq, PartialOrd, Encode, Decode)]
288pub struct VarFloat<const N: u32> {
289 #[bincode(with_serde)]
290 float: rug::Float,
291}
292
293impl<const N: u32> Serialize for VarFloat<N> {
294 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
295 where
296 S: serde::Serializer,
297 {
298 let string = self.float.to_string();
299 string.serialize(serializer)
300 }
301}
302
303impl<'de, const N: u32> Deserialize<'de> for VarFloat<N> {
304 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
305 where
306 D: Deserializer<'de>,
307 {
308 let string: String = serde::Deserialize::deserialize(deserializer)?;
309 let val: Self = string
310 .parse()
311 .unwrap_or_else(|_| panic!("failed to parse arb prec from string: {}", string));
312
313 Ok(val)
314 }
315}
316
317impl<const N: u32> FromStr for VarFloat<N> {
318 type Err = ParseFloatError;
319 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
320 let float = rug::Float::parse(s)?;
321 Ok(Self {
322 float: rug::Float::with_val(N, float),
323 })
324 }
325}
326
327#[derive(
328 Debug,
329 Clone,
330 Copy,
331 Default,
332 PartialEq,
333 PartialOrd,
334 Eq,
335 Hash,
336 Serialize,
337 Deserialize,
338 Encode,
339 Decode,
340)]
341#[repr(transparent)]
342pub struct QuadFloat(DoubleFloat);
343
344impl std::fmt::Display for QuadFloat {
345 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
346 std::fmt::Display::fmt(&self.0, f)
347 }
348}
349
350impl std::fmt::LowerExp for QuadFloat {
351 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
352 std::fmt::LowerExp::fmt(&self.0, f)
353 }
354}
355
356impl From<DoubleFloat> for QuadFloat {
357 fn from(value: DoubleFloat) -> Self {
358 Self(value)
359 }
360}
361
362impl From<QuadFloat> for DoubleFloat {
363 fn from(value: QuadFloat) -> Self {
364 value.0
365 }
366}
367
368impl From<&QuadFloat> for DoubleFloat {
369 fn from(value: &QuadFloat) -> Self {
370 value.0
371 }
372}
373
374impl From<SymbolicaFloat> for QuadFloat {
375 fn from(value: SymbolicaFloat) -> Self {
376 Self(value.to_double_float())
377 }
378}
379
380impl From<Float> for QuadFloat {
381 fn from(value: Float) -> Self {
382 Self(SymbolicaFloat::from(value).to_double_float())
383 }
384}
385
386impl From<&Rational> for QuadFloat {
387 fn from(value: &Rational) -> Self {
388 Self(value.into())
389 }
390}
391
392impl<const N: u32> Rem<&VarFloat<N>> for &VarFloat<N> {
393 type Output = VarFloat<N>;
394
395 fn rem(self, rhs: &VarFloat<N>) -> Self::Output {
396 (&self.float % &rhs.float).complete(N).into()
397 }
398}
399
400impl<const N: u32> From<Float> for VarFloat<N> {
401 fn from(x: Float) -> Self {
402 VarFloat {
403 float: rug::Float::with_val(N, x),
404 }
405 }
406}
407
408impl<const N: u32> From<&Rational> for VarFloat<N> {
409 fn from(x: &Rational) -> Self {
410 let n = x.numerator();
411
412 let n = match n {
413 Integer::Double(f) => Float::with_val(N, f),
414 Integer::Large(f) => Float::with_val(N, f),
415 Integer::Single(f) => Float::with_val(N, f),
416 };
417
418 let d = x.denominator();
419
420 let d = match d {
421 Integer::Double(f) => Float::with_val(N, f),
422 Integer::Large(f) => Float::with_val(N, f),
423 Integer::Single(f) => Float::with_val(N, f),
424 };
425
426 let r = n / d;
427
428 VarFloat {
429 float: rug::Float::with_val(N, r),
430 }
431 }
432}
433
434macro_rules! impl_quad_binary_op {
435 ($trait:ident, $fn:ident, $op:tt) => {
436 impl std::ops::$trait for QuadFloat {
437 type Output = Self;
438
439 fn $fn(self, rhs: Self) -> Self::Output {
440 Self(self.0 $op rhs.0)
441 }
442 }
443
444 impl std::ops::$trait<&QuadFloat> for QuadFloat {
445 type Output = Self;
446
447 fn $fn(self, rhs: &Self) -> Self::Output {
448 Self(self.0 $op rhs.0)
449 }
450 }
451
452 impl std::ops::$trait<QuadFloat> for &QuadFloat {
453 type Output = QuadFloat;
454
455 fn $fn(self, rhs: QuadFloat) -> Self::Output {
456 QuadFloat(self.0 $op rhs.0)
457 }
458 }
459
460 impl std::ops::$trait<&QuadFloat> for &QuadFloat {
461 type Output = QuadFloat;
462
463 fn $fn(self, rhs: &QuadFloat) -> Self::Output {
464 QuadFloat(self.0 $op rhs.0)
465 }
466 }
467 };
468}
469
470macro_rules! impl_quad_assign_op {
471 ($trait:ident, $fn:ident, $op:tt) => {
472 impl std::ops::$trait for QuadFloat {
473 fn $fn(&mut self, rhs: Self) {
474 self.0 $op rhs.0;
475 }
476 }
477
478 impl std::ops::$trait<&QuadFloat> for QuadFloat {
479 fn $fn(&mut self, rhs: &Self) {
480 self.0 $op rhs.0;
481 }
482 }
483 };
484}
485
486impl_quad_binary_op!(Add, add, +);
487impl_quad_binary_op!(Sub, sub, -);
488impl_quad_binary_op!(Mul, mul, *);
489impl_quad_binary_op!(Div, div, /);
490
491impl_quad_assign_op!(AddAssign, add_assign, +=);
492impl_quad_assign_op!(SubAssign, sub_assign, -=);
493impl_quad_assign_op!(MulAssign, mul_assign, *=);
494impl_quad_assign_op!(DivAssign, div_assign, /=);
495
496impl Neg for QuadFloat {
497 type Output = Self;
498
499 fn neg(self) -> Self::Output {
500 Self(-self.0)
501 }
502}
503
504impl Neg for &QuadFloat {
505 type Output = QuadFloat;
506
507 fn neg(self) -> Self::Output {
508 QuadFloat(-self.0)
509 }
510}
511
512impl Rem<&QuadFloat> for &QuadFloat {
513 type Output = QuadFloat;
514
515 fn rem(self, rhs: &QuadFloat) -> Self::Output {
516 self.truncating_remainder(rhs)
517 }
518}
519
520impl<const N: u32> std::ops::Mul for VarFloat<N> {
521 type Output = Self;
522
523 fn mul(self, rhs: Self) -> Self::Output {
524 (self.float * rhs.float).into()
525 }
526}
527
528impl<const N: u32> std::ops::Mul<&VarFloat<N>> for VarFloat<N> {
529 type Output = Self;
530
531 fn mul(self, rhs: &Self) -> Self::Output {
532 (self.float * &rhs.float).into()
533 }
534}
535
536impl<const N: u32> std::ops::Mul<VarFloat<N>> for &VarFloat<N> {
537 type Output = VarFloat<N>;
538
539 fn mul(self, rhs: VarFloat<N>) -> Self::Output {
540 (&self.float * &rhs.float).complete(N).into()
541 }
542}
543
544impl<const N: u32> std::ops::Mul<&VarFloat<N>> for &VarFloat<N> {
545 type Output = VarFloat<N>;
546
547 fn mul(self, rhs: &VarFloat<N>) -> Self::Output {
548 (&self.float * &rhs.float).complete(N).into()
549 }
550}
551
552impl<const N: u32> std::ops::MulAssign for VarFloat<N> {
553 fn mul_assign(&mut self, rhs: Self) {
554 self.float *= rhs.float;
555 self.float.set_prec(N);
556 }
557}
558
559impl<const N: u32> std::ops::MulAssign<&VarFloat<N>> for VarFloat<N> {
560 fn mul_assign(&mut self, rhs: &Self) {
561 self.float *= &rhs.float;
562 self.float.set_prec(N);
563 }
564}
565
566impl<const N: u32> std::ops::Add for VarFloat<N> {
567 type Output = Self;
568
569 fn add(self, rhs: Self) -> Self::Output {
570 (self.float + rhs.float).into()
571 }
572}
573
574impl<const N: u32> std::ops::Add<&VarFloat<N>> for VarFloat<N> {
575 type Output = Self;
576
577 fn add(self, rhs: &Self) -> Self::Output {
578 (self.float + &rhs.float).into()
579 }
580}
581
582impl<const N: u32> std::ops::Add<VarFloat<N>> for &VarFloat<N> {
583 type Output = VarFloat<N>;
584
585 fn add(self, rhs: VarFloat<N>) -> Self::Output {
586 (&self.float + &rhs.float).complete(N).into()
587 }
588}
589
590impl<const N: u32> std::ops::Add<&VarFloat<N>> for &VarFloat<N> {
591 type Output = VarFloat<N>;
592
593 fn add(self, rhs: &VarFloat<N>) -> Self::Output {
594 (&self.float + &rhs.float).complete(N).into()
595 }
596}
597
598impl<const N: u32> std::ops::AddAssign for VarFloat<N> {
599 fn add_assign(&mut self, rhs: Self) {
600 self.float += rhs.float;
601 self.float.set_prec(N);
602 }
603}
604
605impl<const N: u32> std::ops::AddAssign<&VarFloat<N>> for VarFloat<N> {
606 fn add_assign(&mut self, rhs: &Self) {
607 self.float += &rhs.float;
608 self.float.set_prec(N);
609 }
610}
611
612impl<const N: u32> std::ops::Sub for VarFloat<N> {
613 type Output = Self;
614
615 fn sub(self, rhs: Self) -> Self::Output {
616 (self.float - rhs.float).into()
617 }
618}
619
620impl<const N: u32> std::ops::Sub<&VarFloat<N>> for VarFloat<N> {
621 type Output = Self;
622
623 fn sub(self, rhs: &Self) -> Self::Output {
624 (self.float - &rhs.float).into()
625 }
626}
627
628impl<const N: u32> std::ops::Sub<VarFloat<N>> for &VarFloat<N> {
629 type Output = VarFloat<N>;
630
631 fn sub(self, rhs: VarFloat<N>) -> Self::Output {
632 (&self.float - &rhs.float).complete(N).into()
633 }
634}
635
636impl<const N: u32> std::ops::Sub<&VarFloat<N>> for &VarFloat<N> {
637 type Output = VarFloat<N>;
638
639 fn sub(self, rhs: &VarFloat<N>) -> Self::Output {
640 (&self.float - &rhs.float).complete(N).into()
641 }
642}
643
644impl<const N: u32> std::ops::SubAssign for VarFloat<N> {
645 fn sub_assign(&mut self, rhs: Self) {
646 self.float -= rhs.float;
647 self.float.set_prec(N);
648 }
649}
650
651impl<const N: u32> std::ops::SubAssign<&VarFloat<N>> for VarFloat<N> {
652 fn sub_assign(&mut self, rhs: &Self) {
653 self.float -= &rhs.float;
654 self.float.set_prec(N);
655 }
656}
657
658impl<const N: u32> Div for VarFloat<N> {
659 type Output = Self;
660
661 fn div(self, rhs: Self) -> Self::Output {
662 (self.float / rhs.float).into()
663 }
664}
665
666impl<const N: u32> Div<&VarFloat<N>> for VarFloat<N> {
667 type Output = Self;
668
669 fn div(self, rhs: &Self) -> Self::Output {
670 (self.float / &rhs.float).into()
671 }
672}
673
674impl<const N: u32> Div<VarFloat<N>> for &VarFloat<N> {
675 type Output = VarFloat<N>;
676
677 fn div(self, rhs: VarFloat<N>) -> Self::Output {
678 (&self.float / &rhs.float).complete(N).into()
679 }
680}
681
682impl<const N: u32> Div<&VarFloat<N>> for &VarFloat<N> {
683 type Output = VarFloat<N>;
684
685 fn div(self, rhs: &VarFloat<N>) -> Self::Output {
686 (&self.float / &rhs.float).complete(N).into()
687 }
688}
689
690impl<const N: u32> std::ops::DivAssign for VarFloat<N> {
691 fn div_assign(&mut self, rhs: Self) {
692 self.float /= rhs.float;
693 self.float.set_prec(N);
694 }
695}
696
697impl<const N: u32> std::ops::DivAssign<&VarFloat<N>> for VarFloat<N> {
698 fn div_assign(&mut self, rhs: &Self) {
699 self.float /= &rhs.float;
700 self.float.set_prec(N);
701 }
702}
703
704impl<const N: u32> std::ops::Neg for VarFloat<N> {
705 type Output = Self;
706
707 fn neg(self) -> Self::Output {
708 (-self.float).into()
709 }
710}
711
712impl<const N: u32> std::ops::Neg for &VarFloat<N> {
713 type Output = VarFloat<N>;
714
715 fn neg(self) -> Self::Output {
716 (-&self.float).complete(N).into()
717 }
718}
719
720impl<const N: u32> std::fmt::Display for VarFloat<N> {
721 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
722 write!(f, "{}", self.float)
723 }
724}
725
726impl<const N: u32> std::fmt::LowerExp for VarFloat<N> {
727 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
728 write!(f, "{:e}", self.float)
729 }
730}
731
732impl<const N: u32> RefZero for VarFloat<N> {
733 fn ref_zero(&self) -> Self {
734 Self::new_zero()
735 }
736}
737
738impl<const N: u32> R for VarFloat<N> {}
739
740impl<const N: u32> RefOne for VarFloat<N> {
741 fn ref_one(&self) -> Self {
742 self.one()
743 }
744}
745
746impl<const N: u32> SymFloatLike for VarFloat<N> {
747 #[inline]
748 fn set_from(&mut self, other: &Self) {
749 self.float.assign(&other.float);
750 }
751
752 fn mul_add(&self, a: &Self, b: &Self) -> Self {
753 (&self.float * &a.float + &b.float).complete(N).into()
754 }
755
756 fn is_fully_zero(&self) -> bool {
757 self.float.is_zero()
758 }
759
760 fn from_i64(&self, a: i64) -> Self {
765 VarFloat {
766 float: Float::with_val(N, a),
767 }
768 }
769
770 fn from_usize(&self, a: usize) -> Self {
771 VarFloat {
772 float: Float::with_val(N, a),
773 }
774 }
775
776 fn get_precision(&self) -> u32 {
777 N
778 }
779
780 fn zero(&self) -> Self {
781 Self::new_zero()
782 }
783
784 fn one(&self) -> Self {
785 self.from_i64(1)
786 }
787
788 fn new_zero() -> Self {
789 VarFloat {
790 float: Float::new(N),
791 }
792 }
793
794 fn inv(&self) -> Self {
795 self.float.clone().recip().into()
796 }
797
798 fn pow(&self, e: u64) -> Self {
799 rug::ops::Pow::pow(&self.float, e).complete(N).into()
800 }
801
802 fn sample_unit<R: Rng + ?Sized>(&self, rng: &mut R) -> Self {
803 let f: f64 = rng.random();
804 Float::with_val(N, f).into()
805 }
806
807 fn neg(&self) -> Self {
808 (-self.float.clone()).into()
809 }
810
811 fn get_epsilon(&self) -> f64 {
812 2.0f64.powi(1 - (N as i32))
813 }
814
815 fn fixed_precision(&self) -> bool {
816 true
817 }
818}
819
820impl<const N: u32> SingleFloat for VarFloat<N> {
821 fn is_finite(&self) -> bool {
822 self.float.is_finite()
823 }
824
825 fn is_one(&self) -> bool {
826 self.float == 1.
827 }
828
829 fn is_zero(&self) -> bool {
830 self.float == 0.
831 }
832
833 fn from_rational(&self, rat: &Rational) -> Self {
834 rat.into()
835 }
836}
837impl<const N: u32> RealLike for VarFloat<N> {
838 fn to_f64(&self) -> f64 {
839 self.float.to_f64()
840 }
841
842 fn round_to_nearest_integer(&self) -> Integer {
843 self.float.clone().round().to_integer().unwrap().into()
844 }
845
846 fn to_usize_clamped(&self) -> usize {
847 self.float
848 .to_integer()
849 .unwrap()
850 .to_usize()
851 .unwrap_or(usize::MAX)
852 }
853}
854
855impl<const N: u32> Real for VarFloat<N> {
856 fn i(&self) -> Option<Self> {
857 None
858 }
859
860 fn conj(&self) -> Self {
861 self.clone()
862 }
863
864 #[inline(always)]
865 fn pi(&self) -> Self {
866 Float::with_val(N, rug::float::Constant::Pi).into()
867 }
868
869 #[inline(always)]
870 fn e(&self) -> Self {
871 self.one().exp()
872 }
873
874 #[inline(always)]
875 fn euler(&self) -> Self {
876 Float::with_val(N, rug::float::Constant::Euler).into()
877 }
878
879 #[inline(always)]
880 fn phi(&self) -> Self {
881 (self.one() + self.from_i64(5).sqrt()) / Self::from_f64(2.)
882 }
883 fn atan2(&self, x: &Self) -> Self {
884 self.float.clone().atan2(&x.float).into()
885 }
886
887 fn powf(&self, e: &Self) -> Self {
888 self.float.clone().pow(e.float.clone()).into()
889 }
890
891 fn log(&self) -> Self {
892 self.float.ln_ref().complete(N).into()
893 }
894 fn norm(&self) -> Self {
895 self.float.clone().abs().into()
896 }
897
898 delegate! {
899 #[into]
900 to self.float.clone(){
901 fn sqrt(&self) -> Self;
902 fn exp(&self) -> Self;
903 fn sin(&self) -> Self;
904 fn cos(&self) -> Self;
905 fn tan(&self) -> Self;
906 fn asin(&self) -> Self;
907 fn acos(&self) -> Self;
908 fn sinh(&self) -> Self;
909 fn cosh(&self) -> Self;
910 fn tanh(&self) -> Self;
911 fn asinh(&self) -> Self;
912 fn acosh(&self) -> Self;
913 fn atanh(&self) -> Self;
914 }
915 }
916}
917
918impl<const N: u32> From<VarFloat<N>> for SymbolicaFloat {
919 fn from(value: VarFloat<N>) -> Self {
920 SymbolicaFloat::from(value.float)
921 }
922}
923
924impl<const N: u32> From<&VarFloat<N>> for SymbolicaFloat {
925 fn from(value: &VarFloat<N>) -> Self {
926 SymbolicaFloat::from(value.float.clone())
927 }
928}
929
930impl<const N: u32> From<SymbolicaFloat> for VarFloat<N> {
931 fn from(value: SymbolicaFloat) -> Self {
932 Self {
933 float: value.into_inner(),
934 }
935 }
936}
937
938impl From<QuadFloat> for SymbolicaFloat {
939 fn from(value: QuadFloat) -> Self {
940 value.0.into()
941 }
942}
943
944impl From<&QuadFloat> for SymbolicaFloat {
945 fn from(value: &QuadFloat) -> Self {
946 value.0.into()
947 }
948}
949
950impl RefZero for QuadFloat {
951 fn ref_zero(&self) -> Self {
952 Self::new_zero()
953 }
954}
955
956impl R for QuadFloat {}
957
958impl RefOne for QuadFloat {
959 fn ref_one(&self) -> Self {
960 self.one()
961 }
962}
963
964impl SymFloatLike for QuadFloat {
965 #[inline]
966 fn set_from(&mut self, other: &Self) {
967 self.0.set_from(&other.0);
968 }
969
970 fn mul_add(&self, a: &Self, b: &Self) -> Self {
971 Self(self.0.mul_add(&a.0, &b.0))
972 }
973
974 fn is_fully_zero(&self) -> bool {
975 self.0.is_fully_zero()
976 }
977
978 fn from_i64(&self, a: i64) -> Self {
979 Self(self.0.from_i64(a))
980 }
981
982 fn from_usize(&self, a: usize) -> Self {
983 Self(self.0.from_usize(a))
984 }
985
986 fn get_precision(&self) -> u32 {
987 self.0.get_precision()
988 }
989
990 fn zero(&self) -> Self {
991 Self(self.0.zero())
992 }
993
994 fn one(&self) -> Self {
995 Self(self.0.one())
996 }
997
998 fn new_zero() -> Self {
999 Self(DoubleFloat::default().zero())
1000 }
1001
1002 fn inv(&self) -> Self {
1003 Self(self.0.inv())
1004 }
1005
1006 fn pow(&self, e: u64) -> Self {
1007 self.pow_u64_via_mul(e)
1027 }
1028
1029 fn sample_unit<R: Rng + ?Sized>(&self, rng: &mut R) -> Self {
1030 Self(self.0.sample_unit(rng))
1031 }
1032
1033 fn neg(&self) -> Self {
1034 Self(self.0.neg())
1035 }
1036
1037 fn get_epsilon(&self) -> f64 {
1038 self.0.get_epsilon()
1039 }
1040
1041 fn fixed_precision(&self) -> bool {
1042 self.0.fixed_precision()
1043 }
1044}
1045
1046impl SingleFloat for QuadFloat {
1047 fn is_finite(&self) -> bool {
1048 self.0.is_finite()
1049 }
1050
1051 fn is_one(&self) -> bool {
1052 self.0.is_one()
1053 }
1054
1055 fn is_zero(&self) -> bool {
1056 self.0.is_zero()
1057 }
1058
1059 fn from_rational(&self, rat: &Rational) -> Self {
1060 rat.into()
1061 }
1062}
1063
1064impl RealLike for QuadFloat {
1065 fn to_f64(&self) -> f64 {
1066 self.0.to_f64()
1067 }
1068
1069 fn round_to_nearest_integer(&self) -> Integer {
1070 self.0.round_to_nearest_integer()
1071 }
1072
1073 fn to_usize_clamped(&self) -> usize {
1074 self.0.to_usize_clamped()
1075 }
1076}
1077
1078impl Real for QuadFloat {
1079 fn i(&self) -> Option<Self> {
1080 None
1081 }
1082
1083 fn conj(&self) -> Self {
1084 *self
1085 }
1086
1087 #[inline(always)]
1088 fn pi(&self) -> Self {
1089 Self(self.0.pi())
1090 }
1091
1092 #[inline(always)]
1093 fn e(&self) -> Self {
1094 Self(self.0.e())
1095 }
1096
1097 #[inline(always)]
1098 fn euler(&self) -> Self {
1099 Self(self.0.euler())
1100 }
1101
1102 #[inline(always)]
1103 fn phi(&self) -> Self {
1104 Self(self.0.phi())
1105 }
1106
1107 fn atan2(&self, x: &Self) -> Self {
1108 Self(self.0.atan2(&x.0))
1109 }
1110
1111 fn powf(&self, e: &Self) -> Self {
1112 Self(self.0.powf(&e.0))
1113 }
1114
1115 fn log(&self) -> Self {
1116 Self(self.0.log())
1117 }
1118
1119 fn norm(&self) -> Self {
1120 Self(self.0.norm())
1121 }
1122
1123 fn sqrt(&self) -> Self {
1124 Self(self.0.sqrt())
1125 }
1126
1127 fn exp(&self) -> Self {
1128 Self(self.0.exp())
1129 }
1130
1131 fn sin(&self) -> Self {
1132 Self(self.0.sin())
1133 }
1134
1135 fn cos(&self) -> Self {
1136 Self(self.0.cos())
1137 }
1138
1139 fn tan(&self) -> Self {
1140 Self(self.0.tan())
1141 }
1142
1143 fn asin(&self) -> Self {
1144 Self(self.0.asin())
1145 }
1146
1147 fn acos(&self) -> Self {
1148 Self(self.0.acos())
1149 }
1150
1151 fn sinh(&self) -> Self {
1152 Self(self.0.sinh())
1153 }
1154
1155 fn cosh(&self) -> Self {
1156 Self(self.0.cosh())
1157 }
1158
1159 fn tanh(&self) -> Self {
1160 Self(self.0.tanh())
1161 }
1162
1163 fn asinh(&self) -> Self {
1164 Self(self.0.asinh())
1165 }
1166
1167 fn acosh(&self) -> Self {
1168 Self(self.0.acosh())
1169 }
1170
1171 fn atanh(&self) -> Self {
1172 Self(self.0.atanh())
1173 }
1174}
1175
1176impl FloatLike for f128 {
1177 fn E(&self) -> Self {
1178 Self::E()
1179 }
1180
1181 fn PIHALF(&self) -> Self {
1182 Self::PIHALF()
1183 }
1184
1185 fn SQRT_2(&self) -> Self {
1186 Self::from_f64(2.0).sqrt()
1187 }
1188
1189 fn SQRT_2_HALF(&self) -> Self {
1190 Self::from_f64(2.0).sqrt() / Self::from_f64(2.0)
1191 }
1192
1193 fn rem_euclid(&self, rhs: &Self) -> Self {
1194 let r = self.ref_rem(rhs);
1195 if r < r.zero() { r + rhs } else { r }
1196 }
1197
1198 fn FRAC_1_PI(&self) -> Self {
1199 Self::FRAC_1_PI()
1200 }
1201
1202 fn PI(&self) -> Self {
1203 Self::PI()
1204 }
1205
1206 fn TAU(&self) -> Self {
1207 Self::TAU()
1208 }
1209
1210 fn from_f64(x: f64) -> Self {
1211 Self::from_f64_decimal(x)
1220 }
1221
1222 fn into_f64(&self) -> f64 {
1223 self.as_f64()
1224 }
1225
1226 fn is_nan(&self) -> bool {
1227 self.is_nan_value()
1228 }
1229
1230 fn is_infinite(&self) -> bool {
1231 self.is_infinite_value()
1232 }
1233
1234 fn floor(&self) -> Self {
1235 self.floor_value()
1236 }
1237
1238 fn try_extract_externals_from_cache(
1239 externals: &Externals,
1240 ) -> Option<&TiVec<ExternalIndex, FourMomentum<F<Self>>>> {
1241 match externals {
1242 Externals::Constant { f_128_cache, .. } => f_128_cache.as_ref(),
1243 }
1244 }
1245
1246 fn epsilon(&self) -> Self {
1247 self.machine_epsilon()
1248 }
1249}
1250
1251impl FloatLike for ArbPrec {
1252 fn E(&self) -> Self {
1253 Self::E()
1254 }
1255
1256 fn PIHALF(&self) -> Self {
1257 Self::PIHALF()
1258 }
1259
1260 fn SQRT_2(&self) -> Self {
1261 Self::from_f64(2.0).sqrt()
1262 }
1263
1264 fn SQRT_2_HALF(&self) -> Self {
1265 Self::from_f64(2.0).sqrt() / Self::from_f64(2.0)
1266 }
1267
1268 fn rem_euclid(&self, rhs: &Self) -> Self {
1269 let r = self.ref_rem(rhs);
1270 if r < r.zero() { r + rhs } else { r }
1271 }
1272
1273 fn FRAC_1_PI(&self) -> Self {
1274 Self::FRAC_1_PI()
1275 }
1276
1277 fn PI(&self) -> Self {
1278 Self::PI()
1279 }
1280
1281 fn TAU(&self) -> Self {
1282 Self::TAU()
1283 }
1284
1285 fn from_f64(x: f64) -> Self {
1286 VarFloat::from_f64(x)
1295 }
1296
1297 fn into_f64(&self) -> f64 {
1298 self.to_f64()
1299 }
1300
1301 fn is_nan(&self) -> bool {
1302 self.float.is_nan()
1303 }
1304
1305 fn is_infinite(&self) -> bool {
1306 self.float.is_infinite()
1307 }
1308
1309 fn floor(&self) -> Self {
1310 self.float.clone().floor().into()
1311 }
1312
1313 fn try_extract_externals_from_cache(
1314 _externals: &Externals,
1315 ) -> Option<&TiVec<ExternalIndex, FourMomentum<F<Self>>>> {
1316 None
1317 }
1318
1319 fn epsilon(&self) -> Self {
1320 self.machine_epsilon()
1321 }
1322}
1323
1324impl<const N: u32> VarFloat<N> {
1325 fn machine_epsilon(&self) -> Self {
1326 self.from_i64(2).pow((N - 1) as u64).inv()
1327 }
1328
1329 fn one() -> Self {
1330 VarFloat {
1331 float: rug::Float::with_val(N, 1.0),
1332 }
1333 }
1334 #[allow(non_snake_case)]
1335 fn E() -> Self {
1336 Self::one().exp()
1337 }
1338
1339 #[allow(non_snake_case)]
1340 fn PIHALF() -> Self {
1341 Self::PI() / Self::from_f64(2.0)
1342 }
1343
1344 #[allow(non_snake_case)]
1345 fn PI() -> Self {
1346 VarFloat {
1347 float: rug::Float::with_val(N, Constant::Pi),
1348 }
1349 }
1350
1351 #[allow(non_snake_case)]
1352 fn TAU() -> Self {
1353 let mut tau = Self::PI() + Self::PI();
1354 tau.float.set_prec(N);
1355 tau
1356 }
1357
1358 #[allow(non_snake_case)]
1359 fn FRAC_1_PI() -> Self {
1360 Self::PI().inv()
1361 }
1362
1363 #[allow(dead_code)]
1364 pub(crate) fn from_f64_exact_binary(x: f64) -> Self {
1365 VarFloat {
1366 float: rug::Float::with_val(N, x),
1367 }
1368 }
1369
1370 pub(crate) fn from_f64_decimal(x: f64) -> Self {
1371 if !x.is_finite() {
1372 return Self::from_f64_exact_binary(x);
1373 }
1374
1375 let valid = Float::parse(format!("{}", x)).unwrap();
1376 VarFloat {
1377 float: rug::Float::with_val(N, valid),
1378 }
1379 }
1380
1381 pub(crate) fn from_f64(x: f64) -> Self {
1382 Self::from_f64_decimal(x)
1391 }
1392
1393 pub(crate) fn to_f64(&self) -> f64 {
1394 self.float.to_f64()
1395 }
1396
1397 #[allow(dead_code)]
1398 fn as_f64(&self) -> f64 {
1399 self.float.to_f64()
1400 }
1401
1402 #[allow(dead_code)]
1403 fn is_nan_value(&self) -> bool {
1404 self.float.is_nan()
1405 }
1406
1407 #[allow(dead_code)]
1408 fn is_infinite_value(&self) -> bool {
1409 self.float.is_infinite()
1410 }
1411
1412 #[allow(dead_code)]
1413 fn floor_value(&self) -> Self {
1414 self.float.clone().floor().into()
1415 }
1416}
1417
1418impl QuadFloat {
1419 fn pow_u64_via_mul(&self, mut exponent: u64) -> Self {
1420 let mut base = *self;
1421 let mut acc = Self::one();
1422
1423 while exponent > 0 {
1424 if exponent & 1 == 1 {
1425 acc *= base;
1426 }
1427 exponent >>= 1;
1428 if exponent > 0 {
1429 base *= base;
1430 }
1431 }
1432
1433 acc
1434 }
1435
1436 fn machine_epsilon(&self) -> Self {
1437 self.from_i64(2)
1438 .pow((self.get_precision() - 1) as u64)
1439 .inv()
1440 }
1441
1442 #[allow(dead_code)]
1443 fn one() -> Self {
1444 Self(DoubleFloat::from(1.0))
1445 }
1446
1447 #[allow(non_snake_case)]
1448 fn E() -> Self {
1449 Self(DoubleFloat::default().e())
1450 }
1451
1452 #[allow(non_snake_case)]
1453 fn PIHALF() -> Self {
1454 Self::PI() / Self::from_f64(2.0)
1455 }
1456
1457 #[allow(non_snake_case)]
1458 fn PI() -> Self {
1459 Self(DoubleFloat::default().pi())
1460 }
1461
1462 #[allow(non_snake_case)]
1463 fn TAU() -> Self {
1464 Self::PI() + Self::PI()
1465 }
1466
1467 #[allow(non_snake_case)]
1468 fn FRAC_1_PI() -> Self {
1469 Self::PI().inv()
1470 }
1471
1472 #[allow(dead_code)]
1473 pub(crate) fn from_f64_exact_binary(x: f64) -> Self {
1474 Self(DoubleFloat::from(x))
1475 }
1476
1477 pub(crate) fn from_f64_decimal(x: f64) -> Self {
1478 if !x.is_finite() {
1479 return Self::from_f64_exact_binary(x);
1480 }
1481
1482 SymbolicaFloat::parse(
1483 &format!("{}", x),
1484 Some(DoubleFloat::default().get_precision()),
1485 )
1486 .unwrap()
1487 .into()
1488 }
1489
1490 fn from_f64(x: f64) -> Self {
1491 Self::from_f64_decimal(x)
1492 }
1493
1494 fn floor_via_symbolica(&self) -> Self {
1495 SymbolicaFloat::from(self).into_inner().floor().into()
1496 }
1497
1498 fn trunc_toward_zero(&self) -> Self {
1499 if *self < Self::new_zero() {
1500 -(-self).floor_via_symbolica()
1501 } else {
1502 self.floor_via_symbolica()
1503 }
1504 }
1505
1506 fn truncating_remainder(&self, rhs: &Self) -> Self {
1507 if rhs.is_zero() {
1508 return Self::from_f64_exact_binary(f64::NAN);
1509 }
1510
1511 *self - ((*self / *rhs).trunc_toward_zero() * *rhs)
1512 }
1513
1514 fn as_f64(&self) -> f64 {
1515 self.0.to_f64()
1516 }
1517
1518 fn is_nan_value(&self) -> bool {
1519 self.0.to_f64().is_nan()
1520 }
1521
1522 fn is_infinite_value(&self) -> bool {
1523 self.0.to_f64().is_infinite()
1524 }
1525
1526 fn floor_value(&self) -> Self {
1527 self.floor_via_symbolica()
1528 }
1529}
1530
1531impl<const N: u32> Default for VarFloat<N> {
1532 fn default() -> Self {
1533 VarFloat {
1534 float: rug::Float::with_val(N, 0.0),
1535 }
1536 }
1537}
1538
1539impl PrecisionUpgradable for f128 {
1540 type Higher = ArbPrec;
1541 type Lower = f64;
1542
1543 fn higher(&self) -> Self::Higher {
1544 ArbPrec::from(SymbolicaFloat::from(self))
1545 }
1546
1547 fn lower(&self) -> Self::Lower {
1548 self.as_f64()
1549 }
1550}
1551
1552impl PrecisionUpgradable for ArbPrec {
1553 type Higher = ArbPrec;
1554 type Lower = f128;
1555
1556 fn higher(&self) -> Self::Higher {
1557 self.clone()
1558 }
1559
1560 fn lower(&self) -> Self::Lower {
1561 f128::from(self.float.clone())
1562 }
1563}
1564
1565impl<T: Real + PrecisionUpgradable, H: Real, L: Real> PrecisionUpgradable for Complex<T>
1566where
1567 T: PrecisionUpgradable<Higher = H, Lower = L>,
1568{
1569 type Higher = Complex<H>;
1570 type Lower = Complex<L>;
1571
1572 fn higher(&self) -> Self::Higher {
1573 Complex::new(self.re.higher(), self.im.higher())
1574 }
1575
1576 fn lower(&self) -> Self::Lower {
1577 Complex::new(self.re.lower(), self.im.lower())
1578 }
1579}
1580
1581pub trait PrecisionUpgradable {
1585 type Higher;
1586 type Lower;
1587
1588 fn higher(&self) -> Self::Higher;
1589 fn lower(&self) -> Self::Lower;
1590}
1591
1592pub trait FloatLike:
1593 Real
1594 +R
1595 +Default
1596 + Clone
1597 + PartialOrd
1598 + RealLike
1599 + for<'a> RefAdd<&'a Self, Output = Self>
1600 + RefAdd<Self, Output = Self>
1602 + for<'a> RefMul<&'a Self, Output = Self>
1604 + RefMul<Self, Output = Self>
1606 + for<'a> RefSub<&'a Self, Output = Self>
1608 + RefSub<Self, Output = Self>
1610 + for<'a> RefDiv<&'a Self, Output = Self>
1612 + RefDiv<Self, Output = Self>
1614 + for<'a> RefRem<&'a Self, Output = Self>
1615 + RefNeg<Output = Self>
1617 + RefZero
1618 + RefOne
1619 + PrecisionUpgradable
1621 + Serialize
1622 + Display
1623 + GenericEvaluatorFloat
1624 + Into<symbolica::domains::float::Float>
1625{
1626
1627 #[allow(non_snake_case)]
1628 fn PI(&self) -> Self;
1629 #[allow(non_snake_case)]
1630 fn E(&self) -> Self;
1631 #[allow(non_snake_case)]
1632 fn TAU(&self) -> Self;
1633 #[allow(non_snake_case)]
1634 fn SQRT_2(&self) -> Self;
1635 #[allow(non_snake_case)]
1636 fn SQRT_2_HALF(&self) -> Self;
1637 #[allow(non_snake_case)]
1638 fn PIHALF(&self) -> Self;
1639 #[allow(non_snake_case)]
1640 fn FRAC_1_PI(&self) -> Self;
1641
1642 fn from_f64(x: f64) -> Self;
1643
1644 #[allow(clippy::wrong_self_convention)]
1645 fn into_f64(&self) -> f64; fn is_nan(&self) -> bool;
1648
1649 fn is_infinite(&self) -> bool;
1650
1651 fn floor(&self) -> Self;
1652
1653 fn square(&self) -> Self {
1654 self.pow(2)
1655 }
1656
1657 fn powi(&self, n: i32) -> Self {
1658 let absn = n.unsigned_abs() as u64;
1659 if n.is_negative() {
1660 self.pow(absn).inv()
1661 } else {
1662 self.pow(absn)
1663 }
1664 }
1665
1666 fn epsilon(&self) -> Self;
1667
1668 fn less_than_epsilon(&self) -> bool {
1669 self < &self.epsilon()
1670 }
1671
1672 fn positive(&self) -> bool {
1673 self > &self.zero()
1674 }
1675
1676 fn max_value(&self) -> Self {
1677 Self::from_f64(f64::MAX)
1678 }
1679
1680 fn min_value(&self) -> Self {
1681 Self::from_f64(f64::MIN)
1682 }
1683
1684 fn ln(&self) -> Self {
1685 self.log()
1686 }
1687
1688 fn rem_euclid(&self, rhs: &Self) -> Self;
1689
1690 fn try_extract_externals_from_cache(externals: &Externals) -> Option<&TiVec<ExternalIndex, FourMomentum<F<Self>>>>;
1691}
1692
1693#[derive(
1694 Debug,
1695 Clone,
1696 PartialEq,
1697 PartialOrd,
1698 Copy,
1699 Default,
1700 Serialize,
1701 Deserialize,
1702 Encode,
1703 Decode,
1704 Hash,
1705 JsonSchema,
1706)]
1707pub struct F<T: FloatLike>(pub T);
1708
1709impl<T: FloatLike> ToCoefficient for F<T> {
1710 fn to_coefficient(self) -> Coefficient {
1711 let z = self.zero();
1712 Coefficient::Float(symbolica::domains::float::Complex {
1713 re: self.into(),
1714 im: z.into(),
1715 })
1716 }
1717}
1718
1719use symbolica::evaluate::{EvaluationDomain, ExportNumber};
1720
1721impl ExportNumber for QuadFloat {
1722 fn export(&self) -> String {
1723 self.to_string()
1724 }
1725
1726 fn is_real(&self) -> bool {
1727 true
1728 }
1729
1730 fn to_complex_double(&self) -> symbolica::domains::float::Complex<f64> {
1731 symbolica::domains::float::Complex::new(self.0.to_f64(), 0.0)
1732 }
1733}
1734
1735impl<const N: u32> ExportNumber for VarFloat<N> {
1736 fn export(&self) -> String {
1737 self.to_string()
1738 }
1739
1740 fn is_real(&self) -> bool {
1741 true
1742 }
1743
1744 fn to_complex_double(&self) -> symbolica::domains::float::Complex<f64> {
1745 symbolica::domains::float::Complex::new(self.to_f64(), 0.0)
1746 }
1747}
1748
1749impl FixedPrecision for QuadFloat {
1750 const BINARY_PRECISION: usize = <DoubleFloat as FixedPrecision>::BINARY_PRECISION;
1751}
1752
1753impl<const N: u32> FixedPrecision for VarFloat<N> {
1754 const BINARY_PRECISION: usize = N as usize;
1755}
1756
1757impl EvaluationDomain for QuadFloat {
1758 const FIXED_PRECISION: Option<u32> = <DoubleFloat as EvaluationDomain>::FIXED_PRECISION;
1759
1760 fn try_from_complex_float(
1761 f: symbolica::domains::float::Complex<symbolica::domains::float::Float>,
1762 ) -> Result<Self, String> {
1763 if f.is_real() {
1764 Ok(Self(f.re.to_double_float()))
1765 } else {
1766 Err(format!(
1767 "Cannot convert from Complex<Float> to {} because the result is not real",
1768 std::any::type_name::<Self>()
1769 ))
1770 }
1771 }
1772}
1773
1774impl<const N: u32> EvaluationDomain for VarFloat<N> {
1775 const FIXED_PRECISION: Option<u32> = Some(N);
1776
1777 fn try_from_complex_float(
1778 f: symbolica::domains::float::Complex<symbolica::domains::float::Float>,
1779 ) -> Result<Self, String> {
1780 if f.is_real() {
1781 Ok(Self::from(f.re))
1782 } else {
1783 Err(format!(
1784 "Cannot convert from Complex<Float> to {} because the result is not real",
1785 std::any::type_name::<Self>()
1786 ))
1787 }
1788 }
1789}
1790
1791impl<T: FloatLike + ExportNumber> ExportNumber for F<T> {
1792 fn export(&self) -> String {
1793 self.0.to_string()
1794 }
1795
1796 fn is_real(&self) -> bool {
1797 self.0.is_real()
1798 }
1799
1800 fn to_complex_double(&self) -> symbolica::domains::float::Complex<f64> {
1801 self.0.to_complex_double()
1802 }
1803}
1804
1805impl<T: FloatLike + FixedPrecision> FixedPrecision for F<T> {
1806 const BINARY_PRECISION: usize = T::BINARY_PRECISION;
1807}
1808
1809impl<T: FloatLike + EvaluationDomain> EvaluationDomain for F<T> {
1810 const FIXED_PRECISION: Option<u32> = T::FIXED_PRECISION;
1811
1812 fn try_from_complex_float(
1813 f: symbolica::domains::float::Complex<symbolica::domains::float::Float>,
1814 ) -> Result<Self, String> {
1815 if f.is_real() {
1816 T::try_from_complex_float(f).map(F)
1817 } else {
1818 Err(format!(
1819 "Cannot convert from Complex<Float> to {} because the result is not real",
1820 std::any::type_name::<Self>()
1821 ))
1822 }
1823 }
1824}
1825
1826impl<T: TensorLibraryData + FloatLike> TensorLibraryData for F<T> {
1827 fn one() -> Self {
1828 F(<T as TensorLibraryData>::one())
1829 }
1830
1831 fn minus_one() -> Self {
1832 F(<T as TensorLibraryData>::minus_one())
1833 }
1834
1835 fn zero() -> Self {
1836 F(<T as TensorLibraryData>::zero())
1837 }
1838}
1839
1840impl From<F<f64>> for Coefficient {
1841 fn from(value: F<f64>) -> Self {
1842 Coefficient::from(value.0)
1843 }
1844}
1845
1846impl<'a> From<&'a F<f64>> for Coefficient {
1847 fn from(x: &'a F<f64>) -> Self {
1848 x.0.into()
1849 }
1850}
1851
1852impl ToFloat for F<f64> {
1853 fn to_float(&self) -> symbolica::domains::float::Float {
1854 symbolica::domains::float::Float::with_val(53, self.0)
1855 }
1856}
1857
1858impl ToAtom for F<f64> {
1859 fn to_atom(self) -> Atom {
1860 Atom::num(self.0)
1861 }
1862}
1863
1864pub trait ToCoefficient {
1865 fn to_coefficient(self) -> Coefficient;
1866}
1867
1868impl<T: FloatLike> From<F<T>> for symbolica::domains::float::Float {
1869 fn from(value: F<T>) -> Self {
1870 value.0.into()
1871 }
1872}
1873
1874impl<T: FloatLike> ToCoefficient for Complex<F<T>> {
1875 fn to_coefficient(self) -> Coefficient {
1876 Coefficient::Float(symbolica::domains::float::Complex {
1877 re: self.re.0.into(),
1878 im: self.im.0.into(),
1879 })
1880 }
1881}
1882
1883impl TrySmallestUpgrade<F<f64>> for Atom {
1884 type LCM = Atom;
1885
1886 fn try_upgrade(&'_ self) -> Option<std::borrow::Cow<'_, Self::LCM>> {
1887 Some(std::borrow::Cow::Borrowed(self))
1888 }
1889}
1890
1891impl TrySmallestUpgrade<Atom> for F<f64> {
1892 type LCM = Atom;
1893
1894 fn try_upgrade(&'_ self) -> Option<std::borrow::Cow<'_, Self::LCM>> {
1895 <f64 as TrySmallestUpgrade<Atom>>::try_upgrade(&self.0)
1896 }
1897}
1898
1899impl<T: FloatLike> R for F<T> {}
1900
1901impl<T: FloatLike> Rem<&F<T>> for &F<T> {
1902 type Output = F<T>;
1903
1904 fn rem(self, rhs: &F<T>) -> Self::Output {
1905 F(self.0.ref_rem(&rhs.0))
1906 }
1907}
1908
1909impl<T: FloatLike> RefZero<F<T>> for &F<T> {
1910 fn ref_zero(&self) -> F<T> {
1911 F(self.0.ref_zero())
1912 }
1913}
1914
1915impl<T: FloatLike> RealLike for F<T> {
1916 delegate! {
1917 to self.0{
1918 fn to_usize_clamped(&self)->usize;
1919 fn to_f64(&self)->f64;
1920 fn round_to_nearest_integer(&self)->Integer;
1921 }
1922 }
1923}
1924
1925impl<T: FloatLike> SingleFloat for F<T> {
1926 delegate! {
1927 to self.0{
1928 fn is_zero(&self)->bool;
1929 fn is_one(&self)->bool;
1930 fn is_finite(&self)->bool;
1931 }
1932 }
1933 fn from_rational(&self, rat: &Rational) -> Self {
1934 F(self.0.from_rational(rat))
1935 }
1936}
1937
1938impl<T: FloatLike> PrecisionUpgradable for F<T>
1939where
1940 T::Higher: FloatLike,
1941 T::Lower: FloatLike,
1942{
1943 type Higher = F<T::Higher>;
1944 type Lower = F<T::Lower>;
1945
1946 fn higher(&self) -> Self::Higher {
1947 F(self.0.higher())
1948 }
1949
1950 fn lower(&self) -> Self::Lower {
1951 F(self.0.lower())
1952 }
1953}
1954
1955impl<T: FloatLike> RefZero for F<T> {
1956 fn ref_zero(&self) -> Self {
1957 F(self.0.zero())
1958 }
1959}
1960
1961impl<T: FloatLike> RefOne for F<T> {
1962 fn ref_one(&self) -> Self {
1963 F(self.0.one())
1964 }
1965}
1966
1967impl<T: FloatLike> TrySmallestUpgrade<F<T>> for F<T> {
1968 type LCM = F<T>;
1969 fn try_upgrade(&'_ self) -> Option<std::borrow::Cow<'_, Self::LCM>> {
1970 Some(std::borrow::Cow::Borrowed(self))
1971 }
1972}
1973
1974impl<T: FloatLike> TrySmallestUpgrade<F<T>> for Complex<F<T>> {
1975 type LCM = Complex<F<T>>;
1976 fn try_upgrade(&'_ self) -> Option<std::borrow::Cow<'_, Self::LCM>> {
1977 Some(std::borrow::Cow::Borrowed(self))
1978 }
1979}
1980
1981impl<T: FloatLike> TrySmallestUpgrade<Complex<F<T>>> for F<T> {
1982 type LCM = Complex<F<T>>;
1983 fn try_upgrade(&'_ self) -> Option<std::borrow::Cow<'_, Self::LCM>> {
1984 let z = self.ref_zero();
1985
1986 Some(std::borrow::Cow::Owned(Complex::new(self.clone(), z)))
1987 }
1988}
1989
1990impl<'a, T: FloatLike> From<&'a Rational> for F<T> {
1998 fn from(x: &'a Rational) -> Self {
1999 F(T::new_zero().from_rational(x))
2000 }
2001}
2002
2003impl<T: FloatLike> From<T> for F<T> {
2004 fn from(x: T) -> Self {
2005 F(x)
2006 }
2007}
2008
2009impl<T: FloatLike> std::fmt::Display for F<T> {
2010 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2011 write!(f, "{}", self.0)
2012 }
2013}
2014
2015impl<T: FloatLike> std::fmt::LowerExp for F<T> {
2016 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2017 let rendered = match f.precision() {
2018 Some(precision) => format!("{:.*e}", precision, self.0),
2019 None => format!("{:e}", self.0),
2020 };
2021
2022 let (is_nonnegative, body) = match rendered.strip_prefix('-') {
2023 Some(body) => (false, body),
2024 None => (true, rendered.as_str()),
2025 };
2026
2027 f.pad_integral(is_nonnegative, "", body)
2028 }
2029}
2030
2031impl<T: FloatLike> SymFloatLike for F<T> {
2032 #[inline]
2033 fn set_from(&mut self, other: &Self) {
2034 self.0.set_from(&other.0);
2035 }
2036
2037 fn mul_add(&self, a: &Self, b: &Self) -> Self {
2038 F(self.0.mul_add(&a.0, &b.0))
2039 }
2040
2041 fn is_fully_zero(&self) -> bool {
2042 self.0.is_fully_zero()
2043 }
2044
2045 fn new_zero() -> Self {
2046 F(T::new_zero())
2047 }
2048 fn sample_unit<R: Rng + ?Sized>(&self, rng: &mut R) -> Self {
2049 F(self.0.sample_unit(rng))
2050 }
2051 fn neg(&self) -> Self {
2052 F(self.0.ref_neg())
2053 }
2054
2055 fn zero(&self) -> Self {
2056 F(self.0.zero())
2057 }
2058 fn one(&self) -> Self {
2059 F(self.0.one())
2060 }
2061 fn from_usize(&self, x: usize) -> Self {
2063 F(self.0.from_usize(x))
2064 }
2065 fn from_i64(&self, x: i64) -> Self {
2066 F(self.0.from_i64(x))
2067 }
2068 fn pow(&self, n: u64) -> Self {
2069 F(self.0.pow(n))
2070 }
2071 fn inv(&self) -> Self {
2072 F(self.0.inv())
2073 }
2074 fn get_precision(&self) -> u32 {
2075 self.0.get_precision()
2076 }
2077 fn get_epsilon(&self) -> f64 {
2078 self.0.get_epsilon()
2079 }
2080 fn fixed_precision(&self) -> bool {
2081 self.0.fixed_precision()
2082 }
2083}
2084
2085impl<T: FloatLike + Constructible> Constructible for F<T> {
2086 fn new_from_i64(a: i64) -> Self {
2087 F(T::new_from_i64(a))
2088 }
2089
2090 fn new_from_usize(a: usize) -> Self {
2091 F(T::new_from_usize(a))
2092 }
2093
2094 fn new_one() -> Self {
2095 F(T::new_one())
2096 }
2097
2098 fn new_sample_unit<R: Rng + ?Sized>(rng: &mut R) -> Self {
2099 F(T::new_sample_unit(rng))
2100 }
2101}
2102
2103impl<T: FloatLike> Real for F<T> {
2104 fn atan2(&self, x: &Self) -> Self {
2105 F(self.0.atan2(&x.0))
2106 }
2107
2108 fn conj(&self) -> Self {
2109 F(self.0.conj())
2110 }
2111
2112 fn i(&self) -> Option<Self> {
2113 None
2114 }
2115
2116 fn powf(&self, e: &Self) -> Self {
2117 F(self.0.powf(&e.0))
2118 }
2119
2120 fn norm(&self) -> Self {
2121 F(self.0.norm())
2122 }
2123
2124 fn e(&self) -> Self {
2125 F(self.0.e())
2126 }
2127 fn phi(&self) -> Self {
2128 F(self.0.phi())
2129 }
2130 fn euler(&self) -> Self {
2131 F(self.0.euler())
2132 }
2133 fn pi(&self) -> Self {
2134 F(self.0.pi())
2135 }
2136 fn sqrt(&self) -> Self {
2137 F(self.0.sqrt())
2138 }
2139 fn log(&self) -> Self {
2140 F(self.0.log())
2141 }
2142 fn exp(&self) -> Self {
2143 F(self.0.exp())
2144 }
2145 fn sin(&self) -> Self {
2146 F(self.0.sin())
2147 }
2148 fn cos(&self) -> Self {
2149 F(self.0.cos())
2150 }
2151 fn tan(&self) -> Self {
2152 F(self.0.tan())
2153 }
2154 fn asin(&self) -> Self {
2155 F(self.0.asin())
2156 }
2157 fn acos(&self) -> Self {
2158 F(self.0.acos())
2159 }
2160 fn sinh(&self) -> Self {
2161 F(self.0.sinh())
2162 }
2163 fn cosh(&self) -> Self {
2164 F(self.0.cosh())
2165 }
2166 fn tanh(&self) -> Self {
2167 F(self.0.tanh())
2168 }
2169 fn asinh(&self) -> Self {
2170 F(self.0.asinh())
2171 }
2172 fn acosh(&self) -> Self {
2173 F(self.0.acosh())
2174 }
2175 fn atanh(&self) -> Self {
2176 F(self.0.atanh())
2177 }
2178}
2179
2180use delegate::delegate;
2181
2182impl<T: FloatLike> F<T> {
2183 pub(crate) fn max(self, other: F<T>) -> F<T> {
2184 if self < other { other } else { self }
2185 }
2186
2187 pub fn negate(&mut self) {
2188 self.0 = -self.0.clone();
2189 }
2190
2191 pub(crate) fn from_ff64(x: F<f64>) -> Self {
2192 F(T::from_f64(x.0))
2193 }
2194
2195 pub(crate) fn zero(&self) -> Self {
2196 F(self.0.zero())
2197 }
2198
2199 pub(crate) fn one(&self) -> Self {
2200 F(self.0.one())
2201 }
2202
2203 #[allow(clippy::wrong_self_convention)]
2204 pub(crate) fn from_usize(&self, x: usize) -> Self {
2205 F(self.0.from_usize(x))
2206 }
2207
2208 #[allow(clippy::wrong_self_convention)]
2209 pub(crate) fn from_i64(&self, x: i64) -> Self {
2210 F(self.0.from_i64(x))
2211 }
2212
2213 pub(crate) fn higher(&self) -> F<T::Higher>
2214 where
2215 T::Higher: FloatLike,
2216 {
2217 F(self.0.higher())
2218 }
2219
2220 pub(crate) fn lower(&self) -> F<T::Lower>
2221 where
2222 T::Lower: FloatLike,
2223 {
2224 F(self.0.lower())
2225 }
2226
2227 pub fn from_f64(x: f64) -> Self {
2228 F(T::from_f64(x))
2229 }
2230
2231 #[allow(clippy::wrong_self_convention)]
2232 pub(crate) fn into_ff64(&self) -> F<f64> {
2233 F(self.0.into_f64())
2234 }
2235
2236 pub(crate) fn abs(&self) -> Self {
2237 F(self.0.norm())
2238 }
2239
2240 pub(crate) fn sqrt(&self) -> Self {
2241 F(self.0.sqrt())
2242 }
2243
2244 pub(crate) fn powf(&self, e: &Self) -> Self {
2245 F(self.0.powf(&e.0))
2246 }
2247
2248 pub(crate) fn log10(&self) -> Self {
2249 let ten = self.from_i64(10);
2250 self.ln() / ten.ln()
2251 }
2252
2253 pub(crate) fn complex_sqrt(&self) -> Complex<Self> {
2254 if self.positive() {
2255 Complex::new(self.sqrt(), self.zero())
2256 } else {
2257 Complex::new(self.zero(), (-self).sqrt())
2258 }
2259 }
2260
2261 pub(crate) fn rem_euclid(&self, rhs: &Self) -> Self {
2262 F(self.0.rem_euclid(&rhs.0))
2263 }
2264
2265 #[allow(non_snake_case)]
2266 pub(crate) fn PI(&self) -> Self {
2267 F(self.0.PI())
2268 }
2269 #[allow(non_snake_case)]
2270 pub fn E(&self) -> Self {
2271 F(self.0.E())
2272 }
2273 #[allow(non_snake_case)]
2274 pub fn TAU(&self) -> Self {
2275 F(self.0.TAU())
2276 }
2277 #[allow(non_snake_case)]
2278 pub fn PIHALF(&self) -> Self {
2279 F(self.0.PIHALF())
2280 }
2281 #[allow(non_snake_case)]
2282 pub fn SQRT_2(&self) -> Self {
2283 F(self.0.SQRT_2())
2284 }
2285 #[allow(non_snake_case)]
2286 pub fn SQRT_2_HALF(&self) -> Self {
2287 F(self.0.SQRT_2_HALF())
2288 }
2289 #[allow(non_snake_case)]
2290 pub(crate) fn FRAC_1_PI(&self) -> Self {
2291 F(self.0.FRAC_1_PI())
2292 }
2293 #[allow(clippy::wrong_self_convention)]
2294 pub(crate) fn into_f64(&self) -> f64 {
2295 self.0.into_f64()
2296 }
2297 pub(crate) fn square(&self) -> Self {
2298 F(self.0.square())
2299 }
2300 pub(crate) fn powi(&self, n: i32) -> Self {
2301 F(self.0.powi(n))
2302 }
2303 pub(crate) fn epsilon(&self) -> Self {
2304 F(self.0.epsilon())
2305 }
2306 pub(crate) fn less_than_epsilon(&self) -> bool {
2307 self.0.less_than_epsilon()
2308 }
2309 pub(crate) fn positive(&self) -> bool {
2310 self.0.positive()
2311 }
2312 pub(crate) fn max_value(&self) -> Self {
2313 F(self.0.max_value())
2314 }
2315 pub(crate) fn min_value(&self) -> Self {
2316 F(self.0.min_value())
2317 }
2318 pub(crate) fn ln(&self) -> Self {
2319 F(self.0.ln())
2320 }
2321 pub(crate) fn inv(&self) -> Self {
2322 F(self.0.inv())
2323 }
2324 pub(crate) fn is_nan(&self) -> bool {
2325 self.0.is_nan()
2326 }
2327 pub(crate) fn is_infinite(&self) -> bool {
2328 self.0.is_infinite()
2329 }
2330 pub fn floor(&self) -> Self {
2331 F(self.0.floor())
2332 }
2333}
2334
2335impl<T: FloatLike> Add<F<T>> for F<T> {
2355 type Output = F<T>;
2356 fn add(self, rhs: F<T>) -> Self::Output {
2357 F(self.0 + rhs.0)
2358 }
2359}
2360
2361impl<T: FloatLike> Add<&F<T>> for F<T> {
2362 type Output = F<T>;
2363 fn add(self, rhs: &F<T>) -> Self::Output {
2364 F(self.0 + &rhs.0)
2365 }
2366}
2367
2368impl<T: FloatLike> Add<&F<T>> for &F<T> {
2369 type Output = F<T>;
2370 fn add(self, rhs: &F<T>) -> Self::Output {
2371 F(self.0.ref_add(&rhs.0))
2372 }
2373}
2374
2375impl<T: FloatLike> Add<F<T>> for &F<T> {
2376 type Output = F<T>;
2377 fn add(self, rhs: F<T>) -> Self::Output {
2378 F(self.0.ref_add(rhs.0))
2379 }
2380}
2381
2382impl<T: FloatLike> AddAssign<&F<T>> for F<T> {
2383 fn add_assign(&mut self, rhs: &F<T>) {
2384 self.0 += &rhs.0;
2385 }
2386}
2387
2388impl<T: FloatLike> AddAssign<F<T>> for F<T> {
2389 fn add_assign(&mut self, rhs: F<T>) {
2390 self.0 += rhs.0;
2391 }
2392}
2393
2394impl<T: FloatLike> Sub<F<T>> for F<T> {
2395 type Output = F<T>;
2396 fn sub(self, rhs: F<T>) -> Self::Output {
2397 F(self.0 - rhs.0)
2398 }
2399}
2400
2401impl<T: FloatLike> Sub<&F<T>> for F<T> {
2402 type Output = F<T>;
2403 fn sub(self, rhs: &F<T>) -> Self::Output {
2404 F(self.0 - &rhs.0)
2405 }
2406}
2407
2408impl<T: FloatLike> Sub<&F<T>> for &F<T> {
2409 type Output = F<T>;
2410 fn sub(self, rhs: &F<T>) -> Self::Output {
2411 F(self.0.ref_sub(&rhs.0))
2412 }
2413}
2414
2415impl<T: FloatLike> Sub<F<T>> for &F<T> {
2416 type Output = F<T>;
2417 fn sub(self, rhs: F<T>) -> Self::Output {
2418 F(self.0.ref_sub(rhs.0))
2419 }
2420}
2421
2422impl<T: FloatLike> SubAssign<&F<T>> for F<T> {
2423 fn sub_assign(&mut self, rhs: &F<T>) {
2424 self.0 -= &rhs.0;
2425 }
2426}
2427
2428impl<T: FloatLike> SubAssign<F<T>> for F<T> {
2429 fn sub_assign(&mut self, rhs: F<T>) {
2430 self.0 -= rhs.0;
2431 }
2432}
2433
2434impl<T: FloatLike> Mul<F<T>> for F<T> {
2435 type Output = F<T>;
2436 fn mul(self, rhs: F<T>) -> Self::Output {
2437 F(self.0 * rhs.0)
2438 }
2439}
2440
2441impl<T: FloatLike> Mul<&F<T>> for F<T> {
2442 type Output = F<T>;
2443 fn mul(self, rhs: &F<T>) -> Self::Output {
2444 F(self.0 * &rhs.0)
2445 }
2446}
2447
2448impl<T: FloatLike> Mul<&F<T>> for &F<T> {
2449 type Output = F<T>;
2450 fn mul(self, rhs: &F<T>) -> Self::Output {
2451 F(self.0.ref_mul(&rhs.0))
2452 }
2453}
2454
2455impl<T: FloatLike> Mul<F<T>> for &F<T> {
2456 type Output = F<T>;
2457 fn mul(self, rhs: F<T>) -> Self::Output {
2458 F(self.0.ref_mul(rhs.0))
2459 }
2460}
2461
2462impl<T: FloatLike> MulAssign<&F<T>> for F<T> {
2463 fn mul_assign(&mut self, rhs: &F<T>) {
2464 self.0 *= &rhs.0;
2465 }
2466}
2467
2468impl<T: FloatLike> MulAssign<F<T>> for F<T> {
2469 fn mul_assign(&mut self, rhs: F<T>) {
2470 self.0 *= rhs.0;
2471 }
2472}
2473
2474impl<T: FloatLike> Div<F<T>> for F<T> {
2475 type Output = F<T>;
2476 fn div(self, rhs: F<T>) -> Self::Output {
2477 F(self.0 / rhs.0)
2478 }
2479}
2480
2481impl<T: FloatLike> Div<&F<T>> for F<T> {
2482 type Output = F<T>;
2483 fn div(self, rhs: &F<T>) -> Self::Output {
2484 F(self.0 / &rhs.0)
2485 }
2486}
2487
2488impl<T: FloatLike> Div<&F<T>> for &F<T> {
2489 type Output = F<T>;
2490 fn div(self, rhs: &F<T>) -> Self::Output {
2491 F(self.0.ref_div(&rhs.0))
2492 }
2493}
2494
2495impl<T: FloatLike> Div<F<T>> for &F<T> {
2496 type Output = F<T>;
2497 fn div(self, rhs: F<T>) -> Self::Output {
2498 F(self.0.ref_div(rhs.0))
2499 }
2500}
2501
2502impl<T: FloatLike> DivAssign<&F<T>> for F<T> {
2503 fn div_assign(&mut self, rhs: &F<T>) {
2504 self.0 /= &rhs.0;
2505 }
2506}
2507
2508impl<T: FloatLike> DivAssign<F<T>> for F<T> {
2509 fn div_assign(&mut self, rhs: F<T>) {
2510 self.0 /= rhs.0;
2511 }
2512}
2513
2514impl<T: FloatLike> Neg for F<T> {
2515 type Output = F<T>;
2516 fn neg(self) -> Self::Output {
2517 F(-self.0)
2518 }
2519}
2520
2521impl<T: FloatLike> Neg for &F<T> {
2522 type Output = F<T>;
2523 fn neg(self) -> Self::Output {
2524 F(self.0.ref_neg())
2525 }
2526}
2527
2528pub trait RefDefault {
2529 fn default(&self) -> Self;
2530}
2531
2532impl<T: FloatLike> RefDefault for T {
2533 fn default(&self) -> Self {
2534 self.zero()
2535 }
2536}
2537
2538impl<T: FloatLike> RefDefault for F<T> {
2539 fn default(&self) -> Self {
2540 F(self.0.default())
2541 }
2542}
2543impl PrecisionUpgradable for f64 {
2544 type Higher = f128;
2545 type Lower = f64;
2546
2547 fn higher(&self) -> Self::Higher {
2548 f128::from_f64(*self)
2549 }
2550
2551 fn lower(&self) -> Self::Lower {
2552 *self
2553 }
2554}
2555
2556impl FloatLike for f64 {
2557 fn PI(&self) -> Self {
2558 std::f64::consts::PI
2559 }
2560
2561 fn SQRT_2(&self) -> Self {
2562 std::f64::consts::SQRT_2
2563 }
2564
2565 fn SQRT_2_HALF(&self) -> Self {
2566 std::f64::consts::SQRT_2 / 2.0
2567 }
2568
2569 fn PIHALF(&self) -> Self {
2570 std::f64::consts::PI / 2.0
2571 }
2572
2573 fn E(&self) -> Self {
2574 std::f64::consts::E
2575 }
2576
2577 fn TAU(&self) -> Self {
2578 std::f64::consts::TAU
2579 }
2580
2581 fn FRAC_1_PI(&self) -> Self {
2582 std::f64::consts::FRAC_1_PI
2583 }
2584
2585 fn from_f64(x: f64) -> Self {
2586 x
2587 }
2588
2589 fn into_f64(&self) -> f64 {
2590 *self
2591 }
2592
2593 fn is_nan(&self) -> bool {
2594 f64::is_nan(*self)
2595 }
2596
2597 fn is_infinite(&self) -> bool {
2598 f64::is_infinite(*self)
2599 }
2600
2601 fn floor(&self) -> Self {
2602 f64::floor(*self)
2603 }
2604
2605 fn rem_euclid(&self, rhs: &Self) -> Self {
2606 f64::rem_euclid(*self, *rhs)
2607 }
2608
2609 fn try_extract_externals_from_cache(
2610 externals: &Externals,
2611 ) -> Option<&TiVec<ExternalIndex, FourMomentum<F<Self>>>> {
2612 match externals {
2613 Externals::Constant { f_64_cache, .. } => f_64_cache.as_ref(),
2614 }
2615 }
2616
2617 fn epsilon(&self) -> Self {
2618 f64::EPSILON
2619 }
2620}
2621impl From<F<f64>> for f64 {
2622 fn from(value: F<f64>) -> Self {
2623 value.0
2624 }
2625}
2626
2627impl From<F<f64>> for Rational {
2628 fn from(value: F<f64>) -> Self {
2629 value.0.try_into().unwrap()
2630 }
2631}
2632
2633#[allow(non_camel_case_types)]
2634pub type f128 = QuadFloat;
2640pub type ArbPrec = VarFloat<1000>;
2641
2642#[derive(Clone, Debug)]
2644#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
2645pub struct ZipEq<I, J> {
2646 a: I,
2647 b: J,
2648}
2649
2650#[allow(unused)]
2653pub(crate) fn zip_eq<I, J>(i: I, j: J) -> ZipEq<I::IntoIter, J::IntoIter>
2654where
2655 I: IntoIterator,
2656 J: IntoIterator,
2657{
2658 ZipEq {
2659 a: i.into_iter(),
2660 b: j.into_iter(),
2661 }
2662}
2663
2664impl<I, J> Iterator for ZipEq<I, J>
2665where
2666 I: Iterator,
2667 J: Iterator,
2668{
2669 type Item = (I::Item, J::Item);
2670
2671 fn next(&mut self) -> Option<Self::Item> {
2672 match (self.a.next(), self.b.next()) {
2673 (None, None) => None,
2674 (Some(a), Some(b)) => Some((a, b)),
2675 (None, Some(_)) => {
2676 #[cfg(debug_assertions)]
2677 panic!("Unequal length of iterators; first iterator finished first");
2678 #[cfg(not(debug_assertions))]
2679 None
2680 }
2681 (Some(_), None) => {
2682 #[cfg(debug_assertions)]
2683 panic!("Unequal length of iterators; second iterator finished first");
2684 #[cfg(not(debug_assertions))]
2685 None
2686 }
2687 }
2688 }
2689
2690 fn size_hint(&self) -> (usize, Option<usize>) {
2691 let sa = self.a.size_hint();
2692 let sb = self.b.size_hint();
2693 (sa.0.min(sb.0), sa.1.zip(sb.1).map(|(ua, ub)| ua.min(ub)))
2694 }
2695}
2696
2697impl<I, J> ExactSizeIterator for ZipEq<I, J>
2698where
2699 I: ExactSizeIterator,
2700 J: ExactSizeIterator,
2701{
2702}
2703
2704pub(crate) fn parse_python_expression(expression: &str) -> Atom {
2705 initialize();
2706 let _ = UFO.metric;
2707 let processed_string = String::from(expression)
2708 .replace("**", "^")
2709 .replace("cmath.sqrt", "sqrt")
2710 .replace("cmath.pi", "pi")
2711 .replace("math.sqrt", "sqrt")
2712 .replace("math.pi", "pi");
2713
2714 parse!(processed_string)
2715}
2716
2717pub fn format_uncertainty(mean: F<f64>, sdev: F<f64>) -> String {
2720 let mean = mean.0;
2721 let sdev = sdev.0;
2722
2723 fn ndec(x: f64, offset: usize) -> i32 {
2724 let mut ans = (offset as f64 - x.log10()) as i32;
2725 if ans > 0 && x * 10.0.powi(ans) >= [0.5, 9.5, 99.5][offset] {
2726 ans -= 1;
2727 }
2728 if ans < 0 { 0 } else { ans }
2729 }
2730 let v = mean;
2731 let dv = sdev.abs();
2732
2733 if v.is_nan() || dv.is_nan() {
2735 format!("{:e} ± {:e}", v, dv)
2736 } else if dv.is_infinite() {
2737 format!("{:e} ± inf", v)
2738 } else if v.is_zero() && !(1e-4..1e5).contains(&dv) {
2739 if dv.is_zero() {
2740 "0(0)".to_owned()
2741 } else {
2742 let e = format!("{:.1e}", dv);
2743 let mut ans = e.split('e');
2744 let e1 = ans.next().unwrap();
2745 let e2 = ans.next().unwrap();
2746 "0.0(".to_owned() + e1 + ")e" + e2
2747 }
2748 } else if v.is_zero() {
2749 if dv >= 9.95 {
2750 format!("0({:.0})", dv)
2751 } else if dv >= 0.995 {
2752 format!("0.0({:.1})", dv)
2753 } else {
2754 let ndecimal = ndec(dv, 2);
2755 format!(
2756 "{:.*}({:.0})",
2757 ndecimal as usize,
2758 v,
2759 dv * (10.).powi(ndecimal)
2760 )
2761 }
2762 } else if dv.is_zero() {
2763 let e = format!("{:e}", v);
2764 let mut ans = e.split('e');
2765 let e1 = ans.next().unwrap();
2766 let e2 = ans.next().unwrap();
2767 if e2 != "0" {
2768 e1.to_owned() + "(0)e" + e2
2769 } else {
2770 e1.to_owned() + "(0)"
2771 }
2772 } else if dv > 1e4 * v.abs() {
2773 format!("{:.1e} ± {:.2e}", v, dv)
2774 } else if v.abs() >= 1e6 || v.abs() < 1e-5 {
2775 let exponent = v.abs().log10().floor();
2777 let fac = (10.0).powf(&exponent);
2778 let mantissa = format_uncertainty(F(v / fac), F(dv / fac));
2779 let e = format!("{:.0e}", fac);
2780 let mut ee = e.split('e');
2781 mantissa + "e" + ee.nth(1).unwrap()
2782 }
2783 else if dv >= 9.95 {
2785 if v.abs() >= 9.5 {
2786 format!("{:.0}({:.0})", v, dv)
2787 } else {
2788 let ndecimal = ndec(v.abs(), 1);
2789 format!("{:.*}({:.*})", ndecimal as usize, v, ndecimal as usize, dv)
2790 }
2791 } else if dv >= 0.995 {
2792 if v.abs() >= 0.95 {
2793 format!("{:.1}({:.1})", v, dv)
2794 } else {
2795 let ndecimal = ndec(v.abs(), 1);
2796 format!("{:.*}({:.*})", ndecimal as usize, v, ndecimal as usize, dv)
2797 }
2798 } else {
2799 let ndecimal = ndec(v.abs(), 1).max(ndec(dv, 2));
2800 format!(
2801 "{:.*}({:.0})",
2802 ndecimal as usize,
2803 v,
2804 dv * (10.).powi(ndecimal)
2805 )
2806 }
2807}
2808
2809#[allow(unused)]
2811pub(crate) fn compare_slice<T: Ord>(slice1: &[T], slice2: &[T]) -> Ordering {
2812 match slice1.len().cmp(&slice2.len()) {
2813 Ordering::Equal => (),
2814 non_eq => return non_eq,
2815 }
2816
2817 let l = slice1.len();
2818 let lhs = &slice1[..l];
2821 let rhs = &slice2[..l];
2822
2823 for i in 0..l {
2824 match lhs[i].cmp(&rhs[i]) {
2825 Ordering::Equal => (),
2826 non_eq => return non_eq,
2827 }
2828 }
2829
2830 Ordering::Equal
2831}
2832
2833pub trait Signum {
2834 fn multiply_sign(&self, sign: i8) -> Self;
2835}
2836
2837impl Signum for f64 {
2850 #[inline]
2851 fn multiply_sign(&self, sign: i8) -> f64 {
2852 match sign {
2853 1 => *self,
2854 0 => self.zero(),
2855 -1 => -self,
2856 _ => unreachable!("Sign should be -1,0,1"),
2857 }
2858 }
2859}
2860
2861impl Signum for f32 {
2862 #[inline]
2863 fn multiply_sign(&self, sign: i8) -> Self {
2864 match sign {
2865 1 => *self,
2866 0 => 0.0,
2867 -1 => self.neg(),
2868 _ => unreachable!("Sign should be -1,0,1"),
2869 }
2870 }
2871}
2872
2873impl<T: FloatLike> Signum for Complex<F<T>> {
2874 #[inline]
2875 fn multiply_sign(&self, sign: i8) -> Complex<F<T>> {
2876 match sign {
2877 1 => self.clone(),
2878 0 => self.ref_zero(),
2879 -1 => -self.clone(),
2880 _ => unreachable!("Sign should be -1,0,1"),
2881 }
2882 }
2883}
2884
2885impl<T: FloatLike> Signum for FourMomentum<F<T>> {
2886 #[inline]
2887 fn multiply_sign(&self, sign: i8) -> FourMomentum<F<T>> {
2888 match sign {
2889 1 => self.clone(),
2890 0 => self.ref_zero(),
2891 -1 => -self,
2892 _ => unreachable!("Sign should be -1,0,1"),
2893 }
2894 }
2895}
2896
2897#[allow(unused)]
2898#[inline]
2899pub(crate) fn finv<T: FloatLike>(c: Complex<F<T>>) -> Complex<F<T>> {
2901 let norm = c.norm_squared();
2902 c.conj() / norm
2903}
2904
2905#[allow(unused)]
2906#[inline]
2907pub(crate) fn powi<T: FloatLike>(c: Complex<F<T>>, n: i32) -> Complex<F<T>> {
2908 if n.is_negative() {
2909 let u = -n as u64;
2910 finv(c.pow(u))
2911 } else {
2912 let u = n as u64;
2913 c.pow(u)
2914 }
2915}
2916
2917#[allow(unused)]
2918pub(crate) fn evaluate_signature<T>(
2919 signature: &[i8],
2920 momenta: &[FourMomentum<F<T>>],
2921) -> FourMomentum<F<T>>
2922where
2923 T: FloatLike,
2924{
2925 let mut momentum = momenta[0].zero();
2926 for (&sign, mom) in zip_eq(signature, momenta) {
2927 match sign {
2928 0 => {}
2929 1 => momentum += mom,
2930 -1 => momentum -= mom,
2931 _ => {
2932 #[cfg(debug_assertions)]
2933 panic!("Sign should be -1,0,1")
2934 }
2935 }
2936 }
2937
2938 momentum
2939}
2940
2941#[allow(unused)]
2942#[inline]
2943pub(crate) fn pinch_dampening_function<T: FloatLike>(
2944 dampening_arg: F<T>,
2945 delta_t: F<T>,
2946 powers: (u64, u64),
2947 multiplier: f64,
2948) -> F<T> {
2949 assert!(powers.1.is_multiple_of(2));
2951 let a = dampening_arg.pow(powers.0);
2952 &a / (&a + F::<T>::from_f64(multiplier) * delta_t.pow(powers.1))
2953}
2954
2955pub(crate) fn h_dual<T: FloatLike>(
2956 t: &HyperDual<F<T>>,
2957 tstar: Option<HyperDual<F<T>>>,
2958 sigma: Option<F<T>>,
2959 h_function_settings: &crate::settings::runtime::HFunctionSettings,
2960) -> HyperDual<F<T>> {
2961 let sqrt_pi = new_constant(t, &t.values[0].PI().sqrt());
2962 let sig = if let Some(s) = sigma {
2963 new_constant(t, &s)
2964 } else {
2965 new_constant(t, &F::<T>::from_f64(h_function_settings.sigma))
2966 };
2967 let power = h_function_settings.power;
2968 match h_function_settings.function {
2969 crate::settings::runtime::HFunction::Exponential => {
2970 (-(t.clone() * t) / (sig.clone() * sig.clone())).exp()
2971 * new_constant(t, &F::<T>::from_f64(2_f64))
2972 / (sqrt_pi * sig)
2973 }
2974 crate::settings::runtime::HFunction::PolyExponential => {
2975 let normalisation = match power {
2977 None | Some(0) => {
2978 sqrt_pi.clone() * &sig / new_constant(t, &F::<T>::from_f64(2_f64))
2979 }
2980 Some(1) => new_constant(t, &F::<T>::from_f64(0.841_568_215_070_771_4)) * &sig,
2981 Some(3) => new_constant(t, &F::<T>::from_f64(1.033_476_847_068_688_6)) * &sig,
2982 Some(4) => new_constant(t, &F::<T>::from_f64(1.329_340_388_179_137)) * &sig,
2983 Some(6) => new_constant(t, &F::<T>::from_f64(2.880_237_507_721_463_7)) * &sig,
2984 Some(7) => new_constant(t, &F::<T>::from_f64(4.783_566_971_347_609)) * &sig,
2985 Some(9) => new_constant(t, &F::<T>::from_f64(16.225_745_976_182_285)) * &sig,
2986 Some(10) => new_constant(t, &F::<T>::from_f64(32.735_007_058_911_25)) * &sig,
2987 Some(12) => new_constant(t, &F::<T>::from_f64(155.837_465_922_583_42)) * &sig,
2988 Some(13) => new_constant(t, &F::<T>::from_f64(364.658_500_356_566_04)) * &sig,
2989 Some(15) => new_constant(t, &F::<T>::from_f64(2_257.637_553_015_473)) * &sig,
2990 Some(16) => new_constant(t, &F::<T>::from_f64(5_939.804_418_537_864)) * &sig,
2991 _ => panic!(
2992 "Value {} of power in poly exponential h function not supported",
2993 power.unwrap()
2994 ),
2995 };
2996 let prefactor = match power {
2997 None | Some(0) => normalisation.inv(),
2998 Some(p) => (t.clone() / &sig).inv().pow(p as u64) / normalisation,
2999 };
3000 prefactor
3001 * (new_constant(t, &F::<T>::from_f64(2_f64))
3002 - (t.clone() * t) / (sig.clone() * &sig)
3003 - (sig.clone() * sig) / (t.clone() * t))
3004 .exp()
3005 }
3006 crate::settings::runtime::HFunction::PolyLeftRightExponential => {
3007 let normalisation = match power {
3009 None | Some(0) => new_constant(t, &F::<T>::from_f64(2.066_953_694_137_377)) * &sig,
3010 Some(1) => new_constant(t, &F::<T>::from_f64(1.683_136_430_141_542_8)) * &sig,
3011 Some(3) => new_constant(t, &F::<T>::from_f64(3.750_090_124_278_92)) * &sig,
3012 Some(4) => new_constant(t, &F::<T>::from_f64(9.567_133_942_695_218)) * &sig,
3013 Some(6) => new_constant(t, &F::<T>::from_f64(139.373_101_752_153_5)) * &sig,
3014 Some(7) => new_constant(t, &F::<T>::from_f64(729.317_000_713_132_1)) * &sig,
3015 Some(9) => new_constant(t, &F::<T>::from_f64(32_336.242_742_929_753)) * &sig,
3016 Some(10) => new_constant(t, &F::<T>::from_f64(263_205.217_049_469)) * &sig,
3017 Some(12) => new_constant(t, &F::<T>::from_f64(2.427_503_717_893_097_5e7)) * &sig,
3018 Some(13) => new_constant(t, &F::<T>::from_f64(2.694_265_921_644_289e8)) * &sig,
3019 Some(15) => new_constant(t, &F::<T>::from_f64(9.040_742_057_760_125e12)) * &sig,
3020 Some(16) => new_constant(t, &F::<T>::from_f64(1.452_517_480_246_491_3e14)) * &sig,
3021 _ => panic!(
3022 "Value {} of power in poly exponential h function not supported",
3023 power.unwrap()
3024 ),
3025 };
3026
3027 let prefactor = match power {
3033 None | Some(0) => normalisation.inv(),
3034 Some(p) => (t.clone() / &sig).inv().pow(p as u64) / normalisation,
3035 };
3036
3037 prefactor
3039 * (new_constant(t, &F::<T>::from_f64(2_f64))
3040 - ((t.clone() * t) / (sig.clone() * &sig) + t.one()) / (t.clone() / &sig))
3041 .exp()
3042 }
3043 crate::settings::runtime::HFunction::ExponentialCT => {
3044 let delta_t_sq = (tstar.clone().unwrap() - t) * (tstar.clone().unwrap() - t);
3045 let tstar_sq = tstar.clone().unwrap() * tstar.unwrap();
3046 if h_function_settings.enabled_dampening {
3058 let dampener = delta_t_sq.clone() / (delta_t_sq.clone() - &tstar_sq);
3059 (-sig.inv()
3060 * (delta_t_sq.clone() / tstar_sq
3061 + sig.clone() * sig * (dampener.clone() * dampener)))
3062 .exp()
3063 } else {
3064 (-sig.inv() * (delta_t_sq / tstar_sq)).exp()
3065 }
3066 }
3067 }
3068}
3069
3070pub(crate) fn h<T: FloatLike>(
3071 t: &F<T>,
3072 tstar: Option<F<T>>,
3073 sigma: Option<F<T>>,
3074 h_function_settings: &crate::settings::runtime::HFunctionSettings,
3075) -> F<T> {
3076 let sqrt_pi = t.PI().sqrt();
3077 let sig = if let Some(s) = sigma {
3078 s
3079 } else {
3080 F::<T>::from_f64(h_function_settings.sigma)
3081 };
3082 let power = h_function_settings.power;
3083 match h_function_settings.function {
3084 crate::settings::runtime::HFunction::Exponential => {
3085 (-(t.square()) / (sig.square())).exp() * F::<T>::from_f64(2_f64) / (sqrt_pi * &sig)
3086 }
3087 crate::settings::runtime::HFunction::PolyExponential => {
3088 let normalisation = match power {
3090 None | Some(0) => sqrt_pi * &sig / F::<T>::from_f64(2_f64),
3091 Some(1) => F::<T>::from_f64(0.841_568_215_070_771_4) * &sig,
3092 Some(3) => F::<T>::from_f64(1.033_476_847_068_688_6) * &sig,
3093 Some(4) => F::<T>::from_f64(1.329_340_388_179_137) * &sig,
3094 Some(6) => F::<T>::from_f64(2.880_237_507_721_463_7) * &sig,
3095 Some(7) => F::<T>::from_f64(4.783_566_971_347_609) * &sig,
3096 Some(9) => F::<T>::from_f64(16.225_745_976_182_285) * &sig,
3097 Some(10) => F::<T>::from_f64(32.735_007_058_911_25) * &sig,
3098 Some(12) => F::<T>::from_f64(155.837_465_922_583_42) * &sig,
3099 Some(13) => F::<T>::from_f64(364.658_500_356_566_04) * &sig,
3100 Some(15) => F::<T>::from_f64(2_257.637_553_015_473) * &sig,
3101 Some(16) => F::<T>::from_f64(5_939.804_418_537_864) * &sig,
3102 _ => panic!(
3103 "Value {} of power in poly exponential h function not supported",
3104 power.unwrap()
3105 ),
3106 };
3107 let prefactor = match power {
3108 None | Some(0) => normalisation.inv(),
3109 Some(p) => (t / &sig).powi(-(p as i32)) / normalisation,
3110 };
3111 prefactor
3112 * (F::<T>::from_f64(2_f64)
3113 - (t.square()) / (sig.square())
3114 - (sig.square()) / (t.square()))
3115 .exp()
3116 }
3117 crate::settings::runtime::HFunction::PolyLeftRightExponential => {
3118 let normalisation = match power {
3120 None | Some(0) => F::<T>::from_f64(2.066_953_694_137_377) * &sig,
3121 Some(1) => F::<T>::from_f64(1.683_136_430_141_542_8) * &sig,
3122 Some(3) => F::<T>::from_f64(3.750_090_124_278_92) * &sig,
3123 Some(4) => F::<T>::from_f64(9.567_133_942_695_218) * &sig,
3124 Some(6) => F::<T>::from_f64(139.373_101_752_153_5) * &sig,
3125 Some(7) => F::<T>::from_f64(729.317_000_713_132_1) * &sig,
3126 Some(9) => F::<T>::from_f64(32_336.242_742_929_753) * &sig,
3127 Some(10) => F::<T>::from_f64(263_205.217_049_469) * &sig,
3128 Some(12) => F::<T>::from_f64(2.427_503_717_893_097_5e7) * &sig,
3129 Some(13) => F::<T>::from_f64(2.694_265_921_644_289e8) * &sig,
3130 Some(15) => F::<T>::from_f64(9.040_742_057_760_125e12) * &sig,
3131 Some(16) => F::<T>::from_f64(1.452_517_480_246_491_3e14) * &sig,
3132 _ => panic!(
3133 "Value {} of power in poly exponential h function not supported",
3134 power.unwrap()
3135 ),
3136 };
3137
3138 let prefactor = match power {
3144 None | Some(0) => normalisation.inv(),
3145 Some(p) => (t / &sig).powi(-(p as i32)) / normalisation,
3146 };
3147
3148 prefactor
3150 * (F::<T>::from_f64(2_f64) - ((t.square()) / (sig.square()) + t.one()) / (t / sig))
3151 .exp()
3152 }
3153 crate::settings::runtime::HFunction::ExponentialCT => {
3154 let delta_t_sq = (tstar.clone().unwrap() - t).square();
3155 let tstar_sq = tstar.unwrap().square();
3156 if h_function_settings.enabled_dampening {
3168 let dampener = delta_t_sq.clone() / (delta_t_sq.clone() - &tstar_sq);
3169 (-sig.inv() * (delta_t_sq.clone() / tstar_sq + sig.square() * (dampener.square())))
3170 .exp()
3171 } else {
3172 (-sig.inv() * (delta_t_sq / tstar_sq)).exp()
3173 }
3174 }
3175 }
3176}
3177
3178#[allow(unused)]
3181pub(crate) fn determinant<T: FloatLike>(bb: &[Complex<F<T>>], dimension: usize) -> Complex<F<T>> {
3182 let one = bb[0].re.one();
3183 let zero = one.zero();
3184 let mut determinant = Complex::new(one.clone(), zero.clone());
3186 let mut indx = [0; MAX_DIMENSION];
3187 let mut d = 1; let mut aa = bb.to_vec();
3191
3192 let mut imax = 0;
3194 let mut flag = 1;
3195 let mut dumc;
3196 let mut sum;
3197
3198 let mut aamax;
3199 let mut dumr;
3200 let mut vv = vec![zero.clone(); MAX_DIMENSION];
3201
3202 for i in 0..dimension {
3204 aamax = zero.clone();
3205 for j in 0..dimension {
3206 let r = aa[i * dimension + j].norm_squared();
3207 if r > aamax {
3208 aamax = r;
3209 }
3210 }
3211 if aamax.is_zero() {
3213 flag = 0;
3214 }
3215 vv[i] = aamax.inv();
3217 }
3218 if flag == 1 {
3219 for j in 0..dimension {
3220 for i in 0..j {
3221 sum = aa[i * dimension + j].clone();
3222 for k in 0..i {
3223 sum -= &aa[i * dimension + k] * &aa[k * dimension + j];
3224 }
3225 aa[i * dimension + j] = sum;
3226 }
3227 aamax = zero.clone();
3229 for i in j..dimension {
3230 sum = aa[i * dimension + j].clone();
3231 for k in 0..j {
3232 sum -= &aa[i * dimension + k] * &aa[k * dimension + j];
3233 }
3234 aa[i * dimension + j] = sum.clone();
3235 dumr = &vv[i] * sum.norm_squared();
3237 if dumr >= aamax {
3239 imax = i;
3240 aamax = dumr;
3241 }
3242 }
3243 if j != imax {
3245 for k in 0..dimension {
3246 dumc = aa[imax * dimension + k].clone();
3247 aa[imax * dimension + k] = aa[j * dimension + k].clone();
3248 aa[j * dimension + k] = dumc.clone();
3249 }
3250 d = -d;
3252 vv[imax] = vv[j].clone();
3254 }
3255 indx[j] = imax;
3256 if j + 1 != dimension {
3257 dumc = aa[j * dimension + j].inv();
3258 for i in j + 1..dimension {
3259 aa[i * dimension + j] *= &dumc;
3260 }
3261 }
3262 }
3263 }
3264 if flag == 0 {
3266 determinant = Complex::new(zero.clone(), zero.clone());
3267 } else {
3268 for diagonal in 0..dimension {
3270 determinant *= &aa[diagonal * dimension + diagonal];
3271 }
3272 determinant *= &zero.from_i64(d);
3273 }
3274 determinant
3275}
3276
3277#[allow(unused)]
3278pub(crate) fn next_combination_with_replacement(state: &mut [usize], max_entry: usize) -> bool {
3279 for i in (0..state.len()).rev() {
3280 if state[i] < max_entry {
3281 state[i] += 1;
3282 for j in i + 1..state.len() {
3283 state[j] = state[i]
3284 }
3285 return true;
3286 }
3287 }
3288 false
3289}
3290
3291pub(crate) fn compute_loop_part<T: FloatLike>(
3292 loop_signature: &LoopSignature,
3293 loop_moms: &LoopMomenta<F<T>>,
3294) -> ThreeMomentum<F<T>> {
3295 loop_signature.apply_typed(loop_moms)
3296}
3297
3298pub(crate) fn compute_loop_part_subspace<T: FloatLike>(
3299 loop_signature: &LoopSignature,
3300 loop_moms: &LoopMomenta<F<T>>,
3301 subspace: &SubspaceData,
3302) -> ThreeMomentum<F<T>> {
3303 let projected: LoopSignature = subspace.project_loop_signature(loop_signature).collect();
3304 projected.apply_typed(loop_moms)
3305}
3306
3307pub(crate) fn compute_shift_part<T: FloatLike>(
3308 external_signature: &ExternalSignature,
3309 external_moms: &ExternalFourMomenta<F<T>>,
3310) -> FourMomentum<F<T>> {
3311 external_signature
3312 .apply_typed::<FourMomentum<F<T>>, ExternalIndex, ExternalFourMomenta<F<T>>>(external_moms)
3313}
3314
3315pub(crate) fn compute_shift_part_subspace<T: FloatLike>(
3316 loop_signature: &LoopSignature,
3317 external_signature: &ExternalSignature,
3318 loop_moms: &LoopMomenta<F<T>>,
3319 external_moms: &ExternalThreeMomenta<F<T>>,
3320 subspace: &SubspaceData,
3321) -> ThreeMomentum<F<T>> {
3322 let projected_loop_complement: LoopSignature = subspace
3323 .project_complement_loop_signature(loop_signature)
3324 .collect();
3325
3326 let loop_part = projected_loop_complement.apply_typed(loop_moms);
3327 let external_part: ThreeMomentum<F<T>> = external_signature
3328 .apply_typed::<ThreeMomentum<F<T>>, ExternalIndex, ExternalThreeMomenta<F<T>>>(
3329 external_moms,
3330 );
3331 loop_part + external_part
3332}
3333
3334pub(crate) fn compute_t_part_of_shift_part<T: FloatLike>(
3335 external_signature: &ExternalSignature,
3336 external_moms: &ExternalFourMomenta<F<T>>,
3337) -> F<T> {
3338 external_signature
3340 .apply_iter(external_moms.iter().map(|m| m.temporal.value.clone()))
3341 .unwrap_or(external_moms[ExternalIndex(0)].temporal.value.zero())
3342}
3343
3344#[allow(unused, clippy::type_complexity)]
3347pub(crate) fn one_loop_e_surface_bilinear_form<T: FloatLike>(
3348 p1: &[F<T>; 3],
3349 p2: &[F<T>; 3],
3350 m1_sq: F<T>,
3351 m2_sq: F<T>,
3352 e_shift: F<T>,
3353) -> ([[F<T>; 3]; 3], [F<T>; 3], F<T>) {
3354 let zero = e_shift.zero();
3355 let two = zero.from_i64(2);
3356 let e_shift_sq = e_shift.square();
3357 let p1_sq = p1[0].square() + p1[1].square() + p1[2].square();
3358 let p2_sq = p2[0].square() + p2[1].square() + p2[2].square();
3359
3360 let zeros = [zero.clone(), zero.clone(), zero.clone()];
3361 let mut a = [zeros.clone(), zeros.clone(), zeros.clone()];
3362 a[0][0] = (&p1[0] - &p2[0] - &e_shift) * (&p2[0] - &p1[0] - &e_shift);
3363 a[0][1] = (&p1[0] - &p2[0]) * (&p2[1] - &p1[1]);
3364 a[1][0] = a[0][1].clone();
3365 a[0][2] = (&p1[0] - &p2[0]) * (&p2[2] - &p1[2]);
3366 a[2][0] = a[0][2].clone();
3367 a[1][1] = (&p1[1] - &p2[1] - &e_shift) * (&p2[1] - &p1[1] - &e_shift);
3368 a[1][2] = (&p1[1] - &p2[1]) * (&p2[2] - &p1[2]);
3369 a[2][1] = a[1][2].clone();
3370 a[2][2] = (&p1[2] - &p2[2] - &e_shift) * (&p2[2] - &p1[2] - &e_shift);
3371
3372 let mut b = zeros.clone();
3373 b[0] =
3374 (&p2[0] - &p1[0]) * (&m1_sq - &m2_sq + &p1_sq - &p2_sq) + &e_shift_sq * (&p1[0] + &p2[0]);
3375 b[1] =
3376 (&p2[1] - &p1[1]) * (&m1_sq - &m2_sq + &p1_sq - &p2_sq) + &e_shift_sq * (&p1[1] + &p2[1]);
3377 b[2] =
3378 (&p2[2] - &p1[2]) * (&m1_sq - &m2_sq + &p1_sq - &p2_sq) + &e_shift_sq * (&p1[2] + &p2[2]);
3379
3380 let c = -&e_shift_sq * &e_shift_sq + &two * &e_shift_sq * (&m1_sq + &m2_sq + &p1_sq + &p2_sq)
3381 - (&m1_sq - &m2_sq + &p1_sq - &p2_sq) * (&m1_sq - &m2_sq + &p1_sq - &p2_sq);
3382
3383 (a, b, c)
3384}
3385
3386use color_eyre::Result;
3387use eyre::eyre;
3388#[allow(unused)]
3389use std::fmt::LowerExp;
3390
3391pub trait ApproxEq<U: LowerExp, T: LowerExp>: LowerExp {
3392 fn approx_eq(&self, other: &U, tolerance: &T) -> bool;
3393
3394 fn approx_eq_slice(lhs: &[Self], rhs: &[U], tolerance: &T) -> bool
3395 where
3396 Self: Sized,
3397 {
3398 lhs.iter()
3399 .zip_eq(rhs)
3400 .all(|(l, r)| l.approx_eq(r, tolerance))
3401 }
3402
3403 fn approx_eq_iterator<'a, I, J>(lhs: I, rhs: J, tolerance: &'a T) -> bool
3404 where
3405 Self: Sized + 'a,
3406 U: 'a,
3407 I: IntoIterator<Item = &'a Self>,
3408 J: IntoIterator<Item = &'a U>,
3409 {
3410 lhs.into_iter()
3411 .zip_eq(rhs)
3412 .all(|(l, r)| l.approx_eq(r, tolerance))
3413 }
3414
3415 fn assert_approx_eq(&self, other: &U, tolerance: &T) {
3416 assert!(
3417 self.approx_eq(other, tolerance),
3418 "assert_approx_eq failed: \n{:+e} != \n{:+e} with tolerance {:+e}",
3419 self,
3420 other,
3421 tolerance
3422 )
3423 }
3424 fn approx_eq_res(&self, other: &U, tolerance: &T) -> Result<()> {
3425 if self.approx_eq(other, tolerance) {
3426 Ok(())
3427 } else {
3428 Err(eyre!(
3429 "assert_approx_eq failed: \n{:+e} != \n{:+e} with tolerance {:+e}",
3430 self,
3431 other,
3432 tolerance
3433 ))
3434 }
3435 }
3436}
3437
3438impl<T: FloatLike> ApproxEq<F<T>, F<T>> for F<T> {
3443 fn approx_eq(&self, other: &F<T>, tolerance: &F<T>) -> bool {
3444 if other.is_zero() {
3445 self.norm() < tolerance.clone()
3446 } else {
3447 ((self.ref_sub(other)) / other).norm() < tolerance.clone()
3448 }
3449 }
3450}
3451
3452impl<T: FloatLike> ApproxEq<Complex<F<T>>, F<T>> for Complex<F<T>> {
3453 fn approx_eq(&self, other: &Complex<F<T>>, tolerance: &F<T>) -> bool {
3454 if !self.norm().re.approx_eq(&other.norm().re, tolerance) {
3455 return false;
3456 } else if self.norm().is_zero() || other.norm().is_zero() {
3457 return true;
3458 }
3459 let two_pi = self.re.PI() + self.re.PI();
3460 let arg_self = self.arg().rem_euclid(&two_pi);
3461 let arg_other = other.arg().rem_euclid(&two_pi);
3462 if !arg_self.approx_eq(&arg_other, tolerance) {
3463 return false;
3464 }
3465 true
3466 }
3467 fn approx_eq_res(&self, other: &Complex<F<T>>, tolerance: &F<T>) -> Result<()> {
3468 if !self.norm().re.approx_eq(&other.norm().re, tolerance) {
3469 return Err(eyre!(
3470 "Norms are not approximately equal: \n{:+e} != \n{:+e} with tolerance {:+e}",
3471 &self.norm().re,
3472 other.norm().re,
3473 tolerance
3474 ));
3475 } else if self.norm().is_zero() || other.norm().is_zero() {
3476 return Ok(());
3477 }
3478
3479 let two_pi = self.re.PI() + self.re.PI();
3480 let arg_self = self.arg().rem_euclid(&two_pi);
3481 let arg_other = other.arg().rem_euclid(&two_pi);
3482 if !arg_self.approx_eq(&arg_other, tolerance) {
3485 return Err(eyre!(
3486 "Phases are not approximately equal: \n{:+e} - \n{:+e}= \n{:+e}!=0 with tolerance {:+e}",
3487 arg_self,
3488 arg_other,
3489 &arg_self - &arg_other,
3490 tolerance
3491 ));
3492 }
3493 Ok(())
3494 }
3495}
3496
3497impl<T: FloatLike> ApproxEq<F<T>, F<T>> for Complex<F<T>> {
3498 fn approx_eq(&self, other: &F<T>, tolerance: &F<T>) -> bool {
3499 self.re.approx_eq(other, tolerance) && self.im.approx_eq(tolerance, tolerance)
3500 }
3501 fn approx_eq_res(&self, other: &F<T>, tolerance: &F<T>) -> Result<()> {
3502 if self.im.approx_eq(tolerance, tolerance) {
3503 return Err(eyre!(
3504 "Non-zero imaginary part: \n{:+e} with tolerance {:+e}",
3505 &self.im,
3506 tolerance
3507 ));
3508 }
3509 if !self.re.approx_eq(other, tolerance) {
3510 return Err(eyre!(
3511 "Real parts are not approximately equal: \n{:+e} != \n{:+e} with tolerance {:+e}",
3512 &self.re,
3513 other,
3514 tolerance
3515 ));
3516 }
3517 Ok(())
3518 }
3519}
3520
3521impl<T: FloatLike> ApproxEq<Complex<F<T>>, F<T>> for F<T> {
3522 fn approx_eq(&self, other: &Complex<F<T>>, tolerance: &F<T>) -> bool {
3523 other.re.approx_eq(self, tolerance) && other.im.approx_eq(tolerance, tolerance)
3524 }
3525
3526 fn approx_eq_res(&self, other: &Complex<F<T>>, tolerance: &F<T>) -> Result<()> {
3527 if other.im.approx_eq(tolerance, tolerance) {
3528 return Err(eyre!(
3529 "Non-zero imaginary part: \n{:+e} with tolerance {:+e}",
3530 &other.im,
3531 tolerance
3532 ));
3533 }
3534 if !other.re.approx_eq(self, tolerance) {
3535 return Err(eyre!(
3536 "Real parts are not approximately equal: \n{:+e} != \n{:+e} with tolerance {:+e}",
3537 &other.re,
3538 self,
3539 tolerance
3540 ));
3541 }
3542 Ok(())
3543 }
3544}
3545
3546#[allow(unused)]
3547pub(crate) fn one_loop_eval_e_surf<T: FloatLike>(
3548 k: &[F<T>; 3],
3549 p1: &[F<T>; 3],
3550 p2: &[F<T>; 3],
3551 m1_sq: F<T>,
3552 m2_sq: F<T>,
3553 e_shift: F<T>,
3554) -> F<T> {
3555 ((&k[0] + &p1[0]) * (&k[0] + &p1[0])
3556 + (&k[1] + &p1[1]) * (&k[1] + &p1[1])
3557 + (&k[2] + &p1[2]) * (&k[2] + &p1[2])
3558 + m1_sq)
3559 .sqrt()
3560 + ((&k[0] + &p2[0]) * (&k[0] + &p2[0])
3561 + (&k[1] + &p2[1]) * (&k[1] + &p2[1])
3562 + (&k[2] + &p2[2]) * (&k[2] + &p2[2])
3563 + m2_sq)
3564 .sqrt()
3565 + e_shift
3566}
3567
3568#[allow(unused)]
3569pub(crate) fn one_loop_eval_e_surf_k_derivative<T: FloatLike>(
3570 k: &[F<T>; 3],
3571 p1: &[F<T>; 3],
3572 p2: &[F<T>; 3],
3573 m1_sq: F<T>,
3574 m2_sq: F<T>,
3575) -> [F<T>; 3] {
3576 let e1 = ((&k[0] + &p1[0]) * (&k[0] + &p1[0])
3577 + (&k[1] + &p1[1]) * (&k[1] + &p1[1])
3578 + (&k[2] + &p1[2]) * (&k[2] + &p1[2])
3579 + m1_sq)
3580 .sqrt();
3581 let e2 = ((&k[0] + &p2[0]) * (&k[0] + &p2[0])
3582 + (&k[1] + &p2[1]) * (&k[1] + &p2[1])
3583 + (&k[2] + &p2[2]) * (&k[2] + &p2[2])
3584 + m2_sq)
3585 .sqrt();
3586 [
3587 (&k[0] + &p1[0]) / &e1 + (&k[0] + &p2[0]) / &e2,
3588 (&k[1] + &p1[1]) / &e1 + (&k[1] + &p2[1]) / &e2,
3589 (&k[2] + &p1[2]) / &e1 + (&k[2] + &p2[2]) / &e2,
3590 ]
3591}
3592
3593#[allow(unused)]
3594pub(crate) fn one_loop_get_e_surf_t_scaling<T: FloatLike>(
3595 k: &[F<T>; 3],
3596 p1: &[F<T>; 3],
3597 p2: &[F<T>; 3],
3598 m1_sq: F<T>,
3599 m2_sq: F<T>,
3600 e_shift: F<T>,
3601) -> [F<T>; 2] {
3602 let zero = e_shift.zero();
3603 let one = zero.one();
3604 let (a, b, c_coef) = one_loop_e_surface_bilinear_form(p1, p2, m1_sq, m2_sq, e_shift);
3605 let mut a_coef = zero.clone();
3606 for i in 0..=2 {
3607 for j in 0..=2 {
3608 a_coef += &k[i] * &a[i][j] * &k[j];
3609 }
3610 }
3611 a_coef *= zero.from_i64(4);
3612 let mut b_coef = zero.clone();
3613 for i in 0..=2 {
3614 b_coef += &k[i] * &b[i];
3615 }
3616 b_coef *= zero.from_i64(4);
3617 let discr = b_coef.square() - zero.from_i64(4) * &a_coef * &c_coef;
3618 if discr < zero {
3619 [zero.clone(), zero.clone()]
3620 } else {
3621 [
3622 (-&b_coef + discr.sqrt()) / (zero.from_i64(2) * &a_coef),
3623 (-&b_coef - discr.sqrt()) / (zero.from_i64(2) * &a_coef),
3624 ]
3625 }
3626}
3627
3628pub(crate) fn box_muller<T: FloatLike>(x1: F<T>, x2: F<T>) -> (F<T>, F<T>) {
3629 let r = (-x1.from_i64(2) * x1.log()).sqrt();
3630 let theta = r.from_i64(2) * r.PI() * x2;
3631 (r.clone() * theta.cos(), r * theta.sin())
3632}
3633
3634pub(crate) fn compute_surface_and_volume<T: FloatLike>(n_dim: usize, radius: F<T>) -> (F<T>, F<T>) {
3635 let mut surface = radius.from_i64(2);
3636 let one = radius.one();
3637 let mut volume = one.clone();
3638 for i in 1..n_dim + 1 {
3639 (surface, volume) = (
3640 one.from_i64(2) * one.PI() * volume,
3641 surface / one.from_i64(i as i64),
3642 );
3643 }
3644 (
3645 surface * radius.pow(n_dim as u64),
3646 volume * radius.pow(n_dim as u64),
3647 )
3648}
3649
3650pub(crate) fn get_n_dim_for_n_loop_momenta(
3651 settings: &SamplingSettings,
3652 n_loop_momenta: usize,
3653 n_edges: Option<usize>, ) -> usize {
3655 if let Some(parameterization_settings) = settings.get_parameterization_settings() {
3656 match parameterization_settings.mode {
3657 ParameterizationMode::HyperSphericalFlat => {
3658 let mut n_dim = 3 * n_loop_momenta;
3660 if n_dim % 2 == 1 {
3661 n_dim += 1;
3662 }
3663 n_dim + 1
3664 }
3665 ParameterizationMode::HyperSpherical
3666 | ParameterizationMode::Cartesian
3667 | ParameterizationMode::Spherical
3668 | ParameterizationMode::RelativeSpherical
3669 | ParameterizationMode::SphericalCommonRadial
3670 | ParameterizationMode::SphericalProductCommonRadial
3671 | ParameterizationMode::MomentumSpace => 3 * n_loop_momenta,
3672 }
3673 } else {
3674 let tropical_part = 2 * n_edges.expect("No tropical subgraph table generated, please run without tropical sampling or regenerate with tables") - 1;
3675 let d_l = 3 * n_loop_momenta;
3676 if d_l % 2 == 1 {
3677 tropical_part + d_l + 1
3678 } else {
3679 tropical_part + d_l
3680 }
3681 }
3682}
3683
3684pub(crate) fn global_parameterize<T: FloatLike>(
3685 x: &[F<T>],
3686 e_cm: F<T>,
3687 settings: &ParameterizationSettings,
3688) -> (Vec<[F<T>; 3]>, F<T>) {
3689 debug!("b: {}", settings.b);
3690 let zero = e_cm.zero();
3691 let one = zero.one();
3692 match settings.mode {
3693 ParameterizationMode::HyperSpherical | ParameterizationMode::HyperSphericalFlat => {
3694 let mut jac = one.clone();
3695
3696 let radius: F<T> = match settings.mapping {
3697 ParameterizationMapping::Log => {
3698 let b = F::<T>::from_f64(settings.b);
3700 let radius = &e_cm * (&one + &b * &x[0] / (&one - &x[0])).log();
3701 jac *= &e_cm * &b / (&one - &x[0]) / (&one + &x[0] * (&b - &one));
3702 radius
3703 }
3704 ParameterizationMapping::Power => {
3705 let b = F::<T>::from_f64(settings.b);
3707 let power = F::<T>::from_f64(settings.power);
3708 let odds = &x[0] / (&one - &x[0]);
3709 let power_minus_one = &power - &one;
3710 let radius = &e_cm * &b * odds.powf(&power);
3711 jac *=
3712 &e_cm * &b * &power * odds.powf(&power_minus_one) / (&one - &x[0]).square();
3713 radius
3714 }
3715 ParameterizationMapping::Linear => {
3716 let b = F::<T>::from_f64(settings.b);
3718 let radius = &e_cm * &b * &x[0] / (&one - &x[0]);
3719 jac *= (&e_cm * &b + &radius).powi(2) / &e_cm / &b;
3720 radius
3721 }
3722 };
3723 match settings.mode {
3724 ParameterizationMode::HyperSpherical => {
3725 let phi = zero.from_i64(2) * zero.PI() * &x[1];
3726 jac *= zero.from_i64(2) * zero.PI();
3727
3728 let mut cos_thetas = Vec::with_capacity(x.len() - 2);
3729 let mut sin_thetas = Vec::with_capacity(x.len() - 2);
3730
3731 for (i, xi) in x[2..].iter().enumerate() {
3732 let cos_theta = -&one + zero.from_i64(2) * xi;
3733 jac *= zero.from_i64(2);
3734 let sin_theta = (&one - cos_theta.square()).sqrt();
3735 if i > 0 {
3736 jac *= sin_theta.pow(i as u64);
3737 }
3738 cos_thetas.push(cos_theta);
3739 sin_thetas.push(sin_theta);
3740 }
3741
3742 let mut concatenated_vecs = Vec::with_capacity(x.len() / 3);
3743 let mut base = radius.clone();
3744 for (cos_theta, sin_theta) in cos_thetas.iter().zip(sin_thetas.iter()) {
3745 concatenated_vecs.push(&base * cos_theta);
3746 base *= sin_theta;
3747 }
3748 concatenated_vecs.push(&base * phi.cos());
3749 concatenated_vecs.push(&base * phi.sin());
3750
3751 jac *= radius.pow((x.len() - 1) as u64); (
3754 concatenated_vecs
3755 .chunks(3)
3756 .map(|v| [v[0].clone(), v[1].clone(), v[2].clone()])
3757 .collect(),
3758 jac,
3759 )
3760 }
3761 ParameterizationMode::HyperSphericalFlat => {
3762 assert!(x[1..].len().is_multiple_of(2));
3764 let mut normal_distributed_xs = vec![];
3765 for x_pair in x[1..].chunks(2) {
3766 let (z1, z2) = box_muller(x_pair[0].clone(), x_pair[1].clone());
3767 normal_distributed_xs.push(z1);
3768 normal_distributed_xs.push(z2);
3769 }
3770 if normal_distributed_xs.len() % 3 != 0 {
3772 normal_distributed_xs.pop();
3773 }
3774 let curr_norm = normal_distributed_xs[..]
3775 .iter()
3776 .map(|x| x.square())
3777 .reduce(|acc, e| acc + &e)
3778 .unwrap_or(zero.clone())
3779 .sqrt();
3780 let surface =
3781 compute_surface_and_volume(normal_distributed_xs.len() - 1, radius.clone())
3782 .0;
3783 jac *= surface;
3784 let rescaling_factor = &radius / &curr_norm;
3785 (
3786 normal_distributed_xs
3787 .chunks(3)
3788 .map(|v| {
3789 [
3790 &v[0] * &rescaling_factor,
3791 &v[1] * &rescaling_factor,
3792 &v[2] * &rescaling_factor,
3793 ]
3794 })
3795 .collect(),
3796 jac,
3797 )
3798 }
3799 _ => unreachable!(),
3800 }
3801 }
3802 ParameterizationMode::Cartesian | ParameterizationMode::Spherical => {
3803 let mut jac = one.clone();
3804 let mut vecs = Vec::with_capacity(x.len() / 3);
3805 for xi in x.chunks(3) {
3806 let (vec_i, jac_i) = parameterize3d(xi, e_cm.clone(), settings);
3807 vecs.push(vec_i);
3808 jac *= jac_i;
3809 }
3810 (vecs, jac)
3811 }
3812 ParameterizationMode::RelativeSpherical => {
3813 if x.len() != 6 {
3814 panic!(
3815 "relative_spherical parameterization currently requires exactly two loop three-momenta"
3816 );
3817 }
3818 let spherical_settings = ParameterizationSettings {
3819 mode: ParameterizationMode::Spherical,
3820 mapping: settings.mapping.clone(),
3821 b: settings.b,
3822 power: settings.power,
3823 lmb_basis_ids: Default::default(),
3824 };
3825 let (common, common_jac) = parameterize3d(&x[0..3], e_cm.clone(), &spherical_settings);
3826 let (relative, relative_jac) =
3827 parameterize3d(&x[3..6], e_cm.clone(), &spherical_settings);
3828 let half = &one / one.from_i64(2);
3829 let first = [
3830 &common[0] + &half * &relative[0],
3831 &common[1] + &half * &relative[1],
3832 &common[2] + &half * &relative[2],
3833 ];
3834 let second = [
3835 &common[0] - &half * &relative[0],
3836 &common[1] - &half * &relative[1],
3837 &common[2] - &half * &relative[2],
3838 ];
3839 (vec![first, second], common_jac * relative_jac)
3840 }
3841 ParameterizationMode::SphericalCommonRadial => {
3842 if x.is_empty() || !x.len().is_multiple_of(3) {
3843 panic!(
3844 "spherical_common_radial parameterization requires complete loop three-momenta"
3845 );
3846 }
3847 let n_loop_momenta = x.len() / 3;
3848
3849 let mut jac = one.clone();
3850 let radius = match settings.mapping {
3851 ParameterizationMapping::Log => {
3852 let b = F::<T>::from_f64(settings.b);
3853 let radius = &e_cm * (&one + &b * &x[0] / (&one - &x[0])).log();
3854 jac *= &e_cm * &b / (&one - &x[0]) / (&one + &x[0] * (&b - &one));
3855 radius
3856 }
3857 ParameterizationMapping::Power => {
3858 let b = F::<T>::from_f64(settings.b);
3859 let power = F::<T>::from_f64(settings.power);
3860 let odds = &x[0] / (&one - &x[0]);
3861 let power_minus_one = &power - &one;
3862 let radius = &e_cm * &b * odds.powf(&power);
3863 jac *=
3864 &e_cm * &b * &power * odds.powf(&power_minus_one) / (&one - &x[0]).square();
3865 radius
3866 }
3867 ParameterizationMapping::Linear => {
3868 let b = F::<T>::from_f64(settings.b);
3869 let radius = &e_cm * &b * &x[0] / (&one - &x[0]);
3870 jac *= (&e_cm * &b + &radius).powi(2) / &e_cm / &b;
3871 radius
3872 }
3873 };
3874
3875 let mut remaining_fraction = one.clone();
3876 let mut stick_jac = one.clone();
3877 let mut radii = Vec::with_capacity(n_loop_momenta);
3878 for i in 0..n_loop_momenta {
3879 let fraction = if i + 1 == n_loop_momenta {
3880 remaining_fraction.clone()
3881 } else {
3882 stick_jac *= &remaining_fraction;
3883 let fraction = &remaining_fraction * &x[1 + i];
3884 remaining_fraction *= &one - &x[1 + i];
3885 fraction
3886 };
3887 radii.push(&radius * fraction);
3888 }
3889
3890 jac *= radius.pow((n_loop_momenta - 1) as u64) * stick_jac;
3891 for radius_i in &radii {
3892 jac *= radius_i.square();
3893 }
3894
3895 let angle_offset = n_loop_momenta;
3896 let mut momenta = Vec::with_capacity(n_loop_momenta);
3897 for (i, radius_i) in radii.iter().enumerate() {
3898 let phi = F::<T>::from_f64(2.) * zero.PI() * &x[angle_offset + 2 * i];
3899 jac *= F::<T>::from_f64(2.) * zero.PI();
3900 let cos_theta = -&one + F::<T>::from_f64(2.) * &x[angle_offset + 2 * i + 1];
3901 jac *= F::<T>::from_f64(2.);
3902 let sin_theta = (&one - cos_theta.square()).sqrt();
3903 momenta.push([
3904 radius_i * &sin_theta * phi.cos(),
3905 radius_i * &sin_theta * phi.sin(),
3906 radius_i * cos_theta,
3907 ]);
3908 }
3909
3910 (momenta, jac)
3911 }
3912 ParameterizationMode::SphericalProductCommonRadial => {
3913 if x.len() != 6 {
3914 panic!(
3915 "spherical_product_common_radial parameterization currently requires exactly two loop three-momenta"
3916 );
3917 }
3918 let mut branch_x = x.to_vec();
3919 let branch_settings = if x[0] < F::<T>::from_f64(0.5) {
3920 branch_x[0] = &x[0] * F::<T>::from_f64(2.0);
3921 ParameterizationSettings {
3922 mode: ParameterizationMode::Spherical,
3923 mapping: settings.mapping.clone(),
3924 b: settings.b,
3925 power: settings.power,
3926 lmb_basis_ids: Default::default(),
3927 }
3928 } else {
3929 branch_x[0] = (&x[0] - F::<T>::from_f64(0.5)) * F::<T>::from_f64(2.0);
3930 ParameterizationSettings {
3931 mode: ParameterizationMode::SphericalCommonRadial,
3932 mapping: settings.mapping.clone(),
3933 b: settings.b,
3934 power: settings.power,
3935 lmb_basis_ids: Default::default(),
3936 }
3937 };
3938 let (momenta, jac) = global_parameterize(&branch_x, e_cm, &branch_settings);
3939 (momenta, jac * F::<T>::from_f64(2.0))
3940 }
3941 ParameterizationMode::MomentumSpace => (x.as_chunks::<3>().0.to_vec(), one),
3942 }
3943}
3944
3945#[allow(unused)]
3946pub(crate) fn global_inv_parameterize<T: FloatLike>(
3947 moms: &[ThreeMomentum<F<T>>],
3948 e_cm: F<T>,
3949 settings: &ParameterizationSettings,
3950) -> (Vec<F<T>>, F<T>) {
3951 let one = e_cm.one();
3952 let zero = one.zero();
3953
3954 match settings.mode {
3955 ParameterizationMode::HyperSpherical => {
3956 let mut inv_jac = one.clone();
3957 let mut xs = Vec::with_capacity(moms.len() * 3);
3958
3959 let cartesian_xs = moms
3960 .iter()
3961 .flat_map(|lv| lv.clone().into_iter())
3962 .collect::<Vec<F<T>>>();
3963
3964 let mut k_r_sq = cartesian_xs
3965 .iter()
3966 .map(|xi| xi.square())
3967 .reduce(|acc, e| acc + &e)
3968 .unwrap_or(zero.clone());
3969 if k_r_sq.is_zero() {
3971 return (vec![zero.clone(); cartesian_xs.len()], zero);
3972 }
3973 let k_r = k_r_sq.sqrt();
3974 match settings.mapping {
3975 ParameterizationMapping::Log => {
3976 let b = F::<T>::from_f64(settings.b);
3977 let x1 = &one - &b / (-&one + &b + (&k_r / &e_cm).exp());
3978 inv_jac /= e_cm * &b / (&one - &x1) / (&one + &x1 * (&b - &one));
3979 xs.push(x1);
3980 }
3981 ParameterizationMapping::Power => {
3982 let b = F::<T>::from_f64(settings.b);
3983 let power = F::<T>::from_f64(settings.power);
3984 let inv_power = &one / &power;
3985 let odds = (&k_r / (&e_cm * &b)).powf(&inv_power);
3986 let x1 = &odds / (&one + &odds);
3987 let power_minus_one = &power - &one;
3988 inv_jac /=
3989 &e_cm * &b * &power * odds.powf(&power_minus_one) / (&one - &x1).square();
3990 xs.push(x1);
3991 }
3992 ParameterizationMapping::Linear => {
3993 let b = F::<T>::from_f64(settings.b);
3994 inv_jac /= (&e_cm * &b + &k_r).powi(2) / &e_cm / &b;
3995 xs.push(&k_r / (&e_cm * &b + &k_r));
3996 }
3997 };
3998
3999 let y = cartesian_xs[cartesian_xs.len() - 2].clone();
4000 let x = cartesian_xs[cartesian_xs.len() - 1].clone();
4001 let xphi = if x < zero {
4002 &one + F::<T>::from_f64(0.5) * zero.FRAC_1_PI() * x.atan2(&y)
4003 } else {
4004 F::<T>::from_f64(0.5) * zero.FRAC_1_PI() * x.atan2(&y)
4005 };
4006 xs.push(xphi);
4007 inv_jac /= F::<T>::from_f64(2.) * zero.PI();
4008
4009 for (i, x) in cartesian_xs[..cartesian_xs.len() - 2].iter().enumerate() {
4010 xs.push(F::<T>::from_f64(0.5) * (&one + x / k_r_sq.sqrt()));
4011 inv_jac /= F::<T>::from_f64(2.);
4012 if i > 0 {
4013 inv_jac /= (&one - (x * x / &k_r_sq)).sqrt().powi(i as i32);
4014 }
4015 k_r_sq -= x * x;
4016 }
4017
4018 inv_jac /= k_r.powi((cartesian_xs.len() - 1) as i32);
4019
4020 (xs, inv_jac)
4021 }
4022 ParameterizationMode::HyperSphericalFlat => {
4023 panic!(
4024 "Inverse of flat hyperspherical sampling is not available since it is not bijective."
4025 );
4026 }
4027 ParameterizationMode::Cartesian | ParameterizationMode::Spherical => {
4028 let mut inv_jac = one;
4029 let mut xs = Vec::with_capacity(moms.len() * 3);
4030 for (i, mom) in moms.iter().enumerate() {
4031 let (xs_i, inv_jac_i) = inv_parametrize3d(mom, e_cm.clone(), settings);
4032
4033 xs.extend(xs_i);
4034 inv_jac *= inv_jac_i;
4035 }
4036 (xs, inv_jac)
4037 }
4038 ParameterizationMode::RelativeSpherical => {
4039 if moms.len() != 2 {
4040 panic!(
4041 "relative_spherical inverse parameterization currently requires exactly two loop three-momenta"
4042 );
4043 }
4044 let half = &one / one.from_i64(2);
4045 let common = ThreeMomentum::new(
4046 (&moms[0].px + &moms[1].px) * &half,
4047 (&moms[0].py + &moms[1].py) * &half,
4048 (&moms[0].pz + &moms[1].pz) * &half,
4049 );
4050 let relative = ThreeMomentum::new(
4051 &moms[0].px - &moms[1].px,
4052 &moms[0].py - &moms[1].py,
4053 &moms[0].pz - &moms[1].pz,
4054 );
4055 let spherical_settings = ParameterizationSettings {
4056 mode: ParameterizationMode::Spherical,
4057 mapping: settings.mapping.clone(),
4058 b: settings.b,
4059 power: settings.power,
4060 lmb_basis_ids: Default::default(),
4061 };
4062 let (common_xs, common_inv_jac) =
4063 inv_parametrize3d(&common, e_cm.clone(), &spherical_settings);
4064 let (relative_xs, relative_inv_jac) =
4065 inv_parametrize3d(&relative, e_cm.clone(), &spherical_settings);
4066 (
4067 common_xs.into_iter().chain(relative_xs).collect(),
4068 common_inv_jac * relative_inv_jac,
4069 )
4070 }
4071 ParameterizationMode::SphericalCommonRadial => {
4072 if moms.is_empty() {
4073 panic!(
4074 "spherical_common_radial inverse parameterization requires at least one loop three-momentum"
4075 );
4076 }
4077
4078 let phi_x = |x: &F<T>, y: &F<T>| {
4079 if y < &zero {
4080 &one + F::<T>::from_f64(0.5) * zero.FRAC_1_PI() * y.atan2(x)
4081 } else {
4082 F::<T>::from_f64(0.5) * zero.FRAC_1_PI() * y.atan2(x)
4083 }
4084 };
4085
4086 let radii_sq = moms
4087 .iter()
4088 .map(|mom| mom.px.square() + mom.py.square() + mom.pz.square())
4089 .collect::<Vec<_>>();
4090 let radii = radii_sq
4091 .iter()
4092 .map(|radius_sq| radius_sq.sqrt())
4093 .collect::<Vec<_>>();
4094 let radius = radii
4095 .iter()
4096 .fold(zero.clone(), |acc, radius_i| acc + radius_i);
4097
4098 let mut xs = Vec::with_capacity(3 * moms.len());
4099 if radius.is_zero() || radii.iter().any(F::is_zero) {
4100 xs.resize(3 * moms.len(), zero.clone());
4101 return (xs, zero);
4102 }
4103
4104 let mut inv_jac = one.clone();
4105 let radial_x = match settings.mapping {
4106 ParameterizationMapping::Log => {
4107 let b = F::<T>::from_f64(settings.b);
4108 let x1 = &one - &b / (-&one + &b + (&radius / &e_cm).exp());
4109 inv_jac /= &e_cm * &b / (&one - &x1) / (&one + &x1 * (&b - &one));
4110 x1
4111 }
4112 ParameterizationMapping::Power => {
4113 let b = F::<T>::from_f64(settings.b);
4114 let power = F::<T>::from_f64(settings.power);
4115 let inv_power = &one / &power;
4116 let odds = (&radius / (&e_cm * &b)).powf(&inv_power);
4117 let x1 = &odds / (&one + &odds);
4118 let power_minus_one = &power - &one;
4119 inv_jac /=
4120 &e_cm * &b * &power * odds.powf(&power_minus_one) / (&one - &x1).square();
4121 x1
4122 }
4123 ParameterizationMapping::Linear => {
4124 let b = F::<T>::from_f64(settings.b);
4125 inv_jac /= (&e_cm * &b + &radius).powi(2) / &e_cm / &b;
4126 &radius / (&e_cm * &b + &radius)
4127 }
4128 };
4129
4130 xs.push(radial_x);
4131 let mut remaining_fraction = one.clone();
4132 let mut stick_jac = one.clone();
4133 for (i, radius_i) in radii.iter().enumerate().take(moms.len() - 1) {
4134 let fraction = radius_i / &radius;
4135 xs.push(&fraction / &remaining_fraction);
4136 stick_jac *= &remaining_fraction;
4137 remaining_fraction -= fraction;
4138 if remaining_fraction.is_zero() && i + 2 < moms.len() {
4139 return (vec![zero.clone(); 3 * moms.len()], zero);
4140 }
4141 }
4142
4143 inv_jac /= radius.pow((moms.len() - 1) as u64) * stick_jac;
4144 for radius_sq in &radii_sq {
4145 inv_jac /= radius_sq;
4146 }
4147
4148 for (mom, radius_i) in moms.iter().zip(radii.iter()) {
4149 xs.push(phi_x(&mom.px, &mom.py));
4150 inv_jac /= F::<T>::from_f64(2.) * zero.PI();
4151 xs.push(F::<T>::from_f64(0.5) * (&one + &mom.pz / radius_i));
4152 inv_jac /= F::<T>::from_f64(2.);
4153 }
4154
4155 (xs, inv_jac)
4156 }
4157 ParameterizationMode::SphericalProductCommonRadial => {
4158 if moms.len() != 2 {
4159 panic!(
4160 "spherical_product_common_radial inverse parameterization currently requires exactly two loop three-momenta"
4161 );
4162 }
4163 let spherical_settings = ParameterizationSettings {
4164 mode: ParameterizationMode::Spherical,
4165 mapping: settings.mapping.clone(),
4166 b: settings.b,
4167 power: settings.power,
4168 lmb_basis_ids: Default::default(),
4169 };
4170 let common_radial_settings = ParameterizationSettings {
4171 mode: ParameterizationMode::SphericalCommonRadial,
4172 mapping: settings.mapping.clone(),
4173 b: settings.b,
4174 power: settings.power,
4175 lmb_basis_ids: Default::default(),
4176 };
4177 let (mut xs, product_inv_jac) =
4178 global_inv_parameterize(moms, e_cm.clone(), &spherical_settings);
4179 let (_, common_inv_jac) = global_inv_parameterize(moms, e_cm, &common_radial_settings);
4180 if let Some(first_x) = xs.first_mut() {
4181 *first_x = &*first_x / F::<T>::from_f64(2.0);
4182 }
4183 (xs, product_inv_jac + common_inv_jac)
4184 }
4185 ParameterizationMode::MomentumSpace => (
4186 moms.iter()
4187 .flat_map(|mom| [mom.px.clone(), mom.py.clone(), mom.pz.clone()])
4188 .collect(),
4189 one,
4190 ),
4191 }
4192}
4193
4194pub(crate) fn parameterize3d<T: FloatLike>(
4197 x: &[F<T>],
4198 e_cm: F<T>,
4199 settings: &ParameterizationSettings,
4200) -> ([F<T>; 3], F<T>) {
4201 let zero = e_cm.zero();
4202 let one = zero.one();
4203 let mut l_space = [zero.clone(), zero.clone(), zero.clone()];
4204 let mut jac = one.clone();
4205
4206 match settings.mode {
4207 ParameterizationMode::Cartesian => match settings.mapping {
4208 ParameterizationMapping::Log => {
4209 for i in 0..3 {
4210 let x = x[i].clone();
4211 l_space[i] = &e_cm * (&x / (&one - &x)).log();
4212 jac *= &e_cm / (&x - &x * &x);
4213 }
4214 }
4215 ParameterizationMapping::Linear => {
4216 for i in 0..3 {
4217 let x = x[i].clone();
4218 l_space[i] = &e_cm * (&one / (&one - &x) - &one / &x);
4219 jac *= &e_cm * (&one / (&x * &x) + &one / ((&one - &x) * (&one - &x)));
4220 }
4221 }
4222 ParameterizationMapping::Power => {
4223 panic!("Power radial mapping is only supported for spherical coordinates");
4224 }
4225 },
4226 ParameterizationMode::Spherical => {
4227 let radius = match settings.mapping {
4228 ParameterizationMapping::Log => {
4229 let x = x[0].clone();
4231 let b = F::<T>::from_f64(settings.b);
4232 let radius = &e_cm * (&one + &b * &x / (&one - &x)).log();
4233 jac *= &e_cm * &b / (&one - &x) / (&one + &x * (&b - &one));
4234
4235 radius
4236 }
4237 ParameterizationMapping::Power => {
4238 let x = x[0].clone();
4240 let b = F::<T>::from_f64(settings.b);
4241 let power = F::<T>::from_f64(settings.power);
4242 let odds = &x / (&one - &x);
4243 let power_minus_one = &power - &one;
4244 let radius = &e_cm * &b * odds.powf(&power);
4245 jac *= &e_cm * &b * &power * odds.powf(&power_minus_one) / (&one - &x).square();
4246
4247 radius
4248 }
4249 ParameterizationMapping::Linear => {
4250 let b = F::<T>::from_f64(settings.b);
4252 let radius = &e_cm * &b * &x[0] / (&one - &x[0]);
4253 jac *= (&e_cm * &b + &radius).powi(2) / &e_cm / &b;
4254 radius
4255 }
4256 };
4257 let phi = F::<T>::from_f64(2.) * zero.PI() * &x[1];
4258 jac *= F::<T>::from_f64(2.) * zero.PI();
4259
4260 let cos_theta = -&one + F::<T>::from_f64(2.) * &x[2]; jac *= F::<T>::from_f64(2.);
4262 let sin_theta = (&one - cos_theta.square()).sqrt();
4263
4264 l_space[0] = &radius * &sin_theta * phi.cos();
4265 l_space[1] = &radius * &sin_theta * phi.sin();
4266 l_space[2] = &radius * &cos_theta;
4267
4268 jac *= radius.square(); }
4270 _ => {
4271 panic!(
4272 "Inappropriate parameterization mapping specified for parameterize: {:?}.",
4273 settings.mode.clone()
4274 );
4275 }
4276 }
4277
4278 (l_space, jac)
4279}
4280
4281pub(crate) fn inv_parametrize3d<T: FloatLike>(
4282 mom: &ThreeMomentum<F<T>>,
4283 e_cm: F<T>,
4284 settings: &ParameterizationSettings,
4285) -> ([F<T>; 3], F<T>) {
4286 let one = e_cm.one();
4287 let zero = one.zero();
4288 if settings.mode != ParameterizationMode::Spherical {
4289 panic!("Inverse mapping is only implemented for spherical coordinates");
4290 }
4291
4292 let mut jac = one.clone();
4293
4294 let x = &mom.px;
4295 let y = &mom.py;
4296 let z = &mom.pz;
4297
4298 let k_r_sq = x.square() + y.square() + z.square();
4299 let k_r = k_r_sq.sqrt();
4300
4301 let x2 = if y < &zero {
4302 &one + F::<T>::from_f64(0.5) * zero.FRAC_1_PI() * y.atan2(x)
4303 } else {
4304 F::<T>::from_f64(0.5) * zero.FRAC_1_PI() * y.atan2(x)
4305 };
4306
4307 if k_r_sq.is_zero() {
4309 return ([zero.clone(), x2.clone(), zero.clone()], zero.clone());
4310 }
4311
4312 let x1 = match settings.mapping {
4313 ParameterizationMapping::Log => {
4314 let b = F::<T>::from_f64(settings.b);
4315 let x1 = &one - &b / (-&one + &b + (&k_r / &e_cm).exp());
4316 jac /= &e_cm * &b / (&one - &x1) / (&one + &x1 * (&b - &one));
4317 x1
4318 }
4319 ParameterizationMapping::Power => {
4320 let b = F::<T>::from_f64(settings.b);
4321 let power = F::<T>::from_f64(settings.power);
4322 let inv_power = &one / &power;
4323 let odds = (&k_r / (&e_cm * &b)).powf(&inv_power);
4324 let x1 = &odds / (&one + &odds);
4325 let power_minus_one = &power - &one;
4326 jac /= &e_cm * &b * &power * odds.powf(&power_minus_one) / (&one - &x1).square();
4327 x1
4328 }
4329 ParameterizationMapping::Linear => {
4330 let b = F::<T>::from_f64(settings.b);
4331 jac /= (&e_cm * &b + &k_r).powi(2) / &e_cm / &b;
4332 &k_r / (&e_cm * &b + &k_r)
4333 }
4334 };
4335
4336 let x3 = F::<T>::from_f64(0.5) * (&one + z / &k_r);
4337 jac /= F::<T>::from_f64(2.) * zero.PI();
4338 jac /= F::<T>::from_f64(2.);
4339 jac /= k_r.square();
4340
4341 let x = [x1, x2, x3];
4342
4343 (x, jac)
4344}
4345
4346pub const MINUTE: usize = 60;
4347pub const HOUR: usize = 3_600;
4348pub const DAY: usize = 86_400;
4349pub const WEEK: usize = 604_800;
4350pub(crate) fn format_wdhms(seconds: usize) -> String {
4351 let mut compound_duration = vec![];
4352 if seconds == 0 {
4353 compound_duration.push("0s".to_string());
4354 return compound_duration.join(" ");
4355 }
4356
4357 let mut sec = seconds % WEEK;
4358 let ws = seconds / WEEK;
4360 if ws != 0 {
4361 compound_duration.push(format!("{ws}w"));
4362 }
4363
4364 let ds = sec / DAY;
4366 sec %= DAY;
4367 if ds != 0 {
4368 compound_duration.push(format!("{ds}d"));
4369 }
4370
4371 let hs = sec / HOUR;
4373 sec %= HOUR;
4374 if hs != 0 {
4375 compound_duration.push(format!("{hs}h"));
4376 }
4377
4378 let ms = sec / MINUTE;
4380 sec %= MINUTE;
4381 if ms != 0 {
4382 compound_duration.push(format!("{ms}m"));
4383 }
4384
4385 if sec != 0 {
4387 compound_duration.push(format!("{sec}s"));
4388 }
4389
4390 compound_duration.join(" ")
4391}
4392
4393pub(crate) fn format_wdhms_from_duration(duration: Duration) -> String {
4394 format_wdhms(duration.as_secs() as usize)
4395}
4396
4397#[allow(unused)]
4398pub(crate) fn inverse_gamma_lr(a: f64, p: f64, n_iter: usize) -> f64 {
4399 let q = 1.0 - p;
4403
4404 if (1.0 - 1.0e-8..=1.0 + 1.0e-8).contains(&a) {
4405 return -q.ln();
4406 }
4407
4408 let gamma_a = gamma(a);
4409 let b = q * gamma_a;
4410 let c = 0.577_215_664_901_532_9;
4411
4412 let mut x0 = 0.5;
4413 if a < 1.0 {
4414 if b > 0.6 || (b >= 0.45 && a >= 0.3) {
4415 let u = if b * q > 10e-8 {
4416 (p * gamma(a + 1.0)).powf(a.recip())
4417 } else {
4418 (-q / a - c).exp()
4419 };
4420 x0 = u / (1.0 - u / (a + 1.0));
4421 } else if a < 0.3 && (0.35..=0.6).contains(&b) {
4422 let t = (-c - b).exp();
4423 let u = t * t.exp();
4424 x0 = t * u.exp();
4425 } else if (0.15..=0.35).contains(&b) || ((0.15..0.45).contains(&b) && a >= 0.3) {
4426 let y = -b.ln();
4427 let u = y - (1.0 - a) * y.ln();
4428 x0 = y - (1.0 - a) * y.ln() - (1.0 + (1.0 - a) / (1.0 + u)).ln();
4429 } else if 0.01 < b && b < 0.15 {
4430 let y = -b.ln();
4431 let u = y - (1.0 - a) * y.ln();
4432 x0 = y
4433 - (1.0 - a) * u.ln()
4434 - ((u * u + 2.0 * (3.0 - a) * u + (2.0 - a) * (3.0 - a))
4435 / (u * u + (5.0 - a) * u + 2.0))
4436 .ln();
4437 } else if b <= 0.01 {
4438 let y = -b.ln();
4439 let c1 = (a - 1.0) * y.ln();
4440 let c2 = (a - 1.0) * (1.0 + c1);
4441 let c3 = (a - 1.0) * (-0.5 * c1 * c1 + (a - 2.0) * c1 + (3.0 * a - 5.0) * 0.5);
4442 let c4 = (a - 1.0)
4443 * (1.0 / 3.0 * c1 * c1 * c1 - (3.0 * a - 5.0) * 0.5 * c1 * c1
4444 + (a * a - 6.0 * a + 7.0) * c1
4445 + (11.0 * a * a - 46.0 * a + 47.0) / 6.0);
4446 let c5 = (a - 1.0)
4447 * (-0.25 * c1 * c1 * c1 * c1
4448 + (11.0 * a - 7.0) / 6.0 * c1 * c1 * c1
4449 + (-3.0 * a * a - 13.0) * c1 * c1
4450 + (2.0 * a * a * a - 25.0 * a * a + 72.0 * a - 61.0) * 0.5 * c1
4451 + (25.0 * a * a * a - 195.0 * a * a + 477.0 * a - 379.0) / 12.0);
4452 x0 = y + c1 + c2 / (y) + c3 / (y * y) + c4 / (y * y * y) + c5 / (y * y * y * y);
4453
4454 if b <= 1.0e-28 {
4455 return x0;
4456 }
4457 }
4458 } else {
4459 let (pref, tau) = if p < 0.5 { (-1.0, p) } else { (1.0, q) };
4460 let t = (-2.0 * tau.ln()).sqrt();
4461
4462 let a_0 = 3.31125922108741;
4463 let a_1 = 11.6616720288968;
4464 let a_2 = 4.28342155967104;
4465 let a_3 = 0.213623493715853;
4466
4467 let b_1 = 6.61053765625462;
4468 let b_2 = 6.40691597760039;
4469 let b_3 = 1.27364489782223;
4470 let b_4 = 3.611_708_101_884_203e-2;
4471
4472 let t2 = t * t;
4473 let t3 = t2 * t;
4474 let t4 = t3 * t;
4475
4476 let numerator = a_0 + a_1 * t + a_2 * t2 + a_3 * t3;
4477 let denominator = 1.0 + b_1 * t + b_2 * t2 + b_3 * t3 + b_4 * t4;
4478
4479 let s = pref * (t - numerator / denominator);
4480 let s2 = s * s;
4481 let s3 = s * s2;
4482 let s4 = s * s3;
4483 let s5 = s * s4;
4484
4485 let a_sqrt = a.sqrt();
4486
4487 let w = a + s * a_sqrt + (s2 - 1.0) / 3.0 + (s3 - 7.0 * s) / (36.0 * a_sqrt)
4488 - (3.0 * s4 + 7.0 * s2 - 16.0) / (810.0 * a)
4489 + (9.0 * s5 + 256.0 * s3 - 433.0 * s) / (38880.0 * a * a_sqrt);
4490
4491 if a >= 500.0 && (1.0 - w / a).abs() < 1.0e-6 {
4492 return w;
4493 } else if p > 0.5 {
4494 if w < 3.0 * a {
4495 x0 = w;
4496 } else {
4497 let d = 2f64.max(a * (a - 1.0));
4498 if b > 10f64.powf(-d) {
4499 let u = -b.ln() + (a - 1.0) * w.ln() - (1.0 + (1.0 - a) / (1.0 + w)).ln();
4500 x0 = -b.ln() + (a - 1.0) * u.ln() - (1.0 + (1.0 - a) / (1.0 + u)).ln();
4501 } else {
4502 let y = -b.ln();
4503 let c1 = (a - 1.0) * y.ln();
4504 let c2 = (a - 1.0) * (1.0 + c1);
4505 let c3 = (a - 1.0) * (-0.5 * c1 * c1 + (a - 2.0) * c1 + (3.0 * a - 5.0) * 0.5);
4506 let c4 = (a - 1.0)
4507 * (1.0 / 3.0 * c1 * c1 * c1 - (3.0 * a - 5.0) * 0.5 * c1 * c1
4508 + (a * a - 6.0 * a + 7.0) * c1
4509 + (11.0 * a * a - 46.0 * a + 47.0) / 6.0);
4510 let c5 = (a - 1.0)
4511 * (-0.25 * c1 * c1 * c1 * c1
4512 + (11.0 * a - 7.0) / 6.0 * c1 * c1 * c1
4513 + (-3.0 * a * a - 13.0) * c1 * c1
4514 + (2.0 * a * a * a - 25.0 * a * a + 72.0 * a - 61.0) * 0.5 * c1
4515 + (25.0 * a * a * a - 195.0 * a * a + 477.0 * a - 379.0) / 12.0);
4516 x0 = y + c1 + c2 / (y) + c3 / (y * y) + c4 / (y * y * y) + c5 / (y * y * y * y);
4517 }
4518 }
4519 } else {
4520 let v = (p * gamma(a + 1.0)).ln();
4523 x0 = ((v + w) / a).exp();
4524 }
4525 }
4526
4527 let mut x_n = x0;
4529 for _ in 0..n_iter {
4530 let r = x_n.powf(a - 1.0) * (-x_n).exp() / gamma_a;
4531 if x_n <= 0. {
4532 x_n = 1.0e-16;
4533 }
4534 let t_n = if p <= 0.5 {
4535 (gamma_lr(a, x_n) - p) / r
4536 } else {
4537 -(gamma_ur(a, x_n) - q) / r
4538 };
4539 let w_n = (a - 1.0 - x_n) / 2.0;
4540
4541 let h_n = if t_n.abs() <= 0.1 && (w_n * t_n).abs() <= 0.1 {
4542 t_n + w_n * t_n * t_n
4543 } else {
4544 t_n
4545 };
4546
4547 x_n -= h_n;
4548 }
4549
4550 x_n
4551}
4552
4553#[allow(unused)]
4554pub(crate) fn inv_3x3_sig_matrix(mat: [[isize; 3]; 3]) -> [[isize; 3]; 3] {
4555 let denom = -mat[0][2] * mat[1][1] * mat[2][0]
4556 + mat[0][1] * mat[1][2] * mat[2][0]
4557 + mat[0][2] * mat[1][0] * mat[2][1]
4558 - mat[0][0] * mat[1][2] * mat[2][1]
4559 - mat[0][1] * mat[1][0] * mat[2][2]
4560 + mat[0][0] * mat[1][1] * mat[2][2];
4561 if denom != 1 && denom != -1 {
4562 panic!("Non invertible signature matrix.");
4563 }
4564 let mut inv_mat = [[0; 3]; 3];
4565 inv_mat[0][0] = (-mat[1][2] * mat[2][1] + mat[1][1] * mat[2][2]) * denom;
4566 inv_mat[0][1] = (mat[0][2] * mat[2][1] - mat[0][1] * mat[2][2]) * denom;
4567 inv_mat[0][2] = (-mat[0][2] * mat[1][1] + mat[0][1] * mat[1][2]) * denom;
4568 inv_mat[1][0] = (mat[1][2] * mat[2][0] - mat[1][0] * mat[2][2]) * denom;
4569 inv_mat[1][1] = (-mat[0][2] * mat[2][0] + mat[0][0] * mat[2][2]) * denom;
4570 inv_mat[1][2] = (mat[0][2] * mat[1][0] - mat[0][0] * mat[1][2]) * denom;
4571 inv_mat[2][0] = (-mat[1][1] * mat[2][0] + mat[1][0] * mat[2][1]) * denom;
4572 inv_mat[2][1] = (mat[0][1] * mat[2][0] - mat[0][0] * mat[2][1]) * denom;
4573 inv_mat[2][2] = (-mat[0][1] * mat[1][0] + mat[0][0] * mat[1][1]) * denom;
4574
4575 inv_mat
4576}
4577
4578pub mod index_vec;
4579
4580#[allow(unused)]
4581pub(crate) fn format_for_compare_digits(x: F<f64>, y: F<f64>) -> (String, String) {
4582 let mut string_x = format!("{:.16e}", x);
4583 let mut string_y = format!("{:.16e}", y);
4584
4585 #[allow(clippy::comparison_chain)]
4586 if string_x.len() > string_y.len() {
4587 for _ in 0..(string_x.len() - string_y.len()) {
4588 string_y.push(' ');
4589 }
4590 } else if string_y.len() > string_x.len() {
4591 for _ in 0..(string_y.len() - string_x.len()) {
4592 string_x.push(' ');
4593 }
4594 }
4595
4596 let string_vec = string_x
4597 .chars()
4598 .zip(string_y.chars())
4599 .map(|(char_x, char_y)| {
4600 if char_x == char_y {
4601 (char_x.to_string().green(), char_y.to_string().green())
4602 } else {
4603 (char_x.to_string().red(), char_y.to_string().red())
4604 }
4605 })
4606 .collect_vec();
4607
4608 let string_x = string_vec.iter().map(|(x, _)| x).join("");
4609 let string_y = string_vec.iter().map(|(_, y)| y).join("");
4610
4611 (string_x, string_y)
4612}
4613
4614#[allow(unused)]
4615pub(crate) fn format_evaluation_time(time: Duration) -> String {
4616 let seconds = time.as_secs_f64();
4617 let (value, unit) = if seconds >= 1.0 {
4618 (seconds, "s")
4619 } else if seconds >= 1.0e-3 {
4620 (seconds * 1.0e3, "ms")
4621 } else {
4622 (seconds * 1.0e6, "µs")
4623 };
4624
4625 let precision = if value >= 100.0 {
4626 0
4627 } else if value >= 10.0 {
4628 1
4629 } else {
4630 2
4631 };
4632
4633 format!("{value:.precision$} {unit}")
4634}
4635
4636pub(crate) fn duration_from_secs_f64_saturating(time: f64) -> Duration {
4637 if time.is_nan() || time <= 0.0 {
4638 return Duration::ZERO;
4639 }
4640
4641 if time.is_infinite() {
4642 return Duration::MAX;
4643 }
4644
4645 let max_seconds = Duration::MAX.as_secs();
4646 if time >= max_seconds as f64 {
4647 return Duration::MAX;
4648 }
4649
4650 let seconds = time.trunc() as u64;
4651 let fractional = (time - seconds as f64).clamp(0.0, 0.999_999_999_999);
4652 let nanos = (fractional * 1_000_000_000.0) as u32;
4653 Duration::new(seconds, nanos)
4654}
4655
4656pub(crate) fn format_evaluation_time_from_f64(time: f64) -> String {
4657 format_evaluation_time(duration_from_secs_f64_saturating(time))
4658}
4659
4660pub(crate) fn normalize_tabled_separator_rows(rendered: &str) -> String {
4661 let original_lines = rendered.lines().collect_vec();
4662 let visible_lines = original_lines
4663 .iter()
4664 .map(|line| strip_ansi_escape_codes(line).chars().collect_vec())
4665 .collect_vec();
4666
4667 original_lines
4668 .iter()
4669 .enumerate()
4670 .map(|(row_index, line)| {
4671 let visible_line = &visible_lines[row_index];
4672 if is_top_border_row(visible_line) {
4673 return rebuild_top_border_row(&visible_lines, row_index);
4674 }
4675
4676 if is_internal_separator_row(visible_line) {
4677 return rebuild_internal_separator_row(&visible_lines, row_index);
4678 }
4679
4680 if is_bottom_border_row(visible_line) {
4681 return rebuild_bottom_border_row(&visible_lines, row_index);
4682 }
4683
4684 (*line).to_string()
4685 })
4686 .join("\n")
4687}
4688
4689fn rebuild_top_border_row(visible_lines: &[Vec<char>], row_index: usize) -> String {
4690 rebuild_border_row(visible_lines, row_index, '╭', '╮', |_, _, has_below| {
4691 if has_below { '┬' } else { '─' }
4692 })
4693}
4694
4695fn rebuild_internal_separator_row(visible_lines: &[Vec<char>], row_index: usize) -> String {
4696 rebuild_border_row(
4697 visible_lines,
4698 row_index,
4699 '├',
4700 '┤',
4701 |row_index, column, has_below| match (
4702 row_index > 0 && has_vertical_border(&visible_lines[row_index - 1], column),
4703 has_below,
4704 ) {
4705 (false, false) => '─',
4706 (false, true) => '┬',
4707 (true, false) => '┴',
4708 (true, true) => '┼',
4709 },
4710 )
4711}
4712
4713fn rebuild_bottom_border_row(visible_lines: &[Vec<char>], row_index: usize) -> String {
4714 rebuild_border_row(
4715 visible_lines,
4716 row_index,
4717 '╰',
4718 '╯',
4719 |row_index, column, _| {
4720 if row_index > 0 && has_vertical_border(&visible_lines[row_index - 1], column) {
4721 '┴'
4722 } else {
4723 '─'
4724 }
4725 },
4726 )
4727}
4728
4729fn rebuild_border_row<F>(
4730 visible_lines: &[Vec<char>],
4731 row_index: usize,
4732 left: char,
4733 right: char,
4734 mut intersection_for_column: F,
4735) -> String
4736where
4737 F: FnMut(usize, usize, bool) -> char,
4738{
4739 let border = &visible_lines[row_index];
4740 if border.len() < 2 {
4741 return border.iter().collect();
4742 }
4743
4744 let last = border.len() - 1;
4745 let mut rebuilt = String::with_capacity(border.len());
4746 rebuilt.push(left);
4747
4748 for column in 1..last {
4749 let has_vertical_below = row_index + 1 < visible_lines.len()
4750 && has_vertical_border(&visible_lines[row_index + 1], column);
4751 rebuilt.push(intersection_for_column(
4752 row_index,
4753 column,
4754 has_vertical_below,
4755 ));
4756 }
4757
4758 rebuilt.push(right);
4759 rebuilt
4760}
4761
4762fn has_vertical_border(line: &[char], column: usize) -> bool {
4763 line.get(column)
4764 .copied()
4765 .is_some_and(|ch| matches!(ch, '│' | '├' | '┤' | '┬' | '┴' | '┼'))
4766}
4767
4768fn is_internal_separator_row(line: &[char]) -> bool {
4769 matches!(line.first(), Some('├')) && matches!(line.last(), Some('┤'))
4770}
4771
4772fn is_top_border_row(line: &[char]) -> bool {
4773 matches!(line.first(), Some('╭')) && matches!(line.last(), Some('╮'))
4774}
4775
4776fn is_bottom_border_row(line: &[char]) -> bool {
4777 matches!(line.first(), Some('╰')) && matches!(line.last(), Some('╯'))
4778}
4779
4780fn strip_ansi_escape_codes(line: &str) -> String {
4781 let mut stripped = String::with_capacity(line.len());
4782 let mut chars = line.chars().peekable();
4783
4784 while let Some(ch) = chars.next() {
4785 if ch == '\u{1b}' && chars.peek() == Some(&'[') {
4786 chars.next();
4787 for code in chars.by_ref() {
4788 if ('@'..='~').contains(&code) {
4789 break;
4790 }
4791 }
4792 continue;
4793 }
4794
4795 stripped.push(ch);
4796 }
4797
4798 stripped
4799}
4800
4801pub(crate) fn into_complex_ff64<T: FloatLike>(c: &Complex<F<T>>) -> Complex<F<f64>> {
4802 Complex::new(c.re.into_ff64(), c.im.into_ff64())
4803}
4804
4805#[test]
4806fn complex_compare() {
4807 let ltd = Complex::new(0.11773583919739394, -0.22157450463964778).map(F);
4808
4809 let cff = Complex::new(0.11773583589023458, -0.22157450446824836).map(F);
4810
4811 ltd.approx_eq_res(&cff, &F(0.00000001)).unwrap();
4812}
4813
4814#[cfg(test)]
4815mod formatting_tests {
4816 use super::{ArbPrec, F, FloatLike, f128};
4817
4818 fn assert_has_scientific_exponent(rendered: &str) {
4819 let (mantissa, exponent) = rendered
4820 .split_once('e')
4821 .unwrap_or_else(|| panic!("missing exponent marker in: {rendered}"));
4822 assert!(
4823 !mantissa.is_empty(),
4824 "missing mantissa in scientific output: {rendered}"
4825 );
4826 assert!(
4827 exponent.parse::<i32>().is_ok(),
4828 "invalid exponent in scientific output: {rendered}"
4829 );
4830 }
4831
4832 fn assert_fractional_digits(rendered: &str, expected_digits: usize) {
4833 let (mantissa, _) = rendered
4834 .split_once('e')
4835 .unwrap_or_else(|| panic!("missing exponent marker in: {rendered}"));
4836 let (_, fractional) = mantissa
4837 .trim_start_matches(['+', '-'])
4838 .split_once('.')
4839 .unwrap_or_else(|| panic!("missing decimal point in mantissa: {rendered}"));
4840 assert_eq!(
4841 fractional.len(),
4842 expected_digits,
4843 "unexpected number of fractional digits in: {rendered}"
4844 );
4845 }
4846
4847 fn assert_scientific_formatting_for_backend<T: FloatLike>(x: F<T>) {
4848 let plain = format!("{:e}", x.clone());
4849 assert_has_scientific_exponent(&plain);
4850
4851 let plus = format!("{:+e}", x.clone());
4852 assert_has_scientific_exponent(&plus);
4853 assert!(plus.starts_with('+') || plus.starts_with('-'));
4854
4855 let precision = format!("{:+.16e}", x.clone());
4856 assert_has_scientific_exponent(&precision);
4857 assert!(
4858 precision.contains('.'),
4859 "missing decimal point in scientific output: {precision}"
4860 );
4861
4862 let padded = format!("{:>24.6e}", x);
4863 assert!(
4864 padded.len() >= 24,
4865 "formatted width should be at least requested width: {padded}"
4866 );
4867 assert_has_scientific_exponent(padded.trim_start());
4868 }
4869
4870 #[test]
4871 fn display_matches_underlying_f64() {
4872 let value = F(12.5f64);
4873 assert_eq!(format!("{}", value), "12.5");
4874 }
4875
4876 #[test]
4877 fn lower_exp_keeps_exponent_with_precision() {
4878 let rendered = format!("{:+.16e}", F(1.23456789e-8f64));
4879 assert_has_scientific_exponent(&rendered);
4880 assert_fractional_digits(&rendered, 16);
4881 }
4882
4883 #[test]
4884 fn lower_exp_width_and_alignment_behave_as_expected() {
4885 let rendered = format!("{:>20.4e}", F(12.345f64));
4886 assert_eq!(rendered.len(), 20);
4887 assert_has_scientific_exponent(rendered.trim_start());
4888
4889 let left_aligned = format!("{:<20.4e}", F(12.345f64));
4890 assert_eq!(left_aligned.len(), 20);
4891 assert_has_scientific_exponent(left_aligned.trim_end());
4892 }
4893
4894 #[test]
4895 fn lower_exp_sign_and_zero_pad_behave_as_expected() {
4896 let positive = format!("{:+020.4e}", F(12.345f64));
4897 assert_eq!(positive.len(), 20);
4898 assert!(positive.starts_with('+'));
4899 assert_has_scientific_exponent(&positive);
4900
4901 let negative = format!("{:+020.4e}", F(-12.345f64));
4902 assert_eq!(negative.len(), 20);
4903 assert!(negative.starts_with('-'));
4904 assert_has_scientific_exponent(&negative);
4905 }
4906
4907 #[test]
4908 fn lower_exp_precision_controls_fractional_digits() {
4909 let p2 = format!("{:.2e}", F(1.0f64 / 3.0f64));
4910 assert_fractional_digits(&p2, 2);
4911
4912 let p9 = format!("{:.9e}", F(1.0f64 / 3.0f64));
4913 assert_fractional_digits(&p9, 9);
4914 }
4915
4916 #[test]
4917 fn lower_exp_works_across_float_backends() {
4918 assert_scientific_formatting_for_backend(F::<f64>::from_f64(1.23456789e-8));
4919 assert_scientific_formatting_for_backend(F::<f128>::from_f64(1.23456789e-8));
4920 assert_scientific_formatting_for_backend(F::<ArbPrec>::from_f64(1.23456789e-8));
4921 }
4922
4923 #[test]
4924 fn quad_square_matches_multiplication() {
4925 let zero = F::<f128>::from_f64(0.0);
4926 let zero_sq = zero.square();
4927 assert!(!zero_sq.is_nan());
4928 assert!(!zero_sq.is_infinite());
4929 assert_eq!(zero_sq, zero);
4930
4931 let x = F::<f128>::from_f64(500.0);
4932 let x_sq = x.square();
4933 assert!(!x_sq.is_nan());
4934 assert!(!x_sq.is_infinite());
4935 assert_eq!(x_sq, x * x);
4936 assert_eq!(x_sq.sqrt(), x);
4937 }
4938}
4939
4940pub mod symbols;
4941pub use symbols::{GS, W_};
4942
4943type TensorLibStore = RwLock<TensorLibrary<MixedTensor<F<f64>, ExplicitKey<Aind>>, Aind>>;
4944type FunLibStore =
4945 SymbolLib<RealOrComplexTensor<F<f64>, ShadowedStructure<Aind>>, PanicMissingConcrete>;
4946
4947pub static TENSORLIB: LazyLock<TensorLibStore> = LazyLock::new(|| RwLock::new(hep_lib_atom()));
4948
4949pub static FUN_LIB: LazyLock<FunLibStore> = LazyLock::new(|| {
4950 let mut lib = PanicMissingConcrete::new_lib();
4951 lib.insert(INBUILTS.conj, |a| match a {
4952 RealOrComplexTensor::Complex(c) => RealOrComplexTensor::Complex(c.map_data(|x| x.conj())),
4953 RealOrComplexTensor::Real(r) => RealOrComplexTensor::Real(r),
4954 });
4955 lib
4956});
4957
4958pub static PARAM_FUN_LIB: LazyLock<SymbolLib<ParamTensor<ShadowedStructure<Aind>>, Panic>> =
4959 LazyLock::new(|| {
4960 let mut lib = Panic::new_lib();
4961 lib.insert(INBUILTS.conj, |a: ParamTensor<ShadowedStructure<Aind>>| {
4962 a.map_data_self(|x| x.conj())
4963 });
4964 lib
4965 });
4966
4967pub static INT_FUN_LIB: LazyLock<
4968 SymbolLib<RealOrComplexTensor<i64, ShadowedStructure<Aind>>, PanicMissingConcrete>,
4969> = LazyLock::new(|| {
4970 let mut lib = PanicMissingConcrete::new_lib();
4971 lib.insert(INBUILTS.conj, |a| match a {
4972 RealOrComplexTensor::Complex(c) => RealOrComplexTensor::Complex(c.map_data(|x| x.conj())),
4973 RealOrComplexTensor::Real(r) => RealOrComplexTensor::Real(r),
4974 });
4975 lib
4976});
4977
4978pub static VAKINT: OnceLock<Result<Vakint>> = OnceLock::new();
4979
4980pub fn init_vakint() -> Result<()> {
4981 let r: &Result<Vakint, eyre::Report> =
4982 VAKINT.get_or_init(|| Vakint::new().map_err(|e| eyre!(e.to_string())));
4983
4984 match r {
4985 Ok(_) => Ok(()),
4986 Err(e) => Err(eyre!(e.to_string())),
4987 }
4988}
4989
4990pub fn vakint() -> Result<&'static Vakint> {
4991 let r = VAKINT
4992 .get()
4993 .ok_or_else(|| eyre!("Vakint not initialised"))?;
4994 match r {
4995 Ok(v) => Ok(v),
4996 Err(e) => Err(eyre!(e.to_string())),
4997 }
4998}
4999
5000impl<T: FloatLike> momtrop::float::MomTropFloat for F<T> {
5001 #[inline]
5002 fn PI(&self) -> Self {
5003 self.PI()
5004 }
5005
5006 #[inline]
5007 fn abs(&self) -> Self {
5008 self.abs()
5009 }
5010
5011 #[inline]
5012 fn cos(&self) -> Self {
5013 <F<T> as Real>::cos(self)
5014 }
5015
5016 #[inline]
5017 fn exp(&self) -> Self {
5018 <F<T> as Real>::exp(self)
5019 }
5020
5021 #[inline]
5022 fn one(&self) -> Self {
5023 <F<T> as SymFloatLike>::one(self)
5024 }
5025
5026 #[inline]
5027 fn from_f64(&self, value: f64) -> Self {
5028 F::from_f64(value)
5029 }
5030
5031 #[inline]
5032 fn from_isize(&self, value: isize) -> Self {
5033 Self(self.0.from_i64(value as i64))
5034 }
5035
5036 #[inline]
5037 fn inv(&self) -> Self {
5038 <F<T> as SymFloatLike>::inv(self)
5039 }
5040
5041 #[inline]
5042 fn ln(&self) -> Self {
5043 <F<T> as Real>::log(self)
5044 }
5045
5046 #[inline]
5047 fn powf(&self, power: &Self) -> Self {
5048 <F<T> as Real>::powf(self, power)
5049 }
5050
5051 #[inline]
5052 fn sin(&self) -> Self {
5053 <F<T> as Real>::sin(self)
5054 }
5055
5056 #[inline]
5057 fn sqrt(&self) -> Self {
5058 <F<T> as Real>::sqrt(self)
5059 }
5060
5061 #[inline]
5062 fn zero(&self) -> Self {
5063 <F<T> as SymFloatLike>::zero(self)
5064 }
5065
5066 #[inline]
5067 fn to_f64(&self) -> f64 {
5068 self.into_f64()
5069 }
5070}
5071
5072pub trait Length {
5073 fn len(&self) -> usize;
5074 fn is_empty(&self) -> bool {
5075 self.len() == 0
5076 }
5077}
5078
5079impl<I, T> Length for TiVec<I, T> {
5080 fn len(&self) -> usize {
5081 self.len()
5082 }
5083
5084 fn is_empty(&self) -> bool {
5085 self.is_empty()
5086 }
5087}
5088
5089impl<T> Length for Vec<T> {
5090 fn len(&self) -> usize {
5091 self.len()
5092 }
5093}
5094
5095pub(crate) fn ose_atom_from_index(index: EdgeIndex) -> Atom {
5096 function!(
5097 GS.ose,
5098 usize::from(index) as i64 )
5100}
5101
5102pub(crate) fn cut_energy(index: EdgeIndex) -> Atom {
5103 function!(GS.energy, usize::from(index) as i64)
5104}
5105
5106pub(crate) fn external_energy_atom_from_index(index: EdgeIndex) -> Atom {
5107 GS.emr_mom(index, Atom::from(ExpandedIndex::from_iter([0])))
5108}
5109
5110pub mod newton_solver;
5111use include_dir::{Dir, include_dir};
5112static BUILTIN_MODELS: Dir = include_dir!("$CARGO_MANIFEST_DIR/../../assets/models/json");
5113
5114pub fn load_generic_model(name: &str) -> Model {
5115 if let Some(file) = BUILTIN_MODELS.get_file(format!("{}/{}.json", name, name)) {
5116 Model::from_str(file.contents_utf8().unwrap().into(), "json").unwrap()
5117 } else {
5118 panic!("Model {} not found in built-in models.", name);
5119 }
5120}
5121
5122#[cfg(test)]
5123pub mod test_utils;
5124
5125#[cfg(feature = "python_api")]
5126impl<'py> pyo3::IntoPyObject<'py> for F<f64> {
5127 type Target = pyo3::types::PyFloat;
5128 type Output = pyo3::Bound<'py, Self::Target>;
5129 type Error = std::convert::Infallible;
5130
5131 #[inline]
5132 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
5133 Ok(pyo3::types::PyFloat::new(py, self.0))
5134 }
5135}
5136
5137#[cfg(feature = "python_api")]
5138impl<'py> pyo3::IntoPyObject<'py> for &F<f64> {
5139 type Target = pyo3::types::PyFloat;
5140 type Output = pyo3::Bound<'py, Self::Target>;
5141 type Error = std::convert::Infallible;
5142
5143 #[inline]
5144 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
5145 (*self).into_pyobject(py)
5146 }
5147}
5148
5149#[cfg(feature = "python_api")]
5150impl<'a, 'py> pyo3::FromPyObject<'a, 'py> for F<f64> {
5151 type Error = pyo3::PyErr;
5152
5153 fn extract(obj: pyo3::Borrowed<'a, 'py, pyo3::types::PyAny>) -> Result<Self, Self::Error> {
5154 obj.extract::<f64>().map(F)
5155 }
5156}