TuringLang / AbstractPPL.jl

Common types and interfaces for probabilistic programming
http://turinglang.org/AbstractPPL.jl/
MIT License
27 stars 7 forks source link

Colon doesn't subsume itself #99

Closed mhauru closed 2 months ago

mhauru commented 2 months ago

Is there a reason why this is intentional?

julia> DynamicPPL.subsumes(@varname(x[:]), @varname(x[:]))
ERROR: Colons cannot be subsumed
sunxd3 commented 2 months ago

My guess is that x[:] is not concrete (in the sense that it can have different materialization under different circumstance) , so it's not safe to say if a x[:] subsumes x[:]. If I am interpreting this correctly, then it might be an aesthetic choice.

Is there cases you would want otherwise?

mhauru commented 2 months ago

This came about in https://github.com/TuringLang/DynamicPPL.jl/pull/637, when trying to split a type unstable VarInfo into type stable parts, but I already worked around that issue, so that's not really concern. It just seemed strange for a varname not to subsume itself.

Can we assume that when calling subsumes the symbols in the first argument and in the second argument refer to the same object, i.e. that in the above example x on both sides is the "same x"? If yes, it seems to me that x[:] should subsume itself. If not, then I do understand that it doesn't, because x[:] is a very different thing if in one case x is of length 2 and in the other of length 3.

sunxd3 commented 2 months ago

There is a way to concretize a varname like x[:]

e.g.

julia> x = ones(3)
3-element Vector{Float64}:
 1.0
 1.0
 1.0

julia> vn1 = @varname(x[:], true)
x[:]

julia> x = ones(3, 3)
t3×3 Matrix{Float64}:
e 1.0  1.0  1.0
 1.0  1.0  1.0
 1.0  1.0  1.0

julia> vn2 = @varname(x[:], true)
x[:]

julia> dump(vn1)
VarName{:x, Accessors.IndexLens{Tuple{AbstractPPL.ConcretizedSlice{Int64, Base.OneTo{Int64}}}}}
  optic: Accessors.IndexLens{Tuple{AbstractPPL.ConcretizedSlice{Int64, Base.OneTo{Int64}}}}
    indices: Tuple{AbstractPPL.ConcretizedSlice{Int64, Base.OneTo{Int64}}}
      1: AbstractPPL.ConcretizedSlice{Int64, Base.OneTo{Int64}}
        range: Base.OneTo{Int64}
          stop: Int64 3

julia> dump(vn2)
VarName{:x, Accessors.IndexLens{Tuple{AbstractPPL.ConcretizedSlice{Int64, Base.OneTo{Int64}}}}}
  optic: Accessors.IndexLens{Tuple{AbstractPPL.ConcretizedSlice{Int64, Base.OneTo{Int64}}}}
    indices: Tuple{AbstractPPL.ConcretizedSlice{Int64, Base.OneTo{Int64}}}
      1: AbstractPPL.ConcretizedSlice{Int64, Base.OneTo{Int64}}
        range: Base.OneTo{Int64}
          stop: Int64 9

ref: https://github.com/TuringLang/AbstractPPL.jl/blob/b9cd48b24a7c159799a76dc08584c16c8e0b470c/src/varname.jl#L524

Looking at Turing's @model macro.

julia> @macroexpand @model m(x) = x[:] ~ some_d()
quote
    function m(__model__::Model, __varinfo__::AbstractVarInfo, __context__::AbstractPPL.AbstractContext, x::Any; )
        #= REPL[15]:1 =#
        begin
            var"##dist#242" = some_d()
            var"##vn#239" = (DynamicPPL.resolve_varnames)((VarName){:x}((AbstractPPL.concretize)((Accessors.opticcompose)((Accessors.IndexLens)((:,))), x)), var"##dist#242")
            var"##isassumption#240" = begin
                    if (DynamicPPL.contextual_isassumption)(__context__, var"##vn#239")
                        if !((DynamicPPL.inargnames)(var"##vn#239", __model__)) || (DynamicPPL.inmissings)(var"##vn#239", __model__)
                            true
                        else
                            (Base.maybeview)(x, :) === missing
                        end
                    else
                        false
                    end
                end
            begin
                #= /Users/xiandasun/.julia/packages/DynamicPPL/DvdZw/src/compiler.jl:579 =#
                var"##retval#244" = if (DynamicPPL.contextual_isfixed)(__context__, var"##vn#239")
                        x[:] = (DynamicPPL.getfixed_nested)(__context__, var"##vn#239")
                    elseif var"##isassumption#240"
                        begin
                            (var"##value#243", __varinfo__) = (DynamicPPL.tilde_assume!!)(__context__, (DynamicPPL.unwrap_right_vn)((DynamicPPL.check_tilde_rhs)(var"##dist#242"), var"##vn#239")..., __varinfo__)
                            x = (Accessors.set)(x, (BangBang.AccessorsImpl.prefermutation)((Accessors.opticcompose)((Accessors.IndexLens)((:,)))), var"##value#243")
                            var"##value#243"
                        end
                    else
                        if !((DynamicPPL.inargnames)(var"##vn#239", __model__))
                            x[:] = (DynamicPPL.getconditioned_nested)(__context__, var"##vn#239")
                        end
                        (var"##value#241", __varinfo__) = (DynamicPPL.tilde_observe!!)(__context__, (DynamicPPL.check_tilde_rhs)(var"##dist#242"), (Base.maybeview)(x, :), var"##vn#239", __varinfo__)
                        var"##value#241"
                    end
                #= /Users/xiandasun/.julia/packages/DynamicPPL/DvdZw/src/compiler.jl:580 =#
                return (var"##retval#244", __varinfo__)
            end
        end
    end
    begin
        $(Expr(:meta, :doc))
        function m(x::Any; )
            #= REPL[15]:1 =#
            return (Model)(m, NamedTuple{(:x,)}((x,)); )
        end
    end
end

there is a AbstractPPL.concretize call, so maybe unconretized varname like x[:] should not be in the varinfo?

Is there a small example for this?

mhauru commented 2 months ago

Cool thanks, I didn't know concretization was a thing at all.

The case I ran into came from using NamedDist in this test: https://github.com/TuringLang/DynamicPPL.jl/blob/cdd340745271073bac943e73d184c0d0be5aa264/test/compiler.jl#L306

So is the answer then that for unconcretized VarNames it is indeed correct that a VarName may not subsume itself, because e.g. the symbol x might refer to different objects in different contexts, and thus x[:] in one context may not be a subset of x[:] in another context?

sunxd3 commented 2 months ago

for unconcretized VarNames it is indeed correct that a VarName may not subsume itself, because e.g. the symbol x might refer to different objects in different contexts, and thus x[:] in one context may not be a subset of x[:] in another context

I think so, or at least this might be the more correct thing to do