For the high-level interfaces it is nice that
gradient_descent(M, f, grad_f)
works for the allocating gradient grad_f(M,p) by default.
This aims to be easier to use.
Internally this is a bit chaotic since
- one always has to distinguish allocating and in-place everywhere.
- an objective can only be one of a kind. For example for the Hessian, both gradient and Hessian have to be the same kind of function (alloc vs nonalloc)
Since the first step is nice for first-time users, keeping that default would be nice; but internally always preferring in-place would be nice.
One possibiliy is maybe to “wrap allocating early” in a function wrapper
struct AllocationFunctionWrapper{T,F}
function::F
end
# Gradient example:
function (f:: AllocationFunctionWrapper)(M, X, p)
# evaluate allocation function and copy over
return copyto!(M, X, p, f.function(M,p)
end
where T could be a type to determine different things to do (as opposed to copying over the gradient), though most often it is probably just a tangent vector or a point.
Then internally all Objectivbes could always assume having in-place functions; their constructors could maybe be tolerant still.
This is an issue more for the long-time, since it would require a humungous rework.
For the high-level interfaces it is nice that
works for the allocating gradient
grad_f(M,p)by default.This aims to be easier to use.
Internally this is a bit chaotic since
Since the first step is nice for first-time users, keeping that default would be nice; but internally always preferring in-place would be nice.
One possibiliy is maybe to “wrap allocating early” in a function wrapper
where
Tcould be a type to determine different things to do (as opposed to copying over the gradient), though most often it is probably just a tangent vector or a point.Then internally all Objectivbes could always assume having in-place functions; their constructors could maybe be tolerant still.
This is an issue more for the long-time, since it would require a humungous rework.