
Create and Integrate Your Custom Model
FishSET Development Team
2026-09-04
Source:vignettes/likelihood-template.Rmd
likelihood-template.RmdIntroduction
The FishSET R package provides a robust framework for discrete choice
modeling (DCM), utilizing RTMB (R Template Model Builder)
for automatic differentiation and optimized parameter estimation. While
functions like fishset_design() and
fishset_fit() offer a solid foundation for conditional
logit and expected profit models, we recognize that specialized research
often requires custom model structures.
To maximize computational performance and ensure compatibility with
downstream FishSET tools (like our policy function and database
loggers), custom models must be integrated directly into the
RTMB framework. We encourage users to develop their own
objective functions and submit them to be merged into the official
FishSET toolkit.
This vignette outlines the workflow for developing a custom model by
leveraging existing FishSET data pipelines and writing an
RTMB negative log-likelihood function. Reach out to our
team at nmfs.fishset@noaa.gov with
any questions.
Development guidelines
Fork the FishSET repo and clone the forked repository to your computer. More information on “forking” repositories and the GitHub workflow can be found here.
Open the FishSET_RPackage.Rproj file.
Develop your custom data processing and
RTMBobjective function following the steps below.Modify
fishset_fit.Randfishset_design.Rlocally to include your model.Review and test code on local datasets.
Commit, push changes, and submit a pull request.
Step 1: Extract Formatted Data
Rather than formatting data manually, rely on the built-in
format_model_data() function to aggregate your main
dataset, distance matrices, expectations, and gridded data.
This function saves the combined dataset as a .qs2 or
.rds file in the Models/FormattedData
directory inside your project folder. Load this dataset into your
environment to begin designing your model matrix.
# Example: Loading formatted data
project_dir <- file.path(locproject(), "MyProject")
formatted_dir <- file.path(project_dir, "Models", "FormattedData")
my_data <- qs2::qs_read(file.path(formatted_dir, "MyProjectLongFormatData.qs2"))Step 2: Mock the Design Object
The fishset_fit() function expects a specific list
structure, of class fishset_design, to perform estimations.
If your model requires predictors or matrix shapes that the standard
fishset_design() function cannot currently generate, write
a custom data processing script that builds a list mirroring this
required structure.
Your design object must contain the following elements:
y: A numeric vector indicating the binary choice variable (0 or 1).X: A sparse model matrix containing your formatted predictors.settings: A list containing dimensions such asN_obs(number of observations),J_alts(number of alternatives), andK_vars(number of parameters).ids: A list containing unique observation IDs and zone identifiers to be used for post-estimation predictions.
Step 3: Write the RTMB Objective Function
The core of FishSET’s speed relies on RTMB::MakeADFun
and stats::nlminb. To integrate your model, you must write
a negative log-likelihood (NLL) function compatible with this
framework.
Inside fishset_fit(), parameter estimation relies on two
main components passed to RTMB:
data_list: A list containing all known data (Xmatrix, observations, dimensions).start_pars: A list of initial parameter values to be optimized (e.g.,betas).
Your custom function must extract these elements using
RTMB::getAll() and calculate the NLL.
Template: Custom Objective Function
# Example custom model template for an RTMB NLL function
nll_func_custom <- function(pars) {
# Unpack data_list and parameters into the function environment
RTMB::getAll(data_list, pars)
# Perform matrix multiplication (e.g., sparse design matrix %*% parameters)
v <- X %*% betas
# Extract utility of chosen alternatives
v_chosen <- v[chosen_lin_idx]
# Reshape utility vector for log-sum-exp calculations
dim(v) <- c(J_alts, N_obs)
# Calculate log-sum-exp (consider robust numeric scaling if necessary)
log_sum_exp <- log(RTMB::colSums(exp(v)))
# Calculate the negative log-likelihood
nll <- -sum(v_chosen - log_sum_exp)
return(nll)
}Important Note: When writing an RTMB
function, you must use RTMB compatible math functions
(e.g., RTMB::dnorm, RTMB::rowSums) rather than
standard base R variants to ensure automatic differentiation works
properly.
Step 4: Integrate and Submit
Once you have a working design object and an RTMB
objective function, you can integrate your code into the package.
Modify
fishset_fit.R: Add a newif/elseblock within thefishset_fit()function to handle your custom model type. Set up your uniquedata_listandstart_pars, and define your customnll_funcwithin this block.Standardize Outputs: Ensure your code allows
RTMB::MakeADFunandstats::nlminbto run, producing a result formatted as classfishset_fit. This guarantees your model outputs a coefficient table, diagnostics, AIC/BIC, and predicted probabilities that seamlessly integrate with the project database.Submit a Pull Request: Give the Pull Request an informative name and provide a brief description of the mathematical structure of your model. Our team will review the code and notify you of any requested changes prior to merging.