Why is float not comparable

I’m trying to make a generic assertion function for 2 tuples

def tuple_eq[T: Comparable & Copyable & Movable](a: Tuple[T, T], b: Tuple[T, T]) -> Bool:
    return a[0] == b[0] and a[1] == b[1]

when call it with a float64 parameter i get the error shown below.

and when it fill the function with floats it works fine.

def tuple_eqf(a: Tuple[Float64, Float64], b: Tuple[Float64, Float64]) -> Bool:
    return a[0] == b[0] and a[1] == b[1]

Unfortunately, due to a limitation in our current type system, SIMD is not Comparable.

To put it simply, SIMD.__op__ returns SIMD[bool, width] instead of Bool.

Why can i do == when i specify the types fully?

The Comparable variant tries to find a __eq__ with signature fn(T, T) -> Bool, which SIMD doesn’t have. The eqf variant calls the __eq__ defined on SIMD itself.

Thanks for explaining

Out of curiosity though, why isn’t SIMD comparable? It seems like we’ll want floats and sized integer types to work with generic algorithms.

@adakkak do you have an opinion on this?

SIMD isn’t directly comparable because __eq__ and __ne__ return a SIMD[bool, ...] vector, not a single boolean.

  • We can’t make Scalar[dt] comparable (yet) because there’s no support for conditional conformance.

  • We also can’t simply change the Comparable trait to return a Boolable, since that introduces a confluence issue: for example, if bool(v) is defined as v.all(), then we would have the following:

    (not not v) != v
    

    since not v should translate to (~v).any() and not (~v).all().

I had a long discussion with @Joe about this last year, and it’s a rather tricky problem to resolve.

2 Likes

Ah, thank you for explaining this. I don’t know the ramifications, but I’d be in favor of renaming the simd-returning comparisons to make it possible for these types to conform to the comparable/equatable traits. I’m not sure the best way to spell this, but SIMD-returning comparisons are pretty rare, so I think that x.cmp_lt(y) or is_less etc would probably be acceptable.

Is there a good, non-surprising behavior for SIMD(1,2,3) < SIMD(1,3,2)? I would personally expect an element-wise comparison, and numpy also treats arrays this way.

I was also part of that discussion with Joe and Sora, and what we decided was that there was no good consistent behavior for any comparison other than a vector of length one. Equality should be doable for equal size vectors, since we can make that strict equality, but we are still left with how to handle size mismatches.

Conditional conformance should help handle the scalar case, but I don’t know if there’s a good behavior for width >= 2.

IIRC, Joe told me it’s exactly @adakkak who doesn’t like this idea. And for the record, I didn’t like it either.

We simply don’t handle size mismatches; they don’t type-check. That’s not the tricky part. The real contention lies between wanting syntactic sugar for numerical code and supporting generic programming.

That’s not how I remember it. We lack an Eq SIMD instance due to language/type system/API design constraints, not semantic ones. Moreover, being a value type (or a “regular” type, in the Stepanov sense) doesn’t inherently require additional comparison operators, so the absence of e.g. a < operator is far less problematic.

We can imagine a version of Mojo where dunder methods (which provide syntactic convenience) and Eq-conforming methods are distinct. In that hypothetical world, we could already implement Eq SIMD.

trait Eq:
  fn eq(Self, Self) -> Bool: ...

struct SomeStruct(Eq):
  fn eq(...) -> Bool: ...
  fn __eq__(...) -> Bool: return self.eq(rhs)

struct SIMD[...](Eq):
  fn eq(...) -> Bool: ...
  fn __eq__(...) -> SIMD[bool, ...]: ...

Of course, this approach comes with its own set of drawbacks, and Python doesn’t allow us to take this route. I’m merely using this example to illustrate how vast the design space is and how many ways there are to solve this problem (long post pending – stay tuned). My favourite is the Swift approach: introduce .op variants (e.g., .==) for element-wise comparison.

1 Like