-
Notifications
You must be signed in to change notification settings - Fork 3
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 MutableArithmetics #21
base: main
Are you sure you want to change the base?
Conversation
this I'd be happy to accept this! I'm alsow afraid that with such pull against AA you're facing uphill battle there... even implementing some kind of iteration on AA matrix elements took over a year :) Anyway, the discussion must start somewhere, so I'm summoning @fingolfin, @thofma, @rforquet, @fieker |
On Wed, Mar 24, 2021 at 04:23:04PM -0700, Marek Kaluba wrote:
this comm! is actually correct as `conj!`, or `mul` will dealias elements if necessary.
In the test suite we check that both g and h are not mutated by the mutating operation.
I'd be happy to accept this!
The plan is that this interface makes its way into AA (or AA will start depending on it). However if such change is rejected by AbstractAlgebra, I'm not sure how feasible would this be to have it here, but not there?
I'm alsow afraid that with such pull against AA you're facing uphill battle there... even implementing some kind of iteration on AA matrix elements took over a year :) Anyway, the discussion must start somewhere, so I'm summoning @fingolfin, @thofma, @rforquet, @fieker
What is the advantage of using this over the current (in AA and
upstream) approach to mutability? In particular, given that we'd need to
rewrite the entire generic to use this?
A different point is, but that is easier to address: mutablity is not
everything: delayed reduction is also important to all (most) quotient
rings: e.g. given vectors over Z/nZ (n big) the "best" way to compute a
scalar product is to compute the lifted scalar product followed by one
reduction in the end, so this would have to be incorporated as well.
I agree fully that, if this had been available 4 years ago, we'd
possibly use it. But what is the advantage now of rewriting all
foundations?
…
--
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub:
#21 (comment)
|
IIUC, GroupsCore.jl/src/group_elements.jl Line 266 in 10a138a
will not do what we expect because g has changed.
I think there is two sides of this question:
I'd be happy to discuss 1. We could still change MA API if there are advantages in the AA mutable API but it's probably best to discuss 2. first. So there was two downsides in having this mutable API specific to JuMP:
This was the motivation for creating MA and starting from JuMP v0.21, the transition was done and JuMP now use MA for its mutable API. This same line of reasoning seems to apply to AA with the exception that the mutable API of AA seems to be closer to an generic API than the one of JuMP before MA. So if AA implements MA, then if the user calls
Writing this PR gave me a good idea of the small differences between the AA and MA mutable APIs so I could make a PR to AA that implements MA while still keeping the AA API for compatibility for existing code using this API (like I did for this PR).
That's interesting and I agree that this should also be part of the API. We have a similar need for MathOptInterface where you would want to canonicalize the functions only once at the end. How is that handled in AA ?
I totally agree. I would say that it's better late than even later :) I have submitted a talk on MA to JuliaCon 2021 to try to advertise the idea that packages should use this API so that their code is also efficient for types in AA, GroupsCore, JuMP, MultivariatePolynomials, BigInt, ... |
Base.LinearAlgebra is carefully constructed to work only in a numeric bubble (hello |
Apart from everything else, how do you handle recursinve mutable
structures? E.g polynomials over BigRational, or better matrices of
polynomials over BigRational:
here the polynomial (as a matrix entry) might be mutable
but the coefficients might not (shared between others)?
By trait, all of matrix, polynomial and BigRational are mutable, but for
any given object this is more tricky (and we don't have a (good)
solution there as well.
Consider creating a matrix - which will produce lots of zero entries.
Each entry is a polynomial - maybe even the identical one.
Take a zero polynomial and add x^n, then the sum needs to have many
zeroes (for a dense poly). Will they be identical or different objects?
MA.op!(+, poly_x, poly_y, poly_z): it is allowed to call MA.op! on the
coefficients?
=======================
We cannot use the Julia linear algebra, mostly, at all, zero(Type) can
not be made to work for all our types. Putting types into numbers also
makes no sense at all for finite fields, they are not real or complex.
Similarly, matrices with zero rows/ columns do not have the information
of their ring...
So, out of neccessity, we build our own lin. alg. for our own matrix
type(s).
So: what is the benefit to us of also supporting MA? If there are
intersting real applications, we can think about it, but at this point,
there is no advantage for AA (or upstream), instead we have a new
dependency and a new interface.
It is an intersting interface, but too late for us ...
…On Thu, Apr 01, 2021 at 02:24:53AM -0700, Benoît Legat wrote:
> What is the advantage of using this over the current (in AA and upstream) approach to mutability?
I think there is two sides of this question:
1. What is the advantage of the MA mutable API over the AA mutable API
2. What is the advantage of having a package defining a mutable API outside of AA
I'd be happy to discuss 1. We could still change MA API if there are advantages in the AA mutable API but it's probably best to discuss 2. first.
Currently, if you define `+` and `*` for a mutable type then all of LinearAlgebra and SparseArrays will work but be very slow and allocate a lot. This is also the case for some functions in Base such as `sum` and `prod`.
In JuMP, it required a significant amount of work to dispatch matrix multiplication, `sum`, etc... to alternative implementations that would use the mutable API of JuMP.
But then for functions defined outside of Base and stdlib (say in some package X), if we wanted to make it work efficiently with JuMP, we should either make JuMP depend on X and rewrite it for JuMP types or make X depend on JuMP.
The first solution seems unmanageable and the second solution does not seem right: why would package X add such a large dependency just for having the function work better for JuMP types ?
So there was two downsides in having this mutable API specific to JuMP:
1) All the work of rewriting Base and stdlib was only used for JuMP types while it could be also useful for polynomials, etc...
2) Packages wanting to use the mutable API needed to have JuMP as a dependency which is a bit too much.
This was the motivation for creating MA and starting from JuMP v0.21, the transition was done and JuMP now use MA for its mutable API. This same line of reasoning seems to apply to AA with the exception that the mutable API of AA seems to be closer to an generic API than the one of JuMP before MA.
So if AA implements MA, then if the user calls `MA.operate(*, A::Matrix, b::Vector)` where `A` and/or `b` contains any ring element, this will use an efficient implementation instead of the default one, same thing if `A` is a sparse matrix.
In addition, if the ring element is a subtype of `MA.AbstractMutable` then `A * b` will be dispatched to `MA.operate(*, A::Matrix, b::Vector)` automatically.
If we have a significant amount of types implementing MA, it will provide sufficient motivation for packages to start relying on MA. For example, [GenericLinearAlgebra](https://github.com/JuliaLinearAlgebra/GenericLinearAlgebra.jl) or [RowEchelon](https://github.com/blegat/RowEchelon.jl) would directly use the MA API and no need for the user to use `MA.operate` or for MA to re-dispatch in case one of the arguments is a subtype of `MA.AbstractMutable`.
In the long term, we could even imagine having some part of MA be moved to Base so that LinearAlgebra and SparseArrays can directly be written on top of MA and we don't have to reimplement and re-dispatch in MA.
> In particular, given that we'd need to rewrite the entire generic to use this?
Writing this PR gave me a good idea of the small differences between the AA and MA mutable APIs so I could make a PR to AA that implements MA while still keeping the AA API for compatibility for existing code using this API (like I did for this PR).
> A different point is, but that is easier to address: mutablity is not everything: delayed reduction is also important to all (most) quotient rings: e.g. given vectors over Z/nZ (n big) the "best" way to compute a scalar product is to compute the lifted scalar product followed by one reduction in the end, so this would have to be incorporated as well.
That's interesting and I agree that this should also be part of the API. We have a similar need for MathOptInterface where you would want to canonicalize the functions only once at the end. How is that handled in AA ?
> I agree fully that, if this had been available 4 years ago, we'd possibly use it. But what is the advantage now of rewriting all foundations?
I totally agree. I would say that it's better late than even later :) I have submitted a talk on MA to JuliaCon 2021 to try to advertise the idea that packages should use this API so that their code is also efficient for types in AA, GroupsCore, JuMP, MultivariatePolynomials, BigInt, ...
--
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub:
#21 (comment)
|
If I understand you correctly, this is exactly the kind of issues listed in jump-dev/MutableArithmetics.jl#47. We have the same issues with MultivariatePolynomials and JuMP types and MutableArithmetics also solve some of these issues by re-dispatching the calls (for subtypes of
This would be handled by MutableArithmetics so there will not be more maintenance for AA. Why would this be zero-gain ? The speed gain for matrix-vector multiplication or sum is not only a fixed speedup, it even changes the complexity in some cases.
We are still far from it but as attested by jump-dev/MutableArithmetics.jl#47, we've already been able to merge a few PR's to make Base and stdlib more aware that more exotic types exist.
This is indeed tricky and we had exactly this use case with MultivariatePolynomials/DynamicPolynomials/TypedPolynomials.
That's a tricky one. In
If
I completely understand, I had the same issues with JuMP and MultivariatePolynomials as detailed in jump-dev/MutableArithmetics.jl#47. |
I understand that there is a large ecosystem around AA with a lot of code using it's mutable API and a lot of types implementing it. Hence making a breaking change would be a lot of work and you need a very good incentive to do it. function AbstractAlgebra.inv!(out, g)
if out === g
return MA.operate!(inv, g)
else
return MA.operate_to!(out, inv, g)
end
end
function AbstractAlgebra.mul!(out, g, h)
if out === g
return MA.operate!(*, g, h)
elseif out === h
return MA.operate!(mul_left, h, g)
else
return MA.operate_to!(out, *, g, h)
end
end Then, for types that implement the AA mutable API, nothing changes and for types that implement the MA API, they are now usable with either the AA API and the MA API. |
@blegat I've just sent the version 0.5 for registration; since mutable API is specifically described as unstable I think it's fine to rebase this and release under |
MutableArithmetics is designed to be the API for mutable types in Julia. It allows both algorithms to be written in a generic way for any type while still exploiting mutability of any type implementing MutableArithmetics.
It's a bit similar to the mutability API implemented in AbstractAlgebra so I would like to try to merge them.
This PR is a first attempt at this. I implemented backward compatibility which shows what's the difference between the two API's.
In MutableArithmetics, we distinguish
operate_to!
andoperate!
which are what the user should use and that may or may not mutate andmutable_operate_to!
/mutable_operate!
which are what the types should implement and that should always mutate. It's also clearer that there should be not alias and we differentiate the cases with the_to
suffix. It seems to me that the aliasing can be confusing. For instance,GroupsCore.jl/src/group_elements.jl
Lines 263 to 267 in 10a138a
seems to be incorrect in case
out === g
sinceg
is used in the last line.However, this is the case here
GroupsCore.jl/src/group_elements.jl
Line 144 in 10a138a
As we need to use
if
-else
everytime with===
we might as well differentiate with the_to!
suffix ?Let me know what you think. I can do a similar PR to AbstractAlgebra if you think this goes in the right direction.