Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement field and other numeric traits from alga. #16

Open
NeverGivinUp opened this issue Jul 8, 2019 · 4 comments
Open

Implement field and other numeric traits from alga. #16

NeverGivinUp opened this issue Jul 8, 2019 · 4 comments

Comments

@NeverGivinUp
Copy link

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.

@olson-sean-k
Copy link
Owner

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?

@NeverGivinUp
Copy link
Author

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.

@olson-sean-k
Copy link
Owner

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.

@olson-sean-k
Copy link
Owner

olson-sean-k commented Aug 6, 2020

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.

@olson-sean-k olson-sean-k changed the title What strategy does Decorum use for implementing foreign traits? Implement field and other numeric traits from alga. Aug 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants