Skip to content

Commit 51b399a

Browse files
committed
Write more design doc for ArrayLikeBlocks
1 parent 4eb33e9 commit 51b399a

File tree

1 file changed

+23
-0
lines changed

1 file changed

+23
-0
lines changed

docs/src/internals/varnamedtuple.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,29 @@ You can also set the elements with `vnt = setindex!!(vnt, @varname(a[1]), 3.0)`,
144144
At this point you can not set any new values in that array that would be outside of its range, with something like `vnt = setindex!!(vnt, @varname(a[5]), 5.0)`.
145145
The philosophy here is that once a `Base.Array` has been attached to a `VarName`, that takes precedence, and a `PartialArray` is only used as a fallback when we are told to store a value for `@varname(a[i])` without having any previous knowledge about what `@varname(a)` is.
146146

147+
## Non-Array blocks with `IndexLens`es
148+
149+
The above is all that is needed for setting regular scalar values.
150+
However, in DynamicPPL we also have a particular need for something slightly odd:
151+
We sometimes need to do calls like `setindex!!(vnt, @varname(a[1:5]), val)` on a `val` that is _not_ an `AbstractArray`, or even iterable at all.
152+
Normally this would error: As a scalar value with size `()`, `val` is the wrong size to be set with `@varname(a[1:5])`, which clearly wants something with size `(5,)`.
153+
However, we want to allow this even if `val` is not an iterable, if it is some object for which `size` is well-defined, and `size(val) == (5,)`.
154+
In DynamicPPL this comes up when storing e.g. the priors of a model, where a random variable like `@varname(a[1:5])` may be associated with a prior that is a 5-dimensional distribution.
155+
156+
Internally, a `PartialArray` is just a regular `Array` with a mask saying which elements have been set.
157+
Hence we can't store `val` directly in the same `PartialArray`:
158+
We need it to take up a sub-block of the array, in our example case a sub-block of length 5.
159+
To this end, internally, `PartialArray` uses a wrapper type called `ArrayLikeWrapper`, that stores `val` together with the indices that are being used to set it.
160+
The `PartialArray` has all its corresponding elements, in our example elements 1, 2, 3, 4, and, 5, point to the same wrapper object.
161+
162+
While such blocks can be stored using a wrapper like this, some care must be taken in indexing into these blocks.
163+
For instance, after setting a block with `setindex!!(vnt, @varname(a[1:5]), val)`, we can't `getindex(vnt, @varname(a[1]))`, since we can't return "the first element of five in `val`", because `val` may not be indexable in any way.
164+
Similarly, if next we set `setindex!!(vnt, @varname(a[1]), some_other_value)`, that should invalidate/delete the elements `@varname(a[2:5])`, since the block only makes sense as a whole.
165+
Because of these reasons, setting and getting blocks of well-defined size like this is allowed with `VarNamedTuple`s, but _only by always using the full range_.
166+
For instance, if `setindex!!(vnt, @varname(a[1:5]), val)` has been set, then the only valid `getindex` key to access `val` is `@varname(a[1:5])`;
167+
Not `@varname(a[1:10])`, nor `@varname(a[3])`, nor for anything else that overlaps with `@varname(a[1:5])`.
168+
`haskey` likewise only returns true for `@varname(a[1:5])`, and `keys(vnt)` only has that as an element.
169+
147170
## Limitations
148171

149172
This design has a several of benefits, for performance and generality, but it also has limitations:

0 commit comments

Comments
 (0)