decorum
decorum copied to clipboard
Implement field and other numeric traits from alga.
Other crates like approx and alga (used by nalgebra) define traits and implement them for native floating point numbers. They do use generics, so that Decorums numbers can be used. Some of their functionality isn't available though with Decorum's numbers, as their traits are not implemented for Decorums numbers.
Due to Rust's Orphan Rule users of Decorum and those libraries cannot implement the other libraries' traits for their use of Decorum. Either the libraries declaring the traits or Decorum must implement them. What strategy does Decorum use for implementing foreign traits? What dependency hierarchy should be created? Should those libraries depend on Decorum or should Decorum depend on those libraries?
I can imagine creating features in Decorum for use with well known libraries, like the above mentioned, might work.
In general, Decorum takes on the burden of implementing traits in the Rust ecosystem for its types. Are there specific traits that you think should be implemented?
I need the traits from approx as well as ComplexField and RealField from alga (which for its part requires approx's UlpsEq. Here are the full definitions:
pub trait RealField:
ComplexField<RealField = Self>
+ RelativeEq<Epsilon = Self>
+ UlpsEq<Epsilon = Self>
+ Lattice
+ Signed
+ Bounded
{
// NOTE: a real must be bounded because, no matter the chosen representation, being `Copy` implies that it occupies a statically-known size, meaning that it must have min/max values.
// elided
}
pub trait ComplexField:
SubsetOf<Self>
+ SupersetOf<f64>
+ Field
+ Copy
+ Num
+ NumAssign
+ FromPrimitive
+ Neg<Output = Self>
+ MeetSemilattice
+ JoinSemilattice
// + RelativeEq<Epsilon = Self>
// + UlpsEq<Epsilon = Self>
+ Send
+ Sync
+ Any
+ 'static
+ Debug
+ Display
{// elided}
I think implementing approx is doable. E.g. by defining a feature in the toml,
plugging the following in lib.rs:
#[cfg(feature = "approx_traits")]
#[macro_use]
extern crate approx;
and the following in proxy.rs
#[cfg(feature = "approx_traits")]
mod approx_traits {
extern crate approx;
use approx::{AbsDiffEq, RelativeEq, UlpsEq};
use crate::{ConstrainedFloat, Encoding};
use crate::constraint::FloatConstraint;
use num_traits::Float;
use crate::Primitive;
use crate::constraint::ConstraintEq;
impl<T, P> AbsDiffEq for ConstrainedFloat<T, P>
where
T: Float + Primitive + Encoding + AbsDiffEq<Epsilon=T>,
P: ConstraintEq<T> + FloatConstraint<T> {
type Epsilon = Self;
fn default_epsilon() -> Self::Epsilon {
Encoding::epsilon()
}
fn abs_diff_eq(&self, other: &Self, epsilon: Self) -> bool {
self.value.abs_diff_eq(&other.value, epsilon.value)
}
}
impl<T, P> RelativeEq for ConstrainedFloat<T, P>
where
T: Float + Primitive + RelativeEq<Epsilon=T>,
P: ConstraintEq<T> + FloatConstraint<T> {
fn default_max_relative() -> Self::Epsilon {
Self::from_inner_unchecked(T::default_max_relative())
}
fn relative_eq(&self, other: &Self, epsilon: Self, max_relative: Self) -> bool {
self.value.relative_eq(&other.value, epsilon.value, max_relative.value)
}
}
impl<T, P> UlpsEq for ConstrainedFloat<T, P>
where
T: Float + Primitive + UlpsEq<Epsilon=T>,
P: ConstraintEq<T> + FloatConstraint<T> {
fn default_max_ulps() -> u32 {
T::default_max_ulps()
}
fn ulps_eq(&self, other: &Self, epsilon: Self, max_ulps: u32) -> bool {
self.value.ulps_eq(&other.value, epsilon.value, max_ulps)
}
}
#[cfg(test)]
mod tests {
use crate::{N32, R32, R64};
use num_traits::Zero;
#[test]
fn ulps_eq() {
assert_ulps_eq!(R64::zero(), R64::from(0.0000000000000001), max_ulps = 4);
assert_ulps_ne!(R64::zero(), R64::from(0.000000000000001), max_ulps = 4);
assert_ulps_eq!(R32::zero(), R32::from(0.0000001), max_ulps = 4);
assert_ulps_ne!(R32::zero(), R32::from(0.000001), max_ulps = 4);
assert_ulps_eq!(N32::zero(), N32::from(0.0000001), max_ulps = 4);
assert_ulps_ne!(N32::zero(), N32::from(0.000001), max_ulps = 4);
}
}
}
nalgebra on the other hand I don't know how to implement, because (aside from my problems understanding the logic of nalgebra) it seems to require implementors of ComplexField to also declare they can do (logically) everything that f64 can do, which is exactly what I don't want, when I'm using R64 or F64, see my alga issue.
Thanks for the details! This is reminiscent of problems mentioned in #10 and a related issue I opened against num-traits
: numeric traits in the ecosystem are not always consistent with each other. decorum
is mostly concerned with infinities and NaN
, while approx
and alga
are more concerned with epsilons and ULPs.
It's not entirely clear to me where these trait implementations should live, but at first glance it seems appropriate for approx
's traits to be implemented by decorum
behind a Cargo feature (as suggested) and for alga
to provide less restrictive blanket implementations for RealField
. I'll try to put together a change for approx
and wait to see what the alga
maintainers have to say.
Support for approx
was added in 1f5b00f. The alga
issue has not yet been addressed. I'm going to rename this issue to focus on alga
traits.