feanor_math/rings/
float_real.rs

1use std::f64::EPSILON;
2
3use crate::ordered::OrderedRing;
4use crate::pid::{EuclideanRing, PrincipalIdealRing};
5use crate::field::Field;
6use crate::integer::{int_cast, IntegerRing, IntegerRingStore};
7use crate::primitive_int::StaticRing;
8use crate::{impl_eq_based_self_iso, ring::*};
9use crate::homomorphism::*;
10use crate::divisibility::{DivisibilityRing, Domain};
11
12use super::rational::{RationalField, RationalFieldBase};
13
14///
15/// An approximate implementation of the real numbers `R`, using 64 bit floating
16/// point numbers.
17/// 
18/// # Warning
19/// 
20/// Since floating point numbers do not exactly represent the real numbers, and this crate follows
21/// a mathematically precise approach, we cannot provide any function related to equality.
22/// In particular, `Real64Base.eq_el(a, b)` is not supported, and will panic. 
23/// Hence, this ring has only limited use within this crate, and is currently only used for
24/// floating-point FFTs and some approximate computations in the LLL algorithm. 
25/// 
26#[derive(Clone, Copy, PartialEq)]
27pub struct Real64Base;
28
29///
30/// [`RingStore`] corresponding to [`Real64Base`]
31/// 
32pub type Real64 = RingValue<Real64Base>;
33
34impl Real64 {
35
36    pub const RING: RingValue<Real64Base> = RingValue::from(Real64Base);
37}
38
39impl Real64Base {
40
41    pub fn is_absolute_approx_eq(&self, lhs: <Self as RingBase>::Element, rhs: <Self as RingBase>::Element, absolute_threshold: f64) -> bool {
42        (lhs - rhs).abs() < absolute_threshold
43    }
44
45    pub fn is_relative_approx_eq(&self, lhs: <Self as RingBase>::Element, rhs: <Self as RingBase>::Element, relative_threshold: f64) -> bool {
46        self.is_absolute_approx_eq(lhs, rhs, (lhs.abs() + rhs.abs()) * relative_threshold)
47    }
48
49    pub fn is_approx_eq(&self, lhs: <Self as RingBase>::Element, rhs: <Self as RingBase>::Element, precision: u64) -> bool {
50        let scaled_precision = precision as f64 * EPSILON;
51        if self.is_absolute_approx_eq(lhs, self.zero(), scaled_precision) {
52            self.is_absolute_approx_eq(rhs, self.zero(), scaled_precision)
53        } else {
54            self.is_relative_approx_eq(lhs, rhs, scaled_precision)
55        }
56    }
57}
58
59impl RingBase for Real64Base {
60 
61    type Element = f64;
62    
63    fn clone_el(&self, val: &Self::Element) -> Self::Element {
64        *val
65    }
66
67    fn add_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
68        *lhs += rhs;
69    }
70
71    fn negate_inplace(&self, x: &mut Self::Element) {
72        *x = -*x;
73    }
74
75    fn mul_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
76        *lhs *= rhs;
77    }
78
79    fn from_int(&self, value: i32) -> Self::Element {
80        value as f64
81    }
82    
83    fn eq_el(&self, _: &Self::Element, _: &Self::Element) -> bool {
84        panic!("Cannot provide equality on approximate rings")
85    }
86
87    fn pow_gen<R: IntegerRingStore>(&self, x: Self::Element, power: &El<R>, integers: R) -> Self::Element 
88        where R::Type: IntegerRing
89    {
90        if integers.get_ring().representable_bits().is_some() && integers.get_ring().representable_bits().unwrap() < i32::BITS as usize {
91            x.powi(int_cast(integers.clone_el(power), &StaticRing::<i32>::RING, integers))
92        } else {
93            x.powf(integers.to_float_approx(power))
94        }
95    }
96
97    fn is_commutative(&self) -> bool { true }
98
99    fn is_noetherian(&self) -> bool { true }
100
101    fn is_approximate(&self) -> bool { true }
102
103    fn dbg_within<'a>(&self, x: &Self::Element, out: &mut std::fmt::Formatter<'a>, _: EnvBindingStrength) -> std::fmt::Result {
104        write!(out, "{}", x)
105    }
106    
107    fn characteristic<I: IntegerRingStore + Copy>(&self, ZZ: I) -> Option<El<I>>
108        where I::Type: IntegerRing
109    {
110        Some(ZZ.zero())
111    }
112}
113
114impl_eq_based_self_iso!{ Real64Base }
115
116impl Domain for Real64Base {}
117
118impl DivisibilityRing for Real64Base {
119
120    fn checked_left_div(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
121        assert!(*rhs != 0.);
122        return Some(*lhs / *rhs);
123    }
124}
125
126impl PrincipalIdealRing for Real64Base {
127
128    fn checked_div_min(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
129        self.checked_left_div(lhs, rhs)
130    }
131    
132    fn extended_ideal_gen(&self, _lhs: &Self::Element, _rhs: &Self::Element) -> (Self::Element, Self::Element, Self::Element) {
133        panic!("Since Complex64 is only approximate, this cannot be implemented properly")
134    }
135}
136
137impl EuclideanRing for Real64Base {
138
139    fn euclidean_div_rem(&self, _lhs: Self::Element, _rhs: &Self::Element) -> (Self::Element, Self::Element) {
140        panic!("Since Complex64 is only approximate, this cannot be implemented properly")
141    }
142
143    fn euclidean_deg(&self, _: &Self::Element) -> Option<usize> {
144        panic!("Since Complex64 is only approximate, this cannot be implemented properly")
145    }
146}
147
148impl Field for Real64Base {
149
150    fn div(&self, lhs: &Self::Element, rhs: &Self::Element) -> Self::Element {
151        self.checked_left_div(lhs, rhs).unwrap()
152    }
153}
154
155impl OrderedRing for Real64Base {
156
157    fn cmp(&self, lhs: &Self::Element, rhs: &Self::Element) -> std::cmp::Ordering {
158        f64::partial_cmp(lhs, rhs).unwrap()
159    }
160}
161
162impl<I> CanHomFrom<I> for Real64Base 
163    where I: ?Sized + IntegerRing
164{
165    type Homomorphism = ();
166
167    fn has_canonical_hom(&self, _from: &I) -> Option<Self::Homomorphism> {
168        Some(())
169    }
170
171    fn map_in(&self, from: &I, el: <I as RingBase>::Element, _hom: &Self::Homomorphism) -> Self::Element {
172        from.to_float_approx(&el)
173    }
174
175    fn map_in_ref(&self, from: &I, el: &<I as RingBase>::Element, _hom: &Self::Homomorphism) -> Self::Element {
176        from.to_float_approx(el)
177    }
178}
179
180impl<I> CanHomFrom<RationalFieldBase<I>> for Real64Base 
181    where I: IntegerRingStore,
182        I::Type: IntegerRing
183{
184    type Homomorphism = ();
185
186    fn has_canonical_hom(&self, _from: &RationalFieldBase<I>) -> Option<Self::Homomorphism> {
187        Some(())
188    }
189
190    fn map_in(&self, from: &RationalFieldBase<I>, el: El<RationalField<I>>, hom: &Self::Homomorphism) -> Self::Element {
191        self.map_in_ref(from, &el, hom)
192    }
193
194    fn map_in_ref(&self, from: &RationalFieldBase<I>, el: &El<RationalField<I>>, _hom: &Self::Homomorphism) -> Self::Element {
195        from.base_ring().to_float_approx(from.num(el)) / from.base_ring().to_float_approx(from.den(el))
196    }
197}