Skip to content

Multi-/named-output submodels silently ignore where { ... } options (inline variational constraints) #304

Description

@docxology

Summary

When a composite submodel is invoked with a multi-output ((a, b) ~ sub(...)) or
named-output ((a = x, b = y) ~ sub(...)) left-hand side, the child Context is
constructed without the NodeCreationOptions carried by the where { ... } clause.
As a result, inline where { constraints = ... } (e.g. a MeanField or custom factorization
constraint) is silently ignored for that submodel.

Impact

Silent wrong behaviour: inference runs with a different (unconstrained / different
factorization) recognition distribution than the user requested — no warning is emitted.

Root cause

src/model_macro.jl get_make_node_function:

  • single-output LHS (line 838): Context(__parent_context__, $ms_name, __options__)
  • Tuple LHS (line 860): Context(__parent_context__, $ms_name)
  • NamedTuple LHS (line 882): Context(__parent_context__, $ms_name)

The constraint engine reads inline constraints from that field:

src/plugins/variational_constraints/variational_constraints_engine.jl:1003
inline_constraints = get(context_options(child), :constraints, nothing)

with context_options(context) = something(context.options, EmptyNodeCreationOptions)
(src/graph_engine.jl:668). When context.options === nothing it returns
EmptyNodeCreationOptions, so :constraints is never found.

Repro

using GraphPPL, Distributions
import GraphPPL: @model

@model function two_out(a, b, x)
    a ~ Normal(x, 1.0)
    b ~ Normal(a, 1.0)
end

@model function outer_multi(x)
    (a, b) ~ two_out(x = x) where { constraints = GraphPPL.MeanField() }
end

model = GraphPPL.create_model(outer_multi()) do m, ctx
    x = GraphPPL.datalabel(m, ctx, GraphPPL.NodeCreationOptions(kind=:data), :x, 1.0)
    return (x = x,)
end
ctx = GraphPPL.getcontext(model)
for (fid, child) in GraphPPL.pairs(GraphPPL.children(ctx))
    @show child.options          # => nothing  (single-output equivalent: NodeCreationOptions)
end

The equivalent single-output call honors it (see the existing "inline constraints on
submodel calls" test, test/plugins/variational_constraints/variational_constraints_tests.jl:1135).

Expected

Inline where { constraints = ... } behaves identically for multi-/named-output submodel
calls as for single-output calls.

Suggested fix

Pass __options__ in both multi-output paths (mirroring line 838) plus a regression test.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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