-
Notifications
You must be signed in to change notification settings - Fork 230
Update DynamicPPL.jl version #1726
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
Conversation
On master we have the following behavior for a test-case in Turing.jl: ```julia julia> @macroexpand @model empty_model() = begin x = 1; end quote function empty_model(__model__::DynamicPPL.Model, __varinfo__::DynamicPPL.AbstractVarInfo, __context__::DynamicPPL.AbstractContext; ) #= REPL[5]:1 =# begin #= REPL[5]:1 =# #= REPL[5]:1 =# return (x = 1, __varinfo__) end end begin $(Expr(:meta, :doc)) function empty_model(; ) #= REPL[5]:1 =# return (DynamicPPL.Model)(:empty_model, empty_model, NamedTuple(), NamedTuple()) end end end ``` Notice the `return` statement: it converted the statement `x = 1` which returns `1` into an attempt at a `NamedTuple{(:x, :__varinfo__)}`. On Julia 1.6 we don't really notice much of difference, because `first` and `last` will have the same behavior, but on Julia 1.3 the tests would fail in TuringLang/Turing.jl#1726 since "implicit" names in construction of `NamedTuple` isn't supported. This PR addresses this issue by simply capturing the return-value in separate variable, which is then combined with `__varinfo__` in a `Tuple` at the end. This should both fail and succeed whenever standard Julia code would.
src/inference/AdvancedSMC.jl
Outdated
| acclogp!!(vi, lp) | ||
| end | ||
| return r, 0 | ||
| return r, 0, _vi |
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.
Is this the right thing to do @yebai @devmotion ?
Essentially the issue is that when we're using PG with threads, _vi is a ThreadSafeVarInfo while vi = AdvancedPS.current_trace().f.varinfo is not. And so if we return vi instead of _vi, subsequent code in evaluate_threadsafe!! will fail.
I'm not too familiar with the AdvancedPS stuff so it's a bit unclear to me what I should be returning here, e.g. is the above okay?
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.
Or should I overload evaluate!! and all the corresponding methods?
If we're doing this, we might want to consider making the "outer" calls, e.g. (::Model)(...) a bit more general, e.g. dispatch on AbstractProbabilisticProgram instead and then just overloading evaluate_threadsafe!! and evaluate_threadunsafe!! for TracedModel.
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.
| return r, 0, _vi | |
| return r, 0, vi |
Let's perhaps disallow the use of ThreadSafeVarInfo with PG/SMC for now. These samplers are not yet ready to run in threads parallelism due to the interaction of particles (i.e. the resampling step). I suggest that we simply throw an error here explaining that ThreadSafeVarInfo support for PG is to be supported in the near future.
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.
Well, there currently isn't a way to allow/disallow specific combinations like this. And erroring or even just telling people not to use PG/SMC just because they are running with --threads=4 seems wrong since they might not even be using threading inside of the model.
We could add a isthreadsafe(varinfo, context) check in https://github.com/TuringLang/DynamicPPL.jl/blob/57c50f1f46aa5e51ec2125f3ae4883c61e59e9c3/src/model.jl#L391 and then implement this specifically for SamplingContext{<:PG}, etc.
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.
We could add a
isthreadsafe(varinfo, context)check in https://github.com/TuringLang/DynamicPPL.jl/blob/57c50f1f46aa5e51ec2125f3ae4883c61e59e9c3/src/model.jl#L391 and then implement this specifically forSamplingContext{<:PG}, etc.
Yes, I think this would be a better approach. Then we can always choose the non-threadsafe VarInfo for these samplers instead of erroring if they run Julia with multiple threads (e.g. I always do, so I think it would be very inconvenient to throw an error here).
The only possible problem is that then it would be possible to use @threads with a non-threadsafe VarInfo which can lead to incorrect result silently. I don't think there's a clean way to detect if a model tries to use multiple threads (since it is not as simple as detecting a @threads macro call). I wonder if it is possible to add a locking mechanism to the default non-threadsafe VarInfo that makes it threadsafe while not impacting performance if it is run with a single thread. Surely it would be slower if multiple threads are used but with multiple threads we would use the more performant ThreadSafeVarInfo anyway - if we don't use PG etc. The main motivation would be to avoid silent problems with multithreaded models when PG + a non-threadsafe VarInfo is used - it seems fine if it is less performant since it is not recommended.
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.
Yes, I think this would be a better approach. Then we can always choose the non-threadsafe VarInfo for these samplers instead of erroring if they run Julia with multiple threads (e.g. I always do, so I think it would be very inconvenient to throw an error here).
I'll do that then 👍
The only possible problem is that then it would be possible to use @threads with a non-threadsafe VarInfo which can lead to incorrect result silently.
Yup 😕 We could potentially address this by using atomics (though it would probably require very recent Julia versions to be able to do this properly) which I expect would behave just like a Ref in the single-threaded case, or even just making the default VarInfo threadsafe by using an array instead of Ref for the logp field.
But it's worth mentioning that currently PG, etc. already silently does the wrong thing, doesn't it?
Codecov Report
@@ Coverage Diff @@
## master #1726 +/- ##
==========================================
+ Coverage 81.16% 81.29% +0.12%
==========================================
Files 24 24
Lines 1476 1470 -6
==========================================
- Hits 1198 1195 -3
+ Misses 278 275 -3
Continue to review full report at Codecov.
|
Pull Request Test Coverage Report for Build 1588726417
💛 - Coveralls |
See related discussion: TuringLang/Turing.jl#1726 (comment).
See related discussion: TuringLang/Turing.jl#1726 (comment).
|
Tests are now passing 👍 This should now be ready for a review. |
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.
Looks good! To clarify, this PR only updates the DynamicPPL API, but does not make use of SimpleVarInfo yet, right?
|
Correct!
…On Thu, Dec 16, 2021, 10:48 AM Hong Ge ***@***.***> wrote:
***@***.**** approved this pull request.
Looks good! To clarify, this PR only updates the DynamicPPL API, but does
not make use of SimpleVarInfo yet, right?
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#1726 (review)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACUPZZFG4HZSER2UE7IQEJ3URGYYBANCNFSM5HV3ICIA>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
|
|
@devmotion you happy with this? Btw, should we also re-export |
|
I'll have a look this afternoon 👍 I guess we don't have any prominent documentation yet and don't use it in any tutorials or examples? I think maybe we could add them and export these function in a single PR? |
|
Bueno 👍 And yeah, that's a fair point. Next on my TODO list is writing a tutorial/some docs on the recent features, e.g. |
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.
Just had some minor questions, but looks good overall 👍 I guess most questions are related to the SimpleVarInfo integration which will be done in a separate PR.
| setlogp!(vi, ForwardDiff.value(logp)) | ||
| # Don't need to capture the resulting `vi` since this is only | ||
| # needed if `vi` is mutable. | ||
| setlogp!!(vi, ForwardDiff.value(logp)) |
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 guess ideally we would want a no-op here when setlogp!! returns a new object - if we just call setlogp!! with a SimpleVarInfo it would create a new VarInfo object even though it's never used. Maybe one could check if vi === new_vi and only call setlogp!! if it is true (assuming that in this case setlogp!! would not return a new object)?
But I guess this should be left for the SimpleVarInfo PR?
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.
Will this not be optimized away in the case where vi is immutable?
I sort of assumed so. But I guess we might as well give the compiler a helping hand just to be sure.
|
|
||
| function AdvancedPS.reset_logprob!(f::TracedModel) | ||
| DynamicPPL.resetlogp!(f.varinfo) | ||
| DynamicPPL.resetlogp!!(f.varinfo) |
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 also relies on the fact that it mutates f.varinfo.
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.
Yup, but TracedModel isn't compatible with immutable AbstractVarInfo 😕
| resetlogp!!(vi) | ||
| empty!!(vi) |
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.
Does empty!! not call resetlogp!!? Maybe this would be reasonable? In any case, also here it is mandatory that the calls are actually mutating.
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.
Similar to the calls below.
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.
Same response as above: these samplers aren't compatible with immutable VarInfo 😕
src/inference/emcee.jl
Outdated
| # Transform to unconstrained space. | ||
| DynamicPPL.link!.(vis, Ref(spl)) |
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.
Why did you extract it? It seems there are no performance gains but rather now there are both a broadcasting and a map operation whereas before we just used a single map?
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 extracted it because at first I intended to make it compatible with immutable AbstractVarInfo, but then I realized this shouldn't be done in this PR 🙃 I'll revert this change I guess 👍
|
Thanks! I'll merge once tests pass. |
This is a sibling-PR of TuringLang/DynamicPPL.jl#309.
The aim here is to make the bare-minimum changes to ensure that everything works as before. I've not made any significant attempts at making samplers compatible with immutable implementations of
AbstractVarInfo, e.g.SimpleVarInfo, but rather just marked these lines with a# TODO: make compatible with immutable viwhich we can then address in a separate PR.Related: #1725.
EDIT: Tests are passing locally, but they won't pass here until the PR in DPPL has been merged and released.