assignmentAsFirstArgFuns <- c('nimArr_rmnorm_chol', 'nimArr_rwish_chol', 'nimArr_rmulti', 'nimArr_rdirch', 'getValues')
sizeCalls <- c(makeCallList(binaryOperators, 'sizeBinaryCwise'),
makeCallList(binaryMidLogicalOperators, 'sizeBinaryCwiseLogical'),
makeCallList(binaryOrUnaryOperators, 'sizeBinaryUnaryCwise'),
makeCallList(unaryOperators, 'sizeUnaryCwise'),
makeCallList(unaryOrNonaryOperators, 'sizeUnaryNonaryCwise'),
makeCallList(assignmentOperators, 'sizeAssign'),
makeCallList(reductionUnaryOperators, 'sizeUnaryReduction'),
makeCallList(matrixSquareReductionOperators, 'sizeMatrixSquareReduction'),
makeCallList(reductionBinaryOperators, 'sizeBinaryReduction'),
makeCallList(matrixMultOperators, 'sizeMatrixMult'),
makeCallList(matrixFlipOperators, 'sizeTranspose'),
makeCallList(matrixSolveOperators, 'sizeSolveOp'), ## TO DO
makeCallList(matrixEigenOperators, 'sizeEigenOp'), ## TO DO
makeCallList(matrixSquareOperators, 'sizeUnaryCwiseSquare'),
list('return' = 'sizeReturn',
'asRow' = 'sizeAsRowOrCol',
'asCol' = 'sizeAsRowOrCol',
asDoublePtr = 'sizeasDoublePtr',
'[' = 'sizeIndexingBracket',
## '[[' for nimbleFunctionList goes through chainedCall
chainedCall = 'sizeChainedCall',
nfVar = 'sizeNFvar',
map = 'sizemap',
':' = 'sizeColonOperator',
dim = 'sizeDimOperator',
'if' = 'recurseSetSizes', ##OK
'while' = 'recurseSetSizes',
callC = 'sizecallC',
'for' = 'sizeFor',
## mvAccess = 'sizeUnaryCwise',
## numListAccess = 'sizeNumListAccess',
# numListAccess = 'sizemvAccessBracket',
values = 'sizeValues',
'(' = 'sizeUnaryCwise',
setSize = 'sizeSetSize', ## OK but not done for numericLists
resizeNoPtr = 'sizeResizeNoPtr', ##
nimArr_rcat = 'sizeScalarRecurse',
nimPrint = 'sizeforceEigenize',
as.integer = 'sizeUnaryCwise', ## Note as.integer and as.numeric will not work on a non-scalar yet
as.numeric = 'sizeUnaryCwise',
setAll = 'sizeOneEigenCommand'),
makeCallList(distributionFuns, 'sizeScalarRecurse'),
makeCallList(c('isnan','ISNAN','!','ISNA'), 'sizeScalarRecurse'),
makeCallList(c('nimArr_dmnorm_chol', 'nimArr_dwish_chol', 'nimArr_dmulti', 'nimArr_dcat', 'nimArr_ddirch'), 'sizeScalarRecurse'),
makeCallList(c('nimArr_rmnorm_chol', 'nimArr_rwish_chol', 'nimArr_rmulti', 'nimArr_rdirch'), 'sizeRmultivarFirstArg'),
makeCallList(c('calculate', 'getLogProb', 'decide', 'size', 'getsize'), 'sizeScalar'),
makeCallList(c('simulate', 'blank', 'nfMethod', 'nimFunListAccess', 'getPtr'), 'sizeUndefined')
)
scalarOutputTypes <- list(decide = 'logical', size = 'integer', isnan = 'logical', ISNA = 'logical', '!' = 'logical', nimArr_rcat = 'integer')
## exprClasses_setSizes fills in the type information of exprClass code
## code is an exprClas object
## typeEnv is an environment returned by exprClasses_initSizes
## allowUnknown says whether it is ok to have unknown type. This will be true for LHS of assignments
##
## This returns a set of type assertions collected for each line of code
## This function operates recursively, so the type assertions returned from recursive calls are
## put into the exprClass object for which they were recursed.
##
## For example, if we have A2 <- mean(B + C)
## then typeEnv must have size expressions for B and C to get started.
## If these are matrices, the generic size expressions (for B) will be dim(B)[1] and dim(B)[2]
## Then the exprClass object for `+`(B, C) will generate assertions that dim(B)[1] == dim(C)[1] and dim(B[2]) == dim(C)[2]
## and it will copy the size expressions for B as its own size expressions
## Then the exprClass object for mean(`+`(B, C)) will create a size expression of 1 (with the same dimensions as B+C)
## Then the exprClass object for `<-`(A, mean(`+`(B, C))) will generate assertions that the size of A must be 1
## and it will set the size expressions for A and for itself to 1.
exprClasses_setSizes <- function(code, symTab, typeEnv) { ## input code is exprClass
## name:
if(code$isName) {
## If it doesn't exist and must exist, stop
if(code$name != "") { ## e.g. In A[i,], second index gives name==""
if(!exists(code$name, envir = typeEnv, inherits = FALSE)) {
if(symTab$symbolExists(code$name, TRUE)) {
code$type <- class(symTab$getSymbolObject(code$name, TRUE))[1]
} else {
code$type <- 'unknown'
}
} else {
## otherwise fill in type fields from typeEnv object
info <- get(code$name, envir = typeEnv)
if(inherits(info, 'exprTypeInfoClass')) {
code$type <- info$type
code$sizeExprs <- info$sizeExprs
code$nDim <- info$nDim
code$toEigenize <- 'maybe'
## If it is a vector of known length 1 and it has no indexing, insert a [1] after it
if(code$nDim == 1) {
if(code$sizeExprs[[1]] == 1) {
if(code$caller$name != '[') {
insertIndexingBracket(code$caller, code$callerArgID, 1)
code$caller$nDim <- 0
code$caller$sizeExprs <- list()
code$caller$toEigenize <- 'maybe'
code$caller$type <- code$type
}
}
}
}
}
## Note that generation of a symbol for LHS of an assignment is done in the sizeAssign function, which is the handler for assignments
return(NULL)
}
}
if(code$isCall) {
if(code$name == '{') {
## recurse over lines
for(i in seq_along(code$args)) {
if(inherits(code$args[[i]], 'exprClass')) {
newAsserts <- exprClasses_setSizes(code$args[[i]], symTab, typeEnv)
code$args[[i]]$assertions <- if(is.null(newAsserts)) list() else newAsserts
}
}
return(invisible(NULL))
}
sizeCall <- sizeCalls[[code$name]]
if(!is.null(sizeCall)) {
return(eval(call(sizeCall, code, symTab, typeEnv)))
}
## if(!is.null(tempSizeHandlers[[code$name]])) {
## return(tempSizeHandlers[[code$name]](code, symTab, typeEnv))
## }
if(symTab$symbolExists(code$name, TRUE)) { ## could be a nimbleFunction object
return(sizeNimbleFunction(code, symTab, typeEnv) )
}
## Finally, it could be an RCfunction (a nimbleFunction with no setup == a simple function) {
if(exists(code$name)) {
obj <- get(code$name)
if(is.rcf(obj)) { ## it is an RC function
nfmObj <- environment(obj)$nfMethodRCobject
uniqueName <- nfmObj$uniqueName
if(length(uniqueName)==0) stop("Error, a no-setup nimbleFunction with no internal name is being called:", nimDeparse(code), ". Something wasn't set up right.", call. = FALSE)
if(is.null(typeEnv$neededRCfuns[[uniqueName]])) {
typeEnv$neededRCfuns[[uniqueName]] <- nfmObj
}
return(sizeRCfunction(code, symTab, typeEnv, nfmObj))
}
}
}
invisible(NULL)
}
sizeRMVNorm <- function(code, symTab, typeEnv) {
asserts <- recurseSetSizes(code, symTab, typeEnv)
if(code$args[[1]]$nDim != 1) {
writeLines(paste0('Uh oh, first arg to rmvnorm in ', nimDeparse(code), ' is not 1D.'))
browser()
}
if(code$args[[2]]$nDim != 1) {
writeLines(paste0('Uh oh, second arg to rmvnorm in ', nimDeparse(code), ' is not 1D.'))
browser()
}
if(!(code$caller %in% assignmentOperators)) {
asserts <- c(asserts, sizeInsertIntermediate(code$caller, code$callerArgID, symTab, typeEnv))
}
code$type <- 'double'
code$nDim <- 1
code$sizeExprs <- code$args[[1]]$sizeExprs ## already checked that it is 1D
code$toEigenize <- 'no'
}
sizemap <- function(code, symTab, typeEnv) {
## This will only be called on a map generated from setup
## Maps created from indexing in nimble code don't go through this function
sym <- symTab$getSymbolObject(code$args[[1]], TRUE)
code$type <- sym$type
code$nDim <- code$args[[2]]
code$sizeExprs <- code$args[[4]]
code$toEigenize <- 'maybe'
invisible(NULL)
}
sizeAsRowOrCol <- function(code, symTab, typeEnv) {
asserts <- recurseSetSizes(code, symTab, typeEnv)
a1 <- code$args[[1]]
if(!inherits(a1, 'exprClass')) stop(paste0('Error in sizeAsRowOrCol for ', nimDeparse(code), '. Argument must be an expression.'), call. = FALSE)
if(a1$nDim == 0) stop(paste0('Error in sizeAsRowOrCol for ', nimDeparse(code), '. Argument cannot be scalar (could be fixed).'), call. = FALSE)
code$type <- a1$type
code$toEigenize <- 'yes'
if(!code$name %in% c('asRow', 'asCol')) stop('Weird, how did we get to sizeAsRowOrCol without a call to asRow or asCol?', call. = FALSE)
if(a1$nDim == 1) {
if(code$name == 'asRow') {
##setAsRowOrCol(code, 1, 'asRow', type = a1$type)
##code <- removeExprClassLayer(code, 1)
code$nDim <- 2
code$sizeExprs <- c(list(1), a1$sizeExprs[[1]])
} else {
##setAsRowOrCol(code, 1, 'asCol', type = a1$type)
##code <- removeExprClassLayer(code, 1)
code$nDim <- 2
code$sizeExprs <- c(a1$sizeExprs[[1]], list(1))
}
return(asserts)
}
warning(paste0(' asRow or asCol used on something with more than 1 dimension in ', nimDeparse(code)), call. = FALSE)
}
sizeNFvar <- function(code, symTab, typeEnv) {
nfName <- code$args[[1]]$name
nfSym <- symTab$getSymbolObject(nfName, inherits = TRUE)
if(!inherits(nfSym, 'symbolNimbleFunction')) stop(paste0('Error in ', nimDeparse(code), '. First argument is not a nimbleFunction.'), call. = FALSE)
nfProc <- nfSym$nfProc
if(is.null(nfProc)) stop(paste0('Error in ', nimDeparse(code), '. Symbols in this nimbleFunction generation function not set up.'), call. = FALSE)
objName <- code$args[[2]]
if(!is.character(objName)) stop(paste0('Error in ', nimDeparse(code), '. Second argument must be a character string.'), call. = FALSE)
objSym <- nfProc$setupSymTab$getSymbolObject(objName)
if(is.null(objSym)) stop(paste0('Error in ', nimDeparse(code), '. Symbol not found in the nimbleFunction.'), call. = FALSE)
code$nDim <- objSym$nDim
code$type <- objSym$type
if(code$nDim > 0) {
code$sizeExprs <- makeSizeExpressions(objSym$size,
parse(text = nimDeparse(code))[[1]])
} else {
code$sizeExprs <- list()
}
code$toEigenize <- 'maybe'
NULL
}
sizeChainedCall <- function(code, symTab, typeEnv) { ## at the moment we have only nimFunList[[i]](a), nfMethod(nf, 'foo')(a), or nfMethod(nf[[i]], 'foo')(a)
## where actually the [[ would have already been replaced with nimFunListAccess
## In other places we generate chainedCalls for static_cast<int>(a), but those shouldn't be seen here
a1 <- code$args[[1]]
if(!inherits(a1, 'exprClass')) stop(paste0('Error in sizeChainedCall for ', nimDeparse(code), '. First arg is not an expression.'), call. = FALSE)
nfMethodRCobj <- NULL
if(a1$name == '[[') {
## nimFunList[[i]](a)
recurseSetSizes(a1, symTab, typeEnv, c(FALSE, rep(TRUE, length(a1$args)-1))) ## recursion on this is not done in generalFunSizeHandler because it skips arg1 for chainedCall = TRUE
if(a1$args[[2]]$nDim != 0) stop(paste0('Error in sizeChainedCall for ', nimDeparse(code), '. Index for nimbleFunction list is not scalar.'), call. = FALSE)
sym <- symTab$getSymbolObject(a1$args[[1]]$name, TRUE)
if(!inherits(sym, 'symbolNimbleFunctionList')) {
stop(paste0('Error, in ', nimDeparse(code), '. Expecting a nimbleFunction list.'), call. = FALSE)
}
nfMethodRCobj <- getFunctionEnvVar(nf_getGeneratorFunction(sym$baseClass), 'methodList')$run
}
else if(a1$name == 'nfMethod') {
a11 <- a1$args[[1]]
methodName <- a1$args[[2]]
if(a11$isName) { ## e.g. in nfMethod(nf, 'foo'), a11 is nf
sym <- symTab$getSymbolObject(a11$name, TRUE)
nfMethodRCobj <- sym$nfProc$origMethods[[methodName]]
} else {
if(a11$name != '[[') stop(paste0('Error, in ', nimDeparse(code), '. Expecting a nimbleFunction list or a nimFun as first arg of nfMethod.'), call. = FALSE)
## should look like nfMethod(nflist[[i]], 'foo')
a111 <- a11$args[[1]]
sym <- symTab$getSymbolObject(a111$name, TRUE)
if(!inherits(sym, 'symbolNimbleFunctionList')) {
stop(paste0('Error, in ', nimDeparse(code), '. Expecting a nimbleFunction list.'), call. = FALSE)
}
nfMethodRCobj <- getFunctionEnvVar(nf_getGeneratorFunction(sym$baseClass), 'methodList')[[methodName]]
}
}
else warning(paste0('Warning that we did not know what to do in sizeChainedCall for ', nimDeparse(code)))
if(!is.null(nfMethodRCobj)) {
returnType <- nfMethodRCobj$returnType
argInfo <- nfMethodRCobj$argInfo
asserts <- generalFunSizeHandler(code, symTab, typeEnv, returnType, argInfo, chainedCall = TRUE)
return(asserts)
}
invisible(NULL)
writeLines('Warning')
}
sizeValues <- function(code, symTab, typeEnv) {
code$nDim <- 1
code$type <- 'double'
code$toEigenize <- 'no'
sym <- symTab$getSymbolObject(code$args[[1]]$name, TRUE)
if(length(sym$lengthName)==0) stop(paste0("Error the size information for ", nimDeparse(code), " is missing."), call. = FALSE)
code$sizeExprs <- list(as.name(sym$lengthName))
asserts <- list()
if(code$caller$name %in% assignmentOperators) {
if(code$callerArgID == 2) { ## ans <- values(...)
code$name <- 'getValues'
LHS <- code$caller$args[[1]]
if(LHS$isName) { ## It is a little awkward to insert setSize here, but this is different from other cases in sizeAssignAfterRecursing
assertSS <- list(substitute(setSize(LHS), list(LHS = as.name(LHS$name))))
sym <- symTab$getSymbolObject(code$args[[1]]$name, TRUE)
assertSS[[1]][[3]] <- as.name(sym$lengthName)
asserts <- c(assertSS, asserts)
}
} ## values(...) <- P, don't change it
} else { ## values(...) embedded in a RHS expression
code$name <- 'getValues'
code$toEigenize <- 'yes' ## This tricks sizeAssignAfterRecursing to generate the setSize in asserts
asserts <- c(asserts, sizeInsertIntermediate(code$caller, code$callerArgID, symTab, typeEnv))
code$toEigenize <- 'no'
}
if(length(asserts)==0) NULL else asserts
}
sizeRCfunction <- function(code, symTab, typeEnv, nfmObj) {
returnType <- nfmObj$returnType
argInfo <- nfmObj$argInfo
code$name <- nfmObj$uniqueName
asserts <- generalFunSizeHandler(code, symTab, typeEnv, returnType, argInfo)
return(asserts)
}
sizeNimbleFunction <- function(code, symTab, typeEnv) { ## This will handle other nimbleFunction run (operator()) calls or other methods of this nimbleFunction
sym <- symTab$getSymbolObject(code$name, TRUE)
ok <- FALSE
if(inherits(sym, 'symbolNimbleFunction')) {
nfMethodRCobj <- environment(sym$nfProc$nfGenerator)$methodList$run
returnType <- nfMethodRCobj$returnType
argInfo <- nfMethodRCobj$argInfo
ok <- TRUE
}
if(inherits(sym, 'symbolMemberFunction')) {
returnType <- sym$nfMethodRCobj$returnType
argInfo <- sym$nfMethodRCobj$argInfo
ok <- TRUE
}
if(ok) {
asserts <- generalFunSizeHandler(code, symTab, typeEnv, returnType, argInfo)
return(asserts)
}
stop(paste0('Error, in ', nimDeparse(code), ', the function name is not known and is not a nimbleFunction or a member function.'), call. = FALSE)
}
recurseSetSizes <- function(code, symTab, typeEnv, useArgs = rep(TRUE, length(code$args))) {
## won't be here unless code is a call. It will not be a {
asserts <- list()
for(i in seq_along(code$args)) {
if(useArgs[i]) {
if(inherits(code$args[[i]], 'exprClass')) {
asserts <- c(asserts, exprClasses_setSizes(code$args[[i]], symTab, typeEnv))
}
}
}
if(length(asserts)==0) NULL else asserts
}
## promote numeric output to most information-rich type, double > integer > logical
## Note this will not be correct for logical operators, where output type should be logical
arithmeticOutputType <- function(t1, t2) {
if(t1 == 'double') return('double')
if(t2 == 'double') return('double')
if(t1 == 'integer') return('integer')
if(t2 == 'integer') return('integer')
return('logical')
}
## Generate R code for an equality assertion
identityAssert <- function(lhs, rhs, msg = "") {
if(identical(lhs, rhs)) return(NULL)
msg <- gsub("\"", "\\\\\"", msg)
substitute(if(lhs != rhs) nimPrint(msg), list(lhs = lhs, rhs = rhs, msg = msg))
}
## Determine if LHS is less information-rich that RHS and issue a warning.
## e.g. if LHS is int but RHS is double.
assignmentTypeWarn <- function(LHS, RHS) {
if(LHS == 'int' & RHS == 'double') return(TRUE)
if(LHS == 'logical' & RHS != 'logical') return(TRUE)
return(FALSE)
}
## used for setAll
sizeOneEigenCommand <- function(code, symTab, typeEnv) {
if(!code$args[[1]]$isName) stop(paste0('Error for in sizeOneEigenCommand for ', nimDeparse(code), '. First arg should be a name.'), call. = FALSE)
recurseSetSizes(code, symTab, typeEnv)
if(code$args[[1]]$nDim != 2) stop(paste0('Error for in sizeOneEigenCommand for ', nimDeparse(code), '. At the moment only works for 2D objects.'), call. = FALSE)
code$toEigenize <- 'yes'
invisible(NULL)
}
## This is used for nimPrint
## If anything has toEigenize == "maybe", the whole expression gets "yes"
## That way cout<<X; will use an eigen map for X
sizeforceEigenize <- function(code, symTab, typeEnv) {
asserts <- recurseSetSizes(code, symTab, typeEnv)
toEigs <- lapply(code$args, function(x) {
if(inherits(x, 'exprClass')) x$toEigenize else 'unknown'
})
code$toEigenize <- if(any( unlist(toEigs) %in% c('maybe', 'yes'))) 'yes' else 'no'
code$type <- 'unknown'
if(length(asserts) == 0) NULL else asserts
}
sizecallC <- function(code, symTab, typeEnv) {
asserts <- recurseSetSizes(code$args[[1]], symTab, typeEnv)
asserts
}
## This is for when the programmer has directly written "resize(Z, 3, dim(A)[1])".
## When the resize is automatically generated, it skips size inference
sizeSetSize <- function(code, symTab, typeEnv) {
sym <- symTab$getSymbolObject(code$args[[1]]$name, inherits = TRUE)
if(!inherits(sym, 'symbolNumericList')) {
if(sym$nDim == 0) stop(paste0('Error, resizing a scalar does not make sense in: ', nimDeparse(code$args[[1]])), call. = FALSE)
if(length(code$args) != 1 + sym$nDim) stop(paste0('Error, number of dimensions provided in resize for ', nimDeparse(code$args[[1]]), ' is wrong'), call. = FALSE)
asserts <- recurseSetSizes(code, symTab, typeEnv, c(FALSE, rep(TRUE, sym$nDim) ) )
## May need intermediates if any size provided requires eigenization, although that's hard to imagine
assign(code$name, exprTypeInfoClass$new(nDim = sym$nDim, sizeExprs = lapply(code$args[-1], nimDeparse), type = sym$type), envir = typeEnv)
if(length(asserts)==0) NULL else asserts
}
if(inherits(sym, 'symbolNumericList') ) {
if(length(code$args) != 2 + sym$nDim) stop(paste0('Error, number of dimensions provided in resize for ', nimDeparse(code$args[[1]]), ' is wrong'), call. = FALSE )
assign(code$name, exprTypeInfoClass$new(nDim = sym$nDim, sizeExprs = lapply(code$args[-1], nimDeparse), type = sym$type), envir = typeEnv)
invisible(NULL)
}
}
## This was redundant and we should eventually be able to remove it
sizeResizeNoPtr <- function(code, symTab, typeEnv){
sym <- symTab$getSymbolObject(code$args[[1]]$name, inherits = TRUE)
if(length(code$args[[2]]) != 1) stop( paste0('Error, number of dimensions provided in resize for ', nimDeparse(code$args[[1]]), ' is wrong' ), call. = FALSE )
assign(code$name, exprTypeInfoClass$new(nDim = 1, sizeExprs = lapply(code$args[-1], nimDeparse), type = sym$type), envir = typeEnv)
invisible(NULL)
}
## Handler for for-loops: a fairly special case
## e.g. for(i in 1:10) {do(i)}
sizeFor <- function(code, symTab, typeEnv) {
if(length(code$args) != 3) stop('Error in sizeFor: expected 3 arguments to a for-loop', call. = FALSE)
## first handle type of the indexing variable
if(!inherits(code$args[[2]], 'exprClass')) stop('Error in sizeFor: expecting the index range to be an expression (exprClass)', call. = FALSE)
asserts <- exprClasses_setSizes(code$args[[2]], symTab, typeEnv)
code$args[[1]]$nDim <- 0
code$args[[1]]$sizeExprs <- list()
code$args[[1]]$type <- code$args[[2]]$type
code$args[[1]]$toEigenize <- 'no'
## if index is unknown, create it in typeEnv and in the symTab
if(!exists(code$args[[1]]$name, envir = typeEnv, inherits = FALSE)) {
assign(code$args[[1]]$name, exprTypeInfoClass$new(nDim = 0, type = code$args[[1]]$type), envir = typeEnv)
symTab$addSymbol(symbolBasic(name = code$args[[1]]$name, nDim = 0, type = code$args[[1]]$type))
}
typeEnv[[code$args[[1]]$name]]$sizeExprs <- list()
## Now the 3rd arg, the body of the loop, can be processed
asserts <- c(asserts, exprClasses_setSizes(code$args[[3]], symTab, typeEnv))
## I think there shouldn't be any asserts returned since the body should be a bracket expression.
return(invisible(NULL))
}
sizeInsertIntermediate <- function(code, argID, symTab, typeEnv, forceAssign = FALSE) {
newName <- IntermLabelMaker()
## I think it is valid and general to catch maps here.
## For most variables, creating an intermediate involves interN <- expression being lifted
## But for map, which will be using a NimArr if it is lifted here, what we need to generate is setMap call
if(code$args[[argID]]$name == 'map' & !forceAssign) {
ans <- nimArrMapExpr(code$args[[argID]], symTab, typeEnv, newName)
## That should create the symTab entry
ans <- RparseTree2ExprClasses(ans)
newArgExpr <- RparseTree2ExprClasses(as.name(newName))
newArgExpr$type <- code$args[[argID]]$type
newArgExpr$sizeExprs <- code$args[[argID]]$sizeExprs
newArgExpr$toEigenize <- 'maybe'
newArgExpr$nDim <- code$args[[argID]]$nDim
} else {
## One may wonder where the new variable is added to the
## symbolTable. That happens when we do
## sizeAssignAfterRecursing, which identifies unknown LHS and
## creates the symTab entry.
newExpr <- newAssignmentExpression()
setArg(newExpr, 1, RparseTree2ExprClasses(as.name(newName)))
setArg(newExpr, 2, code$args[[argID]]) ## The setArg function should set code$caller (to newExpr) and code$callerArgID (to 3)
ans <- c(sizeAssignAfterRecursing(newExpr, symTab, typeEnv, NoEigenizeMap = TRUE), list(newExpr))
newArgExpr <- RparseTree2ExprClasses(as.name(newName))
newArgExpr$type <- newExpr$args[[1]]$type
newArgExpr$sizeExprs <- newExpr$args[[1]]$sizeExprs
newArgExpr$toEigenize <- 'maybe'
newArgExpr$nDim <- newExpr$args[[1]]$nDim
}
setArg(code, argID, newArgExpr)
return(ans) ## This is to be inserted in a list of asserts, even though it is really core code, not just an a test or assertion
}
sizeAssign <- function(code, symTab, typeEnv) {
asserts <- recurseSetSizes(code, symTab, typeEnv)
asserts <- c(asserts, sizeAssignAfterRecursing(code, symTab, typeEnv))
if(length(asserts) == 0) NULL else asserts
}
## Handler for assignment
sizeAssignAfterRecursing <- function(code, symTab, typeEnv, NoEigenizeMap = FALSE) {
LHS <- code$args[[1]]
RHS <- code$args[[2]]
if(inherits(RHS, 'exprClass')) {
RHSname <- RHS$name
RHSnDim <- RHS$nDim
RHStype <- RHS$type
RHSsizeExprs <- RHS$sizeExprs
} else {
if(is.numeric(RHS)) {
RHSname = ''
RHSnDim <- 0
RHStype <- storage.mode(RHS)
RHSsizeExprs <- list()
} else {
stop(paste("Error in sizeAssign: don't know what to do with", nimDeparse(RHS), "in", nimDeparse(code)), call. = FALSE)
}
}
test <- try(if(inherits(RHStype, 'uninitializedField') | length(RHStype)==0) {
stop('Error with assignment in ', nimDeparse(code), '. Type of RHS is unknown.', call. = FALSE)
})
if(inherits(test, 'try-error')) browser()
if(LHS$isName) {
if(!exists(LHS$name, envir = typeEnv, inherits = FALSE)) { ## not in typeEnv
## If LHS unknown, create it in typeEnv
if(!symTab$symbolExists(LHS$name, TRUE)) { ## not in symTab
if(RHStype %in% c('double','integer', 'logical')) { ## valid type to create here
assign(LHS$name, exprTypeInfoClass$new(nDim = RHSnDim, type = RHStype), envir = typeEnv)
symTab$addSymbol(symbolBasic(name = LHS$name, nDim = RHSnDim, type = RHStype))
} else { ## not valid type ot create here
stop(paste('Error, LHS of ', nimDeparse(code),' is not in typeEnv or symTab but it cannot be added now.'), call. = FALSE)
}
} else { ## yes in symTab
## This case is ok. It is in the symbol table but not the typeEnv. So it is something like ptr <- getPtr(A)
code$toEigenize <- 'no'
code$nDim <- 0
code$type <- 'unknown'
code$sizeExprs <- list()
return(invisible(NULL))
}
} else { ## yes in typeEnv. must be symTab too.
## If LHS known, check if nDim matches RHS
if(length(LHS$nDim) == 0) stop(paste0('Error for ', nimDeparse(code), '. nDim for LHS not set.'), call. = FALSE)
if(length(RHSnDim) == 0) stop(paste0('Error for ', nimDeparse(code), '. nDim for RHS not set.'), call. = FALSE)
if(LHS$nDim != RHSnDim) {
warning(paste0('Warning, mismatched dimensions in assignment: ', nimDeparse(code), '. Going to browser(). Press Q to exit'), call. = FALSE )
browser()
}
## and warn if type issue e.g. int <- double
if(assignmentTypeWarn(LHS$type, RHStype)) {
writeLines(paste0('Warning, RHS numeric type is losing information in assignment to LHS.', nimDeparse(code)))
}
}
}
## update size info in typeEnv
assert <- NULL
if(LHS$name == 'values' && length(LHS$args) == 1) { ## It is values(model_values_accessor) <- STUFF
if(is.numeric(RHS)) stop("Cannot assign into values() from numeric", call. = FALSE)
code$name <- 'setValues'
code$args <- list(2)
setArg(code, 1, RHS)
setArg(code, 2, LHS$args[[1]])
if(!(RHS$isName)) assert <- c(assert, sizeInsertIntermediate(code, 1, symTab, typeEnv) )
return( if(length(assert) == 0) NULL else assert )
}
if(any(unlist(lapply(RHSsizeExprs, is.null)))) RHSsizeExprs <- makeSizeExpressions(rep(NA, RHSnDim), LHS$name) ## reset sizeExprs for the LHS var. re-using RHSsizeExprs for LHS. This would only be valid if it is a nimbleFunction returning something on the RHS. For assignment to be executed in Eigen, the RHS sizes MUST be known
typeEnv[[LHS$name]]$sizeExprs <- RHSsizeExprs
if(LHS$toEigenize == 'yes') writeLines('Warning from sizeAssign: not expecting LHS to have toEigenize == yes')
code$toEigenize <-if(inherits(RHS, 'exprClass')) {
if(RHS$toEigenize == 'no') 'no' else {
if(RHS$toEigenize == 'unknown') 'no' else {
if(RHS$toEigenize != 'yes' & (RHS$nDim == 0 | RHS$isName | (RHS$name == 'map' & NoEigenizeMap))) 'no' ## if it is scalar or is just a name or a map, we will do it via NimArr operator= . Used to have "| RHS$name == 'map'", but this allowed X[1:3] <- X[2:4], which requires eigen, with eval triggered, to get right
else 'yes' ## if it is 'maybe' and non-scalar and not just a name, default to 'yes'
}
}
} else 'no'
if(code$toEigenize == 'yes') { ## this would make more sense in eigenize_assign
## generate setSize(LHS, ...) where ... are dimension expressions
if(length(RHSnDim) == 0) browser()
if(RHSnDim > 0) {
if(TRUE) { ## !identical(LHSdrop$sizeExprs, RHSdrop$sizeExprs)) {## This was too clever: it was to prevent redundant calls to setSize, but the problem is the previous call could have been generated inside an if-then-else, so we can't rely on it
if(LHS$isName) {
assert <- list(substitute(setSize(LHS), list(LHS = as.name(LHS$name))))
for(i in seq_along(RHSsizeExprs)) {
test <- try(assert[[1]][[i + 2]] <- RHS$sizeExprs[[i]])
if(inherits(test, 'try-error')) browser()
}
} else {
## cat('Warning: we do not yet generate size matching assertions for indexed LHS expressions')
}
}
}
} else {
if(inherits(RHS, 'exprClass')) {
## If we have A <- map(B, ...), we need to generate a setMap for the RHS, which will be done by sizeInsertIntermediate
if(RHS$name == 'map') assert <- c(assert, sizeInsertIntermediate(code, 2, symTab, typeEnv) )
}
if(inherits(LHS, 'exprClass')) {
# ditto
if(LHS$name == 'map') assert <- c(assert, sizeInsertIntermediate(code, 1, symTab, typeEnv) )
}
}
code$nDim <- code$args[[1]]$nDim <- RHSnDim
code$type <- code$args[[1]]$type <- RHStype
code$sizeExprs <- code$args[[1]]$sizeExprs <- RHSsizeExprs
if(RHSname %in% assignmentAsFirstArgFuns) {
code$name <- RHS$name
oldArgs <- RHS$args
code$args <- list(length(oldArgs) + 1)
for(i in seq_along(oldArgs)) {
setArg(code, i+1, oldArgs[[i]])
}
setArg(code, 1, LHS)
}
return(assert)
}
sizeasDoublePtr <- function(code, symTab, typeEnv) {
## This could also handle copies from ints to doubles, which would ALWAYS require a copy
asserts <- recurseSetSizes(code, symTab, typeEnv)
nDim <- code$args[[1]]$nDim
targetString <- nimDeparse(code$args[[1]])
targetName <- Rname2CppName(targetString)
targetExpr <- parse(text = targetString, keep.source = FALSE)[[1]]
ptrName <- paste0(targetName, '_DoublePtr')
copyName <- paste0(targetName, '_contigCopy')
subList <- list(var = targetExpr, copy = as.name(copyName), ptr = as.name(ptrName))
codeBefore <- substitute( if(isMap(var) ) { copy <- var; ptr <- getPtr(copy)} else {ptr <- getPtr(var)},
subList )
codeAfter <- substitute( after( if(isMap(var)) { mapCopy(var, copy) } ), ## after() tags the assertion to go after the code line
subList )
if(!symTab$symbolExists( ptrName ))
symTab$addSymbol( symbolPtr(name = ptrName, type = 'double') )
if(!symTab$symbolExists( copyName )) {
symTab$addSymbol( symbolBasic(name = copyName, type = 'double', nDim = nDim) )
}
codeBefore <- RparseTree2ExprClasses(codeBefore)
exprClasses_initSizes(codeBefore, symTab, NULL, typeEnv)
asserts <- c(asserts, exprClasses_setSizes(codeBefore, symTab, typeEnv))
codeAfter <- RparseTree2ExprClasses(codeAfter)
asserts <- c(asserts, exprClasses_setSizes(codeAfter, symTab, typeEnv))
newArgExpr <- RparseTree2ExprClasses( substitute( ptr, subList) )
setArg(code$caller, code$callerArgID, newArgExpr)
c(asserts, list(codeBefore, codeAfter))
}
sizeScalar <- function(code, symTab, typeEnv) {
## use something different for distributionFuns
if(code$args[[1]]$toEigenize == 'yes') {
asserts <- sizeInsertIntermediate(code, 1, symTab, typeEnv)
} else {
asserts <- NULL
}
code$nDim <- 0
outputType <- scalarOutputTypes[[code$name]]
if(is.null(outputType)) code$type <- 'double'
else code$type <- outputType
code$sizeExprs <- list()
code$toEigenize <- 'maybe' ## a scalar can be eigenized or not
invisible(NULL)
}
sizeScalarRecurse <- function(code, symTab, typeEnv) {
## use something different for distributionFuns
asserts <- recurseSetSizes(code, symTab, typeEnv)
## This just forces any argument expression to be lifted. Can we life only things to be eigenized?
for(i in seq_along(code$args)) {
if(inherits(code$args[[i]], 'exprClass')) {
if(!code$args[[i]]$isName) {
asserts <- c(asserts, sizeInsertIntermediate(code, i, symTab, typeEnv) )
}
}
}
code$nDim <- 0
outputType <- scalarOutputTypes[[code$name]]
if(is.null(outputType)) code$type <- 'double'
else code$type <- outputType
code$sizeExprs <- list()
code$toEigenize <- 'maybe' ## a scalar can be eigenized or not
asserts
}
#sizeNumListAccess <- function(code, symTab, typeEnv){
# sym = symTab$getSymbolObject(code$args[[1]]$name, TRUE)
# code$type <- sym$type
#
#}
sizeUndefined <- function(code, symTab, typeEnv) {
code$nDim <- 0
code$type <- as.character(NA)
code$sizeExprs <- list()
code$toEigenize <- 'maybe'
invisible(NULL)
}
sizeBinaryUnaryCwise <- function(code, symTab, typeEnv) {
if(length(code$args) == 1) return(sizeUnaryCwise(code, symTab, typeEnv))
if(length(code$args) == 2) return(sizeBinaryCwise(code, symTab, typeEnv))
stop('Error with sizeBinaryUnarycWise: length of arguments is not 1 or 2', call. = FALSE)
}
sizemvAccessBracket <- function(code, symTab, typeEnv) {
## this gets called from sizeIndexingBracket, so recurse has already been done
asserts <- recurseSetSizes(code, symTab, typeEnv, useArgs = c(FALSE, TRUE))
if(length(code$args) != 2) {
stop(paste0('Error in sizemvAccessBracket for ', nimDeparse(code),'. Wrong number of indices provided.'), call. = FALSE)
}
if(inherits(code$args[[2]], 'exprClass')) {
if(code$args[[2]]$nDim != 0) stop(paste0('Error in sizemvAccessBracket for ', nimDeparse(code),'. Index is not a scalar.'), call. = FALSE)
}
sym <- symTab$getSymbolObject(code$args[[1]]$name, TRUE) ## This is the symbolVecNimArrPtr
code$type = sym$type
code$nDim = sym$nDim
code$sizeExprs <- as.list(sym$size)
code$toEigenize <- 'maybe'
code$name <- 'mvAccessRow'
if(length(asserts)==0) NULL else asserts
}
sizeIndexingBracket <- function(code, symTab, typeEnv) {
asserts <- recurseSetSizes(code, symTab, typeEnv)
if(code$args[[1]]$type == 'symbolVecNimArrPtr') return(c(asserts, sizemvAccessBracket(code, symTab, typeEnv)))
if(code$args[[1]]$type == 'symbolNumericList') return(c(asserts, sizemvAccessBracket(code, symTab, typeEnv)))
nDimVar <- code$args[[1]]$nDim
if(nDimVar != length(code$args) - 1) {
stop(paste('Error, wrong number of indices provided for ', nimDeparse(code)), call. = FALSE)
}
code$nDim <- nDimVar
code$type <- code$args[[1]]$type
code$sizeExprs <- vector('list', length = nDimVar)
## We could generate asserts here to ensure sub-indexing is within bounds
needMap <- FALSE
iSizes <- 1
for(i in 1:nDimVar) {
dropThisDim <- FALSE
if(is.numeric(code$args[[i+1]])) dropThisDim <- TRUE
else if((code$args[[i+1]]$name != "") & (length(dropSingleSizes(code$args[[i+1]]$sizeExprs)$sizeExprs) == 0)) dropThisDim <- TRUE
if(dropThisDim) {
if(nimbleOptions$indexDrop) {
code$sizeExprs[[iSizes]] <- NULL
code$nDim <- code$nDim - 1
} else {
code$sizeExprs[[iSizes]] <- 1; iSizes <- iSizes + 1
}
next
}
needMap <- TRUE
if(inherits(code$args[[i+1]], 'exprClass')) {
if(code$args[[i+1]]$name != "") {
## An entry that is a variable possible with a length
code$sizeExprs[[iSizes]] <- code$args[[i+1]]$sizeExprs[[1]]
} else {
## blank entry (e.g. A[,i]) is an exprClass with isName = TRUE and name = ""
code$sizeExprs[[iSizes]] <- code$args[[1]]$sizeExprs[[i]]
}
iSizes <- iSizes + 1
next
}
}
code$toEigenize <- 'maybe'
if(needMap) {
## If this is a map on an *expression* that is not a map, lift it
## e.g. (A + B)[1:4] must become (Interm <- A + B; Interm[1:4])
if(!code$args[[1]]$isName) if(code$args[[1]]$name != 'map') asserts <- c(asserts, sizeInsertIntermediate(code, 1, symTab, typeEnv))
## Replace with a map expression if needed
newExpr <- makeMapExprFromBrackets(code)
newExpr$sizeExprs <- code$sizeExprs
newExpr$type <- code$type
newExpr$nDim <- code$nDim
newExpr$toEigenize <- code$toEigenize
setArg(code$caller, code$callerArgID, newExpr)
}
if(length(asserts)==0) NULL else asserts
}
sizeColonOperator <- function(code, symTab, typeEnv) {
asserts <- recurseSetSizes(code, symTab, typeEnv)
if(length(code$args) != 2) stop(paste('Error determining size for : without two argument, in expression', nimDeparse(code)), call. = FALSE)
if(inherits(code$args[[1]], 'exprClass')) {
if(code$args[[1]]$nDim != 0) writeLines('WARNING, first arg to : is not scalar in expression', nimDeparse(code))
}
if(inherits(code$args[[2]], 'exprClass')) {
if(code$args[[2]]$nDim != 0) writeLines('WARNING, second arg to : is not scalar in expression', nimDeparse(code))
}
code$type <- 'integer'
code$nDim <- 1
code$toEigenize <- 'maybe' ## doesn't really matter since are used in maps
## could generate an assertiong that second arg is >= first arg
if(is.numeric(code$args[[1]]) & is.numeric(code$args[[2]])) {
code$sizeExprs <- list(code$args[[2]] - code$args[[1]] + 1)
} else { ## at least one part is an expression
## This is an awkward case:
## sizeExprs are R parse trees, not exprClasses
## But in this case, we want the expression from an exprClass.
## so we need to nimDeparse and then parse them
code$sizeExprs <- list(substitute( A - B + 1, list(A = parse(text = nimDeparse(code$args[[2]]), keep.source = FALSE)[[1]],
B = parse(text = nimDeparse(code$args[[1]]), keep.source = FALSE)[[1]] ) ) )
}
invisible(asserts)
}
sizeDimOperator <- function(code, symTab, typeEnv) {
## a special case since the resulte is stored as a vector<int>
## recurse and set Intermediate is if it not a name or map
## we'll want a NimArr<1, int> constructor vector<int>
recurseSetSizes(code, symTab, typeEnv)
code$type <- 'integer'
code$nDim <- 1
code$sizeExprs <- list( code$args[[1]]$nDim )
code$toEigenize <- 'no'
invisible(NULL)
}
sizeTranspose <- function(code, symTab, typeEnv) {
if(length(code$args) != 1) warning(paste0('More than one argument to transpose in ', nimDeparse(code), '.'), call. = FALSE)
ans <- sizeUnaryCwise(code, symTab, typeEnv)
if(is.numeric(code$args[[1]])) {
warning(paste0('Confused by transpose of a numeric scalar in ', nimDeparse(code), '. Will remove transpose.'), call. = FALSE)
removeExprClassLayer(code$caller, 1)
return(ans)
}
code$toEigenize <- 'yes'
code$type <- code$args[[1]]$type
if(length(code$sizeExprs) == 2) {
if(code$nDim != 2) warning(paste0('In sizeTranspose, there are 2 sizeExprs but nDim != 2'), call. = FALSE)
code$sizeExprs <- c(code$sizeExprs[2], code$sizeExprs[1])
} else if(length(code$sizeExprs) == 1) {
if(code$nDim != 1) warning(paste0('In sizeTranspose, there is 1 sizeExpr but nDim != 1'), call. = FALSE)
##setAsRowOrCol(code, 1, 'asRow', code$args[[1]]$type)
##code <- removeExprClassLayer(code, 1)
##code$toEigenize <- 'yes'
code$name <- 'asRow'
code$sizeExprs <- c(list(1), code$sizeExprs[[1]])
code$nDim <- 2
}
return(ans)
}
## Handler for unary functions that operate component-wise
sizeUnaryCwise <- function(code, symTab, typeEnv) {
if(length(code$args) != 1){
stop('Error, sizeUnaryCwise called with argument length != 1', call. = FALSE)
}
asserts <- recurseSetSizes(code, symTab, typeEnv)
## lift intermediates
a1 <- code$args[[1]]
if(inherits(a1, 'exprClass')) {
if(a1$toEigenize == 'no') {
asserts <- c(asserts, sizeInsertIntermediate(code, 1, symTab, typeEnv))
a1 <- code$args[[1]]
}
code$nDim <- a1$nDim
code$sizeExprs <- a1$sizeExprs
code$type <- a1$type
} else {
code$nDim <- 0
code$sizeExprs <- list()
code$type <- 'double'
}
if(length(code$nDim) != 1) stop(paste0('Error for ', nimDeparse(code), '. nDim is not set.'), call. = FALSE)
code$toEigenize <- if(code$nDim > 0) 'yes' else 'maybe'
return(asserts)
}
## currently only inprod(v1, v2)
sizeBinaryReduction <- function(code, symTab, typeEnv) {
if(length(code$args) != 2) stop('Error, sizeBinaryReduction called with argument length != 2', call. = FALSE);
asserts <- recurseSetSizes(code, symTab, typeEnv)
a1 <- code$args[[1]]
a2 <- code$args[[2]]
ok <- TRUE
if(inherits(a1, 'exprClass')) {
if(a1$toEigenize == 'no') {
asserts <- c(asserts, sizeInsertIntermediate(code, 1, symTab, typeEnv))
a1 <- code$args[[1]]
}
} else {
ok <- FALSE
}
if(inherits(a2, 'exprClass')) {
if(a2$toEigenize == 'no') {
asserts <- c(asserts, sizeInsertIntermediate(code, 2, symTab, typeEnv))
a2 <- code$args[[2]]
}
} else {
ok <- FALSE
}
if(!ok) stop(paste0("Cannot call inprod or other binary reduction operator with constant argument. Problem is with: ", nimDeparse(code)), call. = FALSE)
code$nDim <- 0
code$sizeExprs <- list()
code$type <- 'double'
code$toEigenize <- 'yes'
if(!(code$caller$name %in% c('{','<-','<<-','='))) {
asserts <- c(asserts, sizeInsertIntermediate(code$caller, code$callerArgID, symTab, typeEnv))
}
if(length(asserts) == 0) NULL else asserts
}
## things like trace, det, logdet
sizeMatrixSquareReduction <- function(code, symTab, typeEnv) {
if(length(code$args) != 1){
stop('Error, sizeMatrixSquareReduction called with argument length != 1', call. = FALSE)
}
asserts <- recurseSetSizes(code, symTab, typeEnv)
a1 <- code$args[[1]]
if(!inherits(a1, 'exprClass')) stop('Error, sizeUnaryCwiseSquare called with an argument that is not an expression', call. = FALSE)
if(a1$toEigenize == 'no') {
asserts <- c(asserts, sizeInsertIntermediate(code, 1, symTab, typeEnv))
a1 <- code$args[[1]]
}
if(a1$nDim != 2) stop('Error, sizeUnaryCwiseSquare was called with an argument that is not a matrix', call. = FALSE)
code$nDim <- 0
code$sizeExprs <- list()
code$type <- if(code$name == 'trace') code$args[[1]]$type else 'double'
code$toEigenize <- 'yes'
if(!(code$caller$name %in% c('{','<-','<<-','='))) {
asserts <- c(asserts, sizeInsertIntermediate(code$caller, code$callerArgID, symTab, typeEnv))
}
if(length(asserts) == 0) NULL else asserts
}
sizeUnaryCwiseSquare <- function(code, symTab, typeEnv) {
if(length(code$args) != 1){
stop('Error, sizeUnaryCwiseSquare called with argument length != 1', call. = FALSE)
}
asserts <- recurseSetSizes(code, symTab, typeEnv)
a1 <- code$args[[1]]
if(!inherits(a1, 'exprClass')) stop('Error, sizeUnaryCwiseSquare called with an argument that is not an expression', call. = FALSE)
if(a1$toEigenize == 'no') {
asserts <- c(asserts, sizeInsertIntermediate(code, 1, symTab, typeEnv))
a1 <- code$args[[1]]
}
if(a1$nDim != 2) stop('Error, sizeUnaryCwiseSquare was called with an argument that is not a matrix', call. = FALSE)
if(!identical(a1$sizeExprs[[1]], a1$sizeExprs[[2]])) {
asserts <- identityAssert(a1$sizeExprs[[1]], a1$sizeExprs[[2]], paste0("Run-time size error: expected ", nimDeparse(a1), " to be square.") )
if(is.integer(a1$sizeExprs[[1]])) {
newSize <- a1$sizeExprs[[1]]
} else {
if(is.integer(a1$sizeExprs[[2]])) {
newSize <- a1$sizeExprs[[2]]
} else {
newSize <- a1$sizeExprs[[1]]
}
}
} else {
asserts <- NULL
newSize <- a1$sizeExprs[[1]]
}
code$nDim <- 2
code$sizeExprs <- list(newSize, newSize)
code$type <- a1$type
code$toEigenize <- if(code$nDim > 0) 'yes' else 'maybe'
invisible(asserts)
}
sizeUnaryNonaryCwise <- function(code, symTab, typeEnv) {
if(length(code$args) > 1) stop('Error, sizeUnaryNonarCwise called with argument length > 1', call. = FALSE);
if(length(code$args) == 1) return(sizeUnaryCwise(code, symTab, typeEnv))
## default behavior for a nonary (no-argument) function:
code$type <- 'double'
code$nDim <- 0
code$sizeExprs <- list()
code$toEigenize <- 'maybe'
invisible(NULL)
}
## things like min, max, mean, sum
sizeUnaryReduction <- function(code, symTab, typeEnv) {
if(length(code$args) != 1) stop('Error, sizeUnaryReduction called with argument length != 1', call. = FALSE)
asserts <- recurseSetSizes(code, symTab, typeEnv)
if(inherits(code$args[[1]], 'exprClass')) {
if(!code$args[[1]]$isName) {
if(code$args[[1]]$toEigenize == 'no') {
asserts <- c(asserts, sizeInsertIntermediate(code, 1, symTab, typeEnv))
}
}
}
code$nDim <- 0
code$sizeExprs <- list()
code$type <- code$args[[1]]$type
code$toEigenize <- 'yes'
if(!(code$caller$name %in% c('{','<-','<<-','='))) {
asserts <- c(asserts, sizeInsertIntermediate(code$caller, code$callerArgID, symTab, typeEnv))
}
if(length(asserts) == 0) NULL else asserts
}
## There's no real point in annotating return. Just need to recurse and lift
sizeReturn <- function(code, symTab, typeEnv) {
if(length(code$args) > 1) stop('Error, return has argument length > 1', call. = FALSE)
code$toEigenize <- 'no'
if(length(code$args) == 0) return(invisible(NULL))
asserts <- recurseSetSizes(code, symTab, typeEnv)
if(inherits(code$args[[1]], 'exprClass')) {
if(!code$args[[1]]$isName) {
## if(code$args[[1]]$toEigenize == 'yes') {
if(anyNonScalar(code$args[[1]])) {
asserts <- c(asserts, sizeInsertIntermediate(code, 1, symTab, typeEnv))
}
## }
}
}
invisible(asserts)
}
sizeMatrixMult <- function(code, symTab, typeEnv) {
if(length(code$args) != 2) stop('Error, sizeMatrixMult called with argument length != 2', call. = FALSE);
a1 <- code$args[[1]]
a2 <- code$args[[2]]
if(!(inherits(a1, 'exprClass') & inherits(a2, 'exprClass'))) stop('Error in sizeMatrixMult: expecting both arguments to be expressions.', call. = FALSE)
## need promotion from vectors to matrices with asRow or asCol
asserts <- recurseSetSizes(code, symTab, typeEnv)
a1 <- code$args[[1]]
a2 <- code$args[[2]]
if(a1$nDim == 0 | a2$nDim == 0) stop(paste0('Error for ', nimDeparse(code),'. Cannot do matrix multiplication with a scalar.'), call. = FALSE)
if(a1$toEigenize == 'no') {
asserts <- c(asserts, sizeInsertIntermediate(code, 1, symTab, typeEnv))
a1 <- code$args[[1]]
}
if(a2$toEigenize == 'no') {
asserts <- c(asserts, sizeInsertIntermediate(code, 2, symTab, typeEnv))
a2 <- code$args[[2]]
}
## Note that we could insert RUN-TIME adaptation of mat %*% vec and vec %*% mat
## but to do so we would need to generate trickier sizeExprs
## For now, a vector on the right will be turned into a column
## and a vector on the left will be turned into a row
## The programmer can always use asRow or asCol to control it explicitly
if(a1$nDim == 1 & a2$nDim == 1) {
##a1 <- setAsRowOrCol(code, 1, 'asRow', type = a1$type)
origSizeExprs <- a1$sizeExprs[[1]]
a1 <- insertExprClassLayer(code, 1, 'asRow', type = a1$type)
a1$sizeExprs <- c(list(1), origSizeExprs)
##a2 <- setAsRowOrCol(code, 2, 'asCol', type = a2$type)
origSizeExprs <- a2$sizeExprs[[1]]
a2 <- insertExprClassLayer(code, 2, 'asCol', type = a2$type)
a2$sizeExprs <- c(origSizeExprs, list(1))
} else {
if(a1$nDim == 1) {
if(a2$nDim != 2) stop(paste0('Problem in ', nimDeparse(code), '. First arg has nDim = 1 and 2nd arg has nDim = ', a2$nDim, '.'), call. = FALSE)
origSizeExprs <- a1$sizeExprs[[1]]
## For first argument, default to asRow unless second argument has only one row, in which case make first asCol
if(identical(a2$sizeExprs[[1]], 1)) {
a1 <- insertExprClassLayer(code, 1, 'asCol', type = a1$type)
a1$sizeExprs <- c(origSizeExprs, list(1))
}
else {
a1 <- insertExprClassLayer(code, 1, 'asRow', type = a1$type)
a1$sizeExprs <- c(list(1), origSizeExprs)
}
} else if(a2$nDim == 1) {
origSizeExprs <- a2$sizeExprs[[1]]
if(a1$nDim != 2) stop(paste0('Problem in ', nimDeparse(code), '. Second arg has nDim = 1 and 1st arg has nDim = ', a1$nDim, '.'), call. = FALSE)
if(identical(a1$sizeExprs[[2]], 1)) {
##a2 <- setAsRowOrCol(code, 2, 'asRow', type = a2$type)
a2 <- insertExprClassLayer(code, 2, 'asRow', type = a2$type)
a2$sizeExprs <- c(list(1), origSizeExprs)
}
else {
## a2 <- setAsRowOrCol(code, 2, 'asCol', type = a2$type)
a2 <- insertExprClassLayer(code, 2, 'asCol', type = a2$type)
a2$sizeExprs <- c(origSizeExprs, list(1))
}
}
}
code$nDim <- 2
code$sizeExprs <- list(a1$sizeExprs[[1]], a2$sizeExprs[[2]])
code$type <- arithmeticOutputType(a1$type, a2$type)
code$toEigenize <- 'yes'
assertMessage <- paste0("Run-time size error: expected ", deparse(a1$sizeExprs[[2]]), " == ", deparse(a2$sizeExprs[[1]]))
newAssert <- identityAssert(a1$sizeExprs[[2]], a2$sizeExprs[[1]], assertMessage)
if(is.null(newAssert))
return(asserts)
else
return(c(list(identityAssert(a1$sizeExprs[[2]], a2$sizeExprs[[1]], assertMessage)), asserts))
}
sizeSolveOp <- function(code, symTab, typeEnv) { ## this is for solve(A, b) or forwardsolve(A, b). For inverse, use inverse(A), not solve(A)
if(length(code$args) != 2) stop('Error, sizeMatrixMult called with argument length != 2', call. = FALSE)
## need promotion from vectors to matrices with asRow or asCol
a1 <- code$args[[1]]
a2 <- code$args[[2]]
if(!(inherits(a1, 'exprClass') & inherits(a2, 'exprClass'))) stop('Error in sizeSolveOp: expecting both arguments to be exprClasses', call. = FALSE)
code$type <- 'double'
if(a1$nDim != 2) stop('Error, first argument to a matrix solver must be a matrix', call. = FALSE)
if(a2$nDim == 1) {
a2 <- insertExprClassLayer(code, 2, 'asCol', type = as$type)
a2$sizeExprs <- list(code$args[[2]]$sizeExprs[[1]], 1)
a1$nDim <- 2
}
code$nDim <- 2
code$sizeExprs <- c(a1$sizeExprs[[2]], a2$sizeExprs[[2]])
assertMessage <- paste0("Run-time size error: expected ", deparse(a1$sizeExprs[[1]]), " == ", deparse(a2$sizeExprs[[1]]))
return(identityAssert(a1$sizeExprs[[1]], a2$sizeExprs[[1]], assertMessage))
}
## deprecated and will be removed
setAsRowOrCol <- function(code, argID, rowOrCol, type ) {
recurse <- TRUE
if(is.numeric(code$args[[argID]])) return(NULL)
if(code$args[[argID]]$isName) {
recurse <- FALSE
} else {
if(code$args[[argID]]$name == 'map') recurse <- FALSE
}
if(!recurse) {
if(code$args[[argID]]$nDim == 2) {
## stop('Error in setAsRowOrCol: should only be used one 1-dimensional things', call. = FALSE)
if(rowOrCol == 'asRow') {
if(is.numeric(code$args[[argID]]$sizeExprs[[1]])) {
if(code$args[[argID]]$sizeExprs[[1]] == 1) {
return(invisible(NULL)) ## it is already a row
}
}
rowOK <- if(is.numeric(code$args[[argID]]$sizeExprs[[2]])) { ## only ok if a1 2nd size is 1
if(code$sizeExprs[[2]] == 1) TRUE else FALSE
} else FALSE
if(!rowOK) stop(paste0('Error in setAsRowOrCol for ', nimDeparse(code), '. Cannot convert to row.'), call. = FALSE)
lengthExpr <- code$args[[argID]]$sizeExprs[[1]]
insertExprClassLayer(code$caller, code$callerArgID, 'transpose', type = type)
code$nDim <- 2
code$sizeExprs <- c(list(1), lengthExpr)
return(code$args[[argID]])
} else {
if(is.numeric(code$args[[argID]]$sizeExprs[[1]])) {
if(code$args[[argID]]$sizeExprs[[2]] == 1) {
return(invisible(NULL)) ## it is already a col
}
}
colOK <- if(is.numeric(code$args[[argID]]$sizeExprs[[1]])) { ## only ok if a1 1st size is 1
if(code$sizeExprs[[1]] == 1) TRUE else FALSE
} else FALSE
if(!colOK) stop(paste0('Error in setAsRowOrCol for ', nimDeparse(code), '. Cannot convert to col.'), call. = FALSE)
lengthExpr <- code$args[[argID]]$sizeExprs[[1]]
insertExprClassLayer(code$caller, code$callerArgID, 'transpose', type = type)
code$nDim <- 2
code$sizeExprs <- c(lengthExpr, list(1))
return(code$args[[argID]])
}
} else if(code$args[[argID]]$nDim == 1) {
oldSizeExprs <- code$args[[argID]]$sizeExprs
insertExprClassLayer(code, argID, rowOrCol, type = type)
if(rowOrCol == 'asRow') {
code$sizeExprs <- c(list(1), oldSizeExprs)
} else {
code$sizeExprs <- c(oldSizeExprs, list(1))
}
code$nDim <- 2
code$type <- type
ans <- code$args[[argID]]
}
} else {
for(i in seq_along(code$args[[argID]]$args)) {
setAsRowOrCol(code$args[[argID]], i, rowOrCol, type)
}
ans <- code$args[[argID]]
}
ans
}
sizeBinaryCwiseLogical <- function(code, symTab, typeEnv) {
ans <- sizeBinaryCwise(code, symTab, typeEnv)
code$type <- 'logical'
return(ans)
}
## Handler for binary component-wise operators
sizeBinaryCwise <- function(code, symTab, typeEnv) {
if(length(code$args) != 2) stop('Error, sizeBinaryCwise called with argument length != 2', call. = FALSE);
asserts <- recurseSetSizes(code, symTab, typeEnv)
## sizes of arguments must have already been set
## pull out the two arguments
a1 <- code$args[[1]]
a2 <- code$args[[2]]
## pull out aXDropNdim, aXnDim, aXsizeExprs, and aXtype (X = 1 or 2)
if(inherits(a1, 'exprClass')) {
if(a1$toEigenize == 'no') {
asserts <- c(asserts, sizeInsertIntermediate(code, 1, symTab, typeEnv))
a1 <- code$args[[1]]
}
a1Drop <- dropSingleSizes(a1$sizeExprs)
a1DropNdim <- length(a1Drop$sizeExprs)
a1nDim <- a1$nDim
a1sizeExprs <- a1$sizeExprs
a1type <- a1$type
a1toEigenize <- a1$toEigenize
} else {
a1DropNdim <- 0
a1nDim <- 0
a1sizeExprs <- list()
a1type <- 'double'
}
if(inherits(a2, 'exprClass')) {
if(a2$toEigenize == 'no') {
asserts <- c(asserts, sizeInsertIntermediate(code, 2, symTab, typeEnv))
a2 <- code$args[[2]]
}
a2Drop <- dropSingleSizes(a2$sizeExprs)
a2DropNdim <- length(a2Drop$sizeExprs)
a2nDim <- a2$nDim
a2sizeExprs <- a2$sizeExprs
a2type <- a2$type
} else {
a2DropNdim <- 0
a2nDim <- 0
a2sizeExprs <- list()
a2type <- 'double'
}
## Choose the output type by type promotion
if(length(a1type) == 0) {warning('Problem with type of arg1 in sizeBinaryCwise', call. = FALSE); browser()}
if(length(a2type) == 0) {warning('Problem with type of arg2 in sizeBinaryCwise', call. = FALSE); browser()}
code$type <- arithmeticOutputType(a1type, a2type)
code$toEigenize <- if(a1DropNdim == 0 & a2DropNdim == 0) 'maybe' else 'yes'
## Catch the case that there is at least one scalar-equivalent (all lengths == 1)
if(a1DropNdim == 0 | a2DropNdim == 0) {
## Here we will process effective scalar additions
## and not do any other type of size promotion/dropping
if(a1DropNdim == 0) { ## a1 is scalar-equiv
if(a2DropNdim == 0) { ##both are scalar-equiv
code$nDim <- max(a1nDim, a2nDim) ## use the larger nDims
code$sizeExprs <- rep(list(1), code$nDim) ## set sizeExprs to all 1
code$toEigenize <- 'maybe'
} else {
## a2 is not scalar equiv, so take nDim and sizeExprs from it
code$nDim <- a2nDim
code$sizeExprs <- a2sizeExprs
code$toEigenize <- 'yes'
}
} else { ## a2 is scalar-equiv, and a1 is not
code$nDim <- a1nDim
code$sizeExprs <- a1sizeExprs
code$toEigenize <- 'yes'
}
return(if(length(asserts) == 0) NULL else asserts)
}
if(is.null(asserts)) asserts <- list()
## Catch the case that the number of dimensions is not equal.
## This case doesn't arise as much as it used to because [ (sizeIndexingBracket) now drops single dimensions
if(a1nDim != a2nDim) {
## Catch the case that one is 2D and the other is 1D-equivalent.
## This allows e.g. X[1,1:5] + Y[1,1,1:5]. First arg is 2D. 2nd arg is 1D-equivalent. An assertion will check that dim(X)[1] == 1
## If so, wrap the 1D is asRow or asCol to orient it later for Eigen
if(a1DropNdim == 1 & a2DropNdim == 1) {
## Hey, I think this is wrong: I think we should check the aXDropNdims
if(a1nDim > 2 | a2nDim > 2) stop(paste0('Error for ', nimDeparse(code), '. Dimensions do not match and we do not match Array + Vector for dim(Array) > 2'), call. = FALSE)
## a1 is 2D and a2 is 1D
if(a1nDim == 2 & a2nDim == 1) {
a1IsCol <- identical(a1sizeExprs[[2]], 1)
asFun <- if(a1IsCol) 'asCol' else 'asRow'
##a2 <- setAsRowOrCol(code, 2, asFun, type = a2type)
a2 <- insertExprClassLayer(code, 2, asFun, type = a2type)
a2$sizeExprs <- a1sizeExprs
a2$nDim <- a1nDim
a1ind <- if(a1IsCol) 1 else 2
if(!is.numeric(a1sizeExprs[[a1ind]]) | !is.numeric(a2sizeExprs[[1]])) { ## Really do want original a2sizeExprs
assertMessage <- paste0("Run-time size error: expected ", deparse(a1sizeExprs[[a1ind]]), " == ", deparse(a2sizeExprs[[1]]))
thisAssert <- identityAssert(a1sizeExprs[[a1ind]], a2sizeExprs[[1]], assertMessage)
if(!is.null(thisAssert)) asserts[[length(asserts) + 1]] <- thisAssert
} else {
if(a1sizeExprs[[a1ind]] != a2sizeExprs[[1]]) stop('Fixed size mismatch', call. = FALSE)
}
code$nDim <- a1nDim
code$sizeExprs <- a1sizeExprs
} else {
a2IsCol <- identical(a2sizeExprs[[2]], 1)
asFun <- if(a2IsCol) 'asCol' else 'asRow'
##a1 <- setAsRowOrCol(code, 1, asFun, type = a1type)
a1 <- insertExprClassLayer(code, 1, asFun, type = a1type)
a1$sizeExprs <- a2sizeExprs
a1$type <- a1type
a1$nDim <- a1nDim <- a2nDim
a2ind <- if(a2IsCol) 1 else 2
if(!is.numeric(a1sizeExprs[[1]]) | !is.numeric(a2sizeExprs[[a2ind]])) { ## Really do want the original a1sizeExprs[[1]], not the modified one.
assertMessage <- paste0("Run-time size error: expected ", deparse(a1sizeExprs[[1]]), " == ", deparse(a2sizeExprs[[a2ind]]))
thisAssert <- identityAssert(a1sizeExprs[[1]], a2sizeExprs[[a2ind]], assertMessage)
if(!is.null(thisAssert)) asserts[[length(asserts) + 1]] <- thisAssert
} else {
if(a1sizeExprs[[1]] != a2sizeExprs[[a2ind]]) stop('Fixed size mismatch', call. = FALSE)
}
code$nDim <- a2nDim
code$sizeExprs <- a2sizeExprs
}
} else
stop(paste0('Error for ', nimDeparse(code), '. Dimensions do not match and neither is scalar-equivalent.'), call. = FALSE)
} else {
## dimensions match at the outset
nDim <- a1nDim
if(nDim > 0) {
for(i in 1:nDim) {
if(!is.numeric(a1sizeExprs[[i]]) | !is.numeric(a2sizeExprs[[i]])) {
assertMessage <- paste0("Run-time size error: expected ", deparse(a1sizeExprs[[i]]), " == ", deparse(a2sizeExprs[[i]]))
thisAssert <- identityAssert(a1sizeExprs[[i]], a2sizeExprs[[i]], assertMessage)
if(!is.null(thisAssert)) asserts[[length(asserts) + 1]] <- thisAssert
} else {
if(a1sizeExprs[[i]] != a2sizeExprs[[i]]) stop('Fixed size mismatch', call. = FALSE)
}
}
}
code$nDim <- a1$nDim
code$sizeExprs <- vector('list', code$nDim)
for(i in seq_along(code$sizeExprs)) code$sizeExprs[[i]] <- if(is.numeric(a1sizeExprs[[i]])) a1sizeExprs[[i]] else a2sizeExprs[[i]]
}
if(length(asserts) == 0) NULL else asserts
}
mvFirstArgCheckLists <- list(nimArr_rmnorm_chol = list(c(1, 2, 0), ## dimensionality of ordered arguments AFTER the first, which is for the return value. e.g. mean (1D), chol(2D), prec_param(scalar)
1, 'double'), ## 1 = argument from which to take answer size, double = answer type
nimArr_rwish_chol = list(c(2, 0, 0), ## chol, df, prec_param
1, 'double'),
nimArr_rmulti = list(c(0, 1), ## size, probs
2, 'double'), ## We treat integer rv's as doubles
nimArr_rdirch = list(c(1), 1, 'double')) ## alpha
sizeRmultivarFirstArg <- function(code, symTab, typeEnv) {
asserts <- recurseSetSizes(code, symTab, typeEnv)
notOK <- FALSE
checkList <- mvFirstArgCheckLists[[code$name]]
if(!is.null(checkList)) {
if(length(code$args) < length(checkList[[1]])) stop(paste0("Error, not enough arguments provided for ", nimDeparse(code),"."), call. = FALSE)
for(i in seq_along(checkList[[1]])) {
notOK <- if(inherits(code$args[[i]], 'exprClass')) code$args[[i]]$nDim != checkList[[1]][i] else notOK
}
returnSizeArgID <- checkList[[2]]
returnType <- checkList[[3]]
} else {
returnSizeArgID <- 1
returnType <- 'double'
}
if(notOK) {
stop(paste0('Error for ', nimDeparse(code), '. Some argument(s) have the wrong dimension.'), call. = FALSE)
}
code$type <- returnType
code$nDim <- code$args[[returnSizeArgID]]$nDim
code$toEigenize <- 'maybe'
code$sizeExprs <- code$args[[returnSizeArgID]]$sizeExprs
for(i in seq_along(code$args)) {
if(inherits(code$args[[i]], 'exprClass')) {
if(!code$args[[i]]$isName) {
asserts <- c(asserts, sizeInsertIntermediate(code, i, symTab, typeEnv) )
}
}
}
if(code$nDim > 0) {
if(!(code$caller$name %in% c('{','<-','<<-','='))) {
asserts <- c(asserts, sizeInsertIntermediate(code$caller, code$callerArgID, symTab, typeEnv))
}
}
return(asserts)
}
###
## This function would be called with arguments from an RCfunction or nimbleFunction
## the functions dim and length would be taken over to work on the sizeExprs.
## but for now it can just return NAs for size expressions, and then the new returned value will have default size expressions (dim(name)[1], etc)
##
generalFunSizeHandler <- function(code, symTab, typeEnv, returnType, args, chainedCall = FALSE) {
useArgs <- unlist(lapply(args, function(x) as.character(x[[1]]) %in% c('double', 'integer', 'logical')))
if(chainedCall) useArgs <- c(FALSE, useArgs)
if(length(code$args) != length(useArgs)) {
stop(paste0('Error, wrong number of args for ',code$name), call. = FALSE)
}
## Note this is NOT checking the dimensions of each arg. useArgs just means it will recurse on that and lift or do as needed
asserts <- recurseSetSizes(code, symTab, typeEnv, useArgs)
## lift any argument that is an expression
for(i in seq_along(code$args)) {
if(useArgs[i]) {
if(inherits(code$args[[i]], 'exprClass')) {
if(!code$args[[i]]$isName) {
asserts <- c(asserts, sizeInsertIntermediate(code, i, symTab, typeEnv) )
}
}
}
}
returnTypeLabel <- as.character(returnType[[1]])
if(returnTypeLabel == 'void') {
code$type <- returnTypeLabel
code$toEigenize <- 'unknown'
return(asserts)
}
returnNDim <- if(length(returnType) > 1) as.numeric(returnType[[2]]) else 0
returnSizeExprs <- vector('list', returnNDim) ## This stays blank (NULLs), so if assigned as a RHS, the LHS will get default sizes
code$type <- returnTypeLabel
code$nDim <- returnNDim
code$sizeExprs <- returnSizeExprs
code$toEigenize <- if(code$nDim == 0) 'maybe' else 'no'
if(code$nDim > 0) {
if(!(code$caller$name %in% c('{','<-','<<-','='))) {
asserts <- c(asserts, sizeInsertIntermediate(code$caller, code$callerArgID, symTab, typeEnv))
}
}
return(asserts)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.