|
1 | 1 | """
|
2 |
| - VarName(sym[, indexing=()]) |
| 2 | + inargnames(varname::VarName, model::Model) |
3 | 3 |
|
4 |
| -A variable identifier for a symbol `sym` and indices `indexing` in the format |
5 |
| -returned by [`@vinds`](@ref). |
| 4 | +Statically check whether the variable of name `varname` is an argument of the `model`. |
6 | 5 |
|
7 |
| -The Julia variable in the model corresponding to `sym` can refer to a single value or to a |
8 |
| -hierarchical array structure of univariate, multivariate or matrix variables. The field `indexing` |
9 |
| -stores the indices requires to access the random variable from the Julia variable indicated by `sym` |
10 |
| -as a tuple of tuples. Each element of the tuple thereby contains the indices of one indexing |
11 |
| -operation. |
12 |
| -
|
13 |
| -`VarName`s can be manually constructed using the `VarName(sym, indexing)` constructor, or from an |
14 |
| -indexing expression through the [`@varname`](@ref) convenience macro. |
15 |
| -
|
16 |
| -# Examples |
17 |
| -
|
18 |
| -```jldoctest |
19 |
| -julia> vn = VarName(:x, ((Colon(), 1), (2,))) |
20 |
| -x[Colon(),1][2] |
21 |
| -
|
22 |
| -julia> vn.indexing |
23 |
| -((Colon(), 1), (2,)) |
24 |
| -
|
25 |
| -julia> VarName(DynamicPPL.@vsym(x[:, 1][1+1]), DynamicPPL.@vinds(x[:, 1][1+1])) |
26 |
| -x[Colon(),1][2] |
27 |
| -``` |
28 |
| -""" |
29 |
| -struct VarName{sym, T<:Tuple} |
30 |
| - indexing::T |
31 |
| -end |
32 |
| - |
33 |
| -VarName(sym::Symbol, indexing::Tuple = ()) = VarName{sym, typeof(indexing)}(indexing) |
34 |
| - |
35 |
| -""" |
36 |
| - VarName(vn::VarName[, indexing=()]) |
37 |
| -
|
38 |
| -Return a copy of `vn` with a new index `indexing`. |
39 |
| -""" |
40 |
| -function VarName(vn::VarName, indexing::Tuple = ()) |
41 |
| - return VarName{getsym(vn), typeof(indexing)}(indexing) |
42 |
| -end |
43 |
| - |
44 |
| - |
45 |
| -""" |
46 |
| - getsym(vn::VarName) |
47 |
| -
|
48 |
| -Return the symbol of the Julia variable used to generate `vn`. |
49 |
| -""" |
50 |
| -getsym(vn::VarName{sym}) where sym = sym |
51 |
| - |
52 |
| - |
53 |
| -""" |
54 |
| - getindexing(vn::VarName) |
55 |
| -
|
56 |
| -Return the indexing tuple of the Julia variable used to generate `vn`. |
57 |
| -""" |
58 |
| -getindexing(vn::VarName) = vn.indexing |
59 |
| - |
60 |
| - |
61 |
| -Base.hash(vn::VarName, h::UInt) = hash((getsym(vn), getindexing(vn)), h) |
62 |
| -Base.:(==)(x::VarName, y::VarName) = getsym(x) == getsym(y) && getindexing(x) == getindexing(y) |
63 |
| - |
64 |
| -function Base.show(io::IO, vn::VarName) |
65 |
| - print(io, getsym(vn)) |
66 |
| - for indices in getindexing(vn) |
67 |
| - print(io, "[") |
68 |
| - join(io, indices, ",") |
69 |
| - print(io, "]") |
70 |
| - end |
71 |
| -end |
72 |
| - |
73 |
| - |
74 |
| -""" |
75 |
| - Symbol(vn::VarName) |
76 |
| -
|
77 |
| -Return a `Symbol` represenation of the variable identifier `VarName`. |
78 |
| -""" |
79 |
| -Base.Symbol(vn::VarName) = Symbol(string(vn)) # simplified symbol |
80 |
| - |
81 |
| - |
82 |
| -""" |
83 |
| - inspace(vn::Union{VarName, Symbol}, space::Tuple) |
84 |
| -
|
85 |
| -Check whether `vn`'s variable symbol is in `space`. |
86 |
| -""" |
87 |
| -inspace(vn, space::Tuple{}) = true # empty space is treated as universal set |
88 |
| -inspace(vn, space::Tuple) = vn in space |
89 |
| -inspace(vn::VarName, space::Tuple{}) = true |
90 |
| -inspace(vn::VarName, space::Tuple) = any(_in(vn, s) for s in space) |
91 |
| - |
92 |
| -_in(vn::VarName, s::Symbol) = getsym(vn) == s |
93 |
| -_in(vn::VarName, s::VarName) = subsumes(s, vn) |
94 |
| - |
95 |
| - |
96 |
| -""" |
97 |
| - subsumes(u::VarName, v::VarName) |
98 |
| -
|
99 |
| -Check whether the variable name `v` describes a sub-range of the variable `u`. Supported |
100 |
| -indexing: |
101 |
| -
|
102 |
| -- Scalar: `x` subsumes `x[1, 2]`, `x[1, 2]` subsumes `x[1, 2][3]`, etc. |
103 |
| -- Array of scalar: `x[[1, 2], 3]` subsumes `x[1, 3]`, `x[1:3]` subsumes `x[2][1]`, etc. |
104 |
| - (basically everything that fulfills `issubset`). |
105 |
| -- Slices: `x[2, :]` subsumes `x[2, 10][1]`, etc. |
106 |
| -
|
107 |
| -Currently _not_ supported are: |
108 |
| -
|
109 |
| -- Boolean indexing, literal `CartesianIndex` (these could be added, though) |
110 |
| -- Linear indexing of multidimensional arrays: `x[4]` does not subsume `x[2, 2]` for `x` a matrix |
111 |
| -- Trailing ones: `x[2, 1]` does not subsume `x[2]` for `x` a vector |
112 |
| -""" |
113 |
| -function subsumes(u::VarName, v::VarName) |
114 |
| - return getsym(u) == getsym(v) && subsumes(u.indexing, v.indexing) |
115 |
| -end |
116 |
| - |
117 |
| -subsumes(::Tuple{}, ::Tuple{}) = true # x subsumes x |
118 |
| -subsumes(::Tuple{}, ::Tuple) = true # x subsumes x[1] |
119 |
| -subsumes(::Tuple, ::Tuple{}) = false # x[1] does not subsume x |
120 |
| -function subsumes(t::Tuple, u::Tuple) # does x[i]... subsume x[j]...? |
121 |
| - return _issubindex(first(t), first(u)) && subsumes(Base.tail(t), Base.tail(u)) |
122 |
| -end |
123 |
| - |
124 |
| -const AnyIndex = Union{Int, AbstractVector{Int}, Colon} |
125 |
| -_issubindex_(::Tuple{Vararg{AnyIndex}}, ::Tuple{Vararg{AnyIndex}}) = false |
126 |
| -function _issubindex(t::NTuple{N, AnyIndex}, u::NTuple{N, AnyIndex}) where {N} |
127 |
| - return all(_issubrange(j, i) for (i, j) in zip(t, u)) |
128 |
| -end |
129 |
| - |
130 |
| -const ConcreteIndex = Union{Int, AbstractVector{Int}} # this include all kinds of ranges |
131 |
| -"""Determine whether indices `i` are contained in `j`, treating `:` as universal set.""" |
132 |
| -_issubrange(i::ConcreteIndex, j::ConcreteIndex) = issubset(i, j) |
133 |
| -_issubrange(i::Union{ConcreteIndex, Colon}, j::Colon) = true |
134 |
| -_issubrange(i::Colon, j::ConcreteIndex) = true |
135 |
| - |
136 |
| - |
137 |
| - |
138 |
| -""" |
139 |
| - @varname(expr) |
140 |
| -
|
141 |
| -A macro that returns an instance of [`VarName`](@ref) given a symbol or indexing expression `expr`. |
142 |
| -
|
143 |
| -The `sym` value is taken from the actual variable name, and the index values are put appropriately |
144 |
| -into the constructor (and resolved at runtime). |
145 |
| -
|
146 |
| -# Examples |
147 |
| -
|
148 |
| -```jldoctest |
149 |
| -julia> @varname(x).indexing |
150 |
| -() |
151 |
| -
|
152 |
| -julia> @varname(x[1]).indexing |
153 |
| -((1,),) |
154 |
| -
|
155 |
| -julia> @varname(x[:, 1]).indexing |
156 |
| -((Colon(), 1),) |
157 |
| -
|
158 |
| -julia> @varname(x[:, 1][2]).indexing |
159 |
| -((Colon(), 1), (2,)) |
160 |
| -
|
161 |
| -julia> @varname(x[1,2][1+5][45][3]).indexing |
162 |
| -((1, 2), (6,), (45,), (3,)) |
163 |
| -``` |
164 |
| -
|
165 |
| -!!! compat "Julia 1.5" |
166 |
| - Using `begin` in an indexing expression to refer to the first index requires at least |
167 |
| - Julia 1.5. |
| 6 | +Possibly existing indices of `varname` are neglected. |
168 | 7 | """
|
169 |
| -macro varname(expr::Union{Expr, Symbol}) |
170 |
| - return esc(varname(expr)) |
171 |
| -end |
172 |
| - |
173 |
| -varname(expr::Symbol) = VarName(expr) |
174 |
| -function varname(expr::Expr) |
175 |
| - if Meta.isexpr(expr, :ref) |
176 |
| - sym, inds = vsym(expr), vinds(expr) |
177 |
| - return :($(DynamicPPL.VarName)($(QuoteNode(sym)), $inds)) |
178 |
| - else |
179 |
| - throw("VarName: Mis-formed variable name $(expr)!") |
180 |
| - end |
181 |
| -end |
182 |
| - |
183 |
| - |
184 |
| -""" |
185 |
| - @vsym(expr) |
186 |
| -
|
187 |
| -A macro that returns the variable symbol given the input variable expression `expr`. |
188 |
| -For example, `@vsym x[1]` returns `:x`. |
189 |
| -""" |
190 |
| -macro vsym(expr::Union{Expr, Symbol}) |
191 |
| - return QuoteNode(vsym(expr)) |
| 8 | +@generated function inargnames(::VarName{s}, ::Model{_F, argnames}) where {s, argnames, _F} |
| 9 | + return s in argnames |
192 | 10 | end
|
193 | 11 |
|
194 |
| -vsym(expr::Symbol) = expr |
195 |
| -function vsym(expr::Expr) |
196 |
| - if Meta.isexpr(expr, :ref) |
197 |
| - return vsym(expr.args[1]) |
198 |
| - else |
199 |
| - throw("VarName: Mis-formed variable name $(expr)!") |
200 |
| - end |
201 |
| -end |
202 | 12 |
|
203 | 13 | """
|
204 |
| - @vinds(expr) |
| 14 | + inmissings(varname::VarName, model::Model) |
205 | 15 |
|
206 |
| -Returns a tuple of tuples of the indices in `expr`. For example, `@vinds x[1, :][2]` returns |
207 |
| -`((1, Colon()), (2,))`. |
| 16 | +Statically check whether the variable of name `varname` is a statically declared unobserved variable |
| 17 | +of the `model`. |
208 | 18 |
|
209 |
| -!!! compat "Julia 1.5" |
210 |
| - Using `begin` in an indexing expression to refer to the first index requires at least |
211 |
| - Julia 1.5. |
| 19 | +Possibly existing indices of `varname` are neglected. |
212 | 20 | """
|
213 |
| -macro vinds(expr::Union{Expr, Symbol}) |
214 |
| - return esc(vinds(expr)) |
215 |
| -end |
216 |
| - |
217 |
| -vinds(expr::Symbol) = Expr(:tuple) |
218 |
| -function vinds(expr::Expr) |
219 |
| - if Meta.isexpr(expr, :ref) |
220 |
| - ex = copy(expr) |
221 |
| - @static if VERSION < v"1.5.0-DEV.666" |
222 |
| - Base.replace_ref_end!(ex) |
223 |
| - else |
224 |
| - Base.replace_ref_begin_end!(ex) |
225 |
| - end |
226 |
| - last = Expr(:tuple, ex.args[2:end]...) |
227 |
| - init = vinds(ex.args[1]).args |
228 |
| - return Expr(:tuple, init..., last) |
229 |
| - else |
230 |
| - throw("VarName: Mis-formed variable name $(expr)!") |
231 |
| - end |
232 |
| -end |
233 |
| - |
234 |
| -@generated function inargnames(::VarName{s}, ::Model{_F, argnames}) where {s, argnames, _F} |
235 |
| - return s in argnames |
236 |
| -end |
237 |
| - |
238 | 21 | @generated function inmissings(::VarName{s}, ::Model{_F, _a, _T, missings}) where {s, missings, _F, _a, _T}
|
239 | 22 | return s in missings
|
240 | 23 | end
|
0 commit comments