Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop references to parameters throughout packer #88

Merged
merged 6 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: monty
Title: Monte Carlo Models
Version: 0.2.19
Version: 0.2.20
Authors@R: c(person("Rich", "FitzJohn", role = c("aut", "cre"),
email = "[email protected]"),
person("Wes", "Hinsley", role = "aut"),
Expand Down
4 changes: 2 additions & 2 deletions R/domain.R
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ monty_domain_expand <- function(domain, packer) {
arg = "domain")
}

nms_full <- packer$parameters
nms_full <- packer$names()
nms_map <- packer$unpack(nms_full)
nms_logical <- names(nms_map)

Expand All @@ -76,5 +76,5 @@ monty_domain_expand <- function(domain, packer) {
domain[!i, , drop = FALSE])
}

domain[order(match(rownames(domain), packer$parameters)), , drop = FALSE]
domain[order(match(rownames(domain), packer$names())), , drop = FALSE]
}
2 changes: 1 addition & 1 deletion R/dsl-generate.R
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ dsl_generate_direct_sample <- function(dat, env, meta) {
exprs <- lapply(dat$exprs, dsl_generate_sample_expr, meta)
body <- c(call("<-", meta[["pars"]], quote(list())),
exprs,
bquote(unlist(.(meta[["pars"]])[packer$parameters], FALSE, FALSE)))
bquote(unlist(.(meta[["pars"]])[packer$names()], FALSE, FALSE)))
as_function(alist(rng = ), body, env)
}

Expand Down
2 changes: 1 addition & 1 deletion R/model-function.R
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ monty_model_function <- function(density, packer = NULL, fixed = NULL) {
}

