Skip to contents

Overview

This vignette demonstrates how to conduct Bayesian inference for the CCSBT stock assessment model using the adnuts package and the sparse inverse Hessian generated by RTMB. We illustrate the setup, sampling procedure, and posterior diagnostics.

Setup and Optimization

# remotes::install_github("janoleko/RTMBdist")
# remotes::install_github("andrjohns/StanEstimators")
# remotes::install_github("Cole-Monnahan-NOAA/adnuts")
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
##  dplyr     1.1.4      readr     2.1.5
##  forcats   1.0.0      stringr   1.5.1
##  ggplot2   3.5.2      tibble    3.3.0
##  lubridate 1.9.4      tidyr     1.3.1
##  purrr     1.1.0     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
##  dplyr::filter() masks stats::filter()
##  dplyr::lag()    masks stats::lag()
##  Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
## Loading required package: RTMB
## Loading required package: RTMBdist
## 
## Attaching package: 'RTMBdist'
## 
## The following objects are masked from 'package:RTMB':
## 
##     dbeta, dnbinom2
## 
## The following objects are masked from 'package:stats':
## 
##     dbeta, pnbinom, pt
## 
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
## Loading required package: StanEstimators
theme_set(theme_bw())

attach(data_csv1)
lr <- data_labrep1

data <- list(
  last_yr = 2022, age_increase_M = 25,
  length_m50 = 150, length_m95 = 180, 
  catch_UR_on = 0, catch_surf_case = 1, catch_LL1_case = 1, 
  scenarios_surf = scenarios_surface, scenarios_LL1 = scenarios_LL1,
  removal_switch_f = c(0, 0, 0, 1, 0, 0), # 0=harvest rate, 1=direct removals
  sel_min_age_f = c(2, 2, 2, 8, 6, 0, 2),
  sel_max_age_f = c(17, 9, 17, 21, 25, 7, 17),
  sel_end_f = c(1, 0, 1, 1, 1, 0, 1),
  sel_LL1_yrs = c(1952, 1957, 1961, 1965, 1969, 1973, 1977, 1981, 1985, 1989, 1993, 1997, 2001, 2006, 2007, 2008, 2011, 2014, 2017, 2020),
  sel_LL2_yrs = c(1969, 2001, 2005, 2008, 2011, 2014, 2017, 2020),
  sel_LL3_yrs = c(1954, 1961, 1965, 1969, 1970, 1971, 2005, 2006, 2007),
  sel_LL4_yrs = c(1953),
  sel_Ind_yrs = c(1976, 1995, 1997, 1999, 2002, 2004, 2006, 2008, 2010, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022),
  sel_Aus_yrs = c(1952, 1969, 1973, 1977, 1981, 1985, 1989, 1993, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022),
  sel_CPUE_yrs = c(1969, 1973, 1977, 1981, 1985, 1989, 1993, 1997, 2001, 2006, 2007, 2008, 2011, 2014, 2017, 2020),
  af_switch = 9,
  lf_switch = 9, lf_minbin = c(1, 1, 1, 11),
  cpue_switch = 1, cpue_a1 = 5, cpue_a2 = 17,
  aerial_switch = 4, aerial_tau = 0.3, 
  troll_switch = 1, 
  pop_switch = 1, 
  hsp_switch = 1, hsp_false_negative = 0.7467647, 
  gt_switch = 1,
  tag_switch = 1, tag_var_factor = 1.82
)

data <- get_data(data_in = data)
parameters <- get_parameters(data)
map <- get_map(data, parameters)
data$priors <- get_priors(parameters)
evaluate_priors(parameters, data$priors)
## [1] 61.79457
obj <- MakeADFun(func = cmb(sbt_model, data), parameters = parameters, map = map)
bnd <- get_bounds(obj, parameters)
unique(names(obj$par))
##  [1] "par_log_B0"        "par_log_m4"        "par_log_m30"      
##  [4] "par_log_cpue_q"    "par_log_troll_tau" "par_log_sel_1"    
##  [7] "par_log_sel_2"     "par_log_sel_3"     "par_log_sel_5"    
## [10] "par_log_sel_6"     "par_log_sel_7"     "par_rdev_y"
obj$fn()
## [1] 24545.05
control <- list(eval.max = 10000, iter.max = 10000)

opt <- nlminb(start = obj$par, objective = obj$fn, gradient = obj$gr, hessian = obj$he,
              lower = bnd$lower, upper = bnd$upper, control = control)
