-
Notifications
You must be signed in to change notification settings - Fork 31
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
auto wrapper take 2 #131
auto wrapper take 2 #131
Conversation
Ref https://github.com/JuliaMath/GSL.jl/tree/yyc/gsl_func If you want to finish up the ones for other function structures that's fine. Otherwise I can finish it some time later. |
Thanks @yuyichao , I'd be happy to finish them up. I can add them as a pr to your branch just to keep the history more simple. Also, should the changes to |
So a few things, The way the However, So this leaves two possibility for what the macros can be used for:
The second option allows it to be used in more cases though the first one is more backward compatible. It's conceivable that someone could be doing that and manually construct other structures containing And then there's the question of The first step to fix The latter may look/feel nicer though it can be annoy to use with closures so I would recommend still use the multiple callback one as the API, the other one can be added later if needed. And I also think it's safe enough to just allow the user to pass the functions in as a tuple. Both |
Yes, just adding a new |
Actually, for consistency I think the new api for |
That's just a very strange API. Also, it seems that there are other API's that are inherently unsafe to even use the ccall conversion. |
Fair enough, I was thinking a tuple-based api could be a building block for something higher-level. In light of the unsafe apis you mentioned, it could make sense to define a higher-level "safe" api for which a solver can consistently use a function pointer. Although you mentioned it is wrong to use I'm still trying to understand if this is similar to the 'PyCall' solution in this discussion, although what I had in mind was this Possible apimutable struct GSLSolverWrapper{T<:Tuple,S}
isset::Bool # whether the solver has been given a gsl_function
ref # store the output of the gsl_function cconvert here
solver::Ptr{S}
fs::T
end
# default behaves like a gsl_function in a ccall, using the tuple cconvert methods
GSLSolverWrapper(fs...) = GSLSolverWrapper(false, nothing, nothing, C_NULL, fs)
Base.cconvert(T::Type{Ref{gsl_function}}, s::GSLSolverWrapper) = Base.cconvert(T, s.fs)
# for calling a function with a specific solver, pass the desired solver type
function GSLSolverWrapper(solvertype::Ref{gsl_root_fsolver_type}, f)
solver = root_fsolver_alloc(solvertype)
gsl = GSLSolverWrapper(false, (gsl_function(C_NULL, C_NULL), Ref((f,))), solver, (f,))
finalizer(f -> root_fsolver_free(f.solver), gsl)
return gsl
end
# e.g. GSLSolverWrapper(gsl_root_fsolver_brent, sin) then implements
function GSL.root_fsolver_set(solver::GSLSolverWrapper{F,gsl_root_fsolver}, a, b) where F
if !solver.isset
solver.ref = Base.cconvert(Ref{gsl_function}, solver.fs) # we allocate/store our own gsl_function to bypass ccall mechanism
end
root_fsolver_set(solver.solver, solver.ref[1], a, b) # creates a pointer to solver.ref in solver.solver
solver.isset = true
end
function GSL.root_fsolver_set(solver::GSLSolverWrapper{Tuple{F},gsl_root_fsolver}, f::F, a, b) where F
# implicitly free references
solver.ref = (gsl_function(C_NULL, C_NULL), Ref((f,)))
solver.fs = (f,)
solver.isset = false
root_fsolver_set(solver, a, b)
end
function GSL.root_fsolver_iterate(solver::GSLSolverWrapper{F,gsl_root_fsolver}) where F
!solver.isset && error()
root_fsolver_iterate(solver.solver)
end
function GSL.root_fsolver_root(solver::GSLSolverWrapper{F,gsl_root_fsolver}) where F
!solver.isset && error()
root_fsolver_root(solver.solver)
end which behaves like examplejulia> struct Linear
a::Float64
end
julia> (p::Linear)(x) = x-p.a
julia> f = GSLSolverWrapper(gsl_root_fsolver_brent, Linear(0.73));
julia> root_fsolver_set(f, -1.0,1.0)
true
julia> root_fsolver_root(f)
0.0
julia> root_fsolver_iterate(f)
0
julia> root_fsolver_root(f)
0.73
julia> root_fsolver_set(f, Linear(0.2), -1.0,1.0)
true
julia> root_fsolver_root(f)
0.0
julia> root_fsolver_iterate(f)
0
julia> root_fsolver_root(f)
0.19999999999999996
This would be a lot of work to implement for each solver type. I'm more interested if this could be a viable new api that could later be extended to a higher level interface. I'd also have to check if this can express other unsafe apis in the library. |
I've gone ahead and added Just a cranky idea for a workaround: could we use llvm tokens to keep an object alive such as a |
Not automatically. That mutable container has to be protected from the GC.
The details are not all correct but yes that's the idea.
I have not looked at the use of other function types but at least for These, and other similar cases should be restricted on the function signature to make sure they do not accept the new input type. |
# a little bit and avoid hitting some limitation of the allocation optimizer. | ||
@assert ismutable(gsl_function(C_NULL, C_NULL)) | ||
|
||
function Base.cconvert(::Type{Ref{gsl_function}}, t::T) where {F,T<:Tuple{F}} |
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.
Please don't. This tuple is totally unnecessary and it also create more opportunity for allocation.
@@ -47,6 +71,34 @@ macro gsl_function(f) | |||
) | |||
end | |||
|
|||
gsl_function_f_helper(x::Cdouble, (f,))::Cdouble = f(x) | |||
gsl_function_df_helper(x::Cdouble, (f,df,))::Cdouble = df(x) | |||
gsl_function_fdf_helper(x::Cdouble, (f,df,fdf))::Tuple{Cdouble,Cdouble} = fdf(x) |
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 is wrong. Also I suggest using different helper for fdf directly which avoid the creation of a closure unnecessarily.
See 8a5a1d1
Though this is currently a moot point since there's no user of this.
@@ -105,6 +157,32 @@ end | |||
|
|||
export @gsl_multiroot_function, @gsl_multiroot_function_fdf | |||
|
|||
function gsl_multiroot_function_helper(x_vec::Ptr{gsl_vector}, (f,), y_vec::Array{gsl_vector})::Cint |
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 don't think this is correct either.
And see https://github.com/JuliaMath/GSL.jl/compare/yyc/gsl_func, which I believe is all that can be done without introducing new API. For the two FDF variance, I don't believe there's anything that can use them so there's basically no public API for them right now. The wrapper type is added for future API. Note that in this case, not even the macro version currently used is safe since they don't garantee that the object will be live between calls to GSL. Edit: Actually while trying to make the safe wrapper I think the wrapper type is unnecessary and I'll revert those. |
Co-authored-by: Yichao Yu <yyc1992@gmail.com>
Co-authored-by: Yichao Yu <yyc1992@gmail.com>
Yeah, tests would be nice. I'm pretty sure all of the four function types have tests already. The Out of the safe wrapper, only the solvers have tests but not the |
Alternative to
@gsl_function
. See the discussion in #130TODO
gsl_function_fdf
Implement a similar wrapper forgsl_function_vec
gsl_multiroot_function
gsl_multiroot_function_fdf
Make the@gsl_function...
macros null-opsFix and deprecateKeep macros for compatibility@gsl_function
macros