probcomp / Gen.jl

A general-purpose probabilistic programming system with programmable inference
https://gen.dev
Apache License 2.0
1.79k stars 160 forks source link

Consider changing how `update` and `regenerate` handle default arguments. #538

Open ztangent opened 2 months ago

ztangent commented 2 months ago

When I first introduced support for optional trailing arguments in the Gen dynamic modeling language (see #195), the way I added this functionality to update and regenerate was to assume that default argument vaues would always override the arguments specified in the previous trace that update or regenerate was applied to.

This behavior was easier to implement, but now that I think about it, quite unnatural: Usually, if you're running a MCMC or SMC algorithm on a trace, you want to keep any optional trailing arguments fixed, rather than having to repeatedly specify their values.

As such, I'm proposing that for any optional trailing arguments that are not explicitly specified in a call to update or regenerate, update or regenerate should keep the argument values stored in the previous trace. This would not be hard to implement. It would be a breaking change, but I don't think tons of code relies on the current default behavior because of how unnatural it is.

femtomc commented 2 months ago

This makes a lot of sense -- is it fair to think of unspecified default values as implicitly converted to NoChange argdiffs? There's a more general change where positional arguments are optional as well for update and regenerate (anything not specified is cast to NoChange from the previous trace's arguments).

ztangent commented 2 months ago

Interesting, thanks! I think we could make that part of the spec for update, but it may be hard to support across implementations of the GFI?

Alternatively, we could add a new generic implementation of update and regenerate that checks whether the number of newly supplied arguments is equal to the number of previous arguments, then pads the new arguments with the previous arguments as necessary. But has to be done dynamically, since there's no easy way to check the type of the arguments contained in the previous trace. (It'll also require specific implementations of update to have a different name than update, so that the generic implementation of update can call update_impl under the hood.)