2022-10-06 12:25:40 +00:00
|
|
|
#' Fitting Generalized Multi-Linear Models
|
|
|
|
#'
|
|
|
|
#' @export
|
|
|
|
GMLM <- function(...) {
|
|
|
|
stop("Not Implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
#' @export
|
|
|
|
GMLM.default <- function(X, Fy, sample.axis = 1L,
|
|
|
|
family = "normal",
|
|
|
|
...,
|
|
|
|
eps = sqrt(.Machine$double.eps),
|
|
|
|
logger = NULL
|
|
|
|
) {
|
|
|
|
stopifnot(exprs = {
|
2022-10-11 17:09:55 +00:00
|
|
|
length(sample.axis) == 1L
|
|
|
|
(1L <= sample.axis) && (sample.axis <= length(dim(X)))
|
2022-10-06 12:25:40 +00:00
|
|
|
(dim(X) == dim(Fy))[sample.axis]
|
|
|
|
})
|
|
|
|
|
|
|
|
# rearrange `X`, `Fy` such that the last axis enumerates observations
|
|
|
|
axis.perm <- c(seq_along(dim(X))[-sample.axis], sample.axis)
|
|
|
|
X <- aperm(X, axis.perm)
|
|
|
|
Fy <- aperm(Fy, axis.perm)
|
|
|
|
|
|
|
|
# setup family specific GLM (pseudo) "inverse" link
|
|
|
|
family <- make.gmlm.family(family)
|
|
|
|
|
|
|
|
# wrap logger in callback for NAGD
|
|
|
|
callback <- if (is.function(logger)) {
|
|
|
|
function(iter, params) {
|
|
|
|
do.call(logger, c(list(iter), params))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-31 14:14:58 +00:00
|
|
|
# optimize likelihood using Nesterov Excelerated Gradient Descent
|
2022-10-06 12:25:40 +00:00
|
|
|
params.fit <- NAGD(
|
|
|
|
fun.loss = function(params) {
|
2023-11-14 13:35:43 +00:00
|
|
|
# scaled negative log-likelihood
|
2022-10-06 12:25:40 +00:00
|
|
|
# eta1 alphas Omegas
|
|
|
|
family$log.likelihood(X, Fy, params[[1]], params[[2]], params[[3]])
|
|
|
|
},
|
|
|
|
fun.grad = function(params) {
|
2023-11-14 13:35:43 +00:00
|
|
|
# gradient of the scaled negative log-likelihood
|
2022-10-06 12:25:40 +00:00
|
|
|
# eta1 alphas Omegas
|
|
|
|
family$grad(X, Fy, params[[1]], params[[2]], params[[3]])
|
|
|
|
},
|
2022-12-06 14:15:00 +00:00
|
|
|
params = family$initialize(X, Fy), # initial parameter estimates
|
2022-10-06 12:25:40 +00:00
|
|
|
fun.lincomb = function(a, lhs, b, rhs) {
|
|
|
|
list(
|
|
|
|
a * lhs[[1]] + b * rhs[[1]],
|
|
|
|
Map(function(l, r) a * l + b * r, lhs[[2]], rhs[[2]]),
|
|
|
|
Map(function(l, r) a * l + b * r, lhs[[3]], rhs[[3]])
|
|
|
|
)
|
|
|
|
},
|
|
|
|
fun.norm2 = function(params) {
|
|
|
|
sum(unlist(params)^2)
|
|
|
|
},
|
|
|
|
callback = callback,
|
|
|
|
...
|
|
|
|
)
|
|
|
|
|
|
|
|
structure(params.fit, names = c("eta1", "alphas", "Omegas"))
|
|
|
|
}
|