|
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