## outer mgc:  79.91711 
## outer mgc:  287.5945 
## outer mgc:  488.3391
## Warning in nlminb(start = obj$par, objective = obj$fn, gradient = obj$gr, :
## NA/NaN function evaluation
## outer mgc:  99.84999 
## outer mgc:  6027.005
## Warning in nlminb(start = obj$par, objective = obj$fn, gradient = obj$gr, :
## NA/NaN function evaluation
## Warning in nlminb(start = obj$par, objective = obj$fn, gradient = obj$gr, :
## NA/NaN function evaluation
## outer mgc:  10165.79 
## outer mgc:  2230.533 
## outer mgc:  150.7786 
## outer mgc:  526.2794
## Warning in nlminb(start = obj$par, objective = obj$fn, gradient = obj$gr, :
## NA/NaN function evaluation
## outer mgc:  79.14928 
## outer mgc:  612.4936
## Warning in nlminb(start = obj$par, objective = obj$fn, gradient = obj$gr, :
## NA/NaN function evaluation
## outer mgc:  19.18882 
## outer mgc:  5.25693 
## outer mgc:  6.750307 
## outer mgc:  10.69174 
## outer mgc:  5.431459 
## outer mgc:  5.431454
opt <- nlminb(start = opt$par, objective = obj$fn, gradient = obj$gr, hessian = obj$he,
              lower = bnd$lower, upper = bnd$upper, control = control)
## outer mgc:  5.431454
obj$par <- opt$par

Bayesian inferece

MCMC can be done using the sample_snuts function from the adnuts package.

mcmc <- sample_snuts(
  obj = obj, metric = "auto", init = "last.par.best",
  # iter = 1000, warmup = 750, chains = 4, cores = 4, 
  num_samples = 10, num_warmup = 5, chains = 2, cores = 2, 
  control = list(adapt_delta = 0.99), globals = sbt_globals()
)
## Optimizing...
## Warning in .get_inputs(obj = obj, skip_optimization = skip_optimization, : Some
## standard errors estimated to be NaN, filling with dummy values so unit metric
## works. The 'mle' slot will be wrong so do not use it
## Rebuilding RTMB obj without random effects...
## Warning in sqrt(M): NaNs produced
## unit metric selected b/c Qinv was not positive definite
## log-posterior at inits=-23641.289; at conditional mode=-23641.289
## Starting MCMC sampling...
## Preparing parallel workspace...
## Chain 2: Gradient evaluation took 0.010588 seconds
## Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 105.88 seconds.
## Chain 2: Adjust your expectations accordingly!
## Chain 2: Iteration:  1 / 15 [  6%]  (Warmup)
## Chain 1: Gradient evaluation took 0.010734 seconds
## Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 107.34 seconds.
## Chain 1: Adjust your expectations accordingly!
## Chain 1: Iteration:  1 / 15 [  6%]  (Warmup)
## Chain 1: Iteration:  6 / 15 [ 40%]  (Sampling)
## Chain 1: Iteration: 15 / 15 [100%]  (Sampling)
## Chain 1:  Elapsed Time: 6.925 seconds (Warm-up)
## Chain 1:                0.186 seconds (Sampling)
## Chain 1:                7.111 seconds (Total)
## Chain 2: Iteration:  6 / 15 [ 40%]  (Sampling)
## Chain 2: Iteration: 15 / 15 [100%]  (Sampling)
## Chain 2:  Elapsed Time: 7.048 seconds (Warm-up)
## Chain 2:                0.12 seconds (Sampling)
## Chain 2:                7.168 seconds (Total)
## Registered S3 method overwritten by 'quantmod':   method            from   as.zoo.data.frame zoo  Attaching package: ‘RTMBdist’ The following objects are masked from ‘package:RTMB’:     dbeta, dnbinom2 The following objects are masked from ‘package:stats’:     dbeta, pnbinom, pt
## 28 of 30 (93.33%) iterations ended with a divergence.
## These divergent transitions indicate that HMC is not fully able to explore the posterior distribution.
## Try increasing adapt_delta closer to 1.
## If this doesn't remove all divergences, try to reparameterize the model.
## 
## 
## Model 'RTMB' has 1553 pars, and was fit using NUTS with a 'unit' metric
## 2 chain(s) of 15 total iterations (5 warmup) were used
## Average run time per chain was 7.14 seconds 
## Minimum ESS=10 (50%), and maximum Rhat=1
## !! Warning: Signs of non-convergence found. Do not use for inference !!
## There were 20 divergences after warmup

Posterior Diagnostics

# library(decamod)
# plot_sampler_params(fit = mcmc, plot = TRUE)
# decamod::pairs_rtmb(fit = mcmc, order = "mismatch", pars = 1:5)
# pairs_rtmb(fit = mcmc, order = "divergent", pars = 1:5)
# plot_uncertainties(fit = mcmc, log = TRUE, plot = TRUE)

Saving Results

# save(data, parameters, obj, opt, mcmc, file = "mcmc_0divergences.rda")

Notes and Next Steps

  • Adjust pars in pairs_rtmb() to inspect specific parameter blocks.
  • If convergence is slow reduce adapt_delta.
  • If divergent transitions persist, increase warmup, adapt_delta, or use block-wise adaptation.
  • Consider thinning for large posterior draws before plotting.