From 7d892896e4f7e4fb44b1c8019386f9bba0504603 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Wed, 20 May 2026 00:57:50 +0530 Subject: [PATCH 1/7] fix: support Colon() in middle of indices for NodeLabel getindex (#275) --- src/graph_engine.jl | 1 + test/test_colon_index.jl | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 test/test_colon_index.jl diff --git a/src/graph_engine.jl b/src/graph_engine.jl index f9961354..d16a83af 100644 --- a/src/graph_engine.jl +++ b/src/graph_engine.jl @@ -254,6 +254,7 @@ end Base.length(label::NodeLabel) = 1 Base.size(label::NodeLabel) = () Base.getindex(label::NodeLabel, any) = label +Base.getindex(label::NodeLabel, i1, is...) = label Base.:(<)(left::NodeLabel, right::NodeLabel) = left.global_counter < right.global_counter Base.broadcastable(label::NodeLabel) = Ref(label) diff --git a/test/test_colon_index.jl b/test/test_colon_index.jl new file mode 100644 index 00000000..4c98b4a8 --- /dev/null +++ b/test/test_colon_index.jl @@ -0,0 +1,17 @@ +using Test +using GraphPPL +using Distributions + +@model function test_colon_bug(media) + for g in 1:2 + for i in 1:3 + media_effect[g, i] := media[g, :, i] + end + end +end + +@testset "Colon index in middle of indices" begin + media_data = rand(2, 5, 3) + model = create_model(test_colon_bug(media = media_data)) + @test model !== nothing +end From e3a8bcb30f427b5c0910bd853d56600df5a44eb0 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Wed, 20 May 2026 01:02:53 +0530 Subject: [PATCH 2/7] chore: add CHANGELOG entry for #303 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..a785a7d6 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +\## \[Unreleased] + + + +\### Fixed + +\- Fixed `Colon()` not working in the middle of indices for `NodeLabel` getindex (\[#303](https://github.com/ReactiveBayes/GraphPPL.jl/pull/303)) + From 7e030b1669456ae943cadb72e2c0870d09d6e687 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Wed, 20 May 2026 01:06:46 +0530 Subject: [PATCH 3/7] fix: improve error message for invalid factorization constraint set (#293) --- .../variational_constraints/variational_constraints_engine.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/variational_constraints/variational_constraints_engine.jl b/src/plugins/variational_constraints/variational_constraints_engine.jl index 2bf81577..aa64413e 100644 --- a/src/plugins/variational_constraints/variational_constraints_engine.jl +++ b/src/plugins/variational_constraints/variational_constraints_engine.jl @@ -636,7 +636,7 @@ function materialize_constraints!(model::Model, node_label::NodeLabel, node_data if !is_valid_partition(constraint_set) error( - lazy"Factorization constraint set at node $node_label is not a valid constraint set. Please check your model definition and constraint specification. (Constraint set: $constraint_bitset)" + lazy"Factorization constraint set at node $node_label is not a valid constraint set. Please check your model definition and constraint specification. (Constraint set: $constraint_bitset)\n\nHint: A valid constraint set requires that each interface belongs to exactly one factorization group. In the constraint matrix, each column must have exactly one non-zero entry per group. Make sure all variables appearing in your model are included in your factorization constraint." ) end From e84d5c5cefe0fa9da6ba015799c182ef8dab4639 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Wed, 20 May 2026 00:57:50 +0530 Subject: [PATCH 4/7] fix: support Colon() in middle of indices for NodeLabel getindex (#275) --- src/graph_engine.jl | 1 + test/test_colon_index.jl | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 test/test_colon_index.jl diff --git a/src/graph_engine.jl b/src/graph_engine.jl index 22afad94..19a3d3a0 100644 --- a/src/graph_engine.jl +++ b/src/graph_engine.jl @@ -266,6 +266,7 @@ end Base.length(label::NodeLabel) = 1 Base.size(label::NodeLabel) = () Base.getindex(label::NodeLabel, any) = label +Base.getindex(label::NodeLabel, i1, is...) = label Base.:(<)(left::NodeLabel, right::NodeLabel) = left.global_counter < right.global_counter Base.broadcastable(label::NodeLabel) = Ref(label) diff --git a/test/test_colon_index.jl b/test/test_colon_index.jl new file mode 100644 index 00000000..4c98b4a8 --- /dev/null +++ b/test/test_colon_index.jl @@ -0,0 +1,17 @@ +using Test +using GraphPPL +using Distributions + +@model function test_colon_bug(media) + for g in 1:2 + for i in 1:3 + media_effect[g, i] := media[g, :, i] + end + end +end + +@testset "Colon index in middle of indices" begin + media_data = rand(2, 5, 3) + model = create_model(test_colon_bug(media = media_data)) + @test model !== nothing +end From 284c117c351b1f78d792ff4f19ce7412c43e9676 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Wed, 20 May 2026 01:02:53 +0530 Subject: [PATCH 5/7] chore: add CHANGELOG entry for #303 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..a785a7d6 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +\## \[Unreleased] + + + +\### Fixed + +\- Fixed `Colon()` not working in the middle of indices for `NodeLabel` getindex (\[#303](https://github.com/ReactiveBayes/GraphPPL.jl/pull/303)) + From cd3aa1aec002ff70e9b0acbc86f59009ceb194ee Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Wed, 20 May 2026 01:06:46 +0530 Subject: [PATCH 6/7] fix: improve error message for invalid factorization constraint set (#293) --- .../variational_constraints/variational_constraints_engine.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/variational_constraints/variational_constraints_engine.jl b/src/plugins/variational_constraints/variational_constraints_engine.jl index 3d0326aa..50ea6ffe 100644 --- a/src/plugins/variational_constraints/variational_constraints_engine.jl +++ b/src/plugins/variational_constraints/variational_constraints_engine.jl @@ -646,7 +646,7 @@ function materialize_constraints!(model::Model, node_label::NodeLabel, node_data if !is_valid_partition(constraint_set) error( - lazy"Factorization constraint set at node $node_label is not a valid constraint set. Please check your model definition and constraint specification. (Constraint set: $constraint_bitset)" + lazy"Factorization constraint set at node $node_label is not a valid constraint set. Please check your model definition and constraint specification. (Constraint set: $constraint_bitset)\n\nHint: A valid constraint set requires that each interface belongs to exactly one factorization group. In the constraint matrix, each column must have exactly one non-zero entry per group. Make sure all variables appearing in your model are included in your factorization constraint." ) end From cc68b5a211c9c88010c2bdd1afe253c42918bd4a Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Thu, 8 Oct 2026 03:59:52 +0530 Subject: [PATCH 7/7] test: assert the invalid factorization constraint error message includes a hint --- .../variational_constraints_engine_tests.jl | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/plugins/variational_constraints/variational_constraints_engine_tests.jl b/test/plugins/variational_constraints/variational_constraints_engine_tests.jl index 3efaf513..937e259a 100644 --- a/test/plugins/variational_constraints/variational_constraints_engine_tests.jl +++ b/test/plugins/variational_constraints/variational_constraints_engine_tests.jl @@ -1596,3 +1596,42 @@ end @test map(rhs, collect(stack)) == [:a] end +@testitem "invalid factorization constraint set throws a descriptive error with a hint" begin + using BitSetTuples + import GraphPPL: + create_model, + with_plugins, + materialize_constraints!, + getcontext, + setextra!, + VariationalConstraintsPlugin, + PluginsCollection, + VariationalConstraintsFactorizationBitSetKey + + include("../../testutils.jl") + + using .TestUtils.ModelZoo + + # A constraint set where the same interface appears in more than one group is invalid: + # `((1,), (3,), (1, 3))` puts interface `1` in both the first and the third group. + model = create_model(with_plugins(simple_model(), PluginsCollection(VariationalConstraintsPlugin()))) + ctx = getcontext(model) + node = ctx[NormalMeanVariance, 2] + + setextra!(model[node], VariationalConstraintsFactorizationBitSetKey, BoundedBitSetTuple(((1,), (3,), (1, 3)))) + + error = try + materialize_constraints!(model, node) + nothing + catch e + e + end + + @test error !== nothing + @test error isa ErrorException + message = sprint(showerror, error) + @test occursin("not a valid constraint set", message) + @test occursin("valid constraint set requires", message) + @test occursin("each interface belongs to exactly one factorization group", message) + @test occursin("Hint", message) +end \ No newline at end of file