monty_model(
list(parameters = packer$parameters,
list(parameters = packer$names(),
density = function(x) {
rlang::inject(density(!!!packer$unpack(x)))
}))
Expand Down
123 changes: 66 additions & 57 deletions R/packer.R
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
##' Build a parameter packer, which can be used in models to translate
##' between an unstructured vector of numbers (the vector being
##' updated by an MCMC for example) to a structured list of named
##' values, which is easier to program against. We refer to the
##' process of taking a named list of scalars, vectors and arrays and
##' converting into a single vector "packing" and the inverse
##' Build a packer, which can be used to translate beween an
##' unstructured vector of numbers (the vector being updated by an
weshinsley marked this conversation as resolved.
Show resolved Hide resolved
##' MCMC for example) to a structured list of named values, which is
##' easier to program against. This is useful for the bridge between
weshinsley marked this conversation as resolved.
Show resolved Hide resolved
##' model parameters and a models's implementation, but it is also
##' useful for the state vector in a state-space model. We refer to
##' the process of taking a named list of scalars, vectors and arrays
##' and converting into a single vector "packing" and the inverse
##' "unpacking".
##'
##' There are several places where it is most convenient to work in an
Expand Down Expand Up @@ -145,57 +147,64 @@
##' vector vs packing an array where all dimensions are 1. See the
##' examples, and please let us know if the behaviour needs changing.
##'
##' @title Build a parameter packer
##' @title Build a packer
##'
##' @param scalar Names of scalar parameters. This is similar for
##' listing elements in `array` with values of 1, though elements in
##' @param scalar Names of scalars. This is similar for listing
##' elements in `array` with values of 1, though elements in
##' `scalar` will be placed ahead of those listed in `array` within
##' the final parameter vector, and elements in `array` will have
##' generated names that include square brackets.
##'
##' @param array A list, where names correspond to the names of array
##' parameters and values correspond to the lengths of parameters.
##' Multiple dimensions are allowed (so if you provide an element
##' with two entries these represent dimensions of a matrix).
##' Zero-length integer vectors or `NULL` values are counted as
##' scalars, which allows you to put scalars at positions other than
##' the front of the packing vector. In future, you may be able to
##' use *strings* as values for the lengths, in which case these
##' will be looked for within `fixed`.
##'
##' @param fixed A named list of fixed parameters; these will be added
##' into the final list directly. These typically represent
##' additional pieces of data that your model needs to run, but
##' which you are not performing inference on.
##' @param array A list, where names correspond to the names of arrays
##' and values correspond to their lengths. Multiple dimensions are
##' allowed (so if you provide an element with two entries these
##' represent dimensions of a matrix). Zero-length integer vectors
##' or `NULL` values are counted as scalars, which allows you to put
##' scalars at positions other than the front of the packing
##' vector. In future, you may be able to use *strings* as values
##' for the lengths, in which case these will be looked for within
##' `fixed`.
##'
##' @param fixed A named list of fixed data to be inserted into the
##' final unpacked list; these will be added into the final list
##' directly. In the parameter packer context, these typically
##' represent additional pieces of data that your model needs to
##' run, but which you are not performing inference on.
##'
##' @param process An arbitrary R function that will be passed the
##' final assembled parameter list; it may create any *additional*
##' entries, which will be concatenated onto the original list. If
##' you use this you should take care not to return any values with
##' the same names as entries listed in `scalar`, `array` or
##' `fixed`, as this is an error (this is so that `pack()` is
##' not broken). We will likely play around with this process in
##' future in order to get automatic differentiation to work.
##'
##' @return An object of class `monty_packer`, which has three
##' elements:
##'
##' * `parameters`: a character vector of computed parameter names;
##' these are the names that your statistical model will use.
##' final assembled list; it may create any *additional* entries,
##' which will be concatenated onto the original list. If you use
##' this you should take care not to return any values with the same
##' names as entries listed in `scalar`, `array` or `fixed`, as this
##' is an error (this is so that `pack()` is not broken). We will
##' likely play around with this process in future in order to get
##' automatic differentiation to work.
##'
##' @return An object of class `monty_packer`, which has elements:
##'
##' * `names`: a function that returns a character vector of computed
##' names; in the parameter packer context these are the names that
##' your statistical model will use.
##'
##' * `unpack`: a function that can unpack an unstructured vector
##' (say, from your statistical model parameters) into a structured
##' list (say, for your generative model)
##' * `pack`: a function that can pack your structured list of
##' parameters back into a numeric vector suitable for the
##'
##' * `pack`: a function that can pack your structured list of data
##' back into a numeric vector, for example suitable for a
##' statistical model. This ignores values created by a
##' `preprocess` function.
##' `preprocess` function and present in `fixed`.
##'
##' * `index`: a function which produces a named list where each
##' element has the name of a value in `parameters` and each value
##' has the indices within an unstructured vector where these values
##' can be found.
##' * `subset`: an experimental interface which can be used to subset a
##' packer to a packer for a subset of contents. Documentation will
##' be provided once the interface settles.
##' element has the name of a value in `scalar` or `array` and each
##' value has the indices within an unstructured vector where these
##' values can be found, in the shape of the data that would be
##' unpacked. This is of limited most use to most people.
##'
##' * `subset`: an experimental interface which can be used to subset
##' a packer to a packer for a subset of contents. Documentation
##' will be provided once the interface settles, but this is for
##' advanced use only!
##'
##' @export
##'
Expand Down Expand Up @@ -260,7 +269,7 @@ monty_packer <- function(scalar = NULL, array = NULL, fixed = NULL,
process = NULL) {
call <- environment()

parameters <- character(0)
nms <- character(0)
idx <- list()
shape <- list()

Expand All @@ -279,15 +288,15 @@ monty_packer <- function(scalar = NULL, array = NULL, fixed = NULL,
}
shape[scalar] <- rep(list(integer()), length(scalar))
idx[scalar] <- as.list(seq_along(scalar))
parameters <- c(parameters, scalar)
nms <- c(nms, scalar)
}

len <- length(scalar) # start arrays after scalars
if (!is.null(array)) {
assert_named(array, unique = TRUE, call = call)
for (nm in names(array)) {
tmp <- prepare_pack_array(nm, array[[nm]], call)
parameters <- c(parameters, tmp$names)
nms <- c(nms, tmp$names)
shape[[nm]] <- tmp$shape
idx[[nm]] <- seq_len(tmp$n) + len
len <- len + tmp$n
Expand Down Expand Up @@ -325,9 +334,9 @@ monty_packer <- function(scalar = NULL, array = NULL, fixed = NULL,

unpack <- function(x) {
if (is.null(dim(x))) {
unpack_vector(x, parameters, len, idx, shape, fixed, process)
unpack_vector(x, nms, len, idx, shape, fixed, process)
} else {
unpack_array(x, parameters, len, idx, shape, fixed, process)
unpack_array(x, nms, len, idx, shape, fixed, process)
}
}

Expand Down Expand Up @@ -379,7 +388,7 @@ monty_packer <- function(scalar = NULL, array = NULL, fixed = NULL,
list(index = index, packer = packer)
}

ret <- list(parameters = parameters,
ret <- list(names = function() nms,
unpack = unpack,
pack = pack,
index = function() idx,
Expand Down Expand Up @@ -438,7 +447,7 @@ array_indices <- function(shape) {
print.monty_packer <- function(x, ...) {
cli::cli_h1("<monty_packer>")
cli::cli_alert_info(
"Packing {length(x$parameters)} parameter{?s}: {squote(x$parameters)}")
"Packing {length(x$names())} parameter{?s}: {squote(x$names())}")
cli::cli_alert_info(
"Use '$pack()' to convert from a list to a vector")
cli::cli_alert_info(
Expand All @@ -448,10 +457,10 @@ print.monty_packer <- function(x, ...) {
}


unpack_vector <- function(x, parameters, len, idx, shape, fixed, process) {
unpack_vector <- function(x, nms, len, idx, shape, fixed, process) {
call <- parent.frame()
if (!is.null(names(x))) {
if (!identical(names(x), parameters)) {
if (!identical(names(x), nms)) {
## Here, we could do better I think with this message; we
## might pass thropuigh empty names, and produce some summary
## of different names. Something for later though.
Expand All @@ -476,7 +485,7 @@ unpack_vector <- function(x, parameters, len, idx, shape, fixed, process) {
err <- intersect(names(extra), names(res))
if (length(err) > 0) {
cli::cli_abort(
c("'process()' is trying to overwrite entries in parameters",
c("'process()' is trying to overwrite entries in your list",
i = paste("The 'process()' function should only create elements",
"that are not already present in 'scalar', 'array'",
"or 'fixed', as this lets us reverse the transformation",
Expand All @@ -494,10 +503,10 @@ unpack_vector <- function(x, parameters, len, idx, shape, fixed, process) {
}


unpack_array <- function(x, parameters, len, idx, shape, fixed, process) {
unpack_array <- function(x, nms, len, idx, shape, fixed, process) {
call <- parent.frame()
dn <- dimnames(x)
if (!is.null(dn) && !is.null(dn[[1]]) && !identical(dn[[1]], parameters)) {
if (!is.null(dn) && !is.null(dn[[1]]) && !identical(dn[[1]], nms)) {
## See comment above about reporting on this better
cli::cli_abort("Incorrect rownames in input")
}
Expand Down
91 changes: 48 additions & 43 deletions man/monty_packer.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading