Skip to content

fix condition #1012

@penelopeysm

Description

@penelopeysm
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

Result:

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

$isassumption = $(DynamicPPL.isassumption(left, vn))
if $(DynamicPPL.isfixed(left, vn))
$left = $(DynamicPPL.getfixed_nested)(
__context__, $(DynamicPPL.prefix)(__context__, $vn)
)
elseif $isassumption
$(generate_tilde_assume(left, dist, vn))
else
# If `vn` is not in `argnames`, we need to make sure that the variable is defined.
if !$(DynamicPPL.inargnames)($vn, __model__)
$left = $(DynamicPPL.getconditioned_nested)(
__context__, $(DynamicPPL.prefix)(__context__, $vn)
)
end

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

Could be solved in a multitude of ways, but IMO the only correct way to solve this is to merge fix and condition into a single thing (which will be part of #1010).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions