The illustration has a DualNumber<T> type, which I would like to eventually support DualNumber<f32x4> etc. The object is to put one of those inside a nalgebra vector, i.e. Vector3<DualNumber<f32x4>>, and have that type produce vectorized code. So DualNumber<Simd<[f32; N]>> has to implement everything that f32 does. Including SimdRealField/SimdComplexField.
All of the SimdRealField/SimdComplexField traits require SimdPartialOrd. There is a blanket implementation of SimdPartialOrd, and the existence of that blanket impl prevents any type like DualNumber<T> implementing SimdPartialOrd manually and generic over T's SimdValue implementation / choice of SimdBool.
A type like this needs to have type SimdBool = T::SimdBool; in its SimdValue, and then use the vectorised SimdBool in its implementations of SimdRealField/SimdComplexField. Since I can't implement SimdPartialOrd and am forced to rely on the blanket impl, I can't really generate those vectorised bools. And moreover, since I cannot implement SimdPartialOrd, I have to constrain the SimdRealField/SimdComplexField implementations to T: SimdValue<Element = T, SimdBool = bool>, which means I cannot ever use those traits on DualNumber<f32x4>.
Essentially, the blanket implementation on SimdPartialOrd has foreclosed the possibility of vectorising any new algebraic field implementations in such a way that nalgebra can use them. The resulting DualNumber can only be used with f32 and f64 directly. It only has the un-vectorised code in RealField and ComplexField.
There is one workaround, which is unacceptable: do not implement PartialOrd on DualNumber. Then the trait solver will reject that blanket impl and you can make your own. But the whole point of this exercise was to have operator overloading work with nalgebra, I'm not giving up PartialOrd.
My recommendation is to add a marker trait like the one in the illustration, reproduced here:
trait OptIn {}
impl OptIn for f32 {}
impl OptIn for f64 {}
impl<T> SimdPartialOrd for T
where
T: SimdValue<Element = T, SimdBool = bool> + PartialOrd,
// and crucially, add this as a constraint
T: OptIn,
{}
That way, because DualNumber<T> chooses not to implement OptIn, it is unaffected by the blanket impl of SimdPartialOrd. It can then
impl SimdValue for DualNumber<T> where T: SimdValue with Element = DualNumber<T::Element> and SimdBool = T::SimdBool>
impl SimdPartialOrd for DualNumber<T> where T: SimdPartialOrd, very simple forwarding impl (to only compare the real parts)
impl SimdRealField for DualNumber<T> where T: SimdRealField, using T's simd operations to implement dual numbers
SimdPartialOrd is blanket implemented for any
T: SimdValue<Element = T, SimdBool = bool> + PartialOrd
.I am working on the
num_dual
crate, trying to make its dual number algebra work like a scalar does for nalgebra purposes. Here's a simplified illustration of the problem (playground).The illustration has a
DualNumber<T>
type, which I would like to eventually supportDualNumber<f32x4>
etc. The object is to put one of those inside a nalgebra vector, i.e.Vector3<DualNumber<f32x4>>
, and have that type produce vectorized code. SoDualNumber<Simd<[f32; N]>>
has to implement everything that f32 does. Including SimdRealField/SimdComplexField.All of the SimdRealField/SimdComplexField traits require SimdPartialOrd. There is a blanket implementation of SimdPartialOrd, and the existence of that blanket impl prevents any type like
DualNumber<T>
implementing SimdPartialOrd manually and generic over T's SimdValue implementation / choice of SimdBool.A type like this needs to have
type SimdBool = T::SimdBool;
in its SimdValue, and then use the vectorised SimdBool in its implementations of SimdRealField/SimdComplexField. Since I can't implement SimdPartialOrd and am forced to rely on the blanket impl, I can't really generate those vectorised bools. And moreover, since I cannot implement SimdPartialOrd, I have to constrain the SimdRealField/SimdComplexField implementations toT: SimdValue<Element = T, SimdBool = bool>
, which means I cannot ever use those traits onDualNumber<f32x4>
.Essentially, the blanket implementation on SimdPartialOrd has foreclosed the possibility of vectorising any new algebraic field implementations in such a way that nalgebra can use them. The resulting DualNumber can only be used with f32 and f64 directly. It only has the un-vectorised code in RealField and ComplexField.
There is one workaround, which is unacceptable: do not implement PartialOrd on DualNumber. Then the trait solver will reject that blanket impl and you can make your own. But the whole point of this exercise was to have operator overloading work with nalgebra, I'm not giving up PartialOrd.
My recommendation is to add a marker trait like the one in the illustration, reproduced here:
That way, because
DualNumber<T>
chooses not to implementOptIn
, it is unaffected by the blanket impl of SimdPartialOrd. It can thenimpl SimdValue for DualNumber<T> where T: SimdValue
withElement = DualNumber<T::Element>
andSimdBool = T::SimdBool>
impl SimdPartialOrd for DualNumber<T> where T: SimdPartialOrd
, very simple forwarding impl (to only compare the real parts)impl SimdRealField for DualNumber<T> where T: SimdRealField
, using T's simd operations to implement dual numbers