-
Notifications
You must be signed in to change notification settings - Fork 219
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
Bump DynamicPPL to 0.23 #2001
Bump DynamicPPL to 0.23 #2001
Conversation
…ing.jl into torfjelde/dynamicppl-bump
Would be nice to get this through once tests pass:) |
Nvm, it seems like things are broken. Though the resulting errors are quite cryptic to me, so if someone has seen these before, I'd appreciate some guidance 🙏 |
If I drop the usage of I'm a bit uncertain what's going on here 😕 |
src/essential/container.jl
Outdated
return TracedModel{AbstractSampler,AbstractVarInfo,Model,Tuple}( | ||
model, | ||
sampler, | ||
varinfo, | ||
(ArgsAndKwargsF(model.f), args, kwargs), | ||
) |
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.
After some digging, I thought the issue was a lack of deepcopy
for the model
when not explicitly present in args
. So I tried making this into
(f::ArgsAndKwargsF)(model_arg::DynamicPPL.Model, args, kwargs) = f.f(model_arg, args...; kwargs...)
and
return TracedModel{AbstractSampler,AbstractVarInfo,Model,Tuple}(
model,
sampler,
varinfo,
(ArgsAndKwargsF(model.f), args[1], args[2:end], kwargs),
)
With that, the deepcopy
is correctly triggered. But the error in the tests is still occurring 😕
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.
@KDr2 do you remember whether Libtask.TapedFunction
supports keyword arguments?
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.
AFAIK we don't. E.g. TapedTask
uses kwargs to pass the deepcopy_types
.
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.
Also, just for the record, if I do:
(model.f, args...)
i.e. drop the kwargs
+ the diversion completely, then everything works again.
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.
Moreover, it only seems to happen when combined with Gibbs
. I haven't run into these issues when running PG on it's own.
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.
For reference, before I used to do:
struct ArgsAndKwargsF{F}
f::F
end
(f::ArgsAndKwargsF)(args, kwargs) = f.f(args...; kwargs...)
and
return TracedModel{AbstractSampler,AbstractVarInfo,Model,Tuple}(
model,
sampler,
varinfo,
(ArgsAndKwargsF(model.f), args, kwargs),
)
in an attempt to also support kwargs
, but this is causing strange behaviors when using something like PG
in Gibbs
. The above comments are referring to the code that was doing this.
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.
Also, now that we have Core.kwcall
, it should be possible to support from Julia 1.9 on onwards in Libtask.jl
src/essential/container.jl
Outdated
return TracedModel{AbstractSampler,AbstractVarInfo,Model,Tuple}(model, sampler, varinfo, evaluator) | ||
# FIXME: We're just dropping the `kwargs` here. I'm guessing this can | ||
# cause issues if the model ends up mutating any of the keyword arguments. | ||
args, _ = DynamicPPL.make_evaluate_args_and_kwargs(model, varinfo, context) |
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 bad.
See other comments on alternatives: #2001 (comment)
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.
Okay, so I've figured out why it breaks, but I think it's going to be difficult to fix it 😕
Libtask only handles nested produce
in the case where each instruction it sees contains at most one produce
.
julia> using Libtask
julia> f(x) = (produce(x); produce(2x); produce(3x); return nothing)
f (generic function with 1 method)
julia> g(x) = f(x)
g (generic function with 1 method)
julia> task = Libtask.TapedTask(f, 1);
julia> consume(task), consume(task), consume(task)
(1, 2, 3)
julia> task = Libtask.TapedTask(g, 1); # tracing of nested call
julia> consume(task) # goes through all the `produce` calls before even calling the `callback` (which is `Libtask.producer`)
counter=1
tf=TapedFunction:
* .func => g
* .ir =>
------------------
CodeInfo(
1 ─ %1 = Main.f(x)::Core.Const(nothing)
└── return %1
)
------------------
ErrorException("There is a produced value which is not consumed.")Union{Ptr{Nothing}, Base.InterpreterIP}[Ptr{Nothing} @0x00007fa8d200eeeb, Ptr{Nothing} @0x00007fa8a0a30f29, Ptr{Nothing} @0x00007fa8a0a36844, Ptr{Nothing} @0x00007fa8a0a36865, Ptr{Nothing} @0x00007fa8e6b44f1d, Ptr{Nothing} @0x00007fa8a0a366e3, Ptr{Nothing} @0x00007fa8a0a36802, Ptr{Nothing} @0x00007fa8e6b44f1d, Ptr{Nothing} @0x00007fa8a0a35f25, Ptr{Nothing} @0x00007fa8a0a361dd, Ptr{Nothing} @0x00007fa8a0a36512, Ptr{Nothing} @0x00007fa8a0a3652f, Ptr{Nothing} @0x00007fa8e6b44f1d, Ptr{Nothing} @0x00007fa8e6b6656f]
ERROR: There is a produced value which is not consumed.
Stacktrace:
[1] consume(ttask::TapedTask{typeof(g), Tuple{Int64}})
@ Libtask ~/.julia/packages/Libtask/h7Kal/src/tapedtask.jl:153
[2] top-level scope
@ REPL[9]:1
This also makes me realize that Libtask.jl will also just silently do the wrong thing in certain cases where we use @submodel
and the inner-model contains observations..
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.
And all of this effectively means that, with the current Libtask, we cannot support kwargs in a model with out converting it all to positional arguments..
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.
Libtask only handles nested produce in the case where each instruction it sees contains at most one produce.
Yes, this was a design decision -- it becomes very complicated to trace every function in a nested way. In theory, this can be fixed by adding submodels
to the list of functions we need to trace into.
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.
And all of this effectively means that, with the current Libtask, we cannot support kwargs in a model with out converting it all to positional arguments..
That could be a temporary hacky solution before @KDr2 and I add submodels
to the list of functions we need to trace recursively.
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 wanted to make the dependency light so Libtask
is more stable for new Julia releases. Also, recursively unrolling will create unnecessarily large tape in most use cases, i.e. for SMC/PG, it has significant performance penalties.
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.
Ah gotcha. But yeah, it should be possible. I'm actually trying to implement exactly that right now.
It's ok to ignore that for now.
It is very time-consuming to make this feature right if I remember the details correctly.
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'd love to:) But this completely breaks SMC samplers for any model with kwargs 😕
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 think your previous commit fixed all tests except the new grammar
model.
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.
So it will run with all kwarg models but it will silently do the incorrect thing if the default kwargs are not the ones we're supposed to use. In this PR we currently just drop them completely, i.e. we end up using the default kwargs always.
I don't think this quite qualifies as good enough:/
Looking at the new grammar test again, I am not quite sure why keyword arguments worked before this PR? If we don't have support for keywords arguments from |
Kwargs were supported before DPPL before 0.23 to the extent that kwargs were supported by DPPL. With that I mean that splatted kwargs, e.g. After DPPL 0.23, the evaluator now supports kwargs properly, but this means that we've added a additional diversions before actually hitting the "body" of A few comments on the above:
There seem to be two ways to go about this:
IMHO, we should really address (1). And in the process, we should document how Libtask.jl actually works so we ensure that this doesn't happen again. But I've tried doing this now, and boy it seems pretty darn difficult to do with the current approach. The reason is that we're currently relying on All in all, I don't quite see how the current approach taken in Libtask.jl can support these things. Are there any concrete plans regarding this @yebai? We can implement (2), as follows. Instead of converting @model f(args...; kwargs...) into the evaluator f(__model__, __varinfo__, __context__, args...; kwargs...) as is done in DPPL 0.23, we can convert it into f(__model__, __varinfo__, __context__, kwargs::NamedTuple, args...) taking inspiration from But approach (2) will not change the "silently producing the incorret result for |
Let's take the 2nd route since it doesn't block this PR, or subsequent PRs in DynamicPPL. @KDr2 and I can add support for (1) later -- we are aware of the limitation but decided it wasn't used very widely. But I agree to obtain consistent behavior, and we should raise the bar and properly support it.
Yes, but let's consider that in a separate PR.
I'm not aware of this functionality. Can you elaborate? |
Already on it:)
https://docs.julialang.org/en/v1/devdocs/functions/#Keyword-arguments Just saying that the approach I suggest is inspired by that. |
On 2nd thought, this won't solve our issues 😕 We would need to make the evaluator of the model a generated function to achieve what I was thinking 😕 |
So you can actually do this: TuringLang/DynamicPPL.jl#483 Not certain if this is a good idea or not. |
test/inference/Inference.jl
Outdated
@test_throws ErrorException chain = sample(gauss(x), PG(10), 10) | ||
@test_throws ErrorException chain = sample(gauss(x), SMC(), 10) |
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.
These should be passing. guass2
should be the one causing issues.
Codecov ReportPatch and project coverage have no change.
Additional details and impacted files@@ Coverage Diff @@
## master #2001 +/- ##
======================================
Coverage 0.00% 0.00%
======================================
Files 21 21
Lines 1424 1427 +3
======================================
- Misses 1424 1427 +3
☔ View full report in Codecov by Sentry. |
Closes #2004 and #2005