Skip to content

Commit f907ad9

Browse files
committed
Omit unneeded marginalization nodes and rework messaging
for setupMargNodes warnings.
1 parent 6d46400 commit f907ad9

3 files changed

Lines changed: 64 additions & 16 deletions

File tree

packages/nimble/R/Laplace.R

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2105,11 +2105,12 @@ setupMargNodes <- function(model, paramNodes, randomEffectsNodes, calcNodes,
21052105
if(length(reCheck)) {
21062106
errorNodes <- paste0(head(reCheck, n = 4), sep = "", collapse = ", ")
21072107
if(length(reCheck) > 4) errorNodes <- paste(errorNodes, "...")
2108-
messageIfVerbose(" [Warning] There are some random effects (latent states) in the model that look\n",
2109-
" like they should be included in `randomEffectsNodes` for Laplace or AGHQ approximation\n",
2110-
" for the provided (or default) `paramNodes`: ", errorNodes, ".\n",
2111-
" To silence this warning, include `check = FALSE` in the control list\n",
2112-
" to `buildLaplace` or as an argument to `setupMargNodes`.")
2108+
messageIfVerbose(" [Warning] There are some random effects (latent states) in the model that look like\n",
2109+
" they should be marginalized over for the provided (or default) `paramNodes`,\n",
2110+
" but are not included in `randomEffectsNodes`: ", errorNodes, ".\n",
2111+
" To silence this warning, one can usually include `check = FALSE`\n",
2112+
" (potentially in the control list) for the algorithm or as\n",
2113+
" an argument to `setupMargNodes`.")
21132114
}
21142115
# Second check is for random effects that were included but look unnecessary
21152116
reCheck <- setdiff(randomEffectsNodes, reNodesDefault)
@@ -2126,11 +2127,16 @@ setupMargNodes <- function(model, paramNodes, randomEffectsNodes, calcNodes,
21262127
if(length(reCheck)) {
21272128
errorNodes <- paste0(head(reCheck, n = 4), sep = "", collapse = ", ")
21282129
if(length(reCheck) > 4) errorNodes <- paste(errorNodes, "...")
2130+
extraMsg <- ifelse(getNimbleOption('includeUnneededLatentsForMarg'), "",
2131+
" They will be omitted, but one can force inclusion with\n `nimbleOptions(omitUnneededLatentsForMarg=FALSE)`.\n")
21292132
messageIfVerbose(" [Warning] There are some `randomEffectsNodes` provided that look like\n",
2130-
" they are not needed for Laplace or AGHQ approximation for the\n",
2131-
" provided (or default) `paramNodes`: ", errorNodes, ".\n",
2132-
" To silence this warning, include `check = FALSE` in the control list\n",
2133-
" to `buildLaplace` or as an argument to `setupMargNodes`.")
2133+
" they are not needed for marginalization for the\n",
2134+
" provided (or default) `paramNodes`: ", errorNodes, ".\n", extraMsg,
2135+
" To silence this warning, one can usually include `check = FALSE`\n",
2136+
" (potentially in the control list) for the algorithm or as\n",
2137+
" an argument to `setupMargNodes`.")
2138+
if(!getNimbleOption('includeUnneededLatentsForMarg'))
2139+
randomEffectsNodes <- setdiff(randomEffectsNodes, reCheck)
21342140
}
21352141
}
21362142
}
@@ -2151,10 +2157,11 @@ setupMargNodes <- function(model, paramNodes, randomEffectsNodes, calcNodes,
21512157
errorNodes <- paste0(head(calcCheck, n = 4), sep = "", collapse = ", ")
21522158
if(length(calcCheck) > 4) errorNodes <- paste(errorNodes, "...")
21532159
messageIfVerbose(" [Warning] There are some model nodes that look like they should be\n",
2154-
" included in the `calcNodes` for Laplace or AGHQ approximation because\n",
2160+
" included in the `calcNodes` for marginalization algorithms because\n",
21552161
" they are dependencies of some `randomEffectsNodes`: ", errorNodes, ".\n",
2156-
" To silence this warning, include `check = FALSE` in the control list\n",
2157-
" to `buildLaplace` or as an argument to `setupMargNodes`.")
2162+
" To silence this warning, one can usually include `check = FALSE`\n",
2163+
" (potentially in the control list) for the algorithm or as\n",
2164+
" an argument to `setupMargNodes`.")
21582165
}
21592166
# Second check is for calcNodes that look unnecessary
21602167
# If some determ nodes between paramNodes and randomEffectsNodes are provided in calcNodes
@@ -2181,10 +2188,11 @@ setupMargNodes <- function(model, paramNodes, randomEffectsNodes, calcNodes,
21812188
outErrorNodes <- paste0(head(errorNodes, n = 4), sep = "", collapse = ", ")
21822189
if(length(errorNodes) > 4) outErrorNodes <- paste(outErrorNodes, "...")
21832190
messageIfVerbose(" [Warning] There are some `calcNodes` provided that look like\n",
2184-
" they are not needed for Laplace or AGHQ approximation over\n",
2191+
" they are not needed for marginalization over\n",
21852192
" the provided (or default) `randomEffectsNodes`: ", outErrorNodes, ".\n",
2186-
" To silence this warning, include `check = FALSE` in the control list\n",
2187-
" to `buildLaplace` or as an argument to `setupMargNodes`.")
2193+
" To silence this warning, one can usually include `check = FALSE`\n",
2194+
" (potentially in the control list) for the algorithm or as\n",
2195+
" an argument to `setupMargNodes`.")
21882196
}
21892197
}
21902198
# Finish step 4

packages/nimble/R/options.R

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,8 @@ nimOptimMethod("bobyqa",
227227
stripUnusedTypeDefs = TRUE,
228228
digits = NULL,
229229
enableVirtualNodeFunctionDefs = FALSE,
230-
checkDerivsArgs = TRUE
230+
checkDerivsArgs = TRUE,
231+
includeUnneededLatentsForMarg = FALSE
231232
)
232233
)
233234

packages/nimble/tests/testthat/test-setupMargNodes.R

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,3 +464,42 @@ test_that("regression tests that `getConditionallyIndependentSets` works with tr
464464
expect_length(m$getConditionallyIndependentSets(nodes = latents, givenNodes = given, unknownAsGiven = TRUE), 1)
465465
})
466466

467+
test_that("`setupMargNodes` handling of missing/extra latents", {
468+
code <- nimbleCode({
469+
for(i in 1:5)
470+
y[i] ~ dnorm(b[k[i]], 1)
471+
for(i in 1:3)
472+
b[i] ~ dnorm(mu,1)
473+
mu ~ dnorm(0,1)
474+
})
475+
## Only b[1] and b[2] have data dependents.
476+
m <- nimbleModel(code, data = list(y = rnorm(5)), constants = list(k = c(1,1,1,2,2)))
477+
478+
expect_silent(result <- setupMargNodes(m))
479+
expect_identical(result$randomEffectsNodes, c("b[1]","b[2]"))
480+
expect_identical(result$paramNodes, c("mu"))
481+
482+
## `b[2]` won't be marginalized over, and its dependent `y`s are not in calcNodes.
483+
## Could be user error, but we simply accept the user choices.
484+
expect_silent(result <- setupMargNodes(m, randomEffectsNodes = 'b[1]'))
485+
expect_identical(result$randomEffectsNodes, c("b[1]"))
486+
expect_identical(result$paramNodes, c("mu"))
487+
488+
## This gives a warning.
489+
## `b[2]` now in `paramNodes`, presumably since its `y`s are in calcNodes.
490+
expect_warning(result <- setupMargNodes(m, randomEffectsNodes = 'b[1]', calcNodes = c('b[1]','y')),
491+
"they should be marginalized over")
492+
expect_identical(result$randomEffectsNodes, c("b[1]"))
493+
expect_identical(result$paramNodes, c("mu", "b[2]"))
494+
495+
expect_message(result <- setupMargNodes(m, randomEffectsNodes = 'b'), "they are not needed for marginalization")
496+
expect_identical(result$randomEffectsNodes, c("b[1]", "b[2]"))
497+
expect_identical(result$paramNodes, c("mu"))
498+
499+
nimbleOptions(includeUnneededLatentsForMarg = TRUE)
500+
expect_message(result <- setupMargNodes(m, randomEffectsNodes = 'b'), "they are not needed for marginalization")
501+
expect_identical(result$randomEffectsNodes, c("b[1]", "b[2]", "b[3]"))
502+
expect_identical(result$paramNodes, c("mu"))
503+
## Note `b[3]` is not in `calcNodes` because `predictiveNodes` are excluded.
504+
})
505+

0 commit comments

Comments
 (0)