-
Notifications
You must be signed in to change notification settings - Fork 26
NIMBLE design questions
This page contains some NIMBLE design questions under consideration. The NIMBLE development team is seeking input from users on these questions. You can provide input anonymously (or not) here:
Currently the nimbleFunction system, when including setup code, works as follows:
setAndCalc <- nimbleFunction(
setup = function(model, nodes) {
calcNodes <- model$getDependencies(nodes) ## whatever setup work is needed
},
run = function(P) {
values(model, nodes) <<- P
ans <- model$calculate(calcNodes)
return(ans)
returnType(double())
})
## Now say we have model m and nodes c('a','b')
## We create a specialized instance of setAndCalc like this
setAndCalc_for_my_model <- setAndCalc(m, c('a','b'))
## and we use it like this
setAndCalc_for_my_model$run(c(0.1, 0.2))Under the hood, this is a class-like system, where setAndCalc is the class definition and setAndCalc_for_my_model is an object of the class. This is the result of some evolution as NIMBLE grew.
Would it be better now to replace
setAndCalc_for_my_model <- setAndCalc(m, c('a','b'))with the more class-like
setAndCalc_for_my_model <- setAndCalc$new(m, c('a','b'))This may seem like a small change, but we are wondering if it would be more, or less, natural for users.
Early versions of NIMBLE led into some inconsistency and what some might see as awkwardness for type declarations for run-code. For example, we have
doSillyVectorStuff <- nimbleFunction(x = double(2)) {
y <- numeric(10)
y[2:3] <- x[4:5,]
return(y)
returnType(double(1))
}The type declaration double(2) looks like an R function call and a default value, but it is neither. And it is different from matrix(), which we would use in the function body to create a matrix. Similarly, the numeric(10) in the function creates a vector of length 10, but the same thing as a type declaration would appear as double(1, 10).
Currently type declarations use integer(nDim), double(nDim), or logical(nDim) (where nDim is the number of dimensions) but object creation is done by nimble versions of integer(), numeric(), matrix() and array(). The latter are similar to R functions of the same name, but for NIMBLE they can take a (static) type argument such as "integer" or "numeric" (instead of "double").
Based on design discussion [here](Design of type declarations and object creation), we are considering the following changes:
- Declare an argument x to be a vector of length 2 by:
x = numeric(2)orx = numericVector(2) - Declare an argument x to be a matrix of integers by:
x = matrix("integer")orx = integerMatrix(). - Declare an argument x to be a double (numeric) scalar by:
x = scalar("double")orx = scalar()(default type = "double") or "x = scalarDouble()" or "x = 3" (default value 3). - Allow creation of objects using functions of the form "integerMatrix", specifically combinations of
[integer | numeric | logical]with[Scalar | Vector | Matrix | Array]
The goals here is to make type declaration and object creation as similar as possible, to make them familiar but not confusing relative to R programming, and to allow explicit declaration of scalars. Would this be an improvement? If not what do you suggest?
We are building support for a greater range of basic R syntax and functions, including:
- arbitrary logical and integer indexing of vectors and matrices
- c(), append(), rep()
- seq() and
: - for loops over arbitrary numeric sequences
- diag()
- rbind(), cbind()
- dim()
- which()
- as.integer(), as.numeric(), as.logical()
- vectorized distribution functions using R's recycling rule
- While we won't support R lists in full generality, we will be providing nimbleLists, which will require declared types of each element.
What are the other core R features you would find most useful?