--- title: "Speed Comparisons" output: rmarkdown::html_vignette: toc: yes vignette: > %\VignetteIndexEntry{Speed Comparisons} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- \newcommand{\QQ}{\mathbf{Q}} \newcommand{\RR}{\mathbf{R}} \newcommand{\xx}{\mathbf{x}} ```{r setup, include = FALSE} # knitr options knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) run.comparisons <- FALSE set.seed(1234) ``` Here are speed and estimate comparisons of EM, BFGS with the Kalmon filter (no gradient functions), TMB and Tim Clines original code. ## Example data ```{r} library(MARSS) library(dplyr) data(lakeWAplankton, package = "MARSS") phytoplankton <- c("Cryptomonas", "Diatoms", "Greens", "Unicells", "Other.algae") dat <- as.data.frame(lakeWAplanktonTrans) |> subset(Year >= 1980 & Year <= 1989) |> subset(select=phytoplankton) |> t() |> MARSS::zscore() ``` ## Fit models without covariates ```{r} R <- "unconstrained" mod.list <- list(R=R, m=1, tinitx=1) ``` Fit with MARSS with EM or optim and BFGS. ```{r} m1 <- MARSS(dat, model=mod.list, form='dfa', z.score=FALSE, silent = TRUE) m2 <- MARSS(dat, model=mod.list, form='dfa', z.score=FALSE, silent = TRUE, method="BFGS") ``` Fit with TMB. ```{r} library(marssTMB) m3 <- dfaTMB(dat, model=list(m=1, R=R)) m4 <- MARSS(dat, model=mod.list, form='dfa', method="TMB", silent=TRUE) m5 <- MARSS(dat, model=mod.list, method="BFGS_TMB", form='dfa', silent=TRUE) ``` ### Log likelihoods ```{r echo=FALSE} df <- data.frame(name=c("MARSS-EM", "MARSS-BFGS", "dfaTMB-nlminb", "MARSS_tmb-nlminb", "MARSS_tmb-optim-BFGS"), logLik=c(m1$logLik, m2$logLik, m3$logLik, m4$logLik, m5$logLik)) knitr::kable(df) ``` ### Compare parameter estimates ```{r echo=FALSE} library(tidyr) library(ggplot2) # BFGS was swapped in sign so just mult by -1 adj <- 1 if(sign(coefficients(m1)$Z[1]) != sign(coefficients(m2)$Z[1])) adj <- -1 pars <- data.frame( name = c(paste0("R", rownames(coefficients(m1)$R)), paste0("Z", rownames(coefficients(m1)$Z))), EM = c(coefficients(m1)$R, coefficients(m1)$Z), BFGS = c(coefficients(m2)$R, adj*coefficients(m2)$Z), TMB1 = c(as.vector(m3$Estimates$R[lower.tri(m3$Estimates$R,diag=TRUE)]), as.vector(m3$Estimates$Z)), TMB2 = c(coefficients(m4)$R, coefficients(m4)$Z)) pars <- pars %>% tidyr::pivot_longer(!name, names_to = "model") dodge <- position_dodge(width=0.5) ggplot(pars, aes(x=name, y=value, col=model)) + geom_point(position=dodge) + ggtitle("same estimates") ``` ## Add example with covariates For `form="dfa"`, pass in covariates with `covariates=xyz`. If using the default model form (not dfa), then pass in covariates with `model$d` or `model$c`. ### Fit model ```{r} # use a simpler R R <- "diagonal and unequal" mod.list2 <- list(m=3, R=R, tinitx=1) # add a temperature covariate temp <- as.data.frame(lakeWAplanktonTrans) |> subset(Year >= 1980 & Year <= 1989) |> subset(select=Temp) covar <- t(temp) |> zscore() m <- MARSS(dat, model=mod.list2, form="dfa", covariates=covar, silent = TRUE, z.score = FALSE, method="TMB", fit=FALSE) t6 <- system.time(m6 <- marssTMB:::MARSSfit.TMB(m, fun=1)) t6c <- system.time(m6c <- marssTMB:::MARSSfit.TMB(m, fun=2)) t7 <- system.time(m7 <- MARSS(dat, model=mod.list2, form="dfa", covariates=covar, silent = TRUE, control=list(maxit=10000), z.score = FALSE)) t6b <- system.time(m6b <- dfaTMB(dat, model=list(m=3, R=R), EstCovar = TRUE, Covars = covar)) ``` Add a 2nd covariate ```{r} TP <- as.data.frame(lakeWAplanktonTrans) |> subset(Year >= 1980 & Year <= 1989) |> subset(select=TP) covar <- rbind(covar, t(TP)) |> zscore() m <- MARSS(dat, model=mod.list2, form="dfa", covariates=covar, silent = TRUE, z.score=FALSE, method="TMB", fit=FALSE) t8 <- system.time(m8 <- marssTMB:::MARSSfit.TMB(m, fun=1)) t8c <- system.time(m8c <- marssTMB:::MARSSfit.TMB(m, fun=2)) t9 <- system.time(m9 <- MARSS(dat, model=mod.list2, form="dfa", covariates=covar, silent = TRUE, control=list(maxit=10000), z.score=FALSE)) t8b <- system.time(m8b <- dfaTMB(dat, model=list(m=3, R=R), EstCovar = TRUE, Covars = covar)) ``` ### Compare time and log likelihoods The MARSS_tmb-1 uses the splitting approach for the diag versus the chol, while tmb-2 is using just the chol. Clearly splitting is faster. ```{r echo=FALSE} df <- data.frame(name=rep(c("MARSS-EM", "MARSS_tmb-1", "MARSS_tmb-2", "dfaTMB"), 2), num_covar = rep(1:2, each = 4), time=c(t7[1], t6[1], t6c[1], t6b[1], t9[1], t8[1], t8c[1], t8b[1]), logLik=c(m7$logLik, m6$logLik, m6c$logLik, m6b$logLik, m9$logLik, m8$logLik, m8c$logLik, m8b$logLik)) knitr::kable(df) ``` ### Compare parameter estimates ```{r echo=FALSE} library(tidyr) n1 <- length(coef(m6, type="vector")) n2 <- length(coef(m8, type="vector")) pars <- data.frame( name = c(rep(names(coef(m6, type="vector")), 2), rep(names(coef(m8, type="vector")), 2)), ncovar = as.factor(c(rep(c(1, 1), each=n1), rep(c(2, 2), each = n2))), model = c(rep(c("EM", "TMB"), each=n1), rep(c("EM", "TMB"), each = n2)), value = c(coef(m6, type="vector"), coef(m7, type="vector"), coef(m8, type="vector"), coef(m9, type="vector"))) ``` ```{r echo=FALSE} library(ggplot2) dodge <- position_dodge(width=1) ggplot(pars, aes(x=name, y=value, col=model)) + geom_point(position=dodge) + coord_flip() + facet_wrap(~ncovar) + ggtitle("estimates should be the same (roughly)") ``` ## More MARSS models ```{r echo = FALSE} dat <- t(harborSealWA) dat <- dat[2:4, ] # remove the year row contrl <- list(maxit = 10000) mod <- list(Z = matrix(1, 3, 1), R = "diagonal and equal") fit1 <- MARSS(dat, model = mod, control = contrl, silent = TRUE) fit2 <- MARSS(dat, model = mod, control = contrl, silent = TRUE, method="BFGS") fit3 <- MARSS(dat, model = mod, control = contrl, silent = TRUE, method="TMB") ``` ```{r} cat("mod <- list(Z = matrix(1, 3, 1), R = 'diagonal and equal')\n") c(EM=fit1$logLik, BFGS=fit2$logLik, TMB=fit2$logLik) ``` ```{r echo=FALSE} fit1 <- MARSS(dat, control = contrl, silent = TRUE) fit2 <- MARSS(dat, control = contrl, silent = TRUE, method="BFGS") fit3 <- MARSS(dat, control = contrl, silent = TRUE, method="TMB") ``` ```{r} cat("MARSS(dat)\n") c(EM=fit1$logLik, BFGS=fit2$logLik, TMB=fit2$logLik) ``` ```{r echo=FALSE} mod <- list(Q = "unconstrained") fit1 <- MARSS(dat, model = mod, control = contrl, silent = TRUE) fit2 <- MARSS(dat, model = mod, control = contrl, silent = TRUE, method="BFGS") fit3 <- MARSS(dat, model = mod, control = contrl, silent = TRUE, method="TMB") ``` ```{r} cat("mod <- list(Q = 'unconstrained')\n") c(EM=fit1$logLik, BFGS=fit2$logLik, TMB=fit2$logLik) ``` ```{r echo=FALSE} mod <- list(U = matrix(c("N", "S", "S"))) fit1 <- MARSS(dat, model = mod, control = contrl, silent = TRUE) fit2 <- MARSS(dat, model = mod, control = contrl, silent = TRUE, method="BFGS") fit3 <- MARSS(dat, model = mod, control = contrl, silent = TRUE, method="TMB") ``` ```{r} cat('list(U = matrix(c("N", "S", "S")))\n') c(EM=fit1$logLik, BFGS=fit2$logLik, TMB=fit2$logLik) ``` ```{r echo=FALSE} mod <- list(U = matrix(list("N", "N", 0), 3, 1), R = diag(0.01, 3)) fit1 <- MARSS(dat, model = mod, control = contrl, silent = TRUE) fit2 <- MARSS(dat, model = mod, control = contrl, silent = TRUE, method="BFGS") fit3 <- MARSS(dat, model = mod, control = contrl, silent = TRUE, method="TMB") ``` ```{r} cat('list(U = matrix(list("N", "N", 0), 3, 1), R = diag(0.01, 3))\n') c(EM=fit1$logLik, BFGS=fit2$logLik, TMB=fit2$logLik) ``` ```{r echo=FALSE} Z <- matrix(c(1, 1, 0, 0, 0, 1), 3, 2) colnames(Z) <- c("N", "S") mod <- list(Z = Z, Q = "diagonal and equal", R = "diagonal and unequal", U = "equal") fit1 <- MARSS(dat, model = mod, control = contrl, silent = TRUE) fit2 <- MARSS(dat, model = mod, control = contrl, silent = TRUE, method="BFGS") fit3 <- MARSS(dat, model = mod, control = contrl, silent = TRUE, method="TMB") ``` ```{r} cat('list(Z = Z, Q = "diagonal and equal", R = "diagonal and unequal", U = "equal")\n') c(EM=fit1$logLik, BFGS=fit2$logLik, TMB=fit2$logLik) ``` ## Run some time comparisons ```{r include=FALSE} # no covars if(!run.comparisons){ load("dfa-time-comparisons.rda") }else{ df <- c() mods <- list() for(R in c("diagonal and equal", "diagonal and unequal", "unconstrained")){ for(m in 1:3){ mod <- list(m = m, R = R, tinitx=1) tfit <- system.time(fit <- MARSS(dat, model=mod, form="dfa", control=list(maxit=10000))) df <- rbind(df, data.frame(fun="MARSS", opt.function="EM", m=m, R=R, ncovar = 0, time=tfit[1], logLik=fit$logLik, convergence = fit$convergence)) mods <- c(mods, list(fit)) tfit <- system.time(fit <- MARSS(dat, model=mod, form="dfa", method="BFGS")) df <- rbind(df, data.frame(fun="MARSS", opt.function="BFGS", m=m, R=R, ncovar = 0, time=tfit[1], logLik=fit$logLik, convergence = fit$convergence)) mods <- c(mods, list(fit)) tfit <- system.time(fit <- MARSS(dat, model=mod, form="dfa", method="TMB")) df <- rbind(df, data.frame(fun="TMB", opt.function="nlminb", m=m, R=R, ncovar = 0, time=tfit[1], logLik=fit$logLik, convergence = fit$convergence)) mods <- c(mods, list(fit)) } } } ``` ```{r include=FALSE} # with covars if(!run.comparisons){ load("dfa-time-comparisons.rda") }else{ for(R in c("diagonal and equal", "diagonal and unequal", "unconstrained")){ for(m in 1:3){ mod <- list(m = m, R = R, tinitx=1) tfit <- system.time(fit <- MARSS(dat, model=mod, form="dfa", covariates=covar, control=list(maxit=10000))) df <- rbind(df, data.frame(fun="MARSS", opt.function="EM", m=m, R=R, ncovar=2, time=tfit[1], logLik=fit$logLik, convergence = fit$convergence)) mods <- c(mods, list(fit)) tfit <- system.time(fit <- MARSS(dat, model=mod, form="dfa", covariates=covar, method="BFGS")) df <- rbind(df, data.frame(fun="MARSS", opt.function="BFGS", m=m, R=R, ncovar=2, time=tfit[1], logLik=fit$logLik, convergence = fit$convergence)) mods <- c(mods, list(fit)) tfit <- system.time(fit <- MARSS(dat, model=mod, form="dfa", covariates=covar, method="TMB")) df <- rbind(df, data.frame(fun="TMB", opt.function="nlminb", m=m, R=R, ncovar=2, time=tfit[1], logLik=fit$logLik, convergence = fit$convergence)) mods <- c(mods, list(fit)) } } } save(df, mods, file="dfa-time-comparisons2.rda") ``` ```{r include=FALSE} df2 <- df |> mutate(mod = paste0(fun, "-", opt.function)) |> mutate(ncovar = as.factor(ncovar)) |> group_by(ncovar) ggplot(df2, aes(alpha=ncovar, fill=mod, y=time, x=m)) + geom_bar(stat="identity", position="dodge", color="black") + facet_wrap(~R, scales = "free_y") + scale_y_continuous() + ggtitle("TMB is faster than MARSS EM") ``` ```{r} df2 <- df %>% mutate(mod = paste0(fun, "_", opt.function)) |> select(mod, time, m, R, ncovar) %>% pivot_wider(id_cols=c(m, R, ncovar), names_from = mod, values_from = time) |> mutate( MARSS_EM = MARSS_EM/TMB_nlminb, MARSS_BFGS = MARSS_BFGS/TMB_nlminb, TMB_nlminb = TMB_nlminb/TMB_nlminb ) |> mutate(ncovar = as.factor(ncovar)) |> pivot_longer(cols = 4:6) mean.improv <- df2 |> subset(name != "TMB_nlminb") |> summarize(mean = mean(value)) ggplot(df2, aes(alpha=ncovar, fill=name, y=value, x=m)) + geom_bar(stat="identity", position="dodge", color="black") + facet_wrap(~R, scales = "free_y") + ylab("Time relative to TMB") + scale_y_continuous() + ggtitle(paste("speed relative to TMB.", round(mean.improv, digits=2), "fold improvement")) ``` ```{r} df2 <- df |> mutate(mod = paste0(fun, "-", opt.function)) df2$ncovar <- as.factor(df2$ncovar) ggplot(df2, aes(col=ncovar, y=logLik, x=m, shape=mod)) + geom_point(position=position_dodge(width=0.3)) + facet_wrap(~R, scales = "free_y") + scale_y_continuous() + ggtitle("logLik comparison") ```