Adding std::ops::Inv

I'm aware but I think that it would make sense for this to be in core.

This trait in core wouldn't do anything that a 3rd party crate can't. It's not a language operator. It doesn't get special treatment from the compiler.

It would have an incremental cost of making Rust bigger, with more to maintain forever. It would have risk that if this trait isn't ideal, Rust will be stuck with the flaw forever and won't be able to improve it.

7 Likes

The point is that this operation is common enough that it would make sense to add it to core so that crates actually use it and there's an agreement on the API for this operation.

Without it, crates might call it, inverse, invert, inverted etc.

If the trait is missing something, suggest an improvement.

Inv is one of the functions that are in the julia base. It's nice.

I haven't thought through it enough, it certainly doesn't seem like the compiler has all the information to give this formulation of inverse special treatment, or the chops to enforce the properties of inversion by the implementation, but in theory the compiler could fold the composition of a function and its inverse to the identity.

It'd certainly be cool if the compiler could leverage that.

Rust isn't really a mathematical language like Julia is. num is a library of mathematical traits.

But here's the real sticker for making this a std trait:

  • What types (other than library Matrix types) should implement Inv?
    • What std types?
      • Note that many matrix-providing libraries like numpy implement matrix inversion copyless by dynamically switching between a row-major and a column-major interpretation of the data. [[T; N]; M] would be expensive to reorganize to invert to [[T; M]; N].
      • Plus most users would prefer Matrix<M, N> to [[T; M], N] anyway, for indexing [(row, col)] rather than [col][row].
  • What functionality can meaningfully be input parametric over Inv types (probably with other bounds as well)?
    • What std functionality?

If the trait exists solely just to collect the same functionality under one name, but is not really meaningful as input parametricity, it doesn't really deserve (/need) to be a trait.

TL;DR I don't see any benefit of "matrix inversion" being a standard trait if there isn't even a matrix type in std. And [[T; N]; M] isn't a matrix, it's an array of arrays, which is often used to implement a matrix. And if nothing other than matrices can be inverted, then it would be better as a MatrixOps trait, imho, and that would live in the numpy equivalent for Rust (currently, ~num/nalgebra/ndarray, will definitely shift some when const generics stabilize.).

4 Likes

Another point: As far as I remember from my numerical analysis class, you usually don’t even want to invert matrices at all, since that operation is numerically unstable. Most of the things you would think you need to invert matrices for, in particular solving linear equations, can be done better in a different way.

You are paying too much attention to the matrices.

You already have an inv for f32, it's called recip https://doc.rust-lang.org/std/primitive.f32.html#method.recip

You are distracted by matrices. I operate in a world where matrices are always invertible.

Which was exactly my point. Matrices have gotten too central to this discussion, mostly due to the fact that they had been the only example you mentioned so far.

1 Like

OK why talk about instability?

Ordering reverse is also an inverse. https://doc.rust-lang.org/core/cmp/enum.Ordering.html#method.reverse

Having it called "inverse" would make little sense for Ordering. Likewise for getting the reciprocal of a float, which was mentioned earlier.

There are plenty of things far more widespread than this that could be in stdlib. There's also nothing preventing you from creating a crate with this trait and implementing it for whatever types you desire.

Everything in core::ops is overload able. This wouldn't be.

1 Like

"it makes sense" isn't a high enough bar for inclusion to std.

That there are some types with an invertible operation does not necessarily mean that one need to write code that is generic over inversion. I've been writing Rust for a long time and I can't think of any point in time in which I wanted to write code that was generic over inversion. I could imagine it could be useful for some kinds of mathematical code, but this is what the num-traits crate is good for.

12 Likes

That's matrix transposition, not inversion.

1 Like

Whoops, shows I don't know the names of this stuff off the top of my head :sweat_smile:

The general point stands, though: if it's not useful for input parametricity, it doesn't really need to be a trait anyway.

1 Like

Just to bring it up - there exists one inversion operator - std::ops::Not which uses the ! symbol.

Boolean negation is quite familiar as an inversion, and bitwise inversion also uses this operator. That's about it. That said, the trait definition doesn't quite say it's an inversion, it says The unary logical negation operator !

But the std::ops traits don't assign semantic meaning to operations - even if I think having that, is better - you just use the appropriate trait for the operator you want to implement, if you do. (See string concatenation using +.. which brings up what I'd want to add, a concat operator.)

1 Like

There's also std::ops::Neg which is an additive inverse.

Neg has syntax, -x, similarly for the only other math-related prefix operator Not !x

1 Like

I agree with this point, but I don’t necessarily think it applies here. Inverting is a meaningful operation in any situation where multiplication is defined and a multiplicative identity exists. On the other hand though, there aren’t many types in the standard library where such an operation might be useful: only the floating-point types, and potentially Wrapping<_>. For integers, the only invertible elements are 1 and −1, and other types have no multiplication defined at all.

So I agree with everyone saying that for the time being, such a trait should probably live in an external crate. But even if it were added to the standard library, std::ops would be a bad place for it, because traits in that module are tied to operators, and there is no inversion operator in Rust.

5 Likes

I don't really have much to add to Felix's response, I've proved some stuff about inverses before inverse.lean in case anyone wants to look at it stated formally... In that case we're dealing with a relation between a pair of unary functions, I think there is much more room for optimizations in the compiler that way, over the trait based mechanism here.

Anyhow, there is another std type which could implement Inv is std::inter::Rev.

Rust has made a choice to provide a minimal set of math-related traits and methods in core/std, with the goals of allowing operator overloading and exposing intrinsics and native library functions for specific types, rather than providing general mathematical abstractions, delegating those to other crates (currently there is num_traits and alga, possibly others).

Hence, this would not be an appropriate change.

6 Likes