From 438519ace32d4ae84480eb20b945b1411e77aa8c Mon Sep 17 00:00:00 2001 From: 4d Date: Sat, 1 Aug 2026 17:24:39 -0700 Subject: [PATCH 1/2] fix: repair duplicate form-constraint @warn (UndefVarError opt) The 'already has functional form constraint' warnings referenced undefined locals opt and constraint_data inside lazy"...", producing UndefVarError instead of the intended informative warning. Use bound getconstraint(...). --- .../variational_constraints/variational_constraints_engine.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/variational_constraints/variational_constraints_engine.jl b/src/plugins/variational_constraints/variational_constraints_engine.jl index 11878392..dc91c3c2 100644 --- a/src/plugins/variational_constraints/variational_constraints_engine.jl +++ b/src/plugins/variational_constraints/variational_constraints_engine.jl @@ -951,7 +951,7 @@ function apply_constraints!( applicable_nodes = unroll_nocreate(context[getvariables(marginal_constraint)]) for node in applicable_nodes if hasextra(model[node], VariationalConstraintsMarginalFormConstraintKey) - @warn lazy"Node $node already has functional form constraint $(opt[:q]) applied, therefore $constraint_data will not be applied" + @warn "Node $node already has functional form constraint $(getconstraint(marginal_constraint)) applied, therefore it will not be applied" else setextra!(model[node], VariationalConstraintsMarginalFormConstraintKey, getconstraint(marginal_constraint)) end @@ -966,7 +966,7 @@ function apply_constraints!(model::Model, context::Context, message_constraint:: applicable_nodes = unroll_nocreate(context[getvariables(message_constraint)]) for node in applicable_nodes if hasextra(model[node], VariationalConstraintsMessagesFormConstraintKey) - @warn lazy"Node $node already has functional form constraint $(opt[:q]) applied, therefore $constraint_data will not be applied" + @warn "Node $node already has functional form constraint $(getconstraint(message_constraint)) applied, therefore it will not be applied" else setextra!(model[node], VariationalConstraintsMessagesFormConstraintKey, getconstraint(message_constraint)) end From 4b15a8e04a5af825ce577d002c0e34313674c035 Mon Sep 17 00:00:00 2001 From: Bagaev Dmitry Date: Tue, 15 Sep 2026 14:26:47 +0200 Subject: [PATCH 2/2] fix: report the existing constraint in the duplicate-form-constraint warning The warning is meant to say "this node already has constraint X, so the new constraint Y will not be applied". As written it interpolated the *new* constraint into the slot for the existing one, producing "already has functional form constraint Y applied, therefore it will not be applied". Read the existing constraint from the node via `getextra` and name the skipped one, and restore the `lazy"..."` string. Also adds the regression test the PR was missing: applies a form constraint twice (marginal and message variants) and asserts a warning is emitted and the original constraint is preserved. Verified the test errors against the pre-fix `@warn` (UndefVarError: opt) and passes with the fix. Co-Authored-By: Claude Opus 5 (1M context) --- .../variational_constraints_engine.jl | 4 +- .../variational_constraints_engine_tests.jl | 46 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/plugins/variational_constraints/variational_constraints_engine.jl b/src/plugins/variational_constraints/variational_constraints_engine.jl index dc91c3c2..f9f532f1 100644 --- a/src/plugins/variational_constraints/variational_constraints_engine.jl +++ b/src/plugins/variational_constraints/variational_constraints_engine.jl @@ -951,7 +951,7 @@ function apply_constraints!( applicable_nodes = unroll_nocreate(context[getvariables(marginal_constraint)]) for node in applicable_nodes if hasextra(model[node], VariationalConstraintsMarginalFormConstraintKey) - @warn "Node $node already has functional form constraint $(getconstraint(marginal_constraint)) applied, therefore it will not be applied" + @warn lazy"Node $node already has functional form constraint $(getextra(model[node], VariationalConstraintsMarginalFormConstraintKey)) applied, therefore $(getconstraint(marginal_constraint)) will not be applied" else setextra!(model[node], VariationalConstraintsMarginalFormConstraintKey, getconstraint(marginal_constraint)) end @@ -966,7 +966,7 @@ function apply_constraints!(model::Model, context::Context, message_constraint:: applicable_nodes = unroll_nocreate(context[getvariables(message_constraint)]) for node in applicable_nodes if hasextra(model[node], VariationalConstraintsMessagesFormConstraintKey) - @warn "Node $node already has functional form constraint $(getconstraint(message_constraint)) applied, therefore it will not be applied" + @warn lazy"Node $node already has functional form constraint $(getextra(model[node], VariationalConstraintsMessagesFormConstraintKey)) applied, therefore $(getconstraint(message_constraint)) will not be applied" else setextra!(model[node], VariationalConstraintsMessagesFormConstraintKey, getconstraint(message_constraint)) end diff --git a/test/plugins/variational_constraints/variational_constraints_engine_tests.jl b/test/plugins/variational_constraints/variational_constraints_engine_tests.jl index 1e7aae25..f64f37b0 100644 --- a/test/plugins/variational_constraints/variational_constraints_engine_tests.jl +++ b/test/plugins/variational_constraints/variational_constraints_engine_tests.jl @@ -613,6 +613,52 @@ end end end + +@testitem "Applying a second form constraint warns and preserves the original" begin + import GraphPPL: + create_model, + MarginalFormConstraint, + MessageFormConstraint, + IndexedVariable, + apply_constraints!, + getextra, + VariationalConstraintsMarginalFormConstraintKey, + VariationalConstraintsMessagesFormConstraintKey + + include("../../testutils.jl") + + using .TestUtils.ModelZoo + + struct FirstArbitraryFormConstraint end + struct SecondArbitraryFormConstraint end + + # A node that already carries a marginal form constraint must warn (not throw) and keep the first one + model = create_model(simple_model()) + context = GraphPPL.getcontext(model) + apply_constraints!(model, context, MarginalFormConstraint(IndexedVariable(:x, nothing), FirstArbitraryFormConstraint())) + + @test_logs (:warn, r"already has functional form constraint") match_mode = :any apply_constraints!( + model, context, MarginalFormConstraint(IndexedVariable(:x, nothing), SecondArbitraryFormConstraint()) + ) + + for node in filter(GraphPPL.as_variable(:x), model) + @test getextra(model[node], VariationalConstraintsMarginalFormConstraintKey) == FirstArbitraryFormConstraint() + end + + # ... and the same for message form constraints + model = create_model(simple_model()) + context = GraphPPL.getcontext(model) + apply_constraints!(model, context, MessageFormConstraint(IndexedVariable(:x, nothing), FirstArbitraryFormConstraint())) + + @test_logs (:warn, r"already has functional form constraint") match_mode = :any apply_constraints!( + model, context, MessageFormConstraint(IndexedVariable(:x, nothing), SecondArbitraryFormConstraint()) + ) + + for node in filter(GraphPPL.as_variable(:x), model) + @test getextra(model[node], VariationalConstraintsMessagesFormConstraintKey) == FirstArbitraryFormConstraint() + end +end + @testitem "save constraints with constants via `mean_field_constraint!`" begin using BitSetTuples import GraphPPL: