Skip to content

NIMBLE design questions

perrydv edited this page Nov 7, 2016 · 14 revisions

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 here:

1. Writing and using nimbleFunctions

Current 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.

2. Type declarations

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. Currently type declarations use integer(nDim), double(nDim), or logical(nDim) 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, we are considering the following changes:

  • Declare an argument x to be a vector of length 2 by: x = numeric(2) or x = numericVector(2)
  • Declare an argument x to be a matrix of integers by: x = matrix("integer") or x = integerMatrix().
  • Declare an argument x to be a double (numeric) scalar by: x = scalar("double") or x = 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]

3. Coming soon: Compilation support for more core R functions.

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?

Clone this wiki locally