TuringLang / TuringLang/DynamicPPL.jl
Handle fixing and conditioning together
Nobody has claimed this yet.
- Dominant language
- Julia
- Stars
- 286
- Forks
- 41
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 34
Description
Consider:
using DynamicPPL, Distributions
for op1 in [condition, fix]
for op2 in [condition, fix]
@model g() = x ~ Normal()
g_op = op1(g(), (; x = 1.0))
@model f() = a ~ to_submodel(g_op)
f_op = op2(f(), (@varname(a.x) => 2.0))
println("$op1 $op2 => $(f_op())")
end
end
The result (last tested on DynamicPPL 0.41):
condition condition => 2.0
condition fix => 2.0
fix condition => 1.0
fix fix => 2.0
For fixfix and concon, there's no issue because the values are merged in a way that respects the outer. (it's somewhere in getconditioned / getfixed I think)
For confix and fixcon, unfortunately, the logic in the codebase says that fixing always takes precedence over conditioning, because fix is in the first branch and condition is in the last branch
I discovered while writing tests for nested condition/fix. This was the test I had intended to add to test/submodel.jl.
@testset "precedence for conditioning/fixing the same variable twice" begin
# Check that the outermost conditioning takes precedence. I'm not sure
# if there is a strong a priori reason for this, but it is currently
# the case and thus this test just ensures that the behaviour doesn't
# suddenly change without it being a conscious decision to do so.
@testset "$op1,$op2" for op1 in [condition, fix], op2 in [condition, fix]
@model function f()
x ~ Normal()
end
fcond = op1(f(), (; x=1.0)) # this is ignored
@model function g()
return a ~ to_submodel(fcond)
end
gcond = op2(g(), (@varname(a.x) => 2.0)) # this takes precedence
@test gcond() == 2.0
end
end
How to solve?
Right now, conditioning and fixing go via what's now called CondFixContext. However, the conditioned values and fixed values are still stored in separate 'layers' of this.
The way to prevent this would be to 'flatten' the layers such that conditioned variables and fixed variables are part of the same VarNamedTuple. They would be held inside a VNT together with a flag that indicates whether it's conditioned or fixed. Thus instead of having a data structure that holds two VNTs, one conditioned and one fixed, we would only have one VNT that holds both.
We could then use templated_setindex_no_overwrite!! or something similar to issue an error when attempting to fix a variable that is already conditioned.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with src/compiler.jl lines 448-461 and trace the getconditioned/getfixed logic for nested condition and fix operations. Review the intended regression test in test/submodel.jl and the existing CondFixContext representation. Done means the outermost conditioning or fixing takes precedence consistently for all four operation pairs, with tests covering the behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- julia
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100