-
Notifications
You must be signed in to change notification settings - Fork 40
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
JuMP interface #264
JuMP interface #264
Conversation
Wow! Super nice idea! Let me go through the points and try to answer.
The short answer is: In general you don't. The long answer: Sure in some way we always end up representing points on the manifolds with numbers, and most often even a single matrix.
Also this is a bit more complicated. For an isometrically embedded manifold (e.g. the 2-sphere in R3) it is really just the projection onto the tangent space. In many other cases, you have (after projection) still a different metric to adapt to, see https://manoptjl.org/stable/tutorials/AutomaticDifferentiation/#.-Conversion-of-a-Euclidean-Gradient-in-the-Embedding-to-a-Riemannian-Gradient-of-a-(not-Necessarily-Isometrically)-Embedded-Manifold – especially
One could pass a
I am not sure what these are. There are a few (for now internal) function used for the Manopt.jl/src/plans/stopping_criterion.jl Line 18 in a67453a
|
I think MOI should be thought of being slim enough to be a hard dependency? Than a hard dependency here would be ok. At least for JuMP I would prefer that as an extension. But I can help with that when this starts to work :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a few comments to your code. I like the idea, the only thing I am really not sure about is whether we can vectorise the manifold. That does not sound correct to me.
It shouldn't be a free parametrization, it's ok if all elements that can be parametrized does not belong to the manifold. |
Cool. That should solve the part for a lot of manifolds; for those that are not represented as one array, I am still not sure what to do, but that might also only affect 2 or 3 manifolds currently (or certain representation models on 2 manifolds). Out of my head
|
Codecov Report
@@ Coverage Diff @@
## master #264 +/- ##
========================================
Coverage 99.73% 99.74%
========================================
Files 78 79 +1
Lines 7206 7353 +147
========================================
+ Hits 7187 7334 +147
Misses 19 19
📣 Codecov offers a browser extension for seamless coverage viewing on GitHub. Try it in Chrome or Firefox today! |
I added a few questions in the PR description |
The idea is a decorator model, a state can be “wrapped” into a debug state, which runs a slightly different
I am not sure this applies here since the result is always on the manifold so it is always feasible? For carefulness one could check
Again a bit complicated to match probably. We started with a We should probably also get an access function for the stopping criterion. Since that was not accessed from outside, this was not yet done. So currently |
Looking at the docstring for |
Yes Yes, |
I guess you should define some kind of priority. If the solver stops while reaching both the limit of number of iterations and the gradient is zero, you should still say the status is |
Yes, but for that I would also have to see whether the MOI status types have a natural ordering themselves? Kind of from “not cool” to “coolest” – do do that for all stopping criteria. Then the decision is of course easiest; but yes, I would propose something like that, probably on this branch, since the MOI dependency is introduced here? Or would it be fine to just have the RawStatus for now here and do the other one later? |
Yes, we can do it later and leave
We don't have any explicit rule like that but I'd say |
That sounds fair. I can think about a better version than later, maybe even one directly tailored to the existing status enums.
Yes sure, locally solved is cooler. |
Wow, that must've been quite a lot of work. Thank you, it's a good idea! ❤️
We kind of can vectorize points using charts. Not all manifolds have charts yet though. You can take a look here how it works: https://github.com/JuliaManifolds/Manifolds.jl/blob/master/tutorials/working-in-charts.jl . |
In principle all of our representations can be seen as charts locally. So if we would write a vectorisation for all manifold point (and tangent vector) representation, that should also be fine (without the detour via charts). That would be something e.g. the |
Vectorization already has too many meanings, yes 😅 . If we are going for a different name, why not "parametrization" (it's already in use in Manifolds.jl in this context). |
I tried making it work for a matrix manifold by reshaping inside the gradient callback and reshaping the starting value but I'm getting a weird error: julia> include("test/MOI_wrapper.jl");
JuMP tests: Error During Test at /home/blegat/.julia/dev/Manopt/test/MOI_wrapper.jl:63
Got exception outside of a @test
MethodError: injectivity_radius(::AbstractManifold) is ambiguous.
Candidates:
injectivity_radius(M::AbstractPowerManifold)
@ ManifoldsBase ~/.julia/packages/ManifoldsBase/74WVY/src/PowerManifold.jl:729
injectivity_radius(::Grassmann)
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/Grassmann.jl:125
injectivity_radius(::Manifolds.GeneralUnitaryMatrices{n, ℝ}) where n
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/GeneralUnitaryMatrices.jl:528
injectivity_radius(::Manifolds.GeneralUnitaryMatrices{n, ℂ, Manifolds.DeterminantOneMatrices}) where {n, ℂ}
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/GeneralUnitaryMatrices.jl:510
injectivity_radius(M::ProbabilitySimplex)
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/ProbabilitySimplex.jl:235
injectivity_radius(::Euclidean)
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/Euclidean.jl:343
injectivity_radius(::SymmetricPositiveDefinite)
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/SymmetricPositiveDefinite.jl:239
injectivity_radius(::Circle)
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/Circle.jl:242
injectivity_radius(M::TangentBundle{𝔽} where 𝔽)
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/VectorBundle.jl:617
injectivity_radius(::ManifoldsBase.DefaultManifold)
@ ManifoldsBase ~/.julia/packages/ManifoldsBase/74WVY/src/DefaultManifold.jl:106
injectivity_radius(::Flag)
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/Flag.jl:114
injectivity_radius(M::MetricManifold)
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/MetricManifold.jl:363
injectivity_radius(M::ValidationManifold)
@ ManifoldsBase ~/.julia/packages/ManifoldsBase/74WVY/src/ValidationManifold.jl:244
injectivity_radius(M::ProductManifold)
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/ProductManifold.jl:790
injectivity_radius(::AbstractSphere)
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/Sphere.jl:277
injectivity_radius(::AbstractProjectiveSpace)
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/ProjectiveSpace.jl:241
injectivity_radius(::Hyperbolic)
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/Hyperbolic.jl:246
injectivity_radius(::TangentSpaceAtPoint{𝔽} where 𝔽)
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/VectorBundle.jl:608
injectivity_radius(::HeisenbergGroup)
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/groups/heisenberg.jl:229
injectivity_radius(::GeneralizedGrassmann)
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/GeneralizedGrassmann.jl:179
injectivity_radius(::UnitaryMatrices{1, ℍ})
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/Unitary.jl:67
injectivity_radius(::Manifolds.GeneralUnitaryMatrices)
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/GeneralUnitaryMatrices.jl:498
injectivity_radius(M::AbstractDecoratorManifold)
@ ManifoldsBase ~/.julia/packages/ManifoldsBase/74WVY/src/nested_trait.jl:305
injectivity_radius(::PositiveNumbers)
@ Manifolds ~/.julia/packages/Manifolds/edNnV/src/manifolds/PositiveNumbers.jl:184
Stacktrace:
[1] injectivity_radius(::ManifoldsBase.EmptyTrait, M::FixedRankMatrices{3, 2, 2, ℝ})
@ ManifoldsBase ~/.julia/packages/ManifoldsBase/74WVY/src/nested_trait.jl:346
[2] injectivity_radius
@ ~/.julia/packages/ManifoldsBase/74WVY/src/nested_trait.jl:313 [inlined]
[3] injectivity_radius
@ ~/.julia/packages/ManifoldsBase/74WVY/src/nested_trait.jl:306 [inlined]
[4] max_stepsize
@ ~/.julia/dev/Manopt/src/plans/stepsize.jl:39 [inlined]
[5] #default_stepsize#637
@ ~/.julia/dev/Manopt/src/solvers/gradient_descent.jl:88 [inlined]
[6] default_stepsize
@ ~/.julia/dev/Manopt/src/solvers/gradient_descent.jl:82 [inlined]
[7] GradientDescentState(M::FixedRankMatrices{3, 2, 2, ℝ}, p::Matrix{Float64})
@ Manopt ~/.julia/dev/Manopt/src/solvers/gradient_descent.jl:63
[8] optimize!(model::Manopt.Optimizer)
@ Manopt ~/.julia/dev/Manopt/src/MOI_wrapper.jl:212 Any hint ? |
This happens when a method is not implemented on a bit more advanced manifolds, since FixedRank uses other manifolds to “pass tasks on”. So – for FixedRankmatrices the In this case it does happen, because the default step size (I think that should be Armijo) tries to determine a reasonable default starting step size that does not “break stuff” (a step size beyond 2\pi on the sphere for example would. be a bad idea). |
So to fix it, we should set the step size ? |
For fixed rank you have to set (parts of) the step size yourself, yes. Here it is the
I am, however, not sure how Step sizes are handled in MOI/JuMP, nor do I have a good/ better idea for our default, since infectivity_radius is a really good upper bound, but if we are not aware what that is on a manifold, there is not much we can do. You can of course also use any other step size rule available and fitting. |
Getting closer. Now I am getting:
Is I need to call this |
Puh, you really directly try to use all features on one of the most complicate manifolds. Yes, This requires that you can project a tangent vector from the embedding to the tangent space (again, for the sphere easier to imaging – any vector in R3 can be projected onto the plane orthogonal to a point p on the sphere, which is its tangent space) as well as the For both, But why does JuMP Have tp only work with the Euclidean gradient? Usually – Euclidean gradient is a reasonable fallback (that is the conversion then to the Riemannian) besides providing the Riemannian gradient (see here for details), but usually this conversion also costs a bit, so its better to provide the Riemannian one (or at least be able to provide that). So in short: Probably neither of these formulae for project or change_representer is known, but I can check the literature again (Vandereycken 2013, also linked in the docs). edit: Ah at least it seems the Riesz representer conversion is the identity, but I am not sure about projection onto the tangent space. edit2: Now I am confused. that method does exist. Its Formula (2.5) from the Vandereycken paper and implemented. |
Ah, now I see it. If we would store just the matrix, we would compute the SVD many, many, (many many many) times. So we store a point as So we have to find a way to declare points as |
This reverts commit 0c3d527.
Thanks for the careful documentation! For Documenter I am not sure why the extension is not loaded correctly or did you forget to import |
CI is fulling passing now and I believe I addressed all comments. Let me know if there are anything remaining. |
Nice! Thanks for all the work. I am travelling this weekend but will check the remaining open points on Monday the latest; the CI looks great already! There is also one open one by @odow that I can not comment much on I think? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me now – can you make already bump the version (0.4.42 is the next I think?) If I find time I would like to look at the rendered docs, but if not we can merge it throughout next week.
In the long run a short tutorial hon how to use Manopt in/with JuMP would be nice (as a Quarto notebook).
Yes, it's v0.4.42, I have bumped the version |
Nice! Then there are just two unresolved comments above, that I did not start and feel unsure whether they are resolved – but we could still merge maybe on Wednesday or so, if there is no response on those? |
As detailed in the respective comments, the necessary feature was added in jump-dev/MathOptInterface.jl#2305 and this PR now uses it so these are fixed. |
Nice, I think now there is just one open from June 9. If you could check the real quick – I could maybe ask take some time this evening to merge it – I will also write a remark on Discuourse about the new feature then. |
Indeed, I missed that one, I get lost in this massive PR ^^ |
No worries, I mentioned the date exactly for that reason, since I also get easily lost in this large PR. But then – great! We can merge this and register later today! |
Thanks again for all your work. |
This is a proof of concept implementation of MathOptInterface so that this package can be used from JuMP.
Here are a few things that are missing:
Vector{Float64}
. For PSD matrices, it's the vectorization of the upper triangle for instance.MOI.RawOptimizerAttributes
RawStatusString
MOI.Test.runtests
Optimizer
and test with a manifold with a matrix representationHere are a few questions:
Note: Requires jump-dev/JuMP.jl#3106 for nonlinear objective because
NLPBlock
is incompatible with constrained variables because of https://github.com/jump-dev/MathOptInterface.jl/blob/32dbf6056b0b5fb9d44dc583ecc8249f6fd703ea/src/Utilities/copy.jl#L485-L500 but should already be useable from MOI