-
Notifications
You must be signed in to change notification settings - Fork 41
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
Initialise states based on point type already and not requiring an actual point #400
Comments
Yes, manifold + type should be enough for allocation. IIRC we don't currently have a function for that though? |
No, it just came up today in a discussion, so I took a note. I will check what best to do, my idea would be
We should then check all places, where this scheme can be applied. edit: and maybe we also get more feedback here – I think it was Dimitri who told me about that but I do not know his GitHub handle. |
Just to ping him here, it was @bvdmitri who reported this – besides many really nice remarks on how nice Manopt is – thanks again also for the nice discussions! |
@kellertuer Thanks for all the great work you're doing! |
@bvdmitri thanks for your kind words. I now checked all of Manopt and only found one place where we allocate a Could you maybe elaborate in which other cases you encountered that memory was allocated but the actual “data” not used? |
I myself don't recall many places :) I think my main frustration was with result = gradient_descent(M, f, g, p, direction = AverageGradient(M, p)) While I would expect to write something like: result = gradient_descent(M, f, g, p, direction = AverageGradient()) I understand why I need to pass I think during our conversation, I was trying to say that sometimes this I think you can use the factory pattern to implement |
Yes, but I have to initialise the internal memory somehow so that is what I need Sure in principle passing a different manifold is a cause of error here and is a good reason to avoid this if we find something. |
A main problem might be to get that non-breaking, but we could maybe run both ideas in parallel for a while before committing the older. I think I do see the advantage in the factory mode (avoid apscifying M twice) but I personally would have to think a bit how to realise that. If you have the capacities to think about hat and propose something feel free to do so. The factory pattern sounds like the right way to go here; kind of like an interims step, so that one could maybe even accept |
I was thinking something about those lines # Placeholder which are not important for this example
struct SomeManifold end
struct SomeOtherManifold end
function f end
function g end
struct AverageGradient{M, P, F}
manifold::M
point::P
some_internal_field::F
end
# A constructor that requires `M` and `p`
AverageGradient(M, p; some_internal_field = 0) = AverageGradient(M, p, some_internal_field)
# In case the direction has already been instantiated by a user
# only double check that the manifold matches
function prepare_direction(M, p, direction::AverageGradient)
if !isequal(M, direction.manifold)
error("Manifolds do not match!")
end
# simply return the instance created by the user
return direction
end
struct AverageGradientFactory{F}
some_internal_field::F
end
# A constructor that does not require `M` and `p` creates a _factory_
AverageGradient(; some_internal_field = 0) = AverageGradientFactory(some_internal_field)
# In case the direction has *not* been created yet we should create it
function prepare_direction(M, p, factory::AverageGradientFactory)
return AverageGradient(M, p; some_internal_field = factory.some_internal_field)
end
function gradient_descent(M, f, g, p; direction)
direction = prepare_direction(M, p, direction)
# ...
@show direction
return nothing
end And then I can do M = SomeManifold()
p = [ 0 ]
gradient_descent(SomeManifold(), f, g, p; direction = AverageGradient())
gradient_descent(SomeManifold(), f, g, p; direction = AverageGradient(M, p)) which yields identical results and also non-breaking
moreover, it double checks that manifolds are equal gradient_descent(SomeManifold(), f, g, p; direction = AverageGradient(SomeOtherManifold(), p))
|
Very neat idea – a bit of rework for all updates, but something we should definetly do. Thanks! Might take a while until I find time for it (still travelling). |
No worries. There’s no need to rush this. The current setup works all fine (though yeah it requires users to input |
Yes and it is a great idea to follow up on because it does improve user experience (something that I value a lot). edit: one small comment – usually none of these stores the manifold, it is only needed for obtaining a point (or its type) to initialise vectors (of points) or other point storages. But then one could still error if the points types do not match. The approach is still a very good one. I will try to change directions to that and check where-else we can use the factory pattern for improved usability. |
I agree that a factory pattern would improve usability. One small point though: I don't like constructors that don't return an object of the type they name. I would propose making the |
Yes, that one I also dislike a bit, it might still be an okay-interims solution before we do a breaking change. But you are right, having |
Several states and similar require a point to initialise points.
Itmight be a good idea to requite the constructors to also accept just the type (and default the point's type to fill that by default).
Examples
The text was updated successfully, but these errors were encountered: