Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@
## 2026-07-14 - Matrix Cross Product Optimization
**Learning:** In R, matrix multiplication of the form `t(X) %*% Y` explicitly allocates memory for the transposed matrix. Using the optimized base function `crossprod(X, Y)` avoids this allocation.
**Action:** Always replace `t(X) %*% Y` with `crossprod(X, Y)` for faster and more memory-efficient cross-product calculations.
## 2026-10-31 - ifelse Evaluation Overhead
**Learning:** In R codebases, `ifelse` evaluates both true and false branches entirely before subsetting, and drops vector names and attributes which creates overhead.
**Action:** Optimize `ifelse` by using explicitly preallocated assignments like `res <- Y * 0` combined with subsetting `res[cond] <- ...` to significantly boost performance.
33 changes: 28 additions & 5 deletions R/llcont.R
Original file line number Diff line number Diff line change
Expand Up @@ -150,23 +150,40 @@ llcont.hurdle <- function(x, ...) {
zeroPoisson <- function(parms) {
mu <- as.vector(exp(Z %*% parms + offsetz))
loglik0 <- -mu
Y0 * weights * loglik0 + ifelse(Y1, weights * log(1 - exp(loglik0)), 0)
## Bolt: replaced ifelse with explicitly preallocated vector assignment for performance
res <- Y * 0
if (any(Y1)) {
w_sub <- if (length(weights) == 1) weights else weights[Y1]
res[Y1] <- w_sub * log(1 - exp(loglik0[Y1]))
}
Comment on lines +154 to +158
Y0 * weights * loglik0 + res
}

countPoisson <- function(parms) {
mu <- Y1 * as.vector(exp(X %*% parms + offsetx))
loglik0 <- -mu
loglik1 <- Y1 * dpois(Y, lambda = mu, log = TRUE)
Y1 * weights * loglik1 - ifelse(Y1, weights * log(1 - exp(loglik0)), 0)
## Bolt: replaced ifelse with explicitly preallocated vector assignment for performance
res <- Y * 0
if (any(Y1)) {
w_sub <- if (length(weights) == 1) weights else weights[Y1]
res[Y1] <- w_sub * log(1 - exp(loglik0[Y1]))
}
Y1 * weights * loglik1 - res
}

zeroNegBin <- function(parms) {
mu <- as.vector(exp(Z %*% parms[1:kz] + offsetz))
theta <- exp(parms[kz + 1])
loglik0 <- suppressWarnings(dnbinom(0, size = theta,
mu = mu, log = TRUE))
Y0 * weights * loglik0 +
ifelse(Y1, weights * log(1 - exp(loglik0)), 0)
## Bolt: replaced ifelse with explicitly preallocated vector assignment for performance
res <- Y * 0
if (any(Y1)) {
w_sub <- if (length(weights) == 1) weights else weights[Y1]
res[Y1] <- w_sub * log(1 - exp(loglik0[Y1]))
}
Y0 * weights * loglik0 + res
}

countNegBin <- function(parms) {
Expand All @@ -176,7 +193,13 @@ llcont.hurdle <- function(x, ...) {
mu = mu, log = TRUE))
loglik1 <- suppressWarnings(dnbinom(Y, size = theta,
mu = mu, log = TRUE))
ifelse(Y1, weights * loglik1 - weights * log(1 - exp(loglik0)), 0)
## Bolt: replaced ifelse with explicitly preallocated vector assignment for performance
res <- Y * 0
if (any(Y1)) {
w_sub <- if (length(weights) == 1) weights else weights[Y1]
res[Y1] <- w_sub * loglik1[Y1] - w_sub * log(1 - exp(loglik0[Y1]))
}
res
}
Comment on lines +202 to 203

zeroGeom <- function(parms) zeroNegBin(c(parms, 0))
Expand Down
Loading