1use crate::integrands::process::{
2 GenericEvaluatorFloat, LmbMultiChannelingSetup, SamplingChannelBridge,
3 sampling::maps::SamplingEvaluationError,
4};
5use crate::model::Model;
6use crate::momentum::sample::{
7 ExternalFourMomenta, ExternalIndex, ExternalThreeMomenta, LoopMomenta, SubspaceData,
8};
9use crate::momentum::signature::{ExternalSignature, LoopSignature};
10use crate::momentum::{FourMomentum, ThreeMomentum};
11use crate::numerator::aind::Aind;
12use crate::numerator::ufo::UFO;
13use crate::settings::runtime::ParameterizationSettings;
14use crate::settings::runtime::SamplingSettings;
15use crate::settings::runtime::kinematic::Externals;
16use crate::settings::runtime::{ParameterizationMapping, ParameterizationMode};
17use crate::utils::hyperdual_utils::new_constant;
18
19use bincode::{Decode, Encode};
20use colored::Colorize;
21use idenso::representations::initialize;
22use itertools::Itertools;
23use linnet::half_edge::involution::EdgeIndex;
24
25use rand::Rng;
26use ref_ops::{RefAdd, RefDiv, RefMul, RefNeg, RefRem, RefSub};
27use rug::float::{Constant, ParseFloatError, Round};
28use rug::ops::{CompleteRound, Pow};
29use rug::{Assign, Float};
30use schemars::JsonSchema;
31use serde::{Deserialize, Deserializer, Serialize};
32use smallvec::SmallVec;
33use spenso::algebra::algebraic_traits::RefOne;
34use spenso::algebra::algebraic_traits::RefZero;
35use spenso::algebra::complex::Complex;
36use spenso::algebra::complex::R;
37use spenso::algebra::complex::SymbolicaComplex;
38use spenso::algebra::complex::symbolica_traits::ToFloat;
39use spenso::algebra::upgrading_arithmetic::TrySmallestUpgrade;
40use spenso::network::library::TensorLibraryData;
41use spenso::network::library::function_lib::{INBUILTS, Panic, SymbolLib, Wrap};
42use spenso::network::library::symbolic::{ExplicitKey, TensorLibrary};
43use spenso::network::parsing::ShadowedStructure;
44use spenso::structure::concrete_index::ExpandedIndex;
45use spenso::tensors::data::StorageTensor;
46use spenso::tensors::parametric::ParamTensor;
47use spenso::tensors::parametric::to_param::ToAtom;
48use spenso_hep_lib::hep_lib_atom;
49use symbolica::{
50 domains::{
51 dual::HyperDual,
52 float::{FixedPrecision, Float as SymbolicaFloat, FloatLike as SymFloatLike},
53 },
54 prelude::*,
55};
56
57use std::cell::RefCell;
58use std::cmp::{Ord, Ordering};
59use std::collections::HashMap;
60use std::fmt::{Debug, Display};
61use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign};
62use std::str::FromStr;
63use std::sync::{LazyLock, OnceLock, RwLock};
64use std::time::Duration;
65
66use vakint::Vakint;
67use crate::MAX_LOOP;
74use ::tracing::debug;
75use typed_index_collections::TiVec;
76
77#[derive(Clone, Debug, PartialEq)]
80pub struct RuntimeCache<T>(Option<T>);
81
82impl<T> Default for RuntimeCache<T> {
83 fn default() -> Self {
84 Self(None)
85 }
86}
87
88impl<T> RuntimeCache<T> {
89 pub(crate) fn invalidate(&mut self) {
90 self.0 = None;
91 }
92
93 pub(crate) fn set(&mut self, value: T) {
94 self.0 = Some(value);
95 }
96
97 pub(crate) fn take(&mut self) -> Option<T> {
98 self.0.take()
99 }
100
101 pub(crate) fn as_ref(&self) -> Option<&T> {
102 self.0.as_ref()
103 }
104
105 pub(crate) fn as_mut(&mut self) -> Option<&mut T> {
106 self.0.as_mut()
107 }
108}
109
110impl<T> bincode::Encode for RuntimeCache<T> {
111 fn encode<E: bincode::enc::Encoder>(
112 &self,
113 _encoder: &mut E,
114 ) -> std::result::Result<(), bincode::error::EncodeError> {
115 Ok(())
116 }
117}
118
119impl<C, T> bincode::Decode<C> for RuntimeCache<T> {
120 fn decode<D: bincode::de::Decoder<Context = C>>(
121 _decoder: &mut D,
122 ) -> std::result::Result<Self, bincode::error::DecodeError> {
123 Ok(Self::default())
124 }
125}
126
127pub const GIT_VERSION: &str = env!("CARGO_PKG_VERSION");
128pub const VERSION: &str = "0.0.1";
129pub mod fitting;
130pub mod hyperdual_utils;
131pub mod progress;
132pub mod representations;
133pub mod serde_utils;
134pub use progress::{long_running_progress_style, long_running_progress_style_with_eta_warmup};
135#[macro_export]
137macro_rules! disable {
138 ($($tokens:tt)*) => {};
139}
140
141#[macro_export]
236macro_rules! debug_tags {
237 (@collect_tags [$($acc:tt)*] # $tag:ident, $($tail:tt)*) => {
238 $crate::debug_tags!(@collect_tags [$($acc)* $tag = true,] $($tail)*)
239 };
240 (@collect_tags [$($acc:tt)*] # $tag:ident; $($rest:tt)*) => {
241 $crate::debug_tags!(@collect_fields [$($acc)* $tag = true,] [] $($rest)*)
242 };
243 (@collect_fields [$($tags:tt)*] [$($fields:tt)*] log.$name:ident = $value:expr, $($tail:tt)*) => {
244 $crate::debug_tags!(@collect_fields
245 [$($tags)*]
246 [$($fields)*
247 display.$name = %$crate::LogMessage::log_display(&$value),
248 file.$name = %$crate::LogMessage::log_file(&$value),
249 ]
250 $($tail)*
251 )
252 };
253 (@collect_fields [$($tags:tt)*] [$($fields:tt)*] $prefix:ident.$name:ident = %$value:expr, $($tail:tt)*) => {
254 $crate::debug_tags!(@collect_fields [$($tags)*] [$($fields)* $prefix.$name = %$value,] $($tail)*)
255 };
256 (@collect_fields [$($tags:tt)*] [$($fields:tt)*] $prefix:ident.$name:ident = ?$value:expr, $($tail:tt)*) => {
257 $crate::debug_tags!(@collect_fields [$($tags)*] [$($fields)* $prefix.$name = ?$value,] $($tail)*)
258 };
259 (@collect_fields [$($tags:tt)*] [$($fields:tt)*] $prefix:ident.$name:ident = $value:expr, $($tail:tt)*) => {
260 $crate::debug_tags!(@collect_fields [$($tags)*] [$($fields)* $prefix.$name = $value,] $($tail)*)
261 };
262 (@collect_fields [$($tags:tt)*] [$($fields:tt)*] $name:ident = %$value:expr, $($tail:tt)*) => {
263 $crate::debug_tags!(@collect_fields [$($tags)*] [$($fields)* $name = %$value,] $($tail)*)
264 };
265 (@collect_fields [$($tags:tt)*] [$($fields:tt)*] $name:ident = ?$value:expr, $($tail:tt)*) => {
266 $crate::debug_tags!(@collect_fields [$($tags)*] [$($fields)* $name = ?$value,] $($tail)*)
267 };
268 (@collect_fields [$($tags:tt)*] [$($fields:tt)*] $name:ident = $value:expr, $($tail:tt)*) => {
269 $crate::debug_tags!(@collect_fields [$($tags)*] [$($fields)* $name = $value,] $($tail)*)
270 };
271 (@collect_fields [$($tags:tt)*] [$($fields:tt)*] $name:ident, $($tail:tt)*) => {
272 $crate::debug_tags!(@collect_fields [$($tags)*] [$($fields)* $name,] $($tail)*)
273 };
274 (@collect_fields [$($tags:tt)*] [$($fields:tt)*] $($rest:tt)+) => {
275 tracing::debug!($($tags)* $($fields)* $($rest)+)
276 };
277 ($($input:tt)*) => {
278 $crate::debug_tags!(@collect_tags [] $($input)*)
279 };
280}
281
282#[allow(unused)]
283const MAX_DIMENSION: usize = MAX_LOOP * 3;
284
285pub(crate) const ESURFACE_SHIFT_THRESHOLD: f64 = 1.0e-13;
286pub const DEFAULT_ESURFACE_EXISTENCE_THRESHOLD: f64 = 1.0e-7;
288
289pub const LEFT: usize = 0;
290pub const RIGHT: usize = 1;
291
292#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
293pub enum Side {
294 LEFT = 0,
295 RIGHT = 1,
296}
297
298impl From<Side> for usize {
299 fn from(val: Side) -> Self {
300 val as usize
301 }
302}
303
304pub mod linnet_ext;
305pub mod symbolica_ext;
306pub mod tracing;
307
308#[cfg(test)]
309mod tests {
310 #[test]
311 fn native_hypot_preserves_finite_range() {
312 use super::{ArbPrec, F};
313
314 for scale in [1e-200, 1.0, 1e200] {
315 for sign in [-1.0, 1.0] {
316 let norm = F(sign * 3.0 * scale).hypot(&F(4.0 * scale)).0;
317 assert!(norm.is_finite());
318 assert!((norm / scale - 5.0).abs() < 1e-14);
319 }
320 }
321 assert_eq!(F(0.0).hypot(&F(-0.0)), F(0.0));
322 assert!(F(f64::NAN).hypot(&F(1.0)).0.is_nan());
323 assert!(F(1.0).hypot(&F(f64::NAN)).0.is_nan());
324 assert!(F(f64::INFINITY).hypot(&F(1.0)).0.is_infinite());
325 assert!(F(f64::MAX).hypot(&F(f64::MAX)).0.is_infinite());
326
327 let one = F::<ArbPrec>::default().one();
329 for exponent in [-2000, 2000] {
330 let scale = one.from_i64(2).powi(exponent);
331 let norm = (&scale * one.from_i64(3)).hypot(&(&scale * one.from_i64(4)));
332 assert!((norm / scale - one.from_i64(5)).abs() < one.from_i64(10).powi(-50));
333 }
334 }
335
336 #[test]
337 fn canonical_arb_materialization_preserves_native_bits_and_checks_range() {
338 use super::{ArbPrec, F, FloatLike, QuadFloat, SamplingEvaluationError};
339 use rug::Float;
340
341 let low = Float::with_val(1000, 1) >> 400;
342 let source = ArbPrec {
343 float: Float::with_val(1000, 1) + &low,
344 };
345 let quad = F::<QuadFloat>::from_arb(&source).unwrap();
346 let (lower, upper) = quad.0.mpfr_enclosure(1000);
347 assert_eq!(lower, source.float);
348 assert_eq!(upper, source.float);
349 assert_eq!(
350 F::<ArbPrec>::from_arb(&source).unwrap().0.float,
351 source.float
352 );
353 assert_eq!(F::<f64>::from_arb(&source).unwrap().0, 1.0);
354
355 let one = F::<ArbPrec>::default().one();
356 let large = one.from_i64(2).powi(2000);
357 for value in [large.clone(), -large.clone(), large.inv(), -large.inv()] {
358 assert!(value.0.float.is_finite() && value != one.zero());
359 for error in [
360 F::<f64>::from_arb(&value.0).unwrap_err(),
361 F::<QuadFloat>::from_arb(&value.0).unwrap_err(),
362 ] {
363 assert!(matches!(
364 error.downcast_ref::<SamplingEvaluationError>(),
365 Some(SamplingEvaluationError::Unrepresentable { .. })
366 ));
367 }
368 assert_eq!(F::<ArbPrec>::from_arb(&value.0).unwrap(), value);
369 }
370 assert_eq!(F::<f64>::from_arb(&one.zero().0).unwrap().0, 0.0);
371 let invalid = ArbPrec {
372 float: Float::with_val(1000, f64::NAN),
373 };
374 assert!(matches!(
375 F::<ArbPrec>::from_arb(&invalid)
376 .unwrap_err()
377 .downcast_ref::<SamplingEvaluationError>(),
378 Some(SamplingEvaluationError::Unrepresentable { .. })
379 ));
380 }
381
382 #[test]
383 fn canonical_native_embedding_requires_finite_singleton_enclosures() {
384 use super::{F, QuadFloat, SamplingEvaluationError};
385 use rug::Float;
386 use symbolica::domains::float::DoubleFloat;
387 for sign in [-1.0, 1.0] {
388 let low = sign * 2.0_f64.powi(-400);
389 let native = F(QuadFloat::from(DoubleFloat::from_compensated_sum(
390 sign, low,
391 )));
392 let exact = Float::with_val(1000, sign) + Float::with_val(1000, low);
393 assert_eq!(native.to_arb_exact().unwrap().0.float, exact);
394 let separated = F(QuadFloat::from(DoubleFloat::from_compensated_sum(
397 sign,
398 sign * f64::from_bits(1),
399 )));
400 assert!(matches!(
401 separated
402 .to_arb_exact()
403 .unwrap_err()
404 .downcast_ref::<SamplingEvaluationError>(),
405 Some(SamplingEvaluationError::Unrepresentable {
406 operation: "canonical source embedding",
407 ..
408 })
409 ));
410 }
411 for value in [0.0, -0.0, f64::from_bits(1), f64::MAX] {
412 assert_eq!(
413 F(value).to_arb_exact().unwrap().0.float,
414 Float::with_val(1000, value)
415 );
416 }
417 assert!(F(-0.0).to_arb_exact().unwrap().0.float.is_sign_negative());
418 for zero in [0.0, -0.0] {
419 let native = F(QuadFloat::from_f64_exact_binary(zero));
420 assert_eq!(
421 native.to_arb_exact().unwrap().0.float.is_sign_negative(),
422 native.0.as_f64().is_sign_negative()
423 );
424 }
425 for value in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
426 assert!(F(value).to_arb_exact().is_err());
427 }
428 }
429
430 #[test]
431 fn fixed256_source_scalar_embeds_exactly_without_binary64_range_limits() {
432 use super::{ArbPrec, F, FloatLike, SamplingFloat, SamplingPrecision};
433 use crate::settings::runtime::Precision;
434 use rug::Float;
435
436 assert_eq!(
437 SamplingFloat::sampling_precision(),
438 SamplingPrecision::Fixed256
439 );
440 for (physical, sampling) in [
441 (Precision::Double, SamplingPrecision::Double),
442 (Precision::Quad, SamplingPrecision::Quad),
443 (Precision::Arb, SamplingPrecision::Arb),
444 ] {
445 assert_eq!(SamplingPrecision::from(physical), sampling);
446 }
447 let one = F::<SamplingFloat>::default().one();
448 assert_eq!(one.epsilon(), one.from_i64(2).powi(-255));
449 for exponent in [-4096, -255, 0, 255, 4096] {
450 for sign in [-1, 1] {
451 let native = one.from_i64(sign)
452 * one.from_i64(2).powi(exponent)
453 * (&one + one.from_i64(2).powi(-254));
454 let promoted = native.to_arb_exact().unwrap();
455 assert_eq!(promoted.0.float, native.0.float);
456 assert_eq!(F::<SamplingFloat>::from_arb(&promoted.0).unwrap(), native);
457 assert_eq!(promoted.0.float.prec(), 1000);
458 assert_eq!(native.0.float.prec(), 256);
459 }
460 }
461 for zero in [0.0, -0.0] {
462 let native = F(SamplingFloat::from_f64_exact_binary(zero));
463 assert_eq!(
464 native.to_arb_exact().unwrap().0.float.is_sign_negative(),
465 zero.is_sign_negative()
466 );
467 }
468 for invalid in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
469 assert!(
470 F(SamplingFloat::from(Float::with_val(256, invalid)))
471 .to_arb_exact()
472 .is_err()
473 );
474 }
475 let physical =
478 F::<ArbPrec>::default().one() + F::<ArbPrec>::default().from_i64(2).powi(-300);
479 assert_eq!(F::<SamplingFloat>::from_arb(&physical.0).unwrap(), one);
480 assert_ne!(physical, one.to_arb_exact().unwrap());
481 }
482
483 #[test]
484 fn fixed_mpfr_rational_coefficients_are_rounded_once() {
485 use super::VarFloat;
486 use rug::{Float, Integer, Rational};
487 use symbolica::domains::rational::Rational as SymbolicaRational;
488
489 fn check<const N: u32>() {
490 for (numerator, denominator) in [(0, 7), (1, 7), (-7, 3), (7, -3), (-7, -3)] {
491 let exact = Rational::from((numerator, denominator));
492 let rational = SymbolicaRational::from(exact.clone());
493 let actual = VarFloat::<N>::from(&rational);
494 assert_eq!(actual.float, Float::with_val(N, exact));
495 assert_eq!(actual.float.prec(), N);
496 }
497 for bits in [80, N] {
501 let power = Integer::from(1) << bits;
502 for sign in [-1, 1] {
503 let exact = Rational::from(((power.clone() + 3) * sign, power.clone() + 1));
504 let rational = SymbolicaRational::from(exact.clone());
505 let actual = VarFloat::<N>::from(&rational);
506 assert_eq!(actual.float, Float::with_val(N, exact));
507 if bits == N {
508 let expected =
509 (Float::with_val(N, 1) + (Float::with_val(N, 1) >> (N - 1))) * sign;
510 assert_eq!(actual.float, expected);
511 }
512 }
513 }
514 }
515 check::<256>();
516 check::<1000>();
517 }
518
519 #[test]
520 fn fixed_mpfr_symbolica_inputs_use_declared_precision_for_later_arithmetic() {
521 use super::{SymbolicaFloat, VarFloat};
522 use rug::Float;
523
524 fn check<const N: u32>() {
525 for input_precision in [53, N + 31] {
526 let input = Float::with_val(input_precision, 1);
527 let native = VarFloat::<N>::from(SymbolicaFloat::from(input.clone()));
528 assert_eq!(native.float.prec(), N);
529 assert_eq!(native.float, input);
530 let small = VarFloat::<N>::from(Float::with_val(N, 1) >> 200_u32);
533 let expected = Float::with_val(N, 1) + &small.float;
534 assert_eq!((native + small).float, expected);
535 }
536 let precise = Float::with_val(N + 31, 1) + (Float::with_val(N + 31, 1) >> (N + 10));
537 let native = VarFloat::<N>::from(SymbolicaFloat::from(precise.clone()));
538 assert_eq!(native.float, Float::with_val(N, precise));
539 assert_eq!(native.float.prec(), N);
540 for value in [0.0, -0.0, f64::INFINITY, f64::NEG_INFINITY, f64::NAN] {
541 let native = VarFloat::<N>::from(SymbolicaFloat::from(Float::with_val(53, value)));
542 assert_eq!(native.float.prec(), N);
543 if value.is_nan() {
544 assert!(native.float.is_nan());
545 } else {
546 assert_eq!(native.float, value);
547 assert_eq!(native.float.is_sign_negative(), value.is_sign_negative());
548 }
549 }
550 }
551 check::<256>();
552 check::<1000>();
553 }
554
555 #[test]
556 fn canonical_scalar_relative_budget_is_directed_and_signed() {
557 use super::{ArbPrec, F, FloatLike, QuadFloat, SamplingEvaluationError};
558 fn check<T: FloatLike>() {
559 let one = F::<ArbPrec>::default().one();
560 let source = &one - one.from_i64(2).powi(-54);
561 let tolerance = 2.0_f64.powi(-54);
562 let loose = f64::from_bits(tolerance.to_bits() + 1);
563 for sign in [-1, 1] {
564 let native = F::<T>::default().from_i64(sign);
565 let source = &source * one.from_i64(sign);
566 let error = native
569 .verify_arb_materialization(&source.0, tolerance)
570 .unwrap_err();
571 assert!(matches!(
572 error.downcast_ref::<SamplingEvaluationError>(),
573 Some(SamplingEvaluationError::UncertainGeometry { .. })
574 ));
575 native.verify_arb_materialization(&source.0, loose).unwrap();
576 }
577 let zero = F::<T>::default().zero();
578 zero.verify_arb_materialization(&one.zero().0, tolerance)
579 .unwrap();
580 assert!(
581 zero.one()
582 .verify_arb_materialization(&one.zero().0, tolerance)
583 .is_err()
584 );
585 for budget in [0.0, -1.0, f64::INFINITY, f64::NAN] {
586 assert!(
587 zero.verify_arb_materialization(&one.zero().0, budget)
588 .is_err()
589 );
590 }
591 }
592 check::<f64>();
593 check::<QuadFloat>();
594 check::<ArbPrec>();
595 }
596
597 #[test]
598 fn native_mpfr_enclosures_preserve_separated_limbs_and_signs() {
599 use super::{ArbPrec, FloatLike, QuadFloat};
600 use rug::Float;
601 use symbolica::domains::float::DoubleFloat;
602
603 for sign in [-1.0, 1.0] {
604 let low = sign * 2.0_f64.powi(-400);
605 let quad = QuadFloat(DoubleFloat::from_compensated_sum(sign, low));
606 let exact = Float::with_val(512, sign) + Float::with_val(512, low);
607 for precision in [24, 106, 512] {
608 let (lower, upper) = quad.mpfr_enclosure(precision);
609 assert!(lower <= exact && upper >= exact);
610 if precision == 512 {
611 assert_eq!(lower, exact);
612 assert_eq!(upper, exact);
613 } else {
614 assert!(
615 lower < upper,
616 "a separated limb cannot disappear from a certificate"
617 );
618 }
619 let arb = ArbPrec {
620 float: exact.clone(),
621 };
622 let (lower, upper) = arb.mpfr_enclosure(precision);
623 assert!(lower <= exact && upper >= exact);
624 let native = sign * 1.23456789012345;
625 let (lower, upper) = native.mpfr_enclosure(precision);
626 let exact_native = Float::with_val(53, native);
627 assert!(lower <= exact_native && upper >= exact_native);
628 }
629 }
630 }
631
632 #[test]
633 fn normalized_h_functions_integrate_to_one() {
634 use super::{F, h, h_dual, hyperdual_utils::new_constant};
635 use crate::settings::runtime::{HFunction, HFunctionSettings};
636 use symbolica::domains::dual::HyperDual;
637
638 let template = HyperDual::from_values(vec![vec![0], vec![1]], vec![F(1.0), F(0.0)]);
639 let step = 0.01;
642 for function in [
643 HFunction::Exponential,
644 HFunction::PolyExponential,
645 HFunction::PolyLeftRightExponential,
646 ] {
647 for power in [None, Some(1), Some(4), Some(16)] {
649 let settings = HFunctionSettings {
650 function: function.clone(),
651 sigma: 1.5,
652 power,
653 ..Default::default()
654 };
655 let integral = (-4000..=4000)
656 .map(|n| {
657 let t = F(settings.sigma * (n as f64 * step).exp());
658 let value = h(&t, None, None, &settings);
659 let dual = h_dual(&new_constant(&template, &t), None, None, &settings);
660 let scale = value.0.abs().max(1.0);
662 assert!((dual.values[0].0 - value.0).abs() <= 1e-14 * scale);
663 value.0 * t.0 * step
664 })
665 .sum::<f64>();
666 assert!(
667 (integral - 1.0).abs() < 1e-12,
668 "{settings:?} integrates to {integral}"
669 );
670 }
671 }
672 }
673
674 #[test]
675 fn debug_tags_macro_supports_tags_and_regular_fields() {
676 crate::debug_tags!(#integration, #summary;
677 inspect = false,
678 answer = 42,
679 "macro compiles with tag shorthands and regular fields"
680 );
681 }
682}
683
684pub trait FloatConvertFrom<U> {
685 fn convert_from(x: &U) -> Self;
686}
687
688#[derive(Debug, Clone, PartialEq, PartialOrd, Encode, Decode)]
706pub struct VarFloat<const N: u32> {
707 #[bincode(with_serde)]
708 float: rug::Float,
709}
710
711impl<const N: u32> Serialize for VarFloat<N> {
712 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
713 where
714 S: serde::Serializer,
715 {
716 let string = self.float.to_string();
717 string.serialize(serializer)
718 }
719}
720
721impl<'de, const N: u32> Deserialize<'de> for VarFloat<N> {
722 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
723 where
724 D: Deserializer<'de>,
725 {
726 let string: String = serde::Deserialize::deserialize(deserializer)?;
727 let val: Self = string
728 .parse()
729 .unwrap_or_else(|_| panic!("failed to parse arb prec from string: {}", string));
730
731 Ok(val)
732 }
733}
734
735impl<const N: u32> FromStr for VarFloat<N> {
736 type Err = ParseFloatError;
737 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
738 let float = rug::Float::parse(s)?;
739 Ok(Self {
740 float: rug::Float::with_val(N, float),
741 })
742 }
743}
744
745#[derive(
746 Debug,
747 Clone,
748 Copy,
749 Default,
750 PartialEq,
751 PartialOrd,
752 Eq,
753 Hash,
754 Serialize,
755 Deserialize,
756 Encode,
757 Decode,
758)]
759#[repr(transparent)]
760pub struct QuadFloat(DoubleFloat);
761
762impl std::fmt::Display for QuadFloat {
763 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
764 std::fmt::Display::fmt(&self.0, f)
765 }
766}
767
768impl std::fmt::LowerExp for QuadFloat {
769 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
770 std::fmt::LowerExp::fmt(&self.0, f)
771 }
772}
773
774impl From<DoubleFloat> for QuadFloat {
775 fn from(value: DoubleFloat) -> Self {
776 Self(value)
777 }
778}
779
780impl From<QuadFloat> for DoubleFloat {
781 fn from(value: QuadFloat) -> Self {
782 value.0
783 }
784}
785
786impl From<&QuadFloat> for DoubleFloat {
787 fn from(value: &QuadFloat) -> Self {
788 value.0
789 }
790}
791
792impl From<SymbolicaFloat> for QuadFloat {
793 fn from(value: SymbolicaFloat) -> Self {
794 Self(value.to_double_float())
795 }
796}
797
798impl From<Float> for QuadFloat {
799 fn from(value: Float) -> Self {
800 Self(SymbolicaFloat::from(value).to_double_float())
801 }
802}
803
804impl From<&Rational> for QuadFloat {
805 fn from(value: &Rational) -> Self {
806 Self(value.into())
807 }
808}
809
810impl<const N: u32> Rem<&VarFloat<N>> for &VarFloat<N> {
811 type Output = VarFloat<N>;
812
813 fn rem(self, rhs: &VarFloat<N>) -> Self::Output {
814 (&self.float % &rhs.float).complete(N).into()
815 }
816}
817
818impl<const N: u32> From<Float> for VarFloat<N> {
819 fn from(x: Float) -> Self {
820 VarFloat {
821 float: rug::Float::with_val(N, x),
822 }
823 }
824}
825
826impl<const N: u32> From<&Rational> for VarFloat<N> {
827 fn from(x: &Rational) -> Self {
828 Self::from(x.to_multi_prec_float(N))
831 }
832}
833
834macro_rules! impl_quad_binary_op {
835 ($trait:ident, $fn:ident, $op:tt) => {
836 impl std::ops::$trait for QuadFloat {
837 type Output = Self;
838
839 fn $fn(self, rhs: Self) -> Self::Output {
840 Self(self.0 $op rhs.0)
841 }
842 }
843
844 impl std::ops::$trait<&QuadFloat> for QuadFloat {
845 type Output = Self;
846
847 fn $fn(self, rhs: &Self) -> Self::Output {
848 Self(self.0 $op rhs.0)
849 }
850 }
851
852 impl std::ops::$trait<QuadFloat> for &QuadFloat {
853 type Output = QuadFloat;
854
855 fn $fn(self, rhs: QuadFloat) -> Self::Output {
856 QuadFloat(self.0 $op rhs.0)
857 }
858 }
859
860 impl std::ops::$trait<&QuadFloat> for &QuadFloat {
861 type Output = QuadFloat;
862
863 fn $fn(self, rhs: &QuadFloat) -> Self::Output {
864 QuadFloat(self.0 $op rhs.0)
865 }
866 }
867 };
868}
869
870macro_rules! impl_quad_assign_op {
871 ($trait:ident, $fn:ident, $op:tt) => {
872 impl std::ops::$trait for QuadFloat {
873 fn $fn(&mut self, rhs: Self) {
874 self.0 $op rhs.0;
875 }
876 }
877
878 impl std::ops::$trait<&QuadFloat> for QuadFloat {
879 fn $fn(&mut self, rhs: &Self) {
880 self.0 $op rhs.0;
881 }
882 }
883 };
884}
885
886impl_quad_binary_op!(Add, add, +);
887impl_quad_binary_op!(Sub, sub, -);
888impl_quad_binary_op!(Mul, mul, *);
889impl_quad_binary_op!(Div, div, /);
890
891impl_quad_assign_op!(AddAssign, add_assign, +=);
892impl_quad_assign_op!(SubAssign, sub_assign, -=);
893impl_quad_assign_op!(MulAssign, mul_assign, *=);
894impl_quad_assign_op!(DivAssign, div_assign, /=);
895
896impl Neg for QuadFloat {
897 type Output = Self;
898
899 fn neg(self) -> Self::Output {
900 Self(-self.0)
901 }
902}
903
904impl Neg for &QuadFloat {
905 type Output = QuadFloat;
906
907 fn neg(self) -> Self::Output {
908 QuadFloat(-self.0)
909 }
910}
911
912impl Rem<&QuadFloat> for &QuadFloat {
913 type Output = QuadFloat;
914
915 fn rem(self, rhs: &QuadFloat) -> Self::Output {
916 self.truncating_remainder(rhs)
917 }
918}
919
920impl<const N: u32> std::ops::Mul for VarFloat<N> {
921 type Output = Self;
922
923 fn mul(self, rhs: Self) -> Self::Output {
924 (self.float * rhs.float).into()
925 }
926}
927
928impl<const N: u32> std::ops::Mul<&VarFloat<N>> for VarFloat<N> {
929 type Output = Self;
930
931 fn mul(self, rhs: &Self) -> Self::Output {
932 (self.float * &rhs.float).into()
933 }
934}
935
936impl<const N: u32> std::ops::Mul<VarFloat<N>> for &VarFloat<N> {
937 type Output = VarFloat<N>;
938
939 fn mul(self, rhs: VarFloat<N>) -> Self::Output {
940 (&self.float * &rhs.float).complete(N).into()
941 }
942}
943
944impl<const N: u32> std::ops::Mul<&VarFloat<N>> for &VarFloat<N> {
945 type Output = VarFloat<N>;
946
947 fn mul(self, rhs: &VarFloat<N>) -> Self::Output {
948 (&self.float * &rhs.float).complete(N).into()
949 }
950}
951
952impl<const N: u32> std::ops::MulAssign for VarFloat<N> {
953 fn mul_assign(&mut self, rhs: Self) {
954 self.float *= rhs.float;
955 self.float.set_prec(N);
956 }
957}
958
959impl<const N: u32> std::ops::MulAssign<&VarFloat<N>> for VarFloat<N> {
960 fn mul_assign(&mut self, rhs: &Self) {
961 self.float *= &rhs.float;
962 self.float.set_prec(N);
963 }
964}
965
966impl<const N: u32> std::ops::Add for VarFloat<N> {
967 type Output = Self;
968
969 fn add(self, rhs: Self) -> Self::Output {
970 (self.float + rhs.float).into()
971 }
972}
973
974impl<const N: u32> std::ops::Add<&VarFloat<N>> for VarFloat<N> {
975 type Output = Self;
976
977 fn add(self, rhs: &Self) -> Self::Output {
978 (self.float + &rhs.float).into()
979 }
980}
981
982impl<const N: u32> std::ops::Add<VarFloat<N>> for &VarFloat<N> {
983 type Output = VarFloat<N>;
984
985 fn add(self, rhs: VarFloat<N>) -> Self::Output {
986 (&self.float + &rhs.float).complete(N).into()
987 }
988}
989
990impl<const N: u32> std::ops::Add<&VarFloat<N>> for &VarFloat<N> {
991 type Output = VarFloat<N>;
992
993 fn add(self, rhs: &VarFloat<N>) -> Self::Output {
994 (&self.float + &rhs.float).complete(N).into()
995 }
996}
997
998impl<const N: u32> std::ops::AddAssign for VarFloat<N> {
999 fn add_assign(&mut self, rhs: Self) {
1000 self.float += rhs.float;
1001 self.float.set_prec(N);
1002 }
1003}
1004
1005impl<const N: u32> std::ops::AddAssign<&VarFloat<N>> for VarFloat<N> {
1006 fn add_assign(&mut self, rhs: &Self) {
1007 self.float += &rhs.float;
1008 self.float.set_prec(N);
1009 }
1010}
1011
1012impl<const N: u32> std::ops::Sub for VarFloat<N> {
1013 type Output = Self;
1014
1015 fn sub(self, rhs: Self) -> Self::Output {
1016 (self.float - rhs.float).into()
1017 }
1018}
1019
1020impl<const N: u32> std::ops::Sub<&VarFloat<N>> for VarFloat<N> {
1021 type Output = Self;
1022
1023 fn sub(self, rhs: &Self) -> Self::Output {
1024 (self.float - &rhs.float).into()
1025 }
1026}
1027
1028impl<const N: u32> std::ops::Sub<VarFloat<N>> for &VarFloat<N> {
1029 type Output = VarFloat<N>;
1030
1031 fn sub(self, rhs: VarFloat<N>) -> Self::Output {
1032 (&self.float - &rhs.float).complete(N).into()
1033 }
1034}
1035
1036impl<const N: u32> std::ops::Sub<&VarFloat<N>> for &VarFloat<N> {
1037 type Output = VarFloat<N>;
1038
1039 fn sub(self, rhs: &VarFloat<N>) -> Self::Output {
1040 (&self.float - &rhs.float).complete(N).into()
1041 }
1042}
1043
1044impl<const N: u32> std::ops::SubAssign for VarFloat<N> {
1045 fn sub_assign(&mut self, rhs: Self) {
1046 self.float -= rhs.float;
1047 self.float.set_prec(N);
1048 }
1049}
1050
1051impl<const N: u32> std::ops::SubAssign<&VarFloat<N>> for VarFloat<N> {
1052 fn sub_assign(&mut self, rhs: &Self) {
1053 self.float -= &rhs.float;
1054 self.float.set_prec(N);
1055 }
1056}
1057
1058impl<const N: u32> Div for VarFloat<N> {
1059 type Output = Self;
1060
1061 fn div(self, rhs: Self) -> Self::Output {
1062 (self.float / rhs.float).into()
1063 }
1064}
1065
1066impl<const N: u32> Div<&VarFloat<N>> for VarFloat<N> {
1067 type Output = Self;
1068
1069 fn div(self, rhs: &Self) -> Self::Output {
1070 (self.float / &rhs.float).into()
1071 }
1072}
1073
1074impl<const N: u32> Div<VarFloat<N>> for &VarFloat<N> {
1075 type Output = VarFloat<N>;
1076
1077 fn div(self, rhs: VarFloat<N>) -> Self::Output {
1078 (&self.float / &rhs.float).complete(N).into()
1079 }
1080}
1081
1082impl<const N: u32> Div<&VarFloat<N>> for &VarFloat<N> {
1083 type Output = VarFloat<N>;
1084
1085 fn div(self, rhs: &VarFloat<N>) -> Self::Output {
1086 (&self.float / &rhs.float).complete(N).into()
1087 }
1088}
1089
1090impl<const N: u32> std::ops::DivAssign for VarFloat<N> {
1091 fn div_assign(&mut self, rhs: Self) {
1092 self.float /= rhs.float;
1093 self.float.set_prec(N);
1094 }
1095}
1096
1097impl<const N: u32> std::ops::DivAssign<&VarFloat<N>> for VarFloat<N> {
1098 fn div_assign(&mut self, rhs: &Self) {
1099 self.float /= &rhs.float;
1100 self.float.set_prec(N);
1101 }
1102}
1103
1104impl<const N: u32> std::ops::Neg for VarFloat<N> {
1105 type Output = Self;
1106
1107 fn neg(self) -> Self::Output {
1108 (-self.float).into()
1109 }
1110}
1111
1112impl<const N: u32> std::ops::Neg for &VarFloat<N> {
1113 type Output = VarFloat<N>;
1114
1115 fn neg(self) -> Self::Output {
1116 (-&self.float).complete(N).into()
1117 }
1118}
1119
1120impl<const N: u32> std::fmt::Display for VarFloat<N> {
1121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1122 write!(f, "{}", self.float)
1123 }
1124}
1125
1126impl<const N: u32> std::fmt::LowerExp for VarFloat<N> {
1127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1128 write!(f, "{:e}", self.float)
1129 }
1130}
1131
1132impl<const N: u32> RefZero for VarFloat<N> {
1133 fn ref_zero(&self) -> Self {
1134 Self::new_zero()
1135 }
1136}
1137
1138impl<const N: u32> R for VarFloat<N> {}
1139
1140impl<const N: u32> RefOne for VarFloat<N> {
1141 fn ref_one(&self) -> Self {
1142 self.one()
1143 }
1144}
1145
1146impl<const N: u32> SymFloatLike for VarFloat<N> {
1147 #[inline]
1148 fn set_from(&mut self, other: &Self) {
1149 self.float.assign(&other.float);
1150 }
1151
1152 fn mul_add(&self, a: &Self, b: &Self) -> Self {
1153 (&self.float * &a.float + &b.float).complete(N).into()
1154 }
1155
1156 fn is_fully_zero(&self) -> bool {
1157 self.float.is_zero()
1158 }
1159
1160 fn from_i64(&self, a: i64) -> Self {
1165 VarFloat {
1166 float: Float::with_val(N, a),
1167 }
1168 }
1169
1170 fn from_usize(&self, a: usize) -> Self {
1171 VarFloat {
1172 float: Float::with_val(N, a),
1173 }
1174 }
1175
1176 fn get_precision(&self) -> u32 {
1177 N
1178 }
1179
1180 fn set_precision(&mut self, _precision: u32) {}
1181
1182 fn zero(&self) -> Self {
1183 Self::new_zero()
1184 }
1185
1186 fn one(&self) -> Self {
1187 self.from_i64(1)
1188 }
1189
1190 fn new_zero() -> Self {
1191 VarFloat {
1192 float: Float::new(N),
1193 }
1194 }
1195
1196 fn inv(&self) -> Self {
1197 self.float.clone().recip().into()
1198 }
1199
1200 fn pow(&self, e: u64) -> Self {
1201 rug::ops::Pow::pow(&self.float, e).complete(N).into()
1202 }
1203
1204 fn sample_unit<R: Rng + ?Sized>(&self, rng: &mut R) -> Self {
1205 let f: f64 = rng.random();
1206 Float::with_val(N, f).into()
1207 }
1208
1209 fn neg(&self) -> Self {
1210 (-self.float.clone()).into()
1211 }
1212
1213 fn get_epsilon(&self) -> f64 {
1214 2.0f64.powi(1 - (N as i32))
1215 }
1216
1217 fn fixed_precision(&self) -> bool {
1218 true
1219 }
1220}
1221
1222impl<const N: u32> SingleFloat for VarFloat<N> {
1223 fn is_finite(&self) -> bool {
1224 self.float.is_finite()
1225 }
1226
1227 fn is_one(&self) -> bool {
1228 self.float == 1.
1229 }
1230
1231 fn is_zero(&self) -> bool {
1232 self.float == 0.
1233 }
1234
1235 fn from_rational(&self, rat: &Rational) -> Self {
1236 rat.into()
1237 }
1238}
1239impl<const N: u32> RealLike for VarFloat<N> {
1240 fn to_f64(&self) -> f64 {
1241 self.float.to_f64()
1242 }
1243
1244 fn round_to_nearest_integer(&self) -> Integer {
1245 symbolica::domains::integer::MultiPrecisionInteger::from_raw(
1246 self.float.clone().round().to_integer().unwrap(),
1247 )
1248 .into()
1249 }
1250
1251 fn to_usize_clamped(&self) -> usize {
1252 self.float
1253 .to_integer()
1254 .unwrap()
1255 .to_usize()
1256 .unwrap_or(usize::MAX)
1257 }
1258}
1259
1260impl<const N: u32> Real for VarFloat<N> {
1261 fn i(&self) -> Option<Self> {
1262 None
1263 }
1264
1265 fn conj(&self) -> Self {
1266 self.clone()
1267 }
1268
1269 #[inline(always)]
1270 fn pi(&self) -> Self {
1271 Float::with_val(N, rug::float::Constant::Pi).into()
1272 }
1273
1274 #[inline(always)]
1275 fn e(&self) -> Self {
1276 self.one().exp()
1277 }
1278
1279 #[inline(always)]
1280 fn euler(&self) -> Self {
1281 Float::with_val(N, rug::float::Constant::Euler).into()
1282 }
1283
1284 #[inline(always)]
1285 fn phi(&self) -> Self {
1286 (self.one() + self.from_i64(5).sqrt()) / Self::from_f64(2.)
1287 }
1288 fn atan2(&self, x: &Self) -> Self {
1289 self.float.clone().atan2(&x.float).into()
1290 }
1291
1292 fn powf(&self, e: &Self) -> Self {
1293 self.float.clone().pow(e.float.clone()).into()
1294 }
1295
1296 fn log(&self) -> Self {
1297 self.float.ln_ref().complete(N).into()
1298 }
1299 fn norm(&self) -> Self {
1300 self.float.clone().abs().into()
1301 }
1302
1303 delegate! {
1304 #[into]
1305 to self.float.clone(){
1306 fn sqrt(&self) -> Self;
1307 fn exp(&self) -> Self;
1308 fn sin(&self) -> Self;
1309 fn cos(&self) -> Self;
1310 fn tan(&self) -> Self;
1311 fn asin(&self) -> Self;
1312 fn acos(&self) -> Self;
1313 fn sinh(&self) -> Self;
1314 fn cosh(&self) -> Self;
1315 fn tanh(&self) -> Self;
1316 fn asinh(&self) -> Self;
1317 fn acosh(&self) -> Self;
1318 fn atanh(&self) -> Self;
1319 }
1320 }
1321}
1322
1323impl<const N: u32> From<VarFloat<N>> for SymbolicaFloat {
1324 fn from(value: VarFloat<N>) -> Self {
1325 SymbolicaFloat::from(value.float)
1326 }
1327}
1328
1329impl<const N: u32> From<&VarFloat<N>> for SymbolicaFloat {
1330 fn from(value: &VarFloat<N>) -> Self {
1331 SymbolicaFloat::from(value.float.clone())
1332 }
1333}
1334
1335impl<const N: u32> From<SymbolicaFloat> for VarFloat<N> {
1336 fn from(value: SymbolicaFloat) -> Self {
1337 Self::from(value.into_raw())
1340 }
1341}
1342
1343impl From<QuadFloat> for SymbolicaFloat {
1344 fn from(value: QuadFloat) -> Self {
1345 value.0.into()
1346 }
1347}
1348
1349impl From<&QuadFloat> for SymbolicaFloat {
1350 fn from(value: &QuadFloat) -> Self {
1351 value.0.into()
1352 }
1353}
1354
1355impl RefZero for QuadFloat {
1356 fn ref_zero(&self) -> Self {
1357 Self::new_zero()
1358 }
1359}
1360
1361impl R for QuadFloat {}
1362
1363impl RefOne for QuadFloat {
1364 fn ref_one(&self) -> Self {
1365 self.one()
1366 }
1367}
1368
1369impl SymFloatLike for QuadFloat {
1370 #[inline]
1371 fn set_from(&mut self, other: &Self) {
1372 self.0.set_from(&other.0);
1373 }
1374
1375 fn mul_add(&self, a: &Self, b: &Self) -> Self {
1376 Self(self.0.mul_add(&a.0, &b.0))
1377 }
1378
1379 fn is_fully_zero(&self) -> bool {
1380 self.0.is_fully_zero()
1381 }
1382
1383 fn from_i64(&self, a: i64) -> Self {
1384 Self(self.0.from_i64(a))
1385 }
1386
1387 fn from_usize(&self, a: usize) -> Self {
1388 Self(self.0.from_usize(a))
1389 }
1390
1391 fn get_precision(&self) -> u32 {
1392 self.0.get_precision()
1393 }
1394
1395 fn set_precision(&mut self, precision: u32) {
1396 self.0.set_precision(precision);
1397 }
1398
1399 fn zero(&self) -> Self {
1400 Self(self.0.zero())
1401 }
1402
1403 fn one(&self) -> Self {
1404 Self(self.0.one())
1405 }
1406
1407 fn new_zero() -> Self {
1408 Self(DoubleFloat::default().zero())
1409 }
1410
1411 fn inv(&self) -> Self {
1412 Self(self.0.inv())
1413 }
1414
1415 fn pow(&self, e: u64) -> Self {
1416 self.pow_u64_via_mul(e)
1436 }
1437
1438 fn sample_unit<R: Rng + ?Sized>(&self, rng: &mut R) -> Self {
1439 Self(self.0.sample_unit(rng))
1440 }
1441
1442 fn neg(&self) -> Self {
1443 Self(self.0.neg())
1444 }
1445
1446 fn get_epsilon(&self) -> f64 {
1447 self.0.get_epsilon()
1448 }
1449
1450 fn fixed_precision(&self) -> bool {
1451 self.0.fixed_precision()
1452 }
1453}
1454
1455impl SingleFloat for QuadFloat {
1456 fn is_finite(&self) -> bool {
1457 self.0.is_finite()
1458 }
1459
1460 fn is_one(&self) -> bool {
1461 self.0.is_one()
1462 }
1463
1464 fn is_zero(&self) -> bool {
1465 self.0.is_zero()
1466 }
1467
1468 fn from_rational(&self, rat: &Rational) -> Self {
1469 rat.into()
1470 }
1471}
1472
1473impl RealLike for QuadFloat {
1474 fn to_f64(&self) -> f64 {
1475 self.0.to_f64()
1476 }
1477
1478 fn round_to_nearest_integer(&self) -> Integer {
1479 self.0.round_to_nearest_integer()
1480 }
1481
1482 fn to_usize_clamped(&self) -> usize {
1483 self.0.to_usize_clamped()
1484 }
1485}
1486
1487impl Real for QuadFloat {
1488 fn i(&self) -> Option<Self> {
1489 None
1490 }
1491
1492 fn conj(&self) -> Self {
1493 *self
1494 }
1495
1496 #[inline(always)]
1497 fn pi(&self) -> Self {
1498 Self(self.0.pi())
1499 }
1500
1501 #[inline(always)]
1502 fn e(&self) -> Self {
1503 Self(self.0.e())
1504 }
1505
1506 #[inline(always)]
1507 fn euler(&self) -> Self {
1508 Self(self.0.euler())
1509 }
1510
1511 #[inline(always)]
1512 fn phi(&self) -> Self {
1513 Self(self.0.phi())
1514 }
1515
1516 fn atan2(&self, x: &Self) -> Self {
1517 Self(self.0.atan2(&x.0))
1518 }
1519
1520 fn powf(&self, e: &Self) -> Self {
1521 Self(self.0.powf(&e.0))
1522 }
1523
1524 fn log(&self) -> Self {
1525 Self(self.0.log())
1526 }
1527
1528 fn norm(&self) -> Self {
1529 Self(self.0.norm())
1530 }
1531
1532 fn sqrt(&self) -> Self {
1533 Self(self.0.sqrt())
1534 }
1535
1536 fn exp(&self) -> Self {
1537 Self(self.0.exp())
1538 }
1539
1540 fn sin(&self) -> Self {
1541 Self(self.0.sin())
1542 }
1543
1544 fn cos(&self) -> Self {
1545 Self(self.0.cos())
1546 }
1547
1548 fn tan(&self) -> Self {
1549 Self(self.0.tan())
1550 }
1551
1552 fn asin(&self) -> Self {
1553 Self(self.0.asin())
1554 }
1555
1556 fn acos(&self) -> Self {
1557 Self(self.0.acos())
1558 }
1559
1560 fn sinh(&self) -> Self {
1561 Self(self.0.sinh())
1562 }
1563
1564 fn cosh(&self) -> Self {
1565 Self(self.0.cosh())
1566 }
1567
1568 fn tanh(&self) -> Self {
1569 Self(self.0.tanh())
1570 }
1571
1572 fn asinh(&self) -> Self {
1573 Self(self.0.asinh())
1574 }
1575
1576 fn acosh(&self) -> Self {
1577 Self(self.0.acosh())
1578 }
1579
1580 fn atanh(&self) -> Self {
1581 Self(self.0.atanh())
1582 }
1583}
1584
1585impl FloatLike for f128 {
1586 fn mpfr_enclosure(&self, precision: u32) -> (Float, Float) {
1587 let limbs = self.0.into_inner();
1588 let high = Float::with_val(53, limbs.hi());
1589 let low = Float::with_val(53, limbs.lo());
1590 (
1591 Float::with_val_round(precision, &high + &low, Round::Down).0,
1592 Float::with_val_round(precision, &high + &low, Round::Up).0,
1593 )
1594 }
1595
1596 fn E(&self) -> Self {
1597 Self::E()
1598 }
1599
1600 fn PIHALF(&self) -> Self {
1601 Self::PIHALF()
1602 }
1603
1604 fn SQRT_2(&self) -> Self {
1605 Self::from_f64(2.0).sqrt()
1606 }
1607
1608 fn SQRT_2_HALF(&self) -> Self {
1609 Self::from_f64(2.0).sqrt() / Self::from_f64(2.0)
1610 }
1611
1612 fn rem_euclid(&self, rhs: &Self) -> Self {
1613 let r = self.ref_rem(rhs);
1614 if r < r.zero() { r + rhs } else { r }
1615 }
1616
1617 fn FRAC_1_PI(&self) -> Self {
1618 Self::FRAC_1_PI()
1619 }
1620
1621 fn PI(&self) -> Self {
1622 Self::PI()
1623 }
1624
1625 fn TAU(&self) -> Self {
1626 Self::TAU()
1627 }
1628
1629 fn from_f64_exact_binary(x: f64) -> Self {
1630 Self::from_f64_exact_binary(x)
1631 }
1632
1633 fn from_arb(value: &ArbPrec) -> Self {
1634 Self::from(value.float.clone())
1635 }
1636
1637 fn sampling_precision() -> SamplingPrecision {
1638 SamplingPrecision::Quad
1639 }
1640
1641 fn sampling_bridge_cache(
1642 setup: &LmbMultiChannelingSetup,
1643 ) -> &RuntimeCache<std::result::Result<SamplingChannelBridge<Self>, SamplingEvaluationError>>
1644 {
1645 &setup.sampling_bridge_quad
1646 }
1647
1648 fn sampling_bridge_cache_mut(
1649 setup: &mut LmbMultiChannelingSetup,
1650 ) -> &mut RuntimeCache<std::result::Result<SamplingChannelBridge<Self>, SamplingEvaluationError>>
1651 {
1652 &mut setup.sampling_bridge_quad
1653 }
1654
1655 fn from_f64(x: f64) -> Self {
1656 Self::from_f64_decimal(x)
1665 }
1666
1667 fn into_f64(&self) -> f64 {
1668 self.as_f64()
1669 }
1670
1671 fn is_nan(&self) -> bool {
1672 self.is_nan_value()
1673 }
1674
1675 fn is_infinite(&self) -> bool {
1676 self.is_infinite_value()
1677 }
1678
1679 fn floor(&self) -> Self {
1680 self.floor_value()
1681 }
1682
1683 fn try_extract_externals_from_cache(
1684 externals: &Externals,
1685 ) -> Option<&TiVec<ExternalIndex, FourMomentum<F<Self>>>> {
1686 match externals {
1687 Externals::Constant { f_128_cache, .. } => f_128_cache.as_ref(),
1688 }
1689 }
1690
1691 fn epsilon(&self) -> Self {
1692 self.machine_epsilon()
1693 }
1694}
1695
1696macro_rules! impl_mpfr_float_like {
1698 ($scalar:ty, $precision:ident, $bridge:ident, $externals:ident => $external_cache:expr) => {
1699 impl FloatLike for $scalar {
1700 fn mpfr_enclosure(&self, precision: u32) -> (Float, Float) {
1701 (
1702 Float::with_val_round(precision, &self.float, Round::Down).0,
1703 Float::with_val_round(precision, &self.float, Round::Up).0,
1704 )
1705 }
1706
1707 fn E(&self) -> Self {
1708 Self::E()
1709 }
1710
1711 fn PIHALF(&self) -> Self {
1712 Self::PIHALF()
1713 }
1714
1715 fn SQRT_2(&self) -> Self {
1716 self.from_i64(2).sqrt()
1717 }
1718
1719 fn SQRT_2_HALF(&self) -> Self {
1720 self.from_i64(2).sqrt() / self.from_i64(2)
1721 }
1722
1723 fn rem_euclid(&self, rhs: &Self) -> Self {
1724 let r = self.ref_rem(rhs);
1725 if r < r.zero() { r + rhs } else { r }
1726 }
1727
1728 fn FRAC_1_PI(&self) -> Self {
1729 Self::FRAC_1_PI()
1730 }
1731
1732 fn PI(&self) -> Self {
1733 Self::PI()
1734 }
1735
1736 fn TAU(&self) -> Self {
1737 Self::TAU()
1738 }
1739
1740 fn from_f64_exact_binary(x: f64) -> Self {
1741 Self::from_f64_exact_binary(x)
1742 }
1743
1744 fn from_arb(value: &ArbPrec) -> Self {
1745 Self::from(value.float.clone())
1746 }
1747
1748 fn sampling_precision() -> SamplingPrecision {
1749 SamplingPrecision::$precision
1750 }
1751
1752 fn sampling_bridge_cache(
1753 setup: &LmbMultiChannelingSetup,
1754 ) -> &RuntimeCache<
1755 std::result::Result<SamplingChannelBridge<Self>, SamplingEvaluationError>,
1756 > {
1757 &setup.$bridge
1758 }
1759
1760 fn sampling_bridge_cache_mut(
1761 setup: &mut LmbMultiChannelingSetup,
1762 ) -> &mut RuntimeCache<
1763 std::result::Result<SamplingChannelBridge<Self>, SamplingEvaluationError>,
1764 > {
1765 &mut setup.$bridge
1766 }
1767
1768 fn from_f64(x: f64) -> Self {
1769 VarFloat::from_f64(x)
1778 }
1779
1780 fn into_f64(&self) -> f64 {
1781 self.to_f64()
1782 }
1783
1784 fn is_nan(&self) -> bool {
1785 self.float.is_nan()
1786 }
1787
1788 fn is_infinite(&self) -> bool {
1789 self.float.is_infinite()
1790 }
1791
1792 fn floor(&self) -> Self {
1793 self.float.clone().floor().into()
1794 }
1795
1796 fn try_extract_externals_from_cache(
1797 $externals: &Externals,
1798 ) -> Option<&TiVec<ExternalIndex, FourMomentum<F<Self>>>> {
1799 $external_cache
1800 }
1801
1802 fn epsilon(&self) -> Self {
1803 self.machine_epsilon()
1804 }
1805 }
1806
1807 impl PrecisionUpgradable for $scalar {
1808 type Higher = ArbPrec;
1809 type Lower = f128;
1810
1811 fn higher(&self) -> Self::Higher {
1812 ArbPrec::from(self.float.clone())
1813 }
1814
1815 fn lower(&self) -> Self::Lower {
1816 f128::from(self.float.clone())
1817 }
1818 }
1819 };
1820}
1821
1822impl_mpfr_float_like!(ArbPrec, Arb, sampling_bridge_arb, externals => match externals {
1823 Externals::Constant { arb_cache, .. } => arb_cache.as_ref(),
1824});
1825impl_mpfr_float_like!(SamplingFloat, Fixed256, sampling_bridge_fixed256, _externals => None);
1826
1827impl<const N: u32> VarFloat<N> {
1828 fn machine_epsilon(&self) -> Self {
1829 self.from_i64(2).pow((N - 1) as u64).inv()
1830 }
1831
1832 fn one() -> Self {
1833 VarFloat {
1834 float: rug::Float::with_val(N, 1.0),
1835 }
1836 }
1837 #[allow(non_snake_case)]
1838 fn E() -> Self {
1839 Self::one().exp()
1840 }
1841
1842 #[allow(non_snake_case)]
1843 fn PIHALF() -> Self {
1844 Self::PI() / Self::from_f64(2.0)
1845 }
1846
1847 #[allow(non_snake_case)]
1848 fn PI() -> Self {
1849 VarFloat {
1850 float: rug::Float::with_val(N, Constant::Pi),
1851 }
1852 }
1853
1854 #[allow(non_snake_case)]
1855 fn TAU() -> Self {
1856 let mut tau = Self::PI() + Self::PI();
1857 tau.float.set_prec(N);
1858 tau
1859 }
1860
1861 #[allow(non_snake_case)]
1862 fn FRAC_1_PI() -> Self {
1863 Self::PI().inv()
1864 }
1865
1866 #[allow(dead_code)]
1867 pub(crate) fn from_f64_exact_binary(x: f64) -> Self {
1868 VarFloat {
1869 float: rug::Float::with_val(N, x),
1870 }
1871 }
1872
1873 pub(crate) fn from_f64_decimal(x: f64) -> Self {
1874 if !x.is_finite() {
1875 return Self::from_f64_exact_binary(x);
1876 }
1877
1878 thread_local! {
1882 static CACHE: RefCell<HashMap<(u32, u64), Float>> = RefCell::default();
1883 }
1884 CACHE.with_borrow_mut(|cache| {
1885 let key = (N, x.to_bits());
1886 if let Some(float) = cache.get(&key) {
1887 return Self {
1888 float: float.clone(),
1889 };
1890 }
1891 let valid = Float::parse(format!("{}", x)).unwrap();
1892 let float = Float::with_val(N, valid);
1893 if cache.len() == 1024 {
1896 cache.clear();
1897 }
1898 cache.insert(key, float.clone());
1899 Self { float }
1900 })
1901 }
1902
1903 pub(crate) fn from_f64(x: f64) -> Self {
1904 Self::from_f64_decimal(x)
1913 }
1914
1915 pub(crate) fn to_f64(&self) -> f64 {
1916 self.float.to_f64()
1917 }
1918
1919 #[allow(dead_code)]
1920 fn as_f64(&self) -> f64 {
1921 self.float.to_f64()
1922 }
1923
1924 #[allow(dead_code)]
1925 fn is_nan_value(&self) -> bool {
1926 self.float.is_nan()
1927 }
1928
1929 #[allow(dead_code)]
1930 fn is_infinite_value(&self) -> bool {
1931 self.float.is_infinite()
1932 }
1933
1934 #[allow(dead_code)]
1935 fn floor_value(&self) -> Self {
1936 self.float.clone().floor().into()
1937 }
1938}
1939
1940impl QuadFloat {
1941 fn pow_u64_via_mul(&self, mut exponent: u64) -> Self {
1942 let mut base = *self;
1943 let mut acc = Self::one();
1944
1945 while exponent > 0 {
1946 if exponent & 1 == 1 {
1947 acc *= base;
1948 }
1949 exponent >>= 1;
1950 if exponent > 0 {
1951 base *= base;
1952 }
1953 }
1954
1955 acc
1956 }
1957
1958 fn machine_epsilon(&self) -> Self {
1959 self.from_i64(2)
1960 .pow((self.get_precision() - 1) as u64)
1961 .inv()
1962 }
1963
1964 #[allow(dead_code)]
1965 fn one() -> Self {
1966 Self(DoubleFloat::from(1.0))
1967 }
1968
1969 #[allow(non_snake_case)]
1970 fn E() -> Self {
1971 Self(DoubleFloat::default().e())
1972 }
1973
1974 #[allow(non_snake_case)]
1975 fn PIHALF() -> Self {
1976 Self::PI() / Self::from_f64(2.0)
1977 }
1978
1979 #[allow(non_snake_case)]
1980 fn PI() -> Self {
1981 Self(DoubleFloat::default().pi())
1982 }
1983
1984 #[allow(non_snake_case)]
1985 fn TAU() -> Self {
1986 Self::PI() + Self::PI()
1987 }
1988
1989 #[allow(non_snake_case)]
1990 fn FRAC_1_PI() -> Self {
1991 Self::PI().inv()
1992 }
1993
1994 #[allow(dead_code)]
1995 pub(crate) fn from_f64_exact_binary(x: f64) -> Self {
1996 Self(DoubleFloat::from(x))
1997 }
1998
1999 pub(crate) fn from_f64_decimal(x: f64) -> Self {
2000 if !x.is_finite() {
2001 return Self::from_f64_exact_binary(x);
2002 }
2003
2004 thread_local! {
2008 static CACHE: RefCell<HashMap<u64, QuadFloat>> = RefCell::default();
2009 }
2010 CACHE.with_borrow_mut(|cache| {
2011 let key = x.to_bits();
2012 if let Some(value) = cache.get(&key) {
2013 return *value;
2014 }
2015 let value = SymbolicaFloat::parse(
2016 &format!("{}", x),
2017 Some(DoubleFloat::default().get_precision()),
2018 )
2019 .unwrap()
2020 .into();
2021 if cache.len() == 1024 {
2022 cache.clear();
2023 }
2024 cache.insert(key, value);
2025 value
2026 })
2027 }
2028
2029 fn from_f64(x: f64) -> Self {
2030 Self::from_f64_decimal(x)
2031 }
2032
2033 fn floor_via_symbolica(&self) -> Self {
2034 SymbolicaFloat::from(self).into_raw().floor().into()
2035 }
2036
2037 fn trunc_toward_zero(&self) -> Self {
2038 if *self < Self::new_zero() {
2039 -(-self).floor_via_symbolica()
2040 } else {
2041 self.floor_via_symbolica()
2042 }
2043 }
2044
2045 fn truncating_remainder(&self, rhs: &Self) -> Self {
2046 if rhs.is_zero() {
2047 return Self::from_f64_exact_binary(f64::NAN);
2048 }
2049
2050 *self - ((*self / *rhs).trunc_toward_zero() * *rhs)
2051 }
2052
2053 fn as_f64(&self) -> f64 {
2054 self.0.to_f64()
2055 }
2056
2057 fn is_nan_value(&self) -> bool {
2058 self.0.to_f64().is_nan()
2059 }
2060
2061 fn is_infinite_value(&self) -> bool {
2062 self.0.to_f64().is_infinite()
2063 }
2064
2065 fn floor_value(&self) -> Self {
2066 self.floor_via_symbolica()
2067 }
2068}
2069
2070impl<const N: u32> Default for VarFloat<N> {
2071 fn default() -> Self {
2072 VarFloat {
2073 float: rug::Float::with_val(N, 0.0),
2074 }
2075 }
2076}
2077
2078impl PrecisionUpgradable for f128 {
2079 type Higher = ArbPrec;
2080 type Lower = f64;
2081
2082 fn higher(&self) -> Self::Higher {
2083 ArbPrec::from(SymbolicaFloat::from(self))
2084 }
2085
2086 fn lower(&self) -> Self::Lower {
2087 self.as_f64()
2088 }
2089}
2090
2091impl<T: Real + PrecisionUpgradable, H: Real, L: Real> PrecisionUpgradable for Complex<T>
2092where
2093 T: PrecisionUpgradable<Higher = H, Lower = L>,
2094{
2095 type Higher = Complex<H>;
2096 type Lower = Complex<L>;
2097
2098 fn higher(&self) -> Self::Higher {
2099 Complex::new(self.re.higher(), self.im.higher())
2100 }
2101
2102 fn lower(&self) -> Self::Lower {
2103 Complex::new(self.re.lower(), self.im.lower())
2104 }
2105}
2106
2107pub trait PrecisionUpgradable {
2111 type Higher;
2112 type Lower;
2113
2114 fn higher(&self) -> Self::Higher;
2115 fn lower(&self) -> Self::Lower;
2116}
2117
2118pub trait FloatLike:
2119 Real
2120 + Send
2121 + Sync
2122 + 'static
2123 +R
2124 +Default
2125 + Clone
2126 + PartialOrd
2127 + RealLike
2128 + for<'a> RefAdd<&'a Self, Output = Self>
2129 + RefAdd<Self, Output = Self>
2131 + for<'a> RefMul<&'a Self, Output = Self>
2133 + RefMul<Self, Output = Self>
2135 + for<'a> RefSub<&'a Self, Output = Self>
2137 + RefSub<Self, Output = Self>
2139 + for<'a> RefDiv<&'a Self, Output = Self>
2141 + RefDiv<Self, Output = Self>
2143 + for<'a> RefRem<&'a Self, Output = Self>
2144 + RefNeg<Output = Self>
2146 + RefZero
2147 + RefOne
2148 + PrecisionUpgradable
2150 + Serialize
2151 + Display
2152 + GenericEvaluatorFloat
2153 + Into<symbolica::domains::float::Float>
2154{
2155
2156 #[allow(non_snake_case)]
2157 fn PI(&self) -> Self;
2158 #[allow(non_snake_case)]
2159 fn E(&self) -> Self;
2160 #[allow(non_snake_case)]
2161 fn TAU(&self) -> Self;
2162 #[allow(non_snake_case)]
2163 fn SQRT_2(&self) -> Self;
2164 #[allow(non_snake_case)]
2165 fn SQRT_2_HALF(&self) -> Self;
2166 #[allow(non_snake_case)]
2167 fn PIHALF(&self) -> Self;
2168 #[allow(non_snake_case)]
2169 fn FRAC_1_PI(&self) -> Self;
2170
2171 fn from_f64(x: f64) -> Self;
2172
2173 fn from_f64_exact_binary(x: f64) -> Self;
2176
2177 fn from_arb(value: &ArbPrec) -> Self;
2180
2181 fn mpfr_enclosure(&self, precision: u32) -> (Float, Float);
2185
2186 fn sampling_precision() -> SamplingPrecision;
2188 fn sampling_bridge_cache(setup: &LmbMultiChannelingSetup) -> &RuntimeCache<std::result::Result<SamplingChannelBridge<Self>, SamplingEvaluationError>>;
2189 fn sampling_bridge_cache_mut(setup: &mut LmbMultiChannelingSetup) -> &mut RuntimeCache<std::result::Result<SamplingChannelBridge<Self>, SamplingEvaluationError>>;
2190
2191
2192 #[allow(clippy::wrong_self_convention)]
2193 fn into_f64(&self) -> f64; fn is_nan(&self) -> bool;
2196
2197 fn is_infinite(&self) -> bool;
2198
2199 fn floor(&self) -> Self;
2200
2201 fn square(&self) -> Self {
2202 self.pow(2)
2203 }
2204
2205 fn powi(&self, n: i32) -> Self {
2206 let absn = n.unsigned_abs() as u64;
2207 if n.is_negative() {
2208 self.pow(absn).inv()
2209 } else {
2210 self.pow(absn)
2211 }
2212 }
2213
2214 fn epsilon(&self) -> Self;
2215
2216 fn less_than_epsilon(&self) -> bool {
2217 self < &self.epsilon()
2218 }
2219
2220 fn positive(&self) -> bool {
2221 self > &self.zero()
2222 }
2223
2224 fn max_value(&self) -> Self {
2225 Self::from_f64(f64::MAX)
2226 }
2227
2228 fn min_value(&self) -> Self {
2229 Self::from_f64(f64::MIN)
2230 }
2231
2232 fn ln(&self) -> Self {
2233 self.log()
2234 }
2235
2236 fn rem_euclid(&self, rhs: &Self) -> Self;
2237
2238 fn try_extract_externals_from_cache(externals: &Externals) -> Option<&TiVec<ExternalIndex, FourMomentum<F<Self>>>>;
2239}
2240
2241#[derive(
2242 Debug,
2243 Clone,
2244 PartialEq,
2245 PartialOrd,
2246 Copy,
2247 Default,
2248 Serialize,
2249 Deserialize,
2250 Encode,
2251 Decode,
2252 Hash,
2253 JsonSchema,
2254)]
2255pub struct F<T: FloatLike>(pub T);
2256
2257impl<T: FloatLike> ToCoefficient for F<T> {
2258 fn to_coefficient(self) -> Coefficient {
2259 let z = self.zero();
2260 Coefficient::Float(symbolica::domains::float::Complex {
2261 re: self.into(),
2262 im: z.into(),
2263 })
2264 }
2265}
2266
2267use symbolica::evaluate::{EvaluationDomain, ExportNumber};
2268
2269#[cfg(test)]
2270mod evaluation_domain_tests;
2271
2272impl ExportNumber for QuadFloat {
2273 fn export(&self) -> String {
2274 self.to_string()
2275 }
2276
2277 fn is_real(&self) -> bool {
2278 true
2279 }
2280
2281 fn to_complex_double(&self) -> symbolica::domains::float::Complex<f64> {
2282 symbolica::domains::float::Complex::new(self.0.to_f64(), 0.0)
2283 }
2284}
2285
2286impl<const N: u32> ExportNumber for VarFloat<N> {
2287 fn export(&self) -> String {
2288 self.to_string()
2289 }
2290
2291 fn is_real(&self) -> bool {
2292 true
2293 }
2294
2295 fn to_complex_double(&self) -> symbolica::domains::float::Complex<f64> {
2296 symbolica::domains::float::Complex::new(self.to_f64(), 0.0)
2297 }
2298}
2299
2300impl FixedPrecision for QuadFloat {
2301 const BINARY_PRECISION: usize = <DoubleFloat as FixedPrecision>::BINARY_PRECISION;
2302}
2303
2304impl<const N: u32> FixedPrecision for VarFloat<N> {
2305 const BINARY_PRECISION: usize = N as usize;
2306}
2307
2308impl EvaluationDomain for QuadFloat {
2309 const FIXED_PRECISION: Option<u32> = <DoubleFloat as EvaluationDomain>::FIXED_PRECISION;
2310
2311 fn resolve_function(
2312 tags: &[AtomView],
2313 info: &EvaluationInfo,
2314 ) -> Option<Box<dyn ExternalFunction<Self>>> {
2315 if let Some(f) = info.get_evaluator::<Self>(tags) {
2316 return Some(f);
2317 }
2318
2319 let f = DoubleFloat::resolve_function(tags, info)?;
2320 Some(Box::new(move |args: &[Self]| {
2321 let args: SmallVec<[DoubleFloat; 4]> = args.iter().map(|x| x.0).collect();
2322 Self(f(&args))
2323 }))
2324 }
2325
2326 fn try_from_complex_float(
2327 f: symbolica::domains::float::Complex<symbolica::domains::float::Float>,
2328 ) -> Result<Self, String> {
2329 if f.is_real() {
2330 Ok(Self(f.re.to_double_float()))
2331 } else {
2332 Err(format!(
2333 "Cannot convert from Complex<Float> to {} because the result is not real",
2334 std::any::type_name::<Self>()
2335 ))
2336 }
2337 }
2338}
2339
2340impl<const N: u32> EvaluationDomain for VarFloat<N> {
2341 const FIXED_PRECISION: Option<u32> = Some(N);
2342
2343 fn resolve_function(
2344 tags: &[AtomView],
2345 info: &EvaluationInfo,
2346 ) -> Option<Box<dyn ExternalFunction<Self>>> {
2347 if let Some(f) = info.get_evaluator::<Self>(tags) {
2348 return Some(f);
2349 }
2350
2351 let f = SymbolicaFloat::resolve_function(tags, info)?;
2352 Some(Box::new(move |args: &[Self]| {
2353 let args: SmallVec<[SymbolicaFloat; 4]> = args.iter().map(Into::into).collect();
2354 Self::from(f(&args))
2355 }))
2356 }
2357
2358 fn try_from_complex_float(
2359 f: symbolica::domains::float::Complex<symbolica::domains::float::Float>,
2360 ) -> Result<Self, String> {
2361 if f.is_real() {
2362 Ok(Self::from(f.re))
2363 } else {
2364 Err(format!(
2365 "Cannot convert from Complex<Float> to {} because the result is not real",
2366 std::any::type_name::<Self>()
2367 ))
2368 }
2369 }
2370}
2371
2372impl<T: FloatLike + ExportNumber> ExportNumber for F<T> {
2373 fn export(&self) -> String {
2374 self.0.to_string()
2375 }
2376
2377 fn is_real(&self) -> bool {
2378 self.0.is_real()
2379 }
2380
2381 fn to_complex_double(&self) -> symbolica::domains::float::Complex<f64> {
2382 self.0.to_complex_double()
2383 }
2384}
2385
2386impl<T: FloatLike + FixedPrecision> FixedPrecision for F<T> {
2387 const BINARY_PRECISION: usize = T::BINARY_PRECISION;
2388}
2389
2390impl<T: FloatLike + EvaluationDomain> EvaluationDomain for F<T> {
2391 const FIXED_PRECISION: Option<u32> = T::FIXED_PRECISION;
2392
2393 fn resolve_function(
2394 tags: &[AtomView],
2395 info: &EvaluationInfo,
2396 ) -> Option<Box<dyn ExternalFunction<Self>>> {
2397 if let Some(f) = info.get_evaluator::<Self>(tags) {
2398 return Some(f);
2399 }
2400
2401 let f = T::resolve_function(tags, info)?;
2402 Some(Box::new(move |args: &[Self]| {
2403 let args: SmallVec<[T; 4]> = args.iter().map(|x| x.0.clone()).collect();
2404 Self(f(&args))
2405 }))
2406 }
2407
2408 fn try_from_complex_float(
2409 f: symbolica::domains::float::Complex<symbolica::domains::float::Float>,
2410 ) -> Result<Self, String> {
2411 if f.is_real() {
2412 T::try_from_complex_float(f).map(F)
2413 } else {
2414 Err(format!(
2415 "Cannot convert from Complex<Float> to {} because the result is not real",
2416 std::any::type_name::<Self>()
2417 ))
2418 }
2419 }
2420}
2421
2422impl<T: TensorLibraryData + FloatLike> TensorLibraryData for F<T> {
2423 fn one() -> Self {
2424 F(<T as TensorLibraryData>::one())
2425 }
2426
2427 fn minus_one() -> Self {
2428 F(<T as TensorLibraryData>::minus_one())
2429 }
2430
2431 fn zero() -> Self {
2432 F(<T as TensorLibraryData>::zero())
2433 }
2434}
2435
2436impl From<F<f64>> for Coefficient {
2437 fn from(value: F<f64>) -> Self {
2438 Coefficient::from(value.0)
2439 }
2440}
2441
2442impl<'a> From<&'a F<f64>> for Coefficient {
2443 fn from(x: &'a F<f64>) -> Self {
2444 x.0.into()
2445 }
2446}
2447
2448impl ToFloat for F<f64> {
2449 fn to_float(&self) -> symbolica::domains::float::Float {
2450 symbolica::domains::float::Float::with_val(53, self.0)
2451 }
2452}
2453
2454impl ToAtom for F<f64> {
2455 fn to_atom(self) -> Atom {
2456 Atom::num(self.0)
2457 }
2458}
2459
2460pub trait ToCoefficient {
2461 fn to_coefficient(self) -> Coefficient;
2462}
2463
2464impl<T: FloatLike> From<F<T>> for symbolica::domains::float::Float {
2465 fn from(value: F<T>) -> Self {
2466 value.0.into()
2467 }
2468}
2469
2470impl<T: FloatLike> ToCoefficient for Complex<F<T>> {
2471 fn to_coefficient(self) -> Coefficient {
2472 Coefficient::Float(symbolica::domains::float::Complex {
2473 re: self.re.0.into(),
2474 im: self.im.0.into(),
2475 })
2476 }
2477}
2478
2479impl TrySmallestUpgrade<F<f64>> for Atom {
2480 type LCM = Atom;
2481
2482 fn try_upgrade(&'_ self) -> Option<std::borrow::Cow<'_, Self::LCM>> {
2483 Some(std::borrow::Cow::Borrowed(self))
2484 }
2485}
2486
2487impl TrySmallestUpgrade<Atom> for F<f64> {
2488 type LCM = Atom;
2489
2490 fn try_upgrade(&'_ self) -> Option<std::borrow::Cow<'_, Self::LCM>> {
2491 <f64 as TrySmallestUpgrade<Atom>>::try_upgrade(&self.0)
2492 }
2493}
2494
2495impl<T: FloatLike> R for F<T> {}
2496
2497impl<T: FloatLike> Rem<&F<T>> for &F<T> {
2498 type Output = F<T>;
2499
2500 fn rem(self, rhs: &F<T>) -> Self::Output {
2501 F(self.0.ref_rem(&rhs.0))
2502 }
2503}
2504
2505impl<T: FloatLike> RefZero<F<T>> for &F<T> {
2506 fn ref_zero(&self) -> F<T> {
2507 F(self.0.ref_zero())
2508 }
2509}
2510
2511impl<T: FloatLike> RealLike for F<T> {
2512 delegate! {
2513 to self.0{
2514 fn to_usize_clamped(&self)->usize;
2515 fn to_f64(&self)->f64;
2516 fn round_to_nearest_integer(&self)->Integer;
2517 }
2518 }
2519}
2520
2521impl<T: FloatLike> SingleFloat for F<T> {
2522 delegate! {
2523 to self.0{
2524 fn is_zero(&self)->bool;
2525 fn is_one(&self)->bool;
2526 fn is_finite(&self)->bool;
2527 }
2528 }
2529 fn from_rational(&self, rat: &Rational) -> Self {
2530 F(self.0.from_rational(rat))
2531 }
2532}
2533
2534impl<T: FloatLike> PrecisionUpgradable for F<T>
2535where
2536 T::Higher: FloatLike,
2537 T::Lower: FloatLike,
2538{
2539 type Higher = F<T::Higher>;
2540 type Lower = F<T::Lower>;
2541
2542 fn higher(&self) -> Self::Higher {
2543 F(self.0.higher())
2544 }
2545
2546 fn lower(&self) -> Self::Lower {
2547 F(self.0.lower())
2548 }
2549}
2550
2551impl<T: FloatLike> RefZero for F<T> {
2552 fn ref_zero(&self) -> Self {
2553 F(self.0.zero())
2554 }
2555}
2556
2557impl<T: FloatLike> RefOne for F<T> {
2558 fn ref_one(&self) -> Self {
2559 F(self.0.one())
2560 }
2561}
2562
2563impl<T: FloatLike> TrySmallestUpgrade<F<T>> for F<T> {
2564 type LCM = F<T>;
2565 fn try_upgrade(&'_ self) -> Option<std::borrow::Cow<'_, Self::LCM>> {
2566 Some(std::borrow::Cow::Borrowed(self))
2567 }
2568}
2569
2570impl<T: FloatLike> TrySmallestUpgrade<F<T>> for Complex<F<T>> {
2571 type LCM = Complex<F<T>>;
2572 fn try_upgrade(&'_ self) -> Option<std::borrow::Cow<'_, Self::LCM>> {
2573 Some(std::borrow::Cow::Borrowed(self))
2574 }
2575}
2576
2577impl<T: FloatLike> TrySmallestUpgrade<Complex<F<T>>> for F<T> {
2578 type LCM = Complex<F<T>>;
2579 fn try_upgrade(&'_ self) -> Option<std::borrow::Cow<'_, Self::LCM>> {
2580 let z = self.ref_zero();
2581
2582 Some(std::borrow::Cow::Owned(Complex::new(self.clone(), z)))
2583 }
2584}
2585
2586impl<'a, T: FloatLike> From<&'a Rational> for F<T> {
2594 fn from(x: &'a Rational) -> Self {
2595 F(T::new_zero().from_rational(x))
2596 }
2597}
2598
2599impl<T: FloatLike> From<T> for F<T> {
2600 fn from(x: T) -> Self {
2601 F(x)
2602 }
2603}
2604
2605impl<T: FloatLike> std::fmt::Display for F<T> {
2606 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2607 write!(f, "{}", self.0)
2608 }
2609}
2610
2611impl<T: FloatLike> std::fmt::LowerExp for F<T> {
2612 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2613 let rendered = match f.precision() {
2614 Some(precision) => format!("{:.*e}", precision, self.0),
2615 None => format!("{:e}", self.0),
2616 };
2617
2618 let (is_nonnegative, body) = match rendered.strip_prefix('-') {
2619 Some(body) => (false, body),
2620 None => (true, rendered.as_str()),
2621 };
2622
2623 f.pad_integral(is_nonnegative, "", body)
2624 }
2625}
2626
2627impl<T: FloatLike> SymFloatLike for F<T> {
2628 #[inline]
2629 fn set_from(&mut self, other: &Self) {
2630 self.0.set_from(&other.0);
2631 }
2632
2633 fn mul_add(&self, a: &Self, b: &Self) -> Self {
2634 F(self.0.mul_add(&a.0, &b.0))
2635 }
2636
2637 fn is_fully_zero(&self) -> bool {
2638 self.0.is_fully_zero()
2639 }
2640
2641 fn new_zero() -> Self {
2642 F(T::new_zero())
2643 }
2644 fn sample_unit<R: Rng + ?Sized>(&self, rng: &mut R) -> Self {
2645 F(self.0.sample_unit(rng))
2646 }
2647 fn neg(&self) -> Self {
2648 F(self.0.ref_neg())
2649 }
2650
2651 fn zero(&self) -> Self {
2652 F(self.0.zero())
2653 }
2654 fn one(&self) -> Self {
2655 F(self.0.one())
2656 }
2657 fn from_usize(&self, x: usize) -> Self {
2659 F(self.0.from_usize(x))
2660 }
2661 fn from_i64(&self, x: i64) -> Self {
2662 F(self.0.from_i64(x))
2663 }
2664 fn pow(&self, n: u64) -> Self {
2665 F(self.0.pow(n))
2666 }
2667 fn inv(&self) -> Self {
2668 F(self.0.inv())
2669 }
2670 fn get_precision(&self) -> u32 {
2671 self.0.get_precision()
2672 }
2673
2674 fn set_precision(&mut self, precision: u32) {
2675 self.0.set_precision(precision);
2676 }
2677 fn get_epsilon(&self) -> f64 {
2678 self.0.get_epsilon()
2679 }
2680 fn fixed_precision(&self) -> bool {
2681 self.0.fixed_precision()
2682 }
2683}
2684
2685impl<T: FloatLike + Constructible> Constructible for F<T> {
2686 fn new_from_i64(a: i64) -> Self {
2687 F(T::new_from_i64(a))
2688 }
2689
2690 fn new_from_usize(a: usize) -> Self {
2691 F(T::new_from_usize(a))
2692 }
2693
2694 fn new_one() -> Self {
2695 F(T::new_one())
2696 }
2697
2698 fn new_sample_unit<R: Rng + ?Sized>(rng: &mut R) -> Self {
2699 F(T::new_sample_unit(rng))
2700 }
2701}
2702
2703impl<T: FloatLike> Real for F<T> {
2704 fn atan2(&self, x: &Self) -> Self {
2705 F(self.0.atan2(&x.0))
2706 }
2707
2708 fn conj(&self) -> Self {
2709 F(self.0.conj())
2710 }
2711
2712 fn i(&self) -> Option<Self> {
2713 None
2714 }
2715
2716 fn powf(&self, e: &Self) -> Self {
2717 F(self.0.powf(&e.0))
2718 }
2719
2720 fn norm(&self) -> Self {
2721 F(self.0.norm())
2722 }
2723
2724 fn e(&self) -> Self {
2725 F(self.0.e())
2726 }
2727 fn phi(&self) -> Self {
2728 F(self.0.phi())
2729 }
2730 fn euler(&self) -> Self {
2731 F(self.0.euler())
2732 }
2733 fn pi(&self) -> Self {
2734 F(self.0.pi())
2735 }
2736 fn sqrt(&self) -> Self {
2737 F(self.0.sqrt())
2738 }
2739 fn log(&self) -> Self {
2740 F(self.0.log())
2741 }
2742 fn exp(&self) -> Self {
2743 F(self.0.exp())
2744 }
2745 fn sin(&self) -> Self {
2746 F(self.0.sin())
2747 }
2748 fn cos(&self) -> Self {
2749 F(self.0.cos())
2750 }
2751 fn tan(&self) -> Self {
2752 F(self.0.tan())
2753 }
2754 fn asin(&self) -> Self {
2755 F(self.0.asin())
2756 }
2757 fn acos(&self) -> Self {
2758 F(self.0.acos())
2759 }
2760 fn sinh(&self) -> Self {
2761 F(self.0.sinh())
2762 }
2763 fn cosh(&self) -> Self {
2764 F(self.0.cosh())
2765 }
2766 fn tanh(&self) -> Self {
2767 F(self.0.tanh())
2768 }
2769 fn asinh(&self) -> Self {
2770 F(self.0.asinh())
2771 }
2772 fn acosh(&self) -> Self {
2773 F(self.0.acosh())
2774 }
2775 fn atanh(&self) -> Self {
2776 F(self.0.atanh())
2777 }
2778}
2779
2780use delegate::delegate;
2781
2782impl<T: FloatLike> F<T> {
2783 pub(crate) fn max(self, other: F<T>) -> F<T> {
2784 if self < other { other } else { self }
2785 }
2786
2787 pub fn negate(&mut self) {
2788 self.0 = -self.0.clone();
2789 }
2790
2791 pub(crate) fn from_ff64(x: F<f64>) -> Self {
2792 F(T::from_f64(x.0))
2793 }
2794
2795 pub(crate) fn zero(&self) -> Self {
2796 F(self.0.zero())
2797 }
2798
2799 pub(crate) fn one(&self) -> Self {
2800 F(self.0.one())
2801 }
2802
2803 #[allow(clippy::wrong_self_convention)]
2804 pub(crate) fn from_usize(&self, x: usize) -> Self {
2805 F(self.0.from_usize(x))
2806 }
2807
2808 #[allow(clippy::wrong_self_convention)]
2809 pub(crate) fn from_i64(&self, x: i64) -> Self {
2810 F(self.0.from_i64(x))
2811 }
2812
2813 pub(crate) fn higher(&self) -> F<T::Higher>
2814 where
2815 T::Higher: FloatLike,
2816 {
2817 F(self.0.higher())
2818 }
2819
2820 pub(crate) fn lower(&self) -> F<T::Lower>
2821 where
2822 T::Lower: FloatLike,
2823 {
2824 F(self.0.lower())
2825 }
2826
2827 pub fn from_f64(x: f64) -> Self {
2828 F(T::from_f64(x))
2829 }
2830
2831 pub(crate) fn to_arb_exact(&self) -> eyre::Result<F<ArbPrec>> {
2834 let (lower, upper) = self.0.mpfr_enclosure(1000);
2835 if !lower.is_finite() || !upper.is_finite() || lower != upper {
2836 return Err(SamplingEvaluationError::Unrepresentable {
2837 operation: "canonical source embedding",
2838 detail: format!(
2839 "{:?} scalar {self} is not exactly representable at 1000 bits",
2840 T::sampling_precision()
2841 ),
2842 }
2843 .into());
2844 }
2845 let exact = if lower == 0 {
2849 Float::with_val(1000, self.0.into_f64())
2850 } else {
2851 lower
2852 };
2853 Ok(F(ArbPrec::from(exact)))
2854 }
2855
2856 pub(crate) fn from_arb(value: &ArbPrec) -> eyre::Result<Self> {
2859 if !value.float.is_finite() {
2860 return Err(SamplingEvaluationError::Unrepresentable {
2861 operation: "canonical draw materialization",
2862 detail: "nonfinite canonical scalar".into(),
2863 }
2864 .into());
2865 }
2866 let native = T::from_arb(value);
2867 if !native.is_finite() || (native == native.zero() && value.float != 0) {
2868 return Err(SamplingEvaluationError::Unrepresentable {
2869 operation: "canonical draw materialization",
2870 detail: format!(
2871 "canonical scalar {value} is outside {:?} range",
2872 T::sampling_precision()
2873 ),
2874 }
2875 .into());
2876 }
2877 Ok(F(native))
2878 }
2879
2880 pub(crate) fn verify_arb_materialization(
2883 &self,
2884 source: &ArbPrec,
2885 tolerance: f64,
2886 ) -> eyre::Result<()> {
2887 const PRECISION: u32 = 2048;
2888 if !tolerance.is_finite() || tolerance <= 0.0 {
2889 return Err(eyre::eyre!(
2890 "materialization requires a finite positive relative budget"
2891 ));
2892 }
2893 if !self.0.is_finite() || !source.float.is_finite() {
2894 return Err(SamplingEvaluationError::Unrepresentable {
2895 operation: "canonical draw materialization",
2896 detail: "nonfinite source or native scalar in relative check".into(),
2897 }
2898 .into());
2899 }
2900 let (source_lo, source_hi) = source.mpfr_enclosure(PRECISION);
2901 let (native_lo, native_hi) = self.0.mpfr_enclosure(PRECISION);
2902 let error = Float::with_val_round(PRECISION, &native_hi - &source_lo, Round::Up)
2903 .0
2904 .max(&Float::with_val_round(PRECISION, &source_hi - &native_lo, Round::Up).0);
2905 let magnitude = if source_lo >= 0 {
2906 source_lo
2907 } else if source_hi <= 0 {
2908 -source_hi
2909 } else {
2910 Float::with_val(PRECISION, 0)
2911 };
2912 let allowed = Float::with_val_round(PRECISION, &magnitude * tolerance, Round::Down).0;
2914 if !error.is_finite() || !allowed.is_finite() || error > allowed {
2915 return Err(SamplingEvaluationError::UncertainGeometry {
2916 detail: format!("canonical scalar {source} materialized as {self} exceeds relative budget {tolerance} in {:?}", T::sampling_precision()),
2917 }.into());
2918 }
2919 Ok(())
2920 }
2921
2922 #[allow(clippy::wrong_self_convention)]
2923 pub(crate) fn into_ff64(&self) -> F<f64> {
2924 F(self.0.into_f64())
2925 }
2926
2927 pub(crate) fn abs(&self) -> Self {
2928 F(self.0.norm())
2929 }
2930
2931 pub(crate) fn hypot(&self, other: &Self) -> Self {
2933 if !self.0.is_finite() || !other.0.is_finite() {
2934 return self.abs() + other.abs();
2935 }
2936 let scale = self.abs().max(other.abs());
2937 if scale == scale.zero() {
2938 return scale;
2939 }
2940 ((self / &scale).square() + (other / &scale).square()).sqrt() * scale
2941 }
2942
2943 pub(crate) fn sqrt(&self) -> Self {
2944 F(self.0.sqrt())
2945 }
2946
2947 pub(crate) fn powf(&self, e: &Self) -> Self {
2948 F(self.0.powf(&e.0))
2949 }
2950
2951 pub(crate) fn log10(&self) -> Self {
2952 let ten = self.from_i64(10);
2953 self.ln() / ten.ln()
2954 }
2955
2956 pub(crate) fn complex_sqrt(&self) -> Complex<Self> {
2957 if self.positive() {
2958 Complex::new(self.sqrt(), self.zero())
2959 } else {
2960 Complex::new(self.zero(), (-self).sqrt())
2961 }
2962 }
2963
2964 pub(crate) fn rem_euclid(&self, rhs: &Self) -> Self {
2965 F(self.0.rem_euclid(&rhs.0))
2966 }
2967
2968 #[allow(non_snake_case)]
2969 pub(crate) fn PI(&self) -> Self {
2970 F(self.0.PI())
2971 }
2972 #[allow(non_snake_case)]
2973 pub fn E(&self) -> Self {
2974 F(self.0.E())
2975 }
2976 #[allow(non_snake_case)]
2977 pub fn TAU(&self) -> Self {
2978 F(self.0.TAU())
2979 }
2980 #[allow(non_snake_case)]
2981 pub fn PIHALF(&self) -> Self {
2982 F(self.0.PIHALF())
2983 }
2984 #[allow(non_snake_case)]
2985 pub fn SQRT_2(&self) -> Self {
2986 F(self.0.SQRT_2())
2987 }
2988 #[allow(non_snake_case)]
2989 pub fn SQRT_2_HALF(&self) -> Self {
2990 F(self.0.SQRT_2_HALF())
2991 }
2992 #[allow(non_snake_case)]
2993 pub(crate) fn FRAC_1_PI(&self) -> Self {
2994 F(self.0.FRAC_1_PI())
2995 }
2996 #[allow(clippy::wrong_self_convention)]
2997 pub(crate) fn into_f64(&self) -> f64 {
2998 self.0.into_f64()
2999 }
3000 pub(crate) fn square(&self) -> Self {
3001 F(self.0.square())
3002 }
3003 pub(crate) fn powi(&self, n: i32) -> Self {
3004 F(self.0.powi(n))
3005 }
3006 pub(crate) fn epsilon(&self) -> Self {
3007 F(self.0.epsilon())
3008 }
3009 pub(crate) fn less_than_epsilon(&self) -> bool {
3010 self.0.less_than_epsilon()
3011 }
3012 pub(crate) fn positive(&self) -> bool {
3013 self.0.positive()
3014 }
3015 pub(crate) fn max_value(&self) -> Self {
3016 F(self.0.max_value())
3017 }
3018 pub(crate) fn min_value(&self) -> Self {
3019 F(self.0.min_value())
3020 }
3021 pub(crate) fn ln(&self) -> Self {
3022 F(self.0.ln())
3023 }
3024 pub(crate) fn inv(&self) -> Self {
3025 F(self.0.inv())
3026 }
3027 pub(crate) fn is_nan(&self) -> bool {
3028 self.0.is_nan()
3029 }
3030 pub(crate) fn is_infinite(&self) -> bool {
3031 self.0.is_infinite()
3032 }
3033 pub fn floor(&self) -> Self {
3034 F(self.0.floor())
3035 }
3036}
3037
3038impl<T: FloatLike> Add<F<T>> for F<T> {
3058 type Output = F<T>;
3059 fn add(self, rhs: F<T>) -> Self::Output {
3060 F(self.0 + rhs.0)
3061 }
3062}
3063
3064impl<T: FloatLike> Add<&F<T>> for F<T> {
3065 type Output = F<T>;
3066 fn add(self, rhs: &F<T>) -> Self::Output {
3067 F(self.0 + &rhs.0)
3068 }
3069}
3070
3071impl<T: FloatLike> Add<&F<T>> for &F<T> {
3072 type Output = F<T>;
3073 fn add(self, rhs: &F<T>) -> Self::Output {
3074 F(self.0.ref_add(&rhs.0))
3075 }
3076}
3077
3078impl<T: FloatLike> Add<F<T>> for &F<T> {
3079 type Output = F<T>;
3080 fn add(self, rhs: F<T>) -> Self::Output {
3081 F(self.0.ref_add(rhs.0))
3082 }
3083}
3084
3085impl<T: FloatLike> AddAssign<&F<T>> for F<T> {
3086 fn add_assign(&mut self, rhs: &F<T>) {
3087 self.0 += &rhs.0;
3088 }
3089}
3090
3091impl<T: FloatLike> AddAssign<F<T>> for F<T> {
3092 fn add_assign(&mut self, rhs: F<T>) {
3093 self.0 += rhs.0;
3094 }
3095}
3096
3097impl<T: FloatLike> Sub<F<T>> for F<T> {
3098 type Output = F<T>;
3099 fn sub(self, rhs: F<T>) -> Self::Output {
3100 F(self.0 - rhs.0)
3101 }
3102}
3103
3104impl<T: FloatLike> Sub<&F<T>> for F<T> {
3105 type Output = F<T>;
3106 fn sub(self, rhs: &F<T>) -> Self::Output {
3107 F(self.0 - &rhs.0)
3108 }
3109}
3110
3111impl<T: FloatLike> Sub<&F<T>> for &F<T> {
3112 type Output = F<T>;
3113 fn sub(self, rhs: &F<T>) -> Self::Output {
3114 F(self.0.ref_sub(&rhs.0))
3115 }
3116}
3117
3118impl<T: FloatLike> Sub<F<T>> for &F<T> {
3119 type Output = F<T>;
3120 fn sub(self, rhs: F<T>) -> Self::Output {
3121 F(self.0.ref_sub(rhs.0))
3122 }
3123}
3124
3125impl<T: FloatLike> SubAssign<&F<T>> for F<T> {
3126 fn sub_assign(&mut self, rhs: &F<T>) {
3127 self.0 -= &rhs.0;
3128 }
3129}
3130
3131impl<T: FloatLike> SubAssign<F<T>> for F<T> {
3132 fn sub_assign(&mut self, rhs: F<T>) {
3133 self.0 -= rhs.0;
3134 }
3135}
3136
3137impl<T: FloatLike> Mul<F<T>> for F<T> {
3138 type Output = F<T>;
3139 fn mul(self, rhs: F<T>) -> Self::Output {
3140 F(self.0 * rhs.0)
3141 }
3142}
3143
3144impl<T: FloatLike> Mul<&F<T>> for F<T> {
3145 type Output = F<T>;
3146 fn mul(self, rhs: &F<T>) -> Self::Output {
3147 F(self.0 * &rhs.0)
3148 }
3149}
3150
3151impl<T: FloatLike> Mul<&F<T>> for &F<T> {
3152 type Output = F<T>;
3153 fn mul(self, rhs: &F<T>) -> Self::Output {
3154 F(self.0.ref_mul(&rhs.0))
3155 }
3156}
3157
3158impl<T: FloatLike> Mul<F<T>> for &F<T> {
3159 type Output = F<T>;
3160 fn mul(self, rhs: F<T>) -> Self::Output {
3161 F(self.0.ref_mul(rhs.0))
3162 }
3163}
3164
3165impl<T: FloatLike> MulAssign<&F<T>> for F<T> {
3166 fn mul_assign(&mut self, rhs: &F<T>) {
3167 self.0 *= &rhs.0;
3168 }
3169}
3170
3171impl<T: FloatLike> MulAssign<F<T>> for F<T> {
3172 fn mul_assign(&mut self, rhs: F<T>) {
3173 self.0 *= rhs.0;
3174 }
3175}
3176
3177impl<T: FloatLike> Div<F<T>> for F<T> {
3178 type Output = F<T>;
3179 fn div(self, rhs: F<T>) -> Self::Output {
3180 F(self.0 / rhs.0)
3181 }
3182}
3183
3184impl<T: FloatLike> Div<&F<T>> for F<T> {
3185 type Output = F<T>;
3186 fn div(self, rhs: &F<T>) -> Self::Output {
3187 F(self.0 / &rhs.0)
3188 }
3189}
3190
3191impl<T: FloatLike> Div<&F<T>> for &F<T> {
3192 type Output = F<T>;
3193 fn div(self, rhs: &F<T>) -> Self::Output {
3194 F(self.0.ref_div(&rhs.0))
3195 }
3196}
3197
3198impl<T: FloatLike> Div<F<T>> for &F<T> {
3199 type Output = F<T>;
3200 fn div(self, rhs: F<T>) -> Self::Output {
3201 F(self.0.ref_div(rhs.0))
3202 }
3203}
3204
3205impl<T: FloatLike> DivAssign<&F<T>> for F<T> {
3206 fn div_assign(&mut self, rhs: &F<T>) {
3207 self.0 /= &rhs.0;
3208 }
3209}
3210
3211impl<T: FloatLike> DivAssign<F<T>> for F<T> {
3212 fn div_assign(&mut self, rhs: F<T>) {
3213 self.0 /= rhs.0;
3214 }
3215}
3216
3217impl<T: FloatLike> Neg for F<T> {
3218 type Output = F<T>;
3219 fn neg(self) -> Self::Output {
3220 F(-self.0)
3221 }
3222}
3223
3224impl<T: FloatLike> Neg for &F<T> {
3225 type Output = F<T>;
3226 fn neg(self) -> Self::Output {
3227 F(self.0.ref_neg())
3228 }
3229}
3230
3231pub trait RefDefault {
3232 fn default(&self) -> Self;
3233}
3234
3235impl<T: FloatLike> RefDefault for T {
3236 fn default(&self) -> Self {
3237 self.zero()
3238 }
3239}
3240
3241impl<T: FloatLike> RefDefault for F<T> {
3242 fn default(&self) -> Self {
3243 F(self.0.default())
3244 }
3245}
3246impl PrecisionUpgradable for f64 {
3247 type Higher = f128;
3248 type Lower = f64;
3249
3250 fn higher(&self) -> Self::Higher {
3251 f128::from_f64(*self)
3252 }
3253
3254 fn lower(&self) -> Self::Lower {
3255 *self
3256 }
3257}
3258
3259impl FloatLike for f64 {
3260 fn mpfr_enclosure(&self, precision: u32) -> (Float, Float) {
3261 (
3262 Float::with_val_round(precision, *self, Round::Down).0,
3263 Float::with_val_round(precision, *self, Round::Up).0,
3264 )
3265 }
3266
3267 fn PI(&self) -> Self {
3268 std::f64::consts::PI
3269 }
3270
3271 fn SQRT_2(&self) -> Self {
3272 std::f64::consts::SQRT_2
3273 }
3274
3275 fn SQRT_2_HALF(&self) -> Self {
3276 std::f64::consts::SQRT_2 / 2.0
3277 }
3278
3279 fn PIHALF(&self) -> Self {
3280 std::f64::consts::PI / 2.0
3281 }
3282
3283 fn E(&self) -> Self {
3284 std::f64::consts::E
3285 }
3286
3287 fn TAU(&self) -> Self {
3288 std::f64::consts::TAU
3289 }
3290
3291 fn FRAC_1_PI(&self) -> Self {
3292 std::f64::consts::FRAC_1_PI
3293 }
3294
3295 fn from_f64_exact_binary(x: f64) -> Self {
3296 x
3297 }
3298
3299 fn from_arb(value: &ArbPrec) -> Self {
3300 value.float.to_f64()
3301 }
3302
3303 fn sampling_precision() -> SamplingPrecision {
3304 SamplingPrecision::Double
3305 }
3306
3307 fn sampling_bridge_cache(
3308 setup: &LmbMultiChannelingSetup,
3309 ) -> &RuntimeCache<std::result::Result<SamplingChannelBridge<Self>, SamplingEvaluationError>>
3310 {
3311 &setup.sampling_bridge
3312 }
3313
3314 fn sampling_bridge_cache_mut(
3315 setup: &mut LmbMultiChannelingSetup,
3316 ) -> &mut RuntimeCache<std::result::Result<SamplingChannelBridge<Self>, SamplingEvaluationError>>
3317 {
3318 &mut setup.sampling_bridge
3319 }
3320
3321 fn from_f64(x: f64) -> Self {
3322 x
3323 }
3324
3325 fn into_f64(&self) -> f64 {
3326 *self
3327 }
3328
3329 fn is_nan(&self) -> bool {
3330 f64::is_nan(*self)
3331 }
3332
3333 fn is_infinite(&self) -> bool {
3334 f64::is_infinite(*self)
3335 }
3336
3337 fn floor(&self) -> Self {
3338 f64::floor(*self)
3339 }
3340
3341 fn rem_euclid(&self, rhs: &Self) -> Self {
3342 f64::rem_euclid(*self, *rhs)
3343 }
3344
3345 fn try_extract_externals_from_cache(
3346 externals: &Externals,
3347 ) -> Option<&TiVec<ExternalIndex, FourMomentum<F<Self>>>> {
3348 match externals {
3349 Externals::Constant { f_64_cache, .. } => f_64_cache.as_ref(),
3350 }
3351 }
3352
3353 fn epsilon(&self) -> Self {
3354 f64::EPSILON
3355 }
3356}
3357impl From<F<f64>> for f64 {
3358 fn from(value: F<f64>) -> Self {
3359 value.0
3360 }
3361}
3362
3363impl From<F<f64>> for Rational {
3364 fn from(value: F<f64>) -> Self {
3365 value.0.try_into().unwrap()
3366 }
3367}
3368
3369#[allow(non_camel_case_types)]
3370pub type f128 = QuadFloat;
3376pub type ArbPrec = VarFloat<1000>;
3377pub type SamplingFloat = VarFloat<256>;
3379
3380#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
3383pub enum SamplingPrecision {
3384 Double,
3385 Quad,
3386 Fixed256,
3387 Arb,
3388}
3389
3390impl From<crate::settings::runtime::Precision> for SamplingPrecision {
3391 fn from(precision: crate::settings::runtime::Precision) -> Self {
3392 use crate::settings::runtime::Precision;
3393 match precision {
3394 Precision::Double => Self::Double,
3395 Precision::Quad => Self::Quad,
3396 Precision::Arb => Self::Arb,
3397 }
3398 }
3399}
3400
3401impl Display for SamplingPrecision {
3402 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3403 f.write_str(match self {
3404 Self::Double => "f64",
3405 Self::Quad => "f128",
3406 Self::Fixed256 => "fixed256",
3407 Self::Arb => "arb1000",
3408 })
3409 }
3410}
3411
3412#[derive(Clone, Debug)]
3414#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
3415pub struct ZipEq<I, J> {
3416 a: I,
3417 b: J,
3418}
3419
3420#[allow(unused)]
3423pub(crate) fn zip_eq<I, J>(i: I, j: J) -> ZipEq<I::IntoIter, J::IntoIter>
3424where
3425 I: IntoIterator,
3426 J: IntoIterator,
3427{
3428 ZipEq {
3429 a: i.into_iter(),
3430 b: j.into_iter(),
3431 }
3432}
3433
3434impl<I, J> Iterator for ZipEq<I, J>
3435where
3436 I: Iterator,
3437 J: Iterator,
3438{
3439 type Item = (I::Item, J::Item);
3440
3441 fn next(&mut self) -> Option<Self::Item> {
3442 match (self.a.next(), self.b.next()) {
3443 (None, None) => None,
3444 (Some(a), Some(b)) => Some((a, b)),
3445 (None, Some(_)) => {
3446 #[cfg(debug_assertions)]
3447 panic!("Unequal length of iterators; first iterator finished first");
3448 #[cfg(not(debug_assertions))]
3449 None
3450 }
3451 (Some(_), None) => {
3452 #[cfg(debug_assertions)]
3453 panic!("Unequal length of iterators; second iterator finished first");
3454 #[cfg(not(debug_assertions))]
3455 None
3456 }
3457 }
3458 }
3459
3460 fn size_hint(&self) -> (usize, Option<usize>) {
3461 let sa = self.a.size_hint();
3462 let sb = self.b.size_hint();
3463 (sa.0.min(sb.0), sa.1.zip(sb.1).map(|(ua, ub)| ua.min(ub)))
3464 }
3465}
3466
3467impl<I, J> ExactSizeIterator for ZipEq<I, J>
3468where
3469 I: ExactSizeIterator,
3470 J: ExactSizeIterator,
3471{
3472}
3473
3474pub(crate) fn parse_python_expression(expression: &str) -> Atom {
3475 initialize();
3476 let _ = UFO.metric;
3477 let processed_string = String::from(expression)
3478 .replace("**", "^")
3479 .replace("cmath.sqrt", "sqrt")
3480 .replace("cmath.pi", "pi")
3481 .replace("math.sqrt", "sqrt")
3482 .replace("math.pi", "pi");
3483
3484 parse!(processed_string)
3485}
3486
3487#[test]
3488fn python_expression_imaginary_unit_is_numeric() {
3489 for expression in ["𝑖", "1𝑖", "(𝑖)^5", "1+𝑖-1"] {
3491 assert_eq!(parse_python_expression(expression), Atom::i());
3492 }
3493 assert_eq!(parse_python_expression("-𝑖/3"), -Atom::i() / 3);
3494}
3495
3496pub fn format_uncertainty(mean: F<f64>, sdev: F<f64>) -> String {
3499 let mean = mean.0;
3500 let sdev = sdev.0;
3501
3502 fn ndec(x: f64, offset: usize) -> i32 {
3503 let mut ans = (offset as f64 - x.log10()) as i32;
3504 if ans > 0 && x * 10.0.powi(ans) >= [0.5, 9.5, 99.5][offset] {
3505 ans -= 1;
3506 }
3507 if ans < 0 { 0 } else { ans }
3508 }
3509 let v = mean;
3510 let dv = sdev.abs();
3511
3512 if v.is_nan() || dv.is_nan() {
3514 format!("{:e} ± {:e}", v, dv)
3515 } else if dv.is_infinite() {
3516 format!("{:e} ± inf", v)
3517 } else if v.is_zero() && !(1e-4..1e5).contains(&dv) {
3518 if dv.is_zero() {
3519 "0(0)".to_owned()
3520 } else {
3521 let e = format!("{:.1e}", dv);
3522 let mut ans = e.split('e');
3523 let e1 = ans.next().unwrap();
3524 let e2 = ans.next().unwrap();
3525 "0.0(".to_owned() + e1 + ")e" + e2
3526 }
3527 } else if v.is_zero() {
3528 if dv >= 9.95 {
3529 format!("0({:.0})", dv)
3530 } else if dv >= 0.995 {
3531 format!("0.0({:.1})", dv)
3532 } else {
3533 let ndecimal = ndec(dv, 2);
3534 format!(
3535 "{:.*}({:.0})",
3536 ndecimal as usize,
3537 v,
3538 dv * (10.).powi(ndecimal)
3539 )
3540 }
3541 } else if dv.is_zero() {
3542 let e = format!("{:e}", v);
3543 let mut ans = e.split('e');
3544 let e1 = ans.next().unwrap();
3545 let e2 = ans.next().unwrap();
3546 if e2 != "0" {
3547 e1.to_owned() + "(0)e" + e2
3548 } else {
3549 e1.to_owned() + "(0)"
3550 }
3551 } else if dv > 1e4 * v.abs() {
3552 format!("{:.1e} ± {:.2e}", v, dv)
3553 } else if v.abs() >= 1e6 || v.abs() < 1e-5 {
3554 let exponent = v.abs().log10().floor();
3556 let fac = (10.0).powf(&exponent);
3557 let mantissa = format_uncertainty(F(v / fac), F(dv / fac));
3558 let e = format!("{:.0e}", fac);
3559 let mut ee = e.split('e');
3560 mantissa + "e" + ee.nth(1).unwrap()
3561 }
3562 else if dv >= 9.95 {
3564 if v.abs() >= 9.5 {
3565 format!("{:.0}({:.0})", v, dv)
3566 } else {
3567 let ndecimal = ndec(v.abs(), 1);
3568 format!("{:.*}({:.*})", ndecimal as usize, v, ndecimal as usize, dv)
3569 }
3570 } else if dv >= 0.995 {
3571 if v.abs() >= 0.95 {
3572 format!("{:.1}({:.1})", v, dv)
3573 } else {
3574 let ndecimal = ndec(v.abs(), 1);
3575 format!("{:.*}({:.*})", ndecimal as usize, v, ndecimal as usize, dv)
3576 }
3577 } else {
3578 let ndecimal = ndec(v.abs(), 1).max(ndec(dv, 2));
3579 format!(
3580 "{:.*}({:.0})",
3581 ndecimal as usize,
3582 v,
3583 dv * (10.).powi(ndecimal)
3584 )
3585 }
3586}
3587
3588#[allow(unused)]
3590pub(crate) fn compare_slice<T: Ord>(slice1: &[T], slice2: &[T]) -> Ordering {
3591 match slice1.len().cmp(&slice2.len()) {
3592 Ordering::Equal => (),
3593 non_eq => return non_eq,
3594 }
3595
3596 let l = slice1.len();
3597 let lhs = &slice1[..l];
3600 let rhs = &slice2[..l];
3601
3602 for i in 0..l {
3603 match lhs[i].cmp(&rhs[i]) {
3604 Ordering::Equal => (),
3605 non_eq => return non_eq,
3606 }
3607 }
3608
3609 Ordering::Equal
3610}
3611
3612pub trait Signum {
3613 fn multiply_sign(&self, sign: i8) -> Self;
3614}
3615
3616impl Signum for f64 {
3629 #[inline]
3630 fn multiply_sign(&self, sign: i8) -> f64 {
3631 match sign {
3632 1 => *self,
3633 0 => self.zero(),
3634 -1 => -self,
3635 _ => unreachable!("Sign should be -1,0,1"),
3636 }
3637 }
3638}
3639
3640impl Signum for f32 {
3641 #[inline]
3642 fn multiply_sign(&self, sign: i8) -> Self {
3643 match sign {
3644 1 => *self,
3645 0 => 0.0,
3646 -1 => self.neg(),
3647 _ => unreachable!("Sign should be -1,0,1"),
3648 }
3649 }
3650}
3651
3652impl<T: FloatLike> Signum for Complex<F<T>> {
3653 #[inline]
3654 fn multiply_sign(&self, sign: i8) -> Complex<F<T>> {
3655 match sign {
3656 1 => self.clone(),
3657 0 => self.ref_zero(),
3658 -1 => -self.clone(),
3659 _ => unreachable!("Sign should be -1,0,1"),
3660 }
3661 }
3662}
3663
3664impl<T: FloatLike> Signum for FourMomentum<F<T>> {
3665 #[inline]
3666 fn multiply_sign(&self, sign: i8) -> FourMomentum<F<T>> {
3667 match sign {
3668 1 => self.clone(),
3669 0 => self.ref_zero(),
3670 -1 => -self,
3671 _ => unreachable!("Sign should be -1,0,1"),
3672 }
3673 }
3674}
3675
3676#[allow(unused)]
3677#[inline]
3678pub(crate) fn finv<T: FloatLike>(c: Complex<F<T>>) -> Complex<F<T>> {
3680 let norm = c.norm_squared();
3681 c.conj() / norm
3682}
3683
3684#[allow(unused)]
3685#[inline]
3686pub(crate) fn powi<T: FloatLike>(c: Complex<F<T>>, n: i32) -> Complex<F<T>> {
3687 if n.is_negative() {
3688 let u = -n as u64;
3689 finv(c.pow(u))
3690 } else {
3691 let u = n as u64;
3692 c.pow(u)
3693 }
3694}
3695
3696#[allow(unused)]
3697pub(crate) fn evaluate_signature<T>(
3698 signature: &[i8],
3699 momenta: &[FourMomentum<F<T>>],
3700) -> FourMomentum<F<T>>
3701where
3702 T: FloatLike,
3703{
3704 let mut momentum = momenta[0].zero();
3705 for (&sign, mom) in zip_eq(signature, momenta) {
3706 match sign {
3707 0 => {}
3708 1 => momentum += mom,
3709 -1 => momentum -= mom,
3710 _ => {
3711 #[cfg(debug_assertions)]
3712 panic!("Sign should be -1,0,1")
3713 }
3714 }
3715 }
3716
3717 momentum
3718}
3719
3720#[allow(unused)]
3721#[inline]
3722pub(crate) fn pinch_dampening_function<T: FloatLike>(
3723 dampening_arg: F<T>,
3724 delta_t: F<T>,
3725 powers: (u64, u64),
3726 multiplier: f64,
3727) -> F<T> {
3728 assert!(powers.1.is_multiple_of(2));
3730 let a = dampening_arg.pow(powers.0);
3731 &a / (&a + F::<T>::from_f64(multiplier) * delta_t.pow(powers.1))
3732}
3733
3734pub(crate) fn h_dual<T: FloatLike>(
3735 t: &HyperDual<F<T>>,
3736 tstar: Option<HyperDual<F<T>>>,
3737 sigma: Option<F<T>>,
3738 h_function_settings: &crate::settings::runtime::HFunctionSettings,
3739) -> HyperDual<F<T>> {
3740 let sigma = sigma.unwrap_or_else(|| F::<T>::from_f64(h_function_settings.sigma));
3741 let sig = new_constant(t, &sigma);
3742 let two = new_constant(t, &sigma.from_usize(2));
3743 let power = h_function_settings.power;
3744 let prefactor = || {
3745 let normalisation = new_constant(t, &h_function_settings.normalization(&sigma));
3746 match power {
3747 None | Some(0) => normalisation.inv(),
3748 Some(p) => (t.clone() / &sig).inv().pow(p as u64) / normalisation,
3749 }
3750 };
3751 match h_function_settings.function {
3752 crate::settings::runtime::HFunction::Exponential => {
3753 (-(t.clone() * t) / (sig.clone() * &sig)).exp()
3755 / new_constant(t, &h_function_settings.normalization(&sigma))
3756 }
3757 crate::settings::runtime::HFunction::PolyExponential => {
3758 prefactor()
3760 * (two
3761 - (t.clone() * t) / (sig.clone() * &sig)
3762 - (sig.clone() * &sig) / (t.clone() * t))
3763 .exp()
3764 }
3765 crate::settings::runtime::HFunction::PolyLeftRightExponential => {
3766 prefactor()
3768 * (two - ((t.clone() * t) / (sig.clone() * &sig) + t.one()) / (t.clone() / &sig))
3769 .exp()
3770 }
3771 crate::settings::runtime::HFunction::ExponentialCT => {
3772 let delta_t_sq = (tstar.clone().unwrap() - t) * (tstar.clone().unwrap() - t);
3773 let tstar_sq = tstar.clone().unwrap() * tstar.unwrap();
3774 if h_function_settings.enabled_dampening {
3786 let dampener = delta_t_sq.clone() / (delta_t_sq.clone() - &tstar_sq);
3787 (-sig.inv()
3788 * (delta_t_sq.clone() / tstar_sq
3789 + sig.clone() * sig * (dampener.clone() * dampener)))
3790 .exp()
3791 } else {
3792 (-sig.inv() * (delta_t_sq / tstar_sq)).exp()
3793 }
3794 }
3795 }
3796}
3797
3798pub(crate) fn h<T: FloatLike>(
3799 t: &F<T>,
3800 tstar: Option<F<T>>,
3801 sigma: Option<F<T>>,
3802 h_function_settings: &crate::settings::runtime::HFunctionSettings,
3803) -> F<T> {
3804 let sig = sigma.unwrap_or_else(|| F::<T>::from_f64(h_function_settings.sigma));
3805 let two = sig.from_usize(2);
3806 let power = h_function_settings.power;
3807 let prefactor = || {
3808 let normalisation = h_function_settings.normalization(&sig);
3809 match power {
3810 None | Some(0) => normalisation.inv(),
3811 Some(p) => (t / &sig).powi(-(p as i32)) / normalisation,
3812 }
3813 };
3814 match h_function_settings.function {
3815 crate::settings::runtime::HFunction::Exponential => {
3816 (-(t.square()) / (sig.square())).exp() / h_function_settings.normalization(&sig)
3818 }
3819 crate::settings::runtime::HFunction::PolyExponential => {
3820 prefactor()
3822 * (two - (t.square()) / (sig.square()) - (sig.square()) / (t.square())).exp()
3823 }
3824 crate::settings::runtime::HFunction::PolyLeftRightExponential => {
3825 prefactor() * (two - ((t.square()) / (sig.square()) + t.one()) / (t / &sig)).exp()
3827 }
3828 crate::settings::runtime::HFunction::ExponentialCT => {
3829 let delta_t_sq = (tstar.clone().unwrap() - t).square();
3830 let tstar_sq = tstar.unwrap().square();
3831 if h_function_settings.enabled_dampening {
3843 let dampener = delta_t_sq.clone() / (delta_t_sq.clone() - &tstar_sq);
3844 (-sig.inv() * (delta_t_sq.clone() / tstar_sq + sig.square() * (dampener.square())))
3845 .exp()
3846 } else {
3847 (-sig.inv() * (delta_t_sq / tstar_sq)).exp()
3848 }
3849 }
3850 }
3851}
3852
3853#[allow(unused)]
3856pub(crate) fn determinant<T: FloatLike>(bb: &[Complex<F<T>>], dimension: usize) -> Complex<F<T>> {
3857 let one = bb[0].re.one();
3858 let zero = one.zero();
3859 let mut determinant = Complex::new(one.clone(), zero.clone());
3861 let mut indx = [0; MAX_DIMENSION];
3862 let mut d = 1; let mut aa = bb.to_vec();
3866
3867 let mut imax = 0;
3869 let mut flag = 1;
3870 let mut dumc;
3871 let mut sum;
3872
3873 let mut aamax;
3874 let mut dumr;
3875 let mut vv = vec![zero.clone(); MAX_DIMENSION];
3876
3877 for i in 0..dimension {
3879 aamax = zero.clone();
3880 for j in 0..dimension {
3881 let r = aa[i * dimension + j].norm_squared();
3882 if r > aamax {
3883 aamax = r;
3884 }
3885 }
3886 if aamax.is_zero() {
3888 flag = 0;
3889 }
3890 vv[i] = aamax.inv();
3892 }
3893 if flag == 1 {
3894 for j in 0..dimension {
3895 for i in 0..j {
3896 sum = aa[i * dimension + j].clone();
3897 for k in 0..i {
3898 sum -= &aa[i * dimension + k] * &aa[k * dimension + j];
3899 }
3900 aa[i * dimension + j] = sum;
3901 }
3902 aamax = zero.clone();
3904 for i in j..dimension {
3905 sum = aa[i * dimension + j].clone();
3906 for k in 0..j {
3907 sum -= &aa[i * dimension + k] * &aa[k * dimension + j];
3908 }
3909 aa[i * dimension + j] = sum.clone();
3910 dumr = &vv[i] * sum.norm_squared();
3912 if dumr >= aamax {
3914 imax = i;
3915 aamax = dumr;
3916 }
3917 }
3918 if j != imax {
3920 for k in 0..dimension {
3921 dumc = aa[imax * dimension + k].clone();
3922 aa[imax * dimension + k] = aa[j * dimension + k].clone();
3923 aa[j * dimension + k] = dumc.clone();
3924 }
3925 d = -d;
3927 vv[imax] = vv[j].clone();
3929 }
3930 indx[j] = imax;
3931 if j + 1 != dimension {
3932 dumc = aa[j * dimension + j].inv();
3933 for i in j + 1..dimension {
3934 aa[i * dimension + j] *= &dumc;
3935 }
3936 }
3937 }
3938 }
3939 if flag == 0 {
3941 determinant = Complex::new(zero.clone(), zero.clone());
3942 } else {
3943 for diagonal in 0..dimension {
3945 determinant *= &aa[diagonal * dimension + diagonal];
3946 }
3947 determinant *= &zero.from_i64(d);
3948 }
3949 determinant
3950}
3951
3952#[allow(unused)]
3953pub(crate) fn next_combination_with_replacement(state: &mut [usize], max_entry: usize) -> bool {
3954 for i in (0..state.len()).rev() {
3955 if state[i] < max_entry {
3956 state[i] += 1;
3957 for j in i + 1..state.len() {
3958 state[j] = state[i]
3959 }
3960 return true;
3961 }
3962 }
3963 false
3964}
3965
3966pub(crate) fn compute_loop_part<T: FloatLike>(
3967 loop_signature: &LoopSignature,
3968 loop_moms: &LoopMomenta<F<T>>,
3969) -> ThreeMomentum<F<T>> {
3970 loop_signature.apply_typed(loop_moms)
3971}
3972
3973pub(crate) fn compute_loop_part_subspace<T: FloatLike>(
3974 loop_signature: &LoopSignature,
3975 loop_moms: &LoopMomenta<F<T>>,
3976 subspace: &SubspaceData,
3977) -> ThreeMomentum<F<T>> {
3978 let projected: LoopSignature = subspace.project_loop_signature(loop_signature).collect();
3979 projected.apply_typed(loop_moms)
3980}
3981
3982pub(crate) fn compute_shift_part<T: FloatLike>(
3983 external_signature: &ExternalSignature,
3984 external_moms: &ExternalFourMomenta<F<T>>,
3985) -> FourMomentum<F<T>> {
3986 external_signature
3987 .apply_typed::<FourMomentum<F<T>>, ExternalIndex, ExternalFourMomenta<F<T>>>(external_moms)
3988}
3989
3990pub(crate) fn compute_shift_part_subspace<T: FloatLike>(
3991 loop_signature: &LoopSignature,
3992 external_signature: &ExternalSignature,
3993 loop_moms: &LoopMomenta<F<T>>,
3994 external_moms: &ExternalThreeMomenta<F<T>>,
3995 subspace: &SubspaceData,
3996) -> ThreeMomentum<F<T>> {
3997 let projected_loop_complement: LoopSignature = subspace
3998 .project_complement_loop_signature(loop_signature)
3999 .collect();
4000
4001 let loop_part = projected_loop_complement.apply_typed(loop_moms);
4002 let external_part: ThreeMomentum<F<T>> = external_signature
4003 .apply_typed::<ThreeMomentum<F<T>>, ExternalIndex, ExternalThreeMomenta<F<T>>>(
4004 external_moms,
4005 );
4006 loop_part + external_part
4007}
4008
4009pub(crate) fn compute_t_part_of_shift_part<T: FloatLike>(
4010 external_signature: &ExternalSignature,
4011 external_moms: &ExternalFourMomenta<F<T>>,
4012) -> F<T> {
4013 external_signature
4015 .apply_iter(external_moms.iter().map(|m| m.temporal.value.clone()))
4016 .unwrap_or(external_moms[ExternalIndex(0)].temporal.value.zero())
4017}
4018
4019#[allow(unused, clippy::type_complexity)]
4022pub(crate) fn one_loop_e_surface_bilinear_form<T: FloatLike>(
4023 p1: &[F<T>; 3],
4024 p2: &[F<T>; 3],
4025 m1_sq: F<T>,
4026 m2_sq: F<T>,
4027 e_shift: F<T>,
4028) -> ([[F<T>; 3]; 3], [F<T>; 3], F<T>) {
4029 let zero = e_shift.zero();
4030 let two = zero.from_i64(2);
4031 let e_shift_sq = e_shift.square();
4032 let p1_sq = p1[0].square() + p1[1].square() + p1[2].square();
4033 let p2_sq = p2[0].square() + p2[1].square() + p2[2].square();
4034
4035 let zeros = [zero.clone(), zero.clone(), zero.clone()];
4036 let mut a = [zeros.clone(), zeros.clone(), zeros.clone()];
4037 a[0][0] = (&p1[0] - &p2[0] - &e_shift) * (&p2[0] - &p1[0] - &e_shift);
4038 a[0][1] = (&p1[0] - &p2[0]) * (&p2[1] - &p1[1]);
4039 a[1][0] = a[0][1].clone();
4040 a[0][2] = (&p1[0] - &p2[0]) * (&p2[2] - &p1[2]);
4041 a[2][0] = a[0][2].clone();
4042 a[1][1] = (&p1[1] - &p2[1] - &e_shift) * (&p2[1] - &p1[1] - &e_shift);
4043 a[1][2] = (&p1[1] - &p2[1]) * (&p2[2] - &p1[2]);
4044 a[2][1] = a[1][2].clone();
4045 a[2][2] = (&p1[2] - &p2[2] - &e_shift) * (&p2[2] - &p1[2] - &e_shift);
4046
4047 let mut b = zeros.clone();
4048 b[0] =
4049 (&p2[0] - &p1[0]) * (&m1_sq - &m2_sq + &p1_sq - &p2_sq) + &e_shift_sq * (&p1[0] + &p2[0]);
4050 b[1] =
4051 (&p2[1] - &p1[1]) * (&m1_sq - &m2_sq + &p1_sq - &p2_sq) + &e_shift_sq * (&p1[1] + &p2[1]);
4052 b[2] =
4053 (&p2[2] - &p1[2]) * (&m1_sq - &m2_sq + &p1_sq - &p2_sq) + &e_shift_sq * (&p1[2] + &p2[2]);
4054
4055 let c = -&e_shift_sq * &e_shift_sq + &two * &e_shift_sq * (&m1_sq + &m2_sq + &p1_sq + &p2_sq)
4056 - (&m1_sq - &m2_sq + &p1_sq - &p2_sq) * (&m1_sq - &m2_sq + &p1_sq - &p2_sq);
4057
4058 (a, b, c)
4059}
4060
4061use color_eyre::Result;
4062use eyre::eyre;
4063#[allow(unused)]
4064use std::fmt::LowerExp;
4065
4066pub trait ApproxEq<U: LowerExp, T: LowerExp>: LowerExp {
4067 fn approx_eq(&self, other: &U, tolerance: &T) -> bool;
4068
4069 fn approx_eq_slice(lhs: &[Self], rhs: &[U], tolerance: &T) -> bool
4070 where
4071 Self: Sized,
4072 {
4073 lhs.iter()
4074 .zip_eq(rhs)
4075 .all(|(l, r)| l.approx_eq(r, tolerance))
4076 }
4077
4078 fn approx_eq_iterator<'a, I, J>(lhs: I, rhs: J, tolerance: &'a T) -> bool
4079 where
4080 Self: Sized + 'a,
4081 U: 'a,
4082 I: IntoIterator<Item = &'a Self>,
4083 J: IntoIterator<Item = &'a U>,
4084 {
4085 lhs.into_iter()
4086 .zip_eq(rhs)
4087 .all(|(l, r)| l.approx_eq(r, tolerance))
4088 }
4089
4090 fn assert_approx_eq(&self, other: &U, tolerance: &T) {
4091 assert!(
4092 self.approx_eq(other, tolerance),
4093 "assert_approx_eq failed: \n{:+e} != \n{:+e} with tolerance {:+e}",
4094 self,
4095 other,
4096 tolerance
4097 )
4098 }
4099 fn approx_eq_res(&self, other: &U, tolerance: &T) -> Result<()> {
4100 if self.approx_eq(other, tolerance) {
4101 Ok(())
4102 } else {
4103 Err(eyre!(
4104 "assert_approx_eq failed: \n{:+e} != \n{:+e} with tolerance {:+e}",
4105 self,
4106 other,
4107 tolerance
4108 ))
4109 }
4110 }
4111}
4112
4113impl<T: FloatLike> ApproxEq<F<T>, F<T>> for F<T> {
4118 fn approx_eq(&self, other: &F<T>, tolerance: &F<T>) -> bool {
4119 if other.is_zero() {
4120 self.norm() < tolerance.clone()
4121 } else {
4122 ((self.ref_sub(other)) / other).norm() < tolerance.clone()
4123 }
4124 }
4125}
4126
4127impl<T: FloatLike> ApproxEq<Complex<F<T>>, F<T>> for Complex<F<T>> {
4128 fn approx_eq(&self, other: &Complex<F<T>>, tolerance: &F<T>) -> bool {
4129 if !self.norm().re.approx_eq(&other.norm().re, tolerance) {
4130 return false;
4131 } else if self.norm().is_zero() || other.norm().is_zero() {
4132 return true;
4133 }
4134 let two_pi = self.re.PI() + self.re.PI();
4135 let arg_self = self.arg().rem_euclid(&two_pi);
4136 let arg_other = other.arg().rem_euclid(&two_pi);
4137 if !arg_self.approx_eq(&arg_other, tolerance) {
4138 return false;
4139 }
4140 true
4141 }
4142 fn approx_eq_res(&self, other: &Complex<F<T>>, tolerance: &F<T>) -> Result<()> {
4143 if !self.norm().re.approx_eq(&other.norm().re, tolerance) {
4144 return Err(eyre!(
4145 "Norms are not approximately equal: \n{:+e} != \n{:+e} with tolerance {:+e}",
4146 &self.norm().re,
4147 other.norm().re,
4148 tolerance
4149 ));
4150 } else if self.norm().is_zero() || other.norm().is_zero() {
4151 return Ok(());
4152 }
4153
4154 let two_pi = self.re.PI() + self.re.PI();
4155 let arg_self = self.arg().rem_euclid(&two_pi);
4156 let arg_other = other.arg().rem_euclid(&two_pi);
4157 if !arg_self.approx_eq(&arg_other, tolerance) {
4160 return Err(eyre!(
4161 "Phases are not approximately equal: \n{:+e} - \n{:+e}= \n{:+e}!=0 with tolerance {:+e}",
4162 arg_self,
4163 arg_other,
4164 &arg_self - &arg_other,
4165 tolerance
4166 ));
4167 }
4168 Ok(())
4169 }
4170}
4171
4172impl<T: FloatLike> ApproxEq<F<T>, F<T>> for Complex<F<T>> {
4173 fn approx_eq(&self, other: &F<T>, tolerance: &F<T>) -> bool {
4174 self.re.approx_eq(other, tolerance) && self.im.approx_eq(tolerance, tolerance)
4175 }
4176 fn approx_eq_res(&self, other: &F<T>, tolerance: &F<T>) -> Result<()> {
4177 if self.im.approx_eq(tolerance, tolerance) {
4178 return Err(eyre!(
4179 "Non-zero imaginary part: \n{:+e} with tolerance {:+e}",
4180 &self.im,
4181 tolerance
4182 ));
4183 }
4184 if !self.re.approx_eq(other, tolerance) {
4185 return Err(eyre!(
4186 "Real parts are not approximately equal: \n{:+e} != \n{:+e} with tolerance {:+e}",
4187 &self.re,
4188 other,
4189 tolerance
4190 ));
4191 }
4192 Ok(())
4193 }
4194}
4195
4196impl<T: FloatLike> ApproxEq<Complex<F<T>>, F<T>> for F<T> {
4197 fn approx_eq(&self, other: &Complex<F<T>>, tolerance: &F<T>) -> bool {
4198 other.re.approx_eq(self, tolerance) && other.im.approx_eq(tolerance, tolerance)
4199 }
4200
4201 fn approx_eq_res(&self, other: &Complex<F<T>>, tolerance: &F<T>) -> Result<()> {
4202 if other.im.approx_eq(tolerance, tolerance) {
4203 return Err(eyre!(
4204 "Non-zero imaginary part: \n{:+e} with tolerance {:+e}",
4205 &other.im,
4206 tolerance
4207 ));
4208 }
4209 if !other.re.approx_eq(self, tolerance) {
4210 return Err(eyre!(
4211 "Real parts are not approximately equal: \n{:+e} != \n{:+e} with tolerance {:+e}",
4212 &other.re,
4213 self,
4214 tolerance
4215 ));
4216 }
4217 Ok(())
4218 }
4219}
4220
4221#[allow(unused)]
4222pub(crate) fn one_loop_eval_e_surf<T: FloatLike>(
4223 k: &[F<T>; 3],
4224 p1: &[F<T>; 3],
4225 p2: &[F<T>; 3],
4226 m1_sq: F<T>,
4227 m2_sq: F<T>,
4228 e_shift: F<T>,
4229) -> F<T> {
4230 ((&k[0] + &p1[0]) * (&k[0] + &p1[0])
4231 + (&k[1] + &p1[1]) * (&k[1] + &p1[1])
4232 + (&k[2] + &p1[2]) * (&k[2] + &p1[2])
4233 + m1_sq)
4234 .sqrt()
4235 + ((&k[0] + &p2[0]) * (&k[0] + &p2[0])
4236 + (&k[1] + &p2[1]) * (&k[1] + &p2[1])
4237 + (&k[2] + &p2[2]) * (&k[2] + &p2[2])
4238 + m2_sq)
4239 .sqrt()
4240 + e_shift
4241}
4242
4243#[allow(unused)]
4244pub(crate) fn one_loop_eval_e_surf_k_derivative<T: FloatLike>(
4245 k: &[F<T>; 3],
4246 p1: &[F<T>; 3],
4247 p2: &[F<T>; 3],
4248 m1_sq: F<T>,
4249 m2_sq: F<T>,
4250) -> [F<T>; 3] {
4251 let e1 = ((&k[0] + &p1[0]) * (&k[0] + &p1[0])
4252 + (&k[1] + &p1[1]) * (&k[1] + &p1[1])
4253 + (&k[2] + &p1[2]) * (&k[2] + &p1[2])
4254 + m1_sq)
4255 .sqrt();
4256 let e2 = ((&k[0] + &p2[0]) * (&k[0] + &p2[0])
4257 + (&k[1] + &p2[1]) * (&k[1] + &p2[1])
4258 + (&k[2] + &p2[2]) * (&k[2] + &p2[2])
4259 + m2_sq)
4260 .sqrt();
4261 [
4262 (&k[0] + &p1[0]) / &e1 + (&k[0] + &p2[0]) / &e2,
4263 (&k[1] + &p1[1]) / &e1 + (&k[1] + &p2[1]) / &e2,
4264 (&k[2] + &p1[2]) / &e1 + (&k[2] + &p2[2]) / &e2,
4265 ]
4266}
4267
4268#[allow(unused)]
4269pub(crate) fn one_loop_get_e_surf_t_scaling<T: FloatLike>(
4270 k: &[F<T>; 3],
4271 p1: &[F<T>; 3],
4272 p2: &[F<T>; 3],
4273 m1_sq: F<T>,
4274 m2_sq: F<T>,
4275 e_shift: F<T>,
4276) -> [F<T>; 2] {
4277 let zero = e_shift.zero();
4278 let one = zero.one();
4279 let (a, b, c_coef) = one_loop_e_surface_bilinear_form(p1, p2, m1_sq, m2_sq, e_shift);
4280 let mut a_coef = zero.clone();
4281 for i in 0..=2 {
4282 for j in 0..=2 {
4283 a_coef += &k[i] * &a[i][j] * &k[j];
4284 }
4285 }
4286 a_coef *= zero.from_i64(4);
4287 let mut b_coef = zero.clone();
4288 for i in 0..=2 {
4289 b_coef += &k[i] * &b[i];
4290 }
4291 b_coef *= zero.from_i64(4);
4292 let discr = b_coef.square() - zero.from_i64(4) * &a_coef * &c_coef;
4293 if discr < zero {
4294 [zero.clone(), zero.clone()]
4295 } else {
4296 [
4297 (-&b_coef + discr.sqrt()) / (zero.from_i64(2) * &a_coef),
4298 (-&b_coef - discr.sqrt()) / (zero.from_i64(2) * &a_coef),
4299 ]
4300 }
4301}
4302
4303pub(crate) fn box_muller<T: FloatLike>(x1: F<T>, x2: F<T>) -> (F<T>, F<T>) {
4304 let r = (-x1.from_i64(2) * x1.log()).sqrt();
4305 let theta = r.from_i64(2) * r.PI() * x2;
4306 (r.clone() * theta.cos(), r * theta.sin())
4307}
4308
4309pub(crate) fn compute_surface_and_volume<T: FloatLike>(n_dim: usize, radius: F<T>) -> (F<T>, F<T>) {
4310 let mut surface = radius.from_i64(2);
4311 let one = radius.one();
4312 let mut volume = one.clone();
4313 for i in 1..n_dim + 1 {
4314 (surface, volume) = (
4315 one.from_i64(2) * one.PI() * volume,
4316 surface / one.from_i64(i as i64),
4317 );
4318 }
4319 (
4320 surface * radius.pow(n_dim as u64),
4321 volume * radius.pow(n_dim as u64),
4322 )
4323}
4324
4325pub(crate) fn get_n_dim_for_n_loop_momenta(
4326 settings: &SamplingSettings,
4327 n_loop_momenta: usize,
4328 n_edges: Option<usize>, ) -> usize {
4330 if let Some(parameterization_settings) = settings.get_parameterization_settings() {
4331 match parameterization_settings.mode {
4332 ParameterizationMode::HyperSphericalFlat => {
4333 let mut n_dim = 3 * n_loop_momenta;
4335 if n_dim % 2 == 1 {
4336 n_dim += 1;
4337 }
4338 n_dim + 1
4339 }
4340 ParameterizationMode::HyperSpherical
4341 | ParameterizationMode::Cartesian
4342 | ParameterizationMode::Spherical
4343 | ParameterizationMode::RelativeSpherical
4344 | ParameterizationMode::SphericalCommonRadial
4345 | ParameterizationMode::SphericalProductCommonRadial
4346 | ParameterizationMode::MomentumSpace => 3 * n_loop_momenta,
4347 }
4348 } else {
4349 let tropical_part = 2 * n_edges.expect("No tropical subgraph table generated, please run without tropical sampling or regenerate with tables") - 1;
4350 let d_l = 3 * n_loop_momenta;
4351 if d_l % 2 == 1 {
4352 tropical_part + d_l + 1
4353 } else {
4354 tropical_part + d_l
4355 }
4356 }
4357}
4358
4359pub(crate) fn global_parameterize<T: FloatLike>(
4360 x: &[F<T>],
4361 e_cm: F<T>,
4362 settings: &ParameterizationSettings,
4363) -> (Vec<[F<T>; 3]>, F<T>) {
4364 debug!("b: {}", settings.b);
4365 let zero = e_cm.zero();
4366 let one = zero.one();
4367 match settings.mode {
4368 ParameterizationMode::HyperSpherical | ParameterizationMode::HyperSphericalFlat => {
4369 let mut jac = one.clone();
4370
4371 let radius: F<T> = match settings.mapping {
4372 ParameterizationMapping::Log => {
4373 let b = F::<T>::from_f64(settings.b);
4375 let radius = &e_cm * (&one + &b * &x[0] / (&one - &x[0])).log();
4376 jac *= &e_cm * &b / (&one - &x[0]) / (&one + &x[0] * (&b - &one));
4377 radius
4378 }
4379 ParameterizationMapping::Power => {
4380 let b = F::<T>::from_f64(settings.b);
4382 let power = F::<T>::from_f64(settings.power);
4383 let odds = &x[0] / (&one - &x[0]);
4384 let power_minus_one = &power - &one;
4385 let radius = &e_cm * &b * odds.powf(&power);
4386 jac *=
4387 &e_cm * &b * &power * odds.powf(&power_minus_one) / (&one - &x[0]).square();
4388 radius
4389 }
4390 ParameterizationMapping::Linear => {
4391 let b = F::<T>::from_f64(settings.b);
4393 let radius = &e_cm * &b * &x[0] / (&one - &x[0]);
4394 jac *= (&e_cm * &b + &radius).powi(2) / &e_cm / &b;
4395 radius
4396 }
4397 };
4398 match settings.mode {
4399 ParameterizationMode::HyperSpherical => {
4400 let phi = zero.from_i64(2) * zero.PI() * &x[1];
4401 jac *= zero.from_i64(2) * zero.PI();
4402
4403 let mut cos_thetas = Vec::with_capacity(x.len() - 2);
4404 let mut sin_thetas = Vec::with_capacity(x.len() - 2);
4405
4406 for (i, xi) in x[2..].iter().enumerate() {
4407 let cos_theta = -&one + zero.from_i64(2) * xi;
4408 jac *= zero.from_i64(2);
4409 let sin_theta = (&one - cos_theta.square()).sqrt();
4410 let angular_power = x.len() - 3 - i;
4413 if angular_power > 0 {
4414 jac *= sin_theta.pow(angular_power as u64);
4415 }
4416 cos_thetas.push(cos_theta);
4417 sin_thetas.push(sin_theta);
4418 }
4419
4420 let mut concatenated_vecs = Vec::with_capacity(x.len() / 3);
4421 let mut base = radius.clone();
4422 for (cos_theta, sin_theta) in cos_thetas.iter().zip(sin_thetas.iter()) {
4423 concatenated_vecs.push(&base * cos_theta);
4424 base *= sin_theta;
4425 }
4426 concatenated_vecs.push(&base * phi.cos());
4427 concatenated_vecs.push(&base * phi.sin());
4428
4429 jac *= radius.pow((x.len() - 1) as u64); (
4432 concatenated_vecs
4433 .chunks(3)
4434 .map(|v| [v[0].clone(), v[1].clone(), v[2].clone()])
4435 .collect(),
4436 jac,
4437 )
4438 }
4439 ParameterizationMode::HyperSphericalFlat => {
4440 assert!(x[1..].len().is_multiple_of(2));
4442 let mut normal_distributed_xs = vec![];
4443 for x_pair in x[1..].chunks(2) {
4444 let (z1, z2) = box_muller(x_pair[0].clone(), x_pair[1].clone());
4445 normal_distributed_xs.push(z1);
4446 normal_distributed_xs.push(z2);
4447 }
4448 if normal_distributed_xs.len() % 3 != 0 {
4450 normal_distributed_xs.pop();
4451 }
4452 let curr_norm = normal_distributed_xs[..]
4453 .iter()
4454 .map(|x| x.square())
4455 .reduce(|acc, e| acc + &e)
4456 .unwrap_or(zero.clone())
4457 .sqrt();
4458 let surface =
4459 compute_surface_and_volume(normal_distributed_xs.len() - 1, radius.clone())
4460 .0;
4461 jac *= surface;
4462 let rescaling_factor = &radius / &curr_norm;
4463 (
4464 normal_distributed_xs
4465 .chunks(3)
4466 .map(|v| {
4467 [
4468 &v[0] * &rescaling_factor,
4469 &v[1] * &rescaling_factor,
4470 &v[2] * &rescaling_factor,
4471 ]
4472 })
4473 .collect(),
4474 jac,
4475 )
4476 }
4477 _ => unreachable!(),
4478 }
4479 }
4480 ParameterizationMode::Cartesian | ParameterizationMode::Spherical => {
4481 let mut jac = one.clone();
4482 let mut vecs = Vec::with_capacity(x.len() / 3);
4483 for xi in x.chunks(3) {
4484 let (vec_i, jac_i) = parameterize3d(xi, e_cm.clone(), settings);
4485 vecs.push(vec_i);
4486 jac *= jac_i;
4487 }
4488 (vecs, jac)
4489 }
4490 ParameterizationMode::RelativeSpherical => {
4491 if x.len() != 6 {
4492 panic!(
4493 "relative_spherical parameterization currently requires exactly two loop three-momenta"
4494 );
4495 }
4496 let spherical_settings = ParameterizationSettings {
4497 mode: ParameterizationMode::Spherical,
4498 mapping: settings.mapping.clone(),
4499 b: settings.b,
4500 power: settings.power,
4501 lmb_basis_ids: Default::default(),
4502 sampling_channels: Default::default(),
4503 };
4504 let (common, common_jac) = parameterize3d(&x[0..3], e_cm.clone(), &spherical_settings);
4505 let (relative, relative_jac) =
4506 parameterize3d(&x[3..6], e_cm.clone(), &spherical_settings);
4507 let half = &one / one.from_i64(2);
4508 let first = [
4509 &common[0] + &half * &relative[0],
4510 &common[1] + &half * &relative[1],
4511 &common[2] + &half * &relative[2],
4512 ];
4513 let second = [
4514 &common[0] - &half * &relative[0],
4515 &common[1] - &half * &relative[1],
4516 &common[2] - &half * &relative[2],
4517 ];
4518 (vec![first, second], common_jac * relative_jac)
4519 }
4520 ParameterizationMode::SphericalCommonRadial => {
4521 if x.is_empty() || !x.len().is_multiple_of(3) {
4522 panic!(
4523 "spherical_common_radial parameterization requires complete loop three-momenta"
4524 );
4525 }
4526 let n_loop_momenta = x.len() / 3;
4527
4528 let mut jac = one.clone();
4529 let radius = match settings.mapping {
4530 ParameterizationMapping::Log => {
4531 let b = F::<T>::from_f64(settings.b);
4532 let radius = &e_cm * (&one + &b * &x[0] / (&one - &x[0])).log();
4533 jac *= &e_cm * &b / (&one - &x[0]) / (&one + &x[0] * (&b - &one));
4534 radius
4535 }
4536 ParameterizationMapping::Power => {
4537 let b = F::<T>::from_f64(settings.b);
4538 let power = F::<T>::from_f64(settings.power);
4539 let odds = &x[0] / (&one - &x[0]);
4540 let power_minus_one = &power - &one;
4541 let radius = &e_cm * &b * odds.powf(&power);
4542 jac *=
4543 &e_cm * &b * &power * odds.powf(&power_minus_one) / (&one - &x[0]).square();
4544 radius
4545 }
4546 ParameterizationMapping::Linear => {
4547 let b = F::<T>::from_f64(settings.b);
4548 let radius = &e_cm * &b * &x[0] / (&one - &x[0]);
4549 jac *= (&e_cm * &b + &radius).powi(2) / &e_cm / &b;
4550 radius
4551 }
4552 };
4553
4554 let mut remaining_fraction = one.clone();
4555 let mut stick_jac = one.clone();
4556 let mut radii = Vec::with_capacity(n_loop_momenta);
4557 for i in 0..n_loop_momenta {
4558 let fraction = if i + 1 == n_loop_momenta {
4559 remaining_fraction.clone()
4560 } else {
4561 stick_jac *= &remaining_fraction;
4562 let fraction = &remaining_fraction * &x[1 + i];
4563 remaining_fraction *= &one - &x[1 + i];
4564 fraction
4565 };
4566 radii.push(&radius * fraction);
4567 }
4568
4569 jac *= radius.pow((n_loop_momenta - 1) as u64) * stick_jac;
4570 for radius_i in &radii {
4571 jac *= radius_i.square();
4572 }
4573
4574 let angle_offset = n_loop_momenta;
4575 let mut momenta = Vec::with_capacity(n_loop_momenta);
4576 for (i, radius_i) in radii.iter().enumerate() {
4577 let phi = zero.from_i64(2) * zero.PI() * &x[angle_offset + 2 * i];
4578 jac *= zero.from_i64(2) * zero.PI();
4579 let cos_theta = -&one + zero.from_i64(2) * &x[angle_offset + 2 * i + 1];
4580 jac *= zero.from_i64(2);
4581 let sin_theta = (&one - cos_theta.square()).sqrt();
4582 momenta.push([
4583 radius_i * &sin_theta * phi.cos(),
4584 radius_i * &sin_theta * phi.sin(),
4585 radius_i * cos_theta,
4586 ]);
4587 }
4588
4589 (momenta, jac)
4590 }
4591 ParameterizationMode::SphericalProductCommonRadial => {
4592 if x.len() != 6 {
4593 panic!(
4594 "spherical_product_common_radial parameterization currently requires exactly two loop three-momenta"
4595 );
4596 }
4597 let mut branch_x = x.to_vec();
4598 let branch_settings = if x[0] < (&one / one.from_i64(2)) {
4599 branch_x[0] = &x[0] * zero.from_i64(2);
4600 ParameterizationSettings {
4601 mode: ParameterizationMode::Spherical,
4602 mapping: settings.mapping.clone(),
4603 b: settings.b,
4604 power: settings.power,
4605 lmb_basis_ids: Default::default(),
4606 sampling_channels: Default::default(),
4607 }
4608 } else {
4609 branch_x[0] = (&x[0] - (&one / one.from_i64(2))) * zero.from_i64(2);
4610 ParameterizationSettings {
4611 mode: ParameterizationMode::SphericalCommonRadial,
4612 mapping: settings.mapping.clone(),
4613 b: settings.b,
4614 power: settings.power,
4615 lmb_basis_ids: Default::default(),
4616 sampling_channels: Default::default(),
4617 }
4618 };
4619 let (momenta, jac) = global_parameterize(&branch_x, e_cm, &branch_settings);
4620 (momenta, jac * zero.from_i64(2))
4621 }
4622 ParameterizationMode::MomentumSpace => (x.as_chunks::<3>().0.to_vec(), one),
4623 }
4624}
4625
4626#[allow(unused)]
4627pub(crate) fn global_inv_parameterize<T: FloatLike>(
4628 moms: &[ThreeMomentum<F<T>>],
4629 e_cm: F<T>,
4630 settings: &ParameterizationSettings,
4631) -> (Vec<F<T>>, F<T>) {
4632 let one = e_cm.one();
4633 let zero = one.zero();
4634
4635 match settings.mode {
4636 ParameterizationMode::HyperSpherical => {
4637 let mut inv_jac = one.clone();
4638 let mut xs = Vec::with_capacity(moms.len() * 3);
4639
4640 let cartesian_xs = moms
4641 .iter()
4642 .flat_map(|lv| lv.clone().into_iter())
4643 .collect::<Vec<F<T>>>();
4644
4645 let mut k_r_sq = cartesian_xs
4646 .iter()
4647 .map(|xi| xi.square())
4648 .reduce(|acc, e| acc + &e)
4649 .unwrap_or(zero.clone());
4650 if k_r_sq.is_zero() {
4652 return (vec![zero.clone(); cartesian_xs.len()], zero);
4653 }
4654 let k_r = k_r_sq.sqrt();
4655 match settings.mapping {
4656 ParameterizationMapping::Log => {
4657 let b = F::<T>::from_f64(settings.b);
4658 let x1 = &one - &b / (-&one + &b + (&k_r / &e_cm).exp());
4659 inv_jac /= e_cm * &b / (&one - &x1) / (&one + &x1 * (&b - &one));
4660 xs.push(x1);
4661 }
4662 ParameterizationMapping::Power => {
4663 let b = F::<T>::from_f64(settings.b);
4664 let power = F::<T>::from_f64(settings.power);
4665 let inv_power = &one / &power;
4666 let odds = (&k_r / (&e_cm * &b)).powf(&inv_power);
4667 let x1 = &odds / (&one + &odds);
4668 let power_minus_one = &power - &one;
4669 inv_jac /=
4670 &e_cm * &b * &power * odds.powf(&power_minus_one) / (&one - &x1).square();
4671 xs.push(x1);
4672 }
4673 ParameterizationMapping::Linear => {
4674 let b = F::<T>::from_f64(settings.b);
4675 inv_jac /= (&e_cm * &b + &k_r).powi(2) / &e_cm / &b;
4676 xs.push(&k_r / (&e_cm * &b + &k_r));
4677 }
4678 };
4679
4680 let y = cartesian_xs[cartesian_xs.len() - 2].clone();
4681 let x = cartesian_xs[cartesian_xs.len() - 1].clone();
4682 let xphi = if x < zero {
4683 &one + (&one / one.from_i64(2)) * zero.FRAC_1_PI() * x.atan2(&y)
4684 } else {
4685 (&one / one.from_i64(2)) * zero.FRAC_1_PI() * x.atan2(&y)
4686 };
4687 xs.push(xphi);
4688 inv_jac /= zero.from_i64(2) * zero.PI();
4689
4690 for (i, x) in cartesian_xs[..cartesian_xs.len() - 2].iter().enumerate() {
4691 xs.push((&one / one.from_i64(2)) * (&one + x / k_r_sq.sqrt()));
4692 inv_jac /= zero.from_i64(2);
4693 let angular_power = cartesian_xs.len() - 3 - i;
4694 if angular_power > 0 {
4695 inv_jac /= (&one - (x * x / &k_r_sq)).sqrt().pow(angular_power as u64);
4696 }
4697 k_r_sq -= x * x;
4698 }
4699
4700 inv_jac /= k_r.powi((cartesian_xs.len() - 1) as i32);
4701
4702 (xs, inv_jac)
4703 }
4704 ParameterizationMode::HyperSphericalFlat => {
4705 panic!(
4706 "Inverse of flat hyperspherical sampling is not available since it is not bijective."
4707 );
4708 }
4709 ParameterizationMode::Cartesian | ParameterizationMode::Spherical => {
4710 let mut inv_jac = one;
4711 let mut xs = Vec::with_capacity(moms.len() * 3);
4712 for (i, mom) in moms.iter().enumerate() {
4713 let (xs_i, inv_jac_i) = inv_parametrize3d(mom, e_cm.clone(), settings);
4714
4715 xs.extend(xs_i);
4716 inv_jac *= inv_jac_i;
4717 }
4718 (xs, inv_jac)
4719 }
4720 ParameterizationMode::RelativeSpherical => {
4721 if moms.len() != 2 {
4722 panic!(
4723 "relative_spherical inverse parameterization currently requires exactly two loop three-momenta"
4724 );
4725 }
4726 let half = &one / one.from_i64(2);
4727 let common = ThreeMomentum::new(
4728 (&moms[0].px + &moms[1].px) * &half,
4729 (&moms[0].py + &moms[1].py) * &half,
4730 (&moms[0].pz + &moms[1].pz) * &half,
4731 );
4732 let relative = ThreeMomentum::new(
4733 &moms[0].px - &moms[1].px,
4734 &moms[0].py - &moms[1].py,
4735 &moms[0].pz - &moms[1].pz,
4736 );
4737 let spherical_settings = ParameterizationSettings {
4738 mode: ParameterizationMode::Spherical,
4739 mapping: settings.mapping.clone(),
4740 b: settings.b,
4741 power: settings.power,
4742 lmb_basis_ids: Default::default(),
4743 sampling_channels: Default::default(),
4744 };
4745 let (common_xs, common_inv_jac) =
4746 inv_parametrize3d(&common, e_cm.clone(), &spherical_settings);
4747 let (relative_xs, relative_inv_jac) =
4748 inv_parametrize3d(&relative, e_cm.clone(), &spherical_settings);
4749 (
4750 common_xs.into_iter().chain(relative_xs).collect(),
4751 common_inv_jac * relative_inv_jac,
4752 )
4753 }
4754 ParameterizationMode::SphericalCommonRadial => {
4755 if moms.is_empty() {
4756 panic!(
4757 "spherical_common_radial inverse parameterization requires at least one loop three-momentum"
4758 );
4759 }
4760
4761 let phi_x = |x: &F<T>, y: &F<T>| {
4762 if y < &zero {
4763 &one + (&one / one.from_i64(2)) * zero.FRAC_1_PI() * y.atan2(x)
4764 } else {
4765 (&one / one.from_i64(2)) * zero.FRAC_1_PI() * y.atan2(x)
4766 }
4767 };
4768
4769 let radii_sq = moms
4770 .iter()
4771 .map(|mom| mom.px.square() + mom.py.square() + mom.pz.square())
4772 .collect::<Vec<_>>();
4773 let radii = radii_sq
4774 .iter()
4775 .map(|radius_sq| radius_sq.sqrt())
4776 .collect::<Vec<_>>();
4777 let radius = radii
4778 .iter()
4779 .fold(zero.clone(), |acc, radius_i| acc + radius_i);
4780
4781 let mut xs = Vec::with_capacity(3 * moms.len());
4782 if radius.is_zero() || radii.iter().any(F::is_zero) {
4783 xs.resize(3 * moms.len(), zero.clone());
4784 return (xs, zero);
4785 }
4786
4787 let mut inv_jac = one.clone();
4788 let radial_x = match settings.mapping {
4789 ParameterizationMapping::Log => {
4790 let b = F::<T>::from_f64(settings.b);
4791 let x1 = &one - &b / (-&one + &b + (&radius / &e_cm).exp());
4792 inv_jac /= &e_cm * &b / (&one - &x1) / (&one + &x1 * (&b - &one));
4793 x1
4794 }
4795 ParameterizationMapping::Power => {
4796 let b = F::<T>::from_f64(settings.b);
4797 let power = F::<T>::from_f64(settings.power);
4798 let inv_power = &one / &power;
4799 let odds = (&radius / (&e_cm * &b)).powf(&inv_power);
4800 let x1 = &odds / (&one + &odds);
4801 let power_minus_one = &power - &one;
4802 inv_jac /=
4803 &e_cm * &b * &power * odds.powf(&power_minus_one) / (&one - &x1).square();
4804 x1
4805 }
4806 ParameterizationMapping::Linear => {
4807 let b = F::<T>::from_f64(settings.b);
4808 inv_jac /= (&e_cm * &b + &radius).powi(2) / &e_cm / &b;
4809 &radius / (&e_cm * &b + &radius)
4810 }
4811 };
4812
4813 xs.push(radial_x);
4814 let mut remaining_fraction = one.clone();
4815 let mut stick_jac = one.clone();
4816 for (i, radius_i) in radii.iter().enumerate().take(moms.len() - 1) {
4817 let fraction = radius_i / &radius;
4818 xs.push(&fraction / &remaining_fraction);
4819 stick_jac *= &remaining_fraction;
4820 remaining_fraction -= fraction;
4821 if remaining_fraction.is_zero() && i + 2 < moms.len() {
4822 return (vec![zero.clone(); 3 * moms.len()], zero);
4823 }
4824 }
4825
4826 inv_jac /= radius.pow((moms.len() - 1) as u64) * stick_jac;
4827 for radius_sq in &radii_sq {
4828 inv_jac /= radius_sq;
4829 }
4830
4831 for (mom, radius_i) in moms.iter().zip(radii.iter()) {
4832 xs.push(phi_x(&mom.px, &mom.py));
4833 inv_jac /= zero.from_i64(2) * zero.PI();
4834 xs.push((&one / one.from_i64(2)) * (&one + &mom.pz / radius_i));
4835 inv_jac /= zero.from_i64(2);
4836 }
4837
4838 (xs, inv_jac)
4839 }
4840 ParameterizationMode::SphericalProductCommonRadial => {
4841 if moms.len() != 2 {
4842 panic!(
4843 "spherical_product_common_radial inverse parameterization currently requires exactly two loop three-momenta"
4844 );
4845 }
4846 let spherical_settings = ParameterizationSettings {
4847 mode: ParameterizationMode::Spherical,
4848 mapping: settings.mapping.clone(),
4849 b: settings.b,
4850 power: settings.power,
4851 lmb_basis_ids: Default::default(),
4852 sampling_channels: Default::default(),
4853 };
4854 let common_radial_settings = ParameterizationSettings {
4855 mode: ParameterizationMode::SphericalCommonRadial,
4856 mapping: settings.mapping.clone(),
4857 b: settings.b,
4858 power: settings.power,
4859 lmb_basis_ids: Default::default(),
4860 sampling_channels: Default::default(),
4861 };
4862 let (mut xs, product_inv_jac) =
4863 global_inv_parameterize(moms, e_cm.clone(), &spherical_settings);
4864 let (_, common_inv_jac) = global_inv_parameterize(moms, e_cm, &common_radial_settings);
4865 if let Some(first_x) = xs.first_mut() {
4866 *first_x = &*first_x / zero.from_i64(2);
4867 }
4868 (xs, product_inv_jac + common_inv_jac)
4869 }
4870 ParameterizationMode::MomentumSpace => (
4871 moms.iter()
4872 .flat_map(|mom| [mom.px.clone(), mom.py.clone(), mom.pz.clone()])
4873 .collect(),
4874 one,
4875 ),
4876 }
4877}
4878
4879pub(crate) fn parameterize3d<T: FloatLike>(
4882 x: &[F<T>],
4883 e_cm: F<T>,
4884 settings: &ParameterizationSettings,
4885) -> ([F<T>; 3], F<T>) {
4886 let zero = e_cm.zero();
4887 let one = zero.one();
4888 let mut l_space = [zero.clone(), zero.clone(), zero.clone()];
4889 let mut jac = one.clone();
4890
4891 match settings.mode {
4892 ParameterizationMode::Cartesian => match settings.mapping {
4893 ParameterizationMapping::Log => {
4894 for i in 0..3 {
4895 let x = x[i].clone();
4896 l_space[i] = &e_cm * (&x / (&one - &x)).log();
4897 jac *= &e_cm / (&x - &x * &x);
4898 }
4899 }
4900 ParameterizationMapping::Linear => {
4901 for i in 0..3 {
4902 let x = x[i].clone();
4903 l_space[i] = &e_cm * (&one / (&one - &x) - &one / &x);
4904 jac *= &e_cm * (&one / (&x * &x) + &one / ((&one - &x) * (&one - &x)));
4905 }
4906 }
4907 ParameterizationMapping::Power => {
4908 panic!("Power radial mapping is only supported for spherical coordinates");
4909 }
4910 },
4911 ParameterizationMode::Spherical => {
4912 let radius = match settings.mapping {
4913 ParameterizationMapping::Log => {
4914 let x = x[0].clone();
4916 let b = F::<T>::from_f64(settings.b);
4917 let radius = &e_cm * (&one + &b * &x / (&one - &x)).log();
4918 jac *= &e_cm * &b / (&one - &x) / (&one + &x * (&b - &one));
4919
4920 radius
4921 }
4922 ParameterizationMapping::Power => {
4923 let x = x[0].clone();
4925 let b = F::<T>::from_f64(settings.b);
4926 let power = F::<T>::from_f64(settings.power);
4927 let odds = &x / (&one - &x);
4928 let power_minus_one = &power - &one;
4929 let radius = &e_cm * &b * odds.powf(&power);
4930 jac *= &e_cm * &b * &power * odds.powf(&power_minus_one) / (&one - &x).square();
4931
4932 radius
4933 }
4934 ParameterizationMapping::Linear => {
4935 let b = F::<T>::from_f64(settings.b);
4937 let radius = &e_cm * &b * &x[0] / (&one - &x[0]);
4938 jac *= (&e_cm * &b + &radius).powi(2) / &e_cm / &b;
4939 radius
4940 }
4941 };
4942 let phi = zero.from_i64(2) * zero.PI() * &x[1];
4943 jac *= zero.from_i64(2) * zero.PI();
4944
4945 let cos_theta = -&one + zero.from_i64(2) * &x[2]; jac *= zero.from_i64(2);
4947 let sin_theta = (&one - cos_theta.square()).sqrt();
4948
4949 l_space[0] = &radius * &sin_theta * phi.cos();
4950 l_space[1] = &radius * &sin_theta * phi.sin();
4951 l_space[2] = &radius * &cos_theta;
4952
4953 jac *= radius.square(); }
4955 _ => {
4956 panic!(
4957 "Inappropriate parameterization mapping specified for parameterize: {:?}.",
4958 settings.mode.clone()
4959 );
4960 }
4961 }
4962
4963 (l_space, jac)
4964}
4965
4966pub(crate) fn inv_parametrize3d<T: FloatLike>(
4967 mom: &ThreeMomentum<F<T>>,
4968 e_cm: F<T>,
4969 settings: &ParameterizationSettings,
4970) -> ([F<T>; 3], F<T>) {
4971 let one = e_cm.one();
4972 let zero = one.zero();
4973 if settings.mode == ParameterizationMode::Cartesian {
4974 let two = one.from_i64(2);
4975 let mut coordinates = [zero.clone(), zero.clone(), zero.clone()];
4976 let mut jac = one.clone();
4977 for (coordinate, momentum) in coordinates.iter_mut().zip([&mom.px, &mom.py, &mom.pz]) {
4978 let scaled = momentum / &e_cm;
4979 let magnitude = scaled.abs();
4980 let lower = match settings.mapping {
4982 ParameterizationMapping::Log => {
4983 let exponential = (-magnitude).exp();
4984 &exponential / (&one + &exponential)
4985 }
4986 ParameterizationMapping::Linear => {
4987 if magnitude > one {
4988 let reciprocal = &one / &magnitude;
4989 let twice_reciprocal = &two * reciprocal;
4990 &twice_reciprocal
4991 / ((&one + twice_reciprocal.square()).sqrt() + &one + &twice_reciprocal)
4992 } else {
4993 &two / ((magnitude.square() + two.square()).sqrt() + magnitude + &two)
4994 }
4995 }
4996 ParameterizationMapping::Power => {
4997 panic!("Power radial mapping is only supported for spherical coordinates");
4998 }
4999 };
5000 let upper = &one - &lower;
5001 jac *= match settings.mapping {
5002 ParameterizationMapping::Log => &lower * &upper / &e_cm,
5003 ParameterizationMapping::Linear => {
5004 lower.square() * upper.square() / (&e_cm * (lower.square() + upper.square()))
5005 }
5006 ParameterizationMapping::Power => unreachable!(),
5007 };
5008 *coordinate = if scaled < zero { lower } else { upper };
5009 }
5010 return (coordinates, jac);
5011 }
5012 if settings.mode != ParameterizationMode::Spherical {
5013 panic!("Inverse mapping requires cartesian or spherical coordinates");
5014 }
5015
5016 let mut jac = one.clone();
5017
5018 let x = &mom.px;
5019 let y = &mom.py;
5020 let z = &mom.pz;
5021
5022 let k_r_sq = x.square() + y.square() + z.square();
5023 let k_r = k_r_sq.sqrt();
5024
5025 let x2 = if y < &zero {
5026 &one + (&one / one.from_i64(2)) * zero.FRAC_1_PI() * y.atan2(x)
5027 } else {
5028 (&one / one.from_i64(2)) * zero.FRAC_1_PI() * y.atan2(x)
5029 };
5030
5031 if k_r_sq.is_zero() {
5033 return ([zero.clone(), x2.clone(), zero.clone()], zero.clone());
5034 }
5035
5036 let x1 = match settings.mapping {
5037 ParameterizationMapping::Log => {
5038 let b = F::<T>::from_f64(settings.b);
5039 let x1 = &one - &b / (-&one + &b + (&k_r / &e_cm).exp());
5040 jac /= &e_cm * &b / (&one - &x1) / (&one + &x1 * (&b - &one));
5041 x1
5042 }
5043 ParameterizationMapping::Power => {
5044 let b = F::<T>::from_f64(settings.b);
5045 let power = F::<T>::from_f64(settings.power);
5046 let inv_power = &one / &power;
5047 let odds = (&k_r / (&e_cm * &b)).powf(&inv_power);
5048 let x1 = &odds / (&one + &odds);
5049 let power_minus_one = &power - &one;
5050 jac /= &e_cm * &b * &power * odds.powf(&power_minus_one) / (&one - &x1).square();
5051 x1
5052 }
5053 ParameterizationMapping::Linear => {
5054 let b = F::<T>::from_f64(settings.b);
5055 jac /= (&e_cm * &b + &k_r).powi(2) / &e_cm / &b;
5056 &k_r / (&e_cm * &b + &k_r)
5057 }
5058 };
5059
5060 let x3 = (&one / one.from_i64(2)) * (&one + z / &k_r);
5061 jac /= zero.from_i64(2) * zero.PI();
5062 jac /= zero.from_i64(2);
5063 jac /= k_r.square();
5064
5065 let x = [x1, x2, x3];
5066
5067 (x, jac)
5068}
5069
5070pub const MINUTE: usize = 60;
5071pub const HOUR: usize = 3_600;
5072pub const DAY: usize = 86_400;
5073pub const WEEK: usize = 604_800;
5074pub(crate) fn format_wdhms(seconds: usize) -> String {
5075 let mut compound_duration = vec![];
5076 if seconds == 0 {
5077 compound_duration.push("0s".to_string());
5078 return compound_duration.join(" ");
5079 }
5080
5081 let mut sec = seconds % WEEK;
5082 let ws = seconds / WEEK;
5084 if ws != 0 {
5085 compound_duration.push(format!("{ws}w"));
5086 }
5087
5088 let ds = sec / DAY;
5090 sec %= DAY;
5091 if ds != 0 {
5092 compound_duration.push(format!("{ds}d"));
5093 }
5094
5095 let hs = sec / HOUR;
5097 sec %= HOUR;
5098 if hs != 0 {
5099 compound_duration.push(format!("{hs}h"));
5100 }
5101
5102 let ms = sec / MINUTE;
5104 sec %= MINUTE;
5105 if ms != 0 {
5106 compound_duration.push(format!("{ms}m"));
5107 }
5108
5109 if sec != 0 {
5111 compound_duration.push(format!("{sec}s"));
5112 }
5113
5114 compound_duration.join(" ")
5115}
5116
5117pub(crate) fn format_wdhms_from_duration(duration: Duration) -> String {
5118 format_wdhms(duration.as_secs() as usize)
5119}
5120
5121#[allow(unused)]
5122pub(crate) fn inv_3x3_sig_matrix(mat: [[isize; 3]; 3]) -> [[isize; 3]; 3] {
5123 let denom = -mat[0][2] * mat[1][1] * mat[2][0]
5124 + mat[0][1] * mat[1][2] * mat[2][0]
5125 + mat[0][2] * mat[1][0] * mat[2][1]
5126 - mat[0][0] * mat[1][2] * mat[2][1]
5127 - mat[0][1] * mat[1][0] * mat[2][2]
5128 + mat[0][0] * mat[1][1] * mat[2][2];
5129 if denom != 1 && denom != -1 {
5130 panic!("Non invertible signature matrix.");
5131 }
5132 let mut inv_mat = [[0; 3]; 3];
5133 inv_mat[0][0] = (-mat[1][2] * mat[2][1] + mat[1][1] * mat[2][2]) * denom;
5134 inv_mat[0][1] = (mat[0][2] * mat[2][1] - mat[0][1] * mat[2][2]) * denom;
5135 inv_mat[0][2] = (-mat[0][2] * mat[1][1] + mat[0][1] * mat[1][2]) * denom;
5136 inv_mat[1][0] = (mat[1][2] * mat[2][0] - mat[1][0] * mat[2][2]) * denom;
5137 inv_mat[1][1] = (-mat[0][2] * mat[2][0] + mat[0][0] * mat[2][2]) * denom;
5138 inv_mat[1][2] = (mat[0][2] * mat[1][0] - mat[0][0] * mat[1][2]) * denom;
5139 inv_mat[2][0] = (-mat[1][1] * mat[2][0] + mat[1][0] * mat[2][1]) * denom;
5140 inv_mat[2][1] = (mat[0][1] * mat[2][0] - mat[0][0] * mat[2][1]) * denom;
5141 inv_mat[2][2] = (-mat[0][1] * mat[1][0] + mat[0][0] * mat[1][1]) * denom;
5142
5143 inv_mat
5144}
5145
5146pub mod index_vec;
5147
5148#[allow(unused)]
5149pub(crate) fn format_for_compare_digits(x: F<f64>, y: F<f64>) -> (String, String) {
5150 let mut string_x = format!("{:.16e}", x);
5151 let mut string_y = format!("{:.16e}", y);
5152
5153 #[allow(clippy::comparison_chain)]
5154 if string_x.len() > string_y.len() {
5155 for _ in 0..(string_x.len() - string_y.len()) {
5156 string_y.push(' ');
5157 }
5158 } else if string_y.len() > string_x.len() {
5159 for _ in 0..(string_y.len() - string_x.len()) {
5160 string_x.push(' ');
5161 }
5162 }
5163
5164 let string_vec = string_x
5165 .chars()
5166 .zip(string_y.chars())
5167 .map(|(char_x, char_y)| {
5168 if char_x == char_y {
5169 (char_x.to_string().green(), char_y.to_string().green())
5170 } else {
5171 (char_x.to_string().red(), char_y.to_string().red())
5172 }
5173 })
5174 .collect_vec();
5175
5176 let string_x = string_vec.iter().map(|(x, _)| x).join("");
5177 let string_y = string_vec.iter().map(|(_, y)| y).join("");
5178
5179 (string_x, string_y)
5180}
5181
5182#[allow(unused)]
5183pub(crate) fn format_evaluation_time(time: Duration) -> String {
5184 let seconds = time.as_secs_f64();
5185 let (value, unit) = if seconds >= 1.0 {
5186 (seconds, "s")
5187 } else if seconds >= 1.0e-3 {
5188 (seconds * 1.0e3, "ms")
5189 } else {
5190 (seconds * 1.0e6, "µs")
5191 };
5192
5193 let precision = if value >= 100.0 {
5194 0
5195 } else if value >= 10.0 {
5196 1
5197 } else {
5198 2
5199 };
5200
5201 format!("{value:.precision$} {unit}")
5202}
5203
5204pub(crate) fn duration_from_secs_f64_saturating(time: f64) -> Duration {
5205 if time.is_nan() || time <= 0.0 {
5206 return Duration::ZERO;
5207 }
5208
5209 if time.is_infinite() {
5210 return Duration::MAX;
5211 }
5212
5213 let max_seconds = Duration::MAX.as_secs();
5214 if time >= max_seconds as f64 {
5215 return Duration::MAX;
5216 }
5217
5218 let seconds = time.trunc() as u64;
5219 let fractional = (time - seconds as f64).clamp(0.0, 0.999_999_999_999);
5220 let nanos = (fractional * 1_000_000_000.0) as u32;
5221 Duration::new(seconds, nanos)
5222}
5223
5224pub(crate) fn format_evaluation_time_from_f64(time: f64) -> String {
5225 format_evaluation_time(duration_from_secs_f64_saturating(time))
5226}
5227
5228pub(crate) fn normalize_tabled_separator_rows(rendered: &str) -> String {
5229 let original_lines = rendered.lines().collect_vec();
5230 let visible_lines = original_lines
5231 .iter()
5232 .map(|line| strip_ansi_escape_codes(line).chars().collect_vec())
5233 .collect_vec();
5234
5235 original_lines
5236 .iter()
5237 .enumerate()
5238 .map(|(row_index, line)| {
5239 let visible_line = &visible_lines[row_index];
5240 if is_top_border_row(visible_line) {
5241 return rebuild_top_border_row(&visible_lines, row_index);
5242 }
5243
5244 if is_internal_separator_row(visible_line) {
5245 return rebuild_internal_separator_row(&visible_lines, row_index);
5246 }
5247
5248 if is_bottom_border_row(visible_line) {
5249 return rebuild_bottom_border_row(&visible_lines, row_index);
5250 }
5251
5252 (*line).to_string()
5253 })
5254 .join("\n")
5255}
5256
5257fn rebuild_top_border_row(visible_lines: &[Vec<char>], row_index: usize) -> String {
5258 rebuild_border_row(visible_lines, row_index, '╭', '╮', |_, _, has_below| {
5259 if has_below { '┬' } else { '─' }
5260 })
5261}
5262
5263fn rebuild_internal_separator_row(visible_lines: &[Vec<char>], row_index: usize) -> String {
5264 rebuild_border_row(
5265 visible_lines,
5266 row_index,
5267 '├',
5268 '┤',
5269 |row_index, column, has_below| match (
5270 row_index > 0 && has_vertical_border(&visible_lines[row_index - 1], column),
5271 has_below,
5272 ) {
5273 (false, false) => '─',
5274 (false, true) => '┬',
5275 (true, false) => '┴',
5276 (true, true) => '┼',
5277 },
5278 )
5279}
5280
5281fn rebuild_bottom_border_row(visible_lines: &[Vec<char>], row_index: usize) -> String {
5282 rebuild_border_row(
5283 visible_lines,
5284 row_index,
5285 '╰',
5286 '╯',
5287 |row_index, column, _| {
5288 if row_index > 0 && has_vertical_border(&visible_lines[row_index - 1], column) {
5289 '┴'
5290 } else {
5291 '─'
5292 }
5293 },
5294 )
5295}
5296
5297fn rebuild_border_row<F>(
5298 visible_lines: &[Vec<char>],
5299 row_index: usize,
5300 left: char,
5301 right: char,
5302 mut intersection_for_column: F,
5303) -> String
5304where
5305 F: FnMut(usize, usize, bool) -> char,
5306{
5307 let border = &visible_lines[row_index];
5308 if border.len() < 2 {
5309 return border.iter().collect();
5310 }
5311
5312 let last = border.len() - 1;
5313 let mut rebuilt = String::with_capacity(border.len());
5314 rebuilt.push(left);
5315
5316 for column in 1..last {
5317 let has_vertical_below = row_index + 1 < visible_lines.len()
5318 && has_vertical_border(&visible_lines[row_index + 1], column);
5319 rebuilt.push(intersection_for_column(
5320 row_index,
5321 column,
5322 has_vertical_below,
5323 ));
5324 }
5325
5326 rebuilt.push(right);
5327 rebuilt
5328}
5329
5330fn has_vertical_border(line: &[char], column: usize) -> bool {
5331 line.get(column)
5332 .copied()
5333 .is_some_and(|ch| matches!(ch, '│' | '├' | '┤' | '┬' | '┴' | '┼'))
5334}
5335
5336fn is_internal_separator_row(line: &[char]) -> bool {
5337 matches!(line.first(), Some('├')) && matches!(line.last(), Some('┤'))
5338}
5339
5340fn is_top_border_row(line: &[char]) -> bool {
5341 matches!(line.first(), Some('╭')) && matches!(line.last(), Some('╮'))
5342}
5343
5344fn is_bottom_border_row(line: &[char]) -> bool {
5345 matches!(line.first(), Some('╰')) && matches!(line.last(), Some('╯'))
5346}
5347
5348fn strip_ansi_escape_codes(line: &str) -> String {
5349 let mut stripped = String::with_capacity(line.len());
5350 let mut chars = line.chars().peekable();
5351
5352 while let Some(ch) = chars.next() {
5353 if ch == '\u{1b}' && chars.peek() == Some(&'[') {
5354 chars.next();
5355 for code in chars.by_ref() {
5356 if ('@'..='~').contains(&code) {
5357 break;
5358 }
5359 }
5360 continue;
5361 }
5362
5363 stripped.push(ch);
5364 }
5365
5366 stripped
5367}
5368
5369pub(crate) fn into_complex_ff64<T: FloatLike>(c: &Complex<F<T>>) -> Complex<F<f64>> {
5370 Complex::new(c.re.into_ff64(), c.im.into_ff64())
5371}
5372
5373#[test]
5374fn exact_binary_cube_promotion_preserves_original_value_and_settings_policy() {
5375 fn check<T: FloatLike>() {
5376 let one = F::<T>::from_f64(1.0);
5377 let exact = F(T::from_f64_exact_binary(0.1));
5378 let expected = one.from_i64(3602879701896397) / one.from_i64(2).powi(55);
5379 assert_eq!(exact, expected);
5380 assert_eq!(exact.into_f64().to_bits(), 0.1_f64.to_bits());
5381 if one.epsilon() < F::<T>::from_f64(f64::EPSILON) {
5382 let settings_value = F::<T>::from_f64(0.1);
5383 assert!(exact > settings_value);
5384 assert!((settings_value - &one / one.from_i64(10)).abs() <= one.epsilon());
5385 }
5386 }
5387 check::<f64>();
5388 check::<QuadFloat>();
5389 check::<ArbPrec>();
5390}
5391
5392#[test]
5393fn decimal_promotion_cache_preserves_parsing_and_precision() {
5394 let mut inputs = vec![
5397 0.0,
5398 -0.0,
5399 0.1,
5400 -0.3,
5401 1.0,
5402 0.5,
5403 f64::MIN_POSITIVE,
5404 f64::from_bits(1),
5405 f64::MAX,
5406 -f64::MAX,
5407 f64::INFINITY,
5408 f64::NEG_INFINITY,
5409 f64::NAN,
5410 ];
5411 inputs.extend((0..1200).map(|i| f64::from_bits(0x3fb999999999999a + i)));
5412 for _ in 0..2 {
5413 for &input in &inputs {
5414 let expected: QuadFloat = if input.is_finite() {
5415 SymbolicaFloat::parse(
5416 &input.to_string(),
5417 Some(DoubleFloat::default().get_precision()),
5418 )
5419 .unwrap()
5420 .into()
5421 } else {
5422 QuadFloat::from_f64_exact_binary(input)
5423 };
5424 for _ in 0..2 {
5427 let actual = QuadFloat::from_f64_decimal(input);
5428 if input.is_nan() {
5429 assert!(F(actual).is_nan());
5430 } else {
5431 assert_eq!(actual, expected);
5432 assert_eq!(
5433 F(actual).into_f64().to_bits(),
5434 F(expected).into_f64().to_bits()
5435 );
5436 }
5437 }
5438 for precision in [256, 1000, 256, 1000] {
5441 let actual = match precision {
5442 256 => VarFloat::<256>::from_f64_decimal(input).float,
5443 1000 => VarFloat::<1000>::from_f64_decimal(input).float,
5444 _ => unreachable!(),
5445 };
5446 let expected = if input.is_finite() {
5447 Float::with_val(precision, Float::parse(input.to_string()).unwrap())
5448 } else {
5449 Float::with_val(precision, input)
5450 };
5451 assert_eq!(actual.prec(), precision);
5452 if input.is_nan() {
5453 assert!(actual.is_nan());
5454 } else {
5455 assert_eq!(actual, expected);
5456 assert_eq!(actual.is_sign_negative(), expected.is_sign_negative());
5457 }
5458 }
5459 }
5460 }
5461}
5462
5463#[test]
5464fn complex_compare() {
5465 let ltd = Complex::new(0.11773583919739394, -0.22157450463964778).map(F);
5466
5467 let cff = Complex::new(0.11773583589023458, -0.22157450446824836).map(F);
5468
5469 ltd.approx_eq_res(&cff, &F(0.00000001)).unwrap();
5470}
5471
5472#[cfg(test)]
5473mod formatting_tests {
5474 use super::{ArbPrec, F, FloatLike, f128};
5475
5476 fn assert_has_scientific_exponent(rendered: &str) {
5477 let (mantissa, exponent) = rendered
5478 .split_once('e')
5479 .unwrap_or_else(|| panic!("missing exponent marker in: {rendered}"));
5480 assert!(
5481 !mantissa.is_empty(),
5482 "missing mantissa in scientific output: {rendered}"
5483 );
5484 assert!(
5485 exponent.parse::<i32>().is_ok(),
5486 "invalid exponent in scientific output: {rendered}"
5487 );
5488 }
5489
5490 fn assert_fractional_digits(rendered: &str, expected_digits: usize) {
5491 let (mantissa, _) = rendered
5492 .split_once('e')
5493 .unwrap_or_else(|| panic!("missing exponent marker in: {rendered}"));
5494 let (_, fractional) = mantissa
5495 .trim_start_matches(['+', '-'])
5496 .split_once('.')
5497 .unwrap_or_else(|| panic!("missing decimal point in mantissa: {rendered}"));
5498 assert_eq!(
5499 fractional.len(),
5500 expected_digits,
5501 "unexpected number of fractional digits in: {rendered}"
5502 );
5503 }
5504
5505 fn assert_scientific_formatting_for_backend<T: FloatLike>(x: F<T>) {
5506 let plain = format!("{:e}", x.clone());
5507 assert_has_scientific_exponent(&plain);
5508
5509 let plus = format!("{:+e}", x.clone());
5510 assert_has_scientific_exponent(&plus);
5511 assert!(plus.starts_with('+') || plus.starts_with('-'));
5512
5513 let precision = format!("{:+.16e}", x.clone());
5514 assert_has_scientific_exponent(&precision);
5515 assert!(
5516 precision.contains('.'),
5517 "missing decimal point in scientific output: {precision}"
5518 );
5519
5520 let padded = format!("{:>24.6e}", x);
5521 assert!(
5522 padded.len() >= 24,
5523 "formatted width should be at least requested width: {padded}"
5524 );
5525 assert_has_scientific_exponent(padded.trim_start());
5526 }
5527
5528 #[test]
5529 fn display_matches_underlying_f64() {
5530 let value = F(12.5f64);
5531 assert_eq!(format!("{}", value), "12.5");
5532 }
5533
5534 #[test]
5535 fn lower_exp_keeps_exponent_with_precision() {
5536 let rendered = format!("{:+.16e}", F(1.23456789e-8f64));
5537 assert_has_scientific_exponent(&rendered);
5538 assert_fractional_digits(&rendered, 16);
5539 }
5540
5541 #[test]
5542 fn lower_exp_width_and_alignment_behave_as_expected() {
5543 let rendered = format!("{:>20.4e}", F(12.345f64));
5544 assert_eq!(rendered.len(), 20);
5545 assert_has_scientific_exponent(rendered.trim_start());
5546
5547 let left_aligned = format!("{:<20.4e}", F(12.345f64));
5548 assert_eq!(left_aligned.len(), 20);
5549 assert_has_scientific_exponent(left_aligned.trim_end());
5550 }
5551
5552 #[test]
5553 fn lower_exp_sign_and_zero_pad_behave_as_expected() {
5554 let positive = format!("{:+020.4e}", F(12.345f64));
5555 assert_eq!(positive.len(), 20);
5556 assert!(positive.starts_with('+'));
5557 assert_has_scientific_exponent(&positive);
5558
5559 let negative = format!("{:+020.4e}", F(-12.345f64));
5560 assert_eq!(negative.len(), 20);
5561 assert!(negative.starts_with('-'));
5562 assert_has_scientific_exponent(&negative);
5563 }
5564
5565 #[test]
5566 fn lower_exp_precision_controls_fractional_digits() {
5567 let p2 = format!("{:.2e}", F(1.0f64 / 3.0f64));
5568 assert_fractional_digits(&p2, 2);
5569
5570 let p9 = format!("{:.9e}", F(1.0f64 / 3.0f64));
5571 assert_fractional_digits(&p9, 9);
5572 }
5573
5574 #[test]
5575 fn lower_exp_works_across_float_backends() {
5576 assert_scientific_formatting_for_backend(F::<f64>::from_f64(1.23456789e-8));
5577 assert_scientific_formatting_for_backend(F::<f128>::from_f64(1.23456789e-8));
5578 assert_scientific_formatting_for_backend(F::<ArbPrec>::from_f64(1.23456789e-8));
5579 }
5580
5581 #[test]
5582 fn quad_square_matches_multiplication() {
5583 let zero = F::<f128>::from_f64(0.0);
5584 let zero_sq = zero.square();
5585 assert!(!zero_sq.is_nan());
5586 assert!(!zero_sq.is_infinite());
5587 assert_eq!(zero_sq, zero);
5588
5589 let x = F::<f128>::from_f64(500.0);
5590 let x_sq = x.square();
5591 assert!(!x_sq.is_nan());
5592 assert!(!x_sq.is_infinite());
5593 assert_eq!(x_sq, x * x);
5594 assert_eq!(x_sq.sqrt(), x);
5595 }
5596}
5597
5598pub mod symbols;
5599pub use symbols::{GS, W_};
5600
5601type TensorLibStore = RwLock<TensorLibrary<ParamTensor<ExplicitKey<Aind>>, Aind>>;
5602type FunLibStore = SymbolLib<ParamTensor<ShadowedStructure<Aind>>, Wrap>;
5603
5604pub static TENSORLIB: LazyLock<TensorLibStore> = LazyLock::new(|| RwLock::new(hep_lib_atom()));
5605
5606pub static FUN_LIB: LazyLock<FunLibStore> = LazyLock::new(|| {
5607 let mut lib = Wrap::new_lib();
5608 lib.insert(INBUILTS.conj, |a: ParamTensor<ShadowedStructure<Aind>>| {
5609 a.map_data_self(|x| x.conj())
5610 });
5611 lib
5612});
5613
5614pub static PARAM_FUN_LIB: LazyLock<SymbolLib<ParamTensor<ShadowedStructure<Aind>>, Panic>> =
5615 LazyLock::new(|| {
5616 let mut lib = Panic::new_lib();
5617 lib.insert(INBUILTS.conj, |a: ParamTensor<ShadowedStructure<Aind>>| {
5618 a.map_data_self(|x| x.conj())
5619 });
5620 lib
5621 });
5622
5623pub static VAKINT: OnceLock<Result<Vakint>> = OnceLock::new();
5624
5625pub fn init_vakint() -> Result<()> {
5626 let r: &Result<Vakint, eyre::Report> =
5627 VAKINT.get_or_init(|| Vakint::new().map_err(|e| eyre!(e.to_string())));
5628
5629 match r {
5630 Ok(_) => Ok(()),
5631 Err(e) => Err(eyre!(e.to_string())),
5632 }
5633}
5634
5635pub fn vakint() -> Result<&'static Vakint> {
5636 let r = VAKINT
5637 .get()
5638 .ok_or_else(|| eyre!("Vakint not initialised"))?;
5639 match r {
5640 Ok(v) => Ok(v),
5641 Err(e) => Err(eyre!(e.to_string())),
5642 }
5643}
5644
5645impl<T: FloatLike> momtrop::float::MomTropFloat for F<T> {
5646 #[inline]
5647 fn PI(&self) -> Self {
5648 self.PI()
5649 }
5650
5651 #[inline]
5652 fn abs(&self) -> Self {
5653 self.abs()
5654 }
5655
5656 #[inline]
5657 fn cos(&self) -> Self {
5658 <F<T> as Real>::cos(self)
5659 }
5660
5661 #[inline]
5662 fn exp(&self) -> Self {
5663 <F<T> as Real>::exp(self)
5664 }
5665
5666 #[inline]
5667 fn one(&self) -> Self {
5668 <F<T> as SymFloatLike>::one(self)
5669 }
5670
5671 #[inline]
5672 fn from_f64(&self, value: f64) -> Self {
5673 F::from_f64(value)
5674 }
5675
5676 #[inline]
5677 fn from_isize(&self, value: isize) -> Self {
5678 Self(self.0.from_i64(value as i64))
5679 }
5680
5681 #[inline]
5682 fn inv(&self) -> Self {
5683 <F<T> as SymFloatLike>::inv(self)
5684 }
5685
5686 #[inline]
5687 fn ln(&self) -> Self {
5688 <F<T> as Real>::log(self)
5689 }
5690
5691 #[inline]
5692 fn powf(&self, power: &Self) -> Self {
5693 <F<T> as Real>::powf(self, power)
5694 }
5695
5696 #[inline]
5697 fn sin(&self) -> Self {
5698 <F<T> as Real>::sin(self)
5699 }
5700
5701 #[inline]
5702 fn sqrt(&self) -> Self {
5703 <F<T> as Real>::sqrt(self)
5704 }
5705
5706 #[inline]
5707 fn zero(&self) -> Self {
5708 <F<T> as SymFloatLike>::zero(self)
5709 }
5710
5711 #[inline]
5712 fn to_f64(&self) -> f64 {
5713 self.into_f64()
5714 }
5715}
5716
5717pub trait Length {
5718 fn len(&self) -> usize;
5719 fn is_empty(&self) -> bool {
5720 self.len() == 0
5721 }
5722}
5723
5724impl<I, T> Length for TiVec<I, T> {
5725 fn len(&self) -> usize {
5726 self.len()
5727 }
5728
5729 fn is_empty(&self) -> bool {
5730 self.is_empty()
5731 }
5732}
5733
5734impl<T> Length for Vec<T> {
5735 fn len(&self) -> usize {
5736 self.len()
5737 }
5738}
5739
5740pub(crate) fn ose_atom_from_index(index: EdgeIndex) -> Atom {
5741 function!(
5742 GS.ose,
5743 usize::from(index) as i64 )
5745}
5746
5747pub(crate) fn cut_energy(index: EdgeIndex) -> Atom {
5748 function!(GS.energy, usize::from(index) as i64)
5749}
5750
5751pub(crate) fn external_energy_atom_from_index(index: EdgeIndex) -> Atom {
5752 GS.emr_mom(index, Atom::from(ExpandedIndex::from_iter([0])))
5753}
5754
5755pub mod newton_solver;
5756use include_dir::{Dir, include_dir};
5757static BUILTIN_MODELS: Dir = include_dir!("$CARGO_MANIFEST_DIR/../../assets/models/json");
5758
5759pub fn load_generic_model(name: &str) -> Model {
5760 if let Some(file) = BUILTIN_MODELS.get_file(format!("{}/{}.json", name, name)) {
5761 Model::from_str(file.contents_utf8().unwrap().into(), "json").unwrap()
5762 } else {
5763 panic!("Model {} not found in built-in models.", name);
5764 }
5765}
5766
5767#[cfg(test)]
5768pub mod test_utils;
5769
5770#[cfg(feature = "python")]
5771impl<'py> pyo3::IntoPyObject<'py> for F<f64> {
5772 type Target = pyo3::types::PyFloat;
5773 type Output = pyo3::Bound<'py, Self::Target>;
5774 type Error = std::convert::Infallible;
5775
5776 #[inline]
5777 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
5778 Ok(pyo3::types::PyFloat::new(py, self.0))
5779 }
5780}
5781
5782#[cfg(feature = "python")]
5783impl<'py> pyo3::IntoPyObject<'py> for &F<f64> {
5784 type Target = pyo3::types::PyFloat;
5785 type Output = pyo3::Bound<'py, Self::Target>;
5786 type Error = std::convert::Infallible;
5787
5788 #[inline]
5789 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
5790 (*self).into_pyobject(py)
5791 }
5792}
5793
5794#[cfg(feature = "python")]
5795impl<'a, 'py> pyo3::FromPyObject<'a, 'py> for F<f64> {
5796 type Error = pyo3::PyErr;
5797
5798 fn extract(obj: pyo3::Borrowed<'a, 'py, pyo3::types::PyAny>) -> Result<Self, Self::Error> {
5799 obj.extract::<f64>().map(F)
5800 }
5801}