Skip to content
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

Stricter types for evaluate!! methods #629

Merged
merged 1 commit into from
Jul 12, 2024
Merged

Conversation

mhauru
Copy link
Member

@mhauru mhauru commented Jul 10, 2024

Addresses TuringLang/Turing.jl#2182

The earlier

function AbstractPPL.evaluate!!(model::Model, args...)
    return evaluate!!(model, Random.default_rng(), args...)
end

could lead to infinite recursion. Restricting the types of the varargs solves that.

I would have wanted to rework the method definitions of evaluate!! more generally because I find it hard to understand what all the valid ways of calling it are. I thought about it for a bit and couldn't come up with a good solution though, so maybe later.

@mhauru mhauru requested a review from sunxd3 July 10, 2024 16:35
@coveralls
Copy link

coveralls commented Jul 10, 2024

Pull Request Test Coverage Report for Build 9909759705

Details

  • 1 of 1 (100.0%) changed or added relevant line in 1 file are covered.
  • 24 unchanged lines in 4 files lost coverage.
  • Overall coverage remained the same at 81.419%

Files with Coverage Reduction New Missed Lines %
src/model.jl 1 87.0%
src/simple_varinfo.jl 5 82.18%
src/varinfo.jl 6 84.85%
src/threadsafe.jl 12 46.73%
Totals Coverage Status
Change from base Build 9793413975: 0.0%
Covered Lines: 2800
Relevant Lines: 3439

💛 - Coveralls

@sunxd3
Copy link
Member

sunxd3 commented Jul 11, 2024

Looks good! Feels like there might be some hidden implications, so let me take a look now.

@sunxd3
Copy link
Member

sunxd3 commented Jul 11, 2024

My reading of the issue is this: sometimes user confuse the "model creating function" with the functor DynamicPPL.Model. They are both "callable"s, so somebody may want to create a Model, but called an already created Model on the input data.

In this case,

(model::Model)(args...) = first(evaluate!!(model, args...))
will be invoked, and then

DynamicPPL.jl/src/model.jl

Lines 912 to 914 in 3b3840d

function AbstractPPL.evaluate!!(model::Model, args...)
return evaluate!!(model, Random.default_rng(), args...)
end

At this time, infinite recurse will occur, because no other evaluate!! will be type-matched.

Current proposed resolution works, but I wonder maybe we can just combine the two functions above, i.e., modifying

(model::Model)(args...) = first(evaluate!!(model, args...))
to

(model::Model)(args...) = first(evaluate!!(model, Random.default_rng(), args...))

Would this be cleaner? I am not sure.

Additionally, the thrown error will be something like

ERROR: MethodError: no method matching evaluate!!(::Model{…}, ::Vector{…}, ::Vector{…})
The function `evaluate!!` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  evaluate!!(::Model, ::AbstractVarInfo, ::AbstractContext)
   @ DynamicPPL ~/DynamicPPL.jl/src/model.jl:888
  evaluate!!(::Model, ::Random.AbstractRNG)
   @ DynamicPPL ~/DynamicPPL.jl/src/model.jl:898
  evaluate!!(::Model, ::Random.AbstractRNG, ::AbstractVarInfo)
   @ DynamicPPL ~/DynamicPPL.jl/src/model.jl:898
  ...

which, arguably is still not great. Because it exposes the un-exported evaluate!! function, and not sure user will know what to do with the message. But on this, I don't have a good solution.

@mhauru
Copy link
Member Author

mhauru commented Jul 11, 2024

With (model::Model)(args...) = first(evaluate!!(model, Random.default_rng(), args...)) if the user provides an rng as part of args evaluate!! would then be called with two rngs in its arguments, which would be trouble.

Is your main wish here to have the error message be clearer, or for the code to be cleaner?

Even if we change the relation of (model::Model)(args...) and evaluate!!, I would still propose adding the varargs type bounds for evaluate!!, because someone might end up calling evaluate!! directly or through other routes, and having any function have the possiblity of going into infinite recursion I think is worth fixing.

@sunxd3
Copy link
Member

sunxd3 commented Jul 11, 2024

Is your main wish here to have the error message be clearer, or for the code to be cleaner?
I thought it makes the code cleaner.

I don't have objections to type-restrict evaluate!!. Thoughts @yebai @torfjelde?

Copy link
Member

@devmotion devmotion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be cleaner and more correct to just write out the varargs we want to support? For instance, args:::Union{AbstractVarInfo,AbstractSampler,AbstractContext}... will allow to specify multiple contexts or samplers etc., which is not useful at all (and will error in the next step in the evaluate!! pipeline). Similarly, instead of arbitrary numbers o f contexts as specified by ::AbstractContext... we actually only want 0 or 1 contexts.

@mhauru
Copy link
Member Author

mhauru commented Jul 11, 2024

I agree @devmotion, but I wonder if we embark on that we should actually rework the method definitions generally to make the whole definition of evaluate!! easier to read. To be clear, I'm not proposing breaking changes, but rather readability improvements that also catch corner case bugs like the one this one is fixing.

Copy link
Member

@sunxd3 sunxd3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Harmonizing with @mhauru, I agree we can improve the evalaute!! function interface. But it will have to be a future PR. For now, I think the fix is okay.

@mhauru mhauru force-pushed the mhauru/turingjl-2182 branch from 72cfd15 to 2baff61 Compare July 12, 2024 14:46
function AbstractPPL.evaluate!!(model::Model, args...)
function AbstractPPL.evaluate!!(
model::Model, args::Union{AbstractVarInfo,AbstractSampler,AbstractContext}...
)
return evaluate!!(model, Random.default_rng(), args...)
Copy link
Member

@yebai yebai Jul 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency, maybe

Suggested change
return evaluate!!(model, Random.default_rng(), args...)
return AbstractPPL.evaluate!!(model, Random.default_rng(), args...)

@mhauru mhauru enabled auto-merge July 12, 2024 15:12
@mhauru mhauru added this pull request to the merge queue Jul 12, 2024
Merged via the queue into master with commit ad848b2 Jul 12, 2024
11 of 12 checks passed
@mhauru mhauru deleted the mhauru/turingjl-2182 branch July 12, 2024 17:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants