Adding std::ops::Inv

Howdy, It might be a good to add a trait to std::ops that expresses invertibility.

pub trait Inv {
    type Output;
    fn inv(&self) -> Self:: Output;
} 
1 Like

You need to elaborate what you mean by “inverting”.

1 Like

Calculating an inverse. E.g. A matrix inverse.

Not all matrices are invertible, so your proposed traitʼs method wonʼt necessarily fit. Edit: Thinking about it, that’s not problematic since Output is unconstrained, hence Output = Option<Matrix> or something would work.

What would be the advantage of such a trait over just giving types that can be canonically inverted an .inv() method?

2 Likes

I'm well aware. In case of matrices, your output would be Option

What is the advantage of having std::ops::Add?

AFAIK the main reason it exists is to allow overloading the + operator.

5 Likes

And so that you can define methods on the trait as opposed to concrete types.

There is num_traits::Inv. It takes self by value, but you can implement the trait for your &T if you don't want to consume it.

Since there's no operator for this, it doesn't really need to be in std::ops.

7 Likes

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