Thank you for contributing to our project.
This document needs quite a bit of work, but the section on Development Tools
should be useful -- please stay tuned ...
- the main system dependencies are R, git, and pandoc (latest versions are recommended, but not strictly required) -- there are other dependencies (e.g. curl, openssl), but the specific requirements will vary among platforms (see
dockerfile
for details on Debian Linux) - as a convenience you can install all of the R and tex package dependencies using
make dependencies
or running/misc/dependencies.R
- to re-install the package, including re-building and incorporating vignettes, use
make build-package
- If you modify function arguments, you should change the roxygen documentation accordingly. If you change the roxygen documentation, please use
make doc-update
to update the.Rd
files. - please test/check the package periodically as you go (use
make pkgcheck
andmake pkgtest
from the shell ordevtools::check()
anddevtools::test()
from within R). (Tests are also run on GitHub Actions; if you want to skip CI testing, e.g. for a trivial commit, put[skip ci]
somewhere in your commit message.) Please don't make a habit of pushing without testing. - To avoid whitespace diffs between versions/branches, automatically style the package with
make style
or runmisc/macpan_style.R
.make style
(or runningmisc/macpan_lint.R
) also creates a new file,misc/lints.csv
, which contains stylistic and other lints thatstyler
cannot automatically fix. - rebuild the
pkgdown
site using GitHub Actions: click "run workflow" on the link to rebuild. - Code that is used in the refactoring process should go in the top-level
refactor
folder. - Slow tests are skipped by default; this process is controlled by the presence of a
MACPAN_TEST_LEVEL
environment (shell) variable. Many of the test files contain this code:
testLevel <- if (nzchar(s <- Sys.getenv("MACPAN_TEST_LEVEL"))) as.numeric(s) else 1
which sets testLevel
to a default value of 1 unless the environment variable is found. You can set this environment variable outside of your R session (e.g. via export MACPAN_TEST_LEVEL=2
in a bash shell), or via Sys.setenv(MACPAN_TEST_LEVEL=2)
from within R. In principle this mechanism allows for a hierarchy of slowness; at present only 1 and >1 are distinguished.
- Currently one client -- the R client -- and hopefully/probably we will only ever have one client
- Currently two engines
- Original R
- TMB
- Stuff that goes in the engine:
- Indices
- Simulation steps
- Simulation output summarization (e.g. convolutions, differences)
- Objective function computation
- Stuff that goes in the client:
- Names
- Names of states
- Names of parameters (e.g. transmission rate)
- Names of loss functions (e.g. negative binomial, gaussian)
- Topology of flow among states
We try to be disciplined in how we maintain the C++
code, in that the correctness of this code can in principle be judged against this specification document. These specifications are versioned with the reference implementation for each version located in inst/tmb. The currently recommended version is saved in inst/tmb/recommended_spec_version.
The package itself can only use one of these reference implementations at a time. The default spec version can be set by copying the appropriate versioned cpp
file into src/McMasterPandemic.cpp
, and this can be accomplished using the following make
rule.
make src/McMasterPandemic.cpp
This make
rule copies the currently recommended version into src/McMasterPandemic.cpp
.
One may also switch the spec version being used during an R
session by using set_spec_version
function. For example, if executed from the project root directory the following line will update the spec version and associated C++
code being used.
set_spec_version('0.1.0', 'inst/tmb')
Note that this will also likely trigger compilation of the C++
code unless the objects, shared objects, and/or DLLs can be found in the same directory as the associated .cpp
file or some method of caching has been established. One may find the spec version that they are using at any time with the following function.
spec_version()
On the R
side one may also test for a particular spec version using the following family of functions: spec_ver_eq
, spec_ver_gt
, spec_ver_lt
, spec_ver_btwn
. Similarly, one may assert a certain spec version using the spec_check
and feature_check
functions.
TODO: describe global options, tmb_mode
, R_mode
, etc ...
TODO: describe compare_*
family of functions ...on
TODO: point to a help file (not yet written) that describes the flexmodel
class
TODO: describe the get_*
family of functions and how they should be used to create functions like rate_summary
-
flexmodel
- Use-cases:
- Deterministic simulation
- Use-cases:
-
flexmodel_obs_error
- Use-cases:
- Simulating with observation error
- Components:
observed$loss_params
- Use-cases:
-
flexmodel_obs_data
- Use-cases:
- Comparing simulations (without observation error) with observed data
- Components:
observed$data
- Use-cases:
-
flexmodel_obs_info
- Use-cases:
- Comparing simulations (with observation error) with observed data
- Components:
observed$loss_params
observed$data
- Use-cases:
-
flexmodel_to_calibrate
- Use-cases:
- Calibrating to observed data
- Comparing simulations (with observation error) with observed data
- Components:
observed$loss_params
observed$data
opt_params
and/oropt_tv_params
- Use-cases:
-
`flexmodel_calibrated
- Use-cases:
- Calibrating to observed data
- Comparing simulations (with observation error and parameter uncertainty) with observed data
- Use-cases:
-
flexmodel
base class -
flexmodel_to_calibrate
used to pass to an optimizer -
flexmodel_calibrated
used to simulate from a calibrated model -
flexmodel_for_forecasts
-
flexmodel_obs_error
-
flexmodel_optim
-
flexmodel_nlminb
-
flexmodel_bbmle
-
flexmodel_failed_calibration
S3 class vectors that appear in the package:
-
error type parameter tuning type use case
c( 'flexmodel')
c( 'flexmodel_scenario', 'flexmodel')
(this is not necessary)c('flexmodel_obs_error', 'flexmodel')
c('flexmodel_obs_error' 'flexmodel_to_calibrate', 'flexmodel')
c('flexmodel_obs_error', 'flexmodel_scenario', 'flexmodel')
c( 'flexmodel_bbmle', 'flexmodel_scenario', 'flexmodel')
(what's this??)c('flexmodel_obs_error', 'flexmodel_optim', 'flexmodel_calibrated', 'flexmodel')
c('flexmodel_obs_error', 'flexmodel_nlminb', 'flexmodel_calibrated', 'flexmodel')
c('flexmodel_obs_error', 'flexmodel_bbmle', 'flexmodel_calibrated', 'flexmodel')
c('flexmodel_obs_error', 'flexmodel_bbmle', 'flexmodel_scenario', 'flexmodel')
c( 'flexmodel_failed_calibration', 'flexmodel')
To identify McMasterPandemic-specific global options on the R
side we prefix their names with MP_
. For example, the MP_use_state_rounding
option is used to indicate whether the R engine should ever round the state variable. The defaults for all McMasterPandemic-specific options should be set in R/zzz.R.
make cleanobjects
make src/McMasterPandemic.cpp
make newinstall
There are several ways for the objective function to come back as a NaN. When this happens it can be used to run the simulate method within the TMB object. This simulate method might return a series of warnings, which can be useful for generating hypotheses about why the likelihood came out as a NaN. It can also be helpful in these cases to set options(warn = 2)
so that warnings halt execution. By halting, one may inspect the parameters that were used during at the time of the warning, by looking in the env$last.par
component within the TMB object.
See https://kaskr.github.io/adcomp/_book/Errors.html#floating-point-exception for another approach using GDB. Unfortunately this doesn't appear to be available for mac os (related discussion: ArduPilot/ardupilot#1941), so I prefer just using the simulate method.
There are two functions that take a flexmodel
and return a vector of parameters
Simulation functions are all driven by the simulate
method of TMB objects. These functions have optional sim_params
arguments, which should give a vector to pass to TMB simulate methods. If there are no sim_params
provided by the user, the tmb_params
function is used to attempt to guess.
flexmodel
objects contain two parameter vectors: params
and tv_mult
When one only wants to test the TMB refactored functionality, please use the following commands.
export MACPAN_TEST_LEVEL=1
make test-tmb-all
The test level can be increased to >1
to run tests that are slower because they compare refactored TMB/C++ results with equivalent jobs that use the original R-engine.
It is also possible to only run tests associated with specific areas.
make test-tmb
make test-tmb-forecast
make test-tmb-struc
make test-tmb-calibrate
make test-tmb-make-state
make test-tmb-timevar
DATA_IVECTOR(powidx);
DATA_IVECTOR(powarg1idx);
DATA_IVECTOR(powarg2idx);
See the area of code with sumSize
for example.
// more code ...
int powSize = powidx.size(); // powers
// more code ...
matrix<Type> simulation_history(numIterations+1, \
stateSize+tvElementsNum+sumSize+factrSize+powSize+extraExprNum+lagNum+convNum);
// more code ...
vector<Type> sp(state.size()+params.size()+sumSize+factrSize+powSize);
// more code ...
vector<Type> place_holder_pow(powSize);
sp << state, params, place_holder, place_holder_factr, place_holder_pow;
// more code ...
if (powSize>0)
simulation_history.block(0, stateSize+tvElementsNum+sumSize+factrSize, 1, powSize) = \
sp.segment(stateSize+params.size()+sumSize+factrSize, powSize).transpose();
(similar to previous step)
template <class Type>
struct update_state_functor{
// Data members
vector<Type> params_;
vector<int> from_;
// more code ...
vector<int> powidx_;
vector<int> powarg1idx_;
vector<int> powarg2idx_;
// more code ...
// Constructor
update_state_functor(
vector<Type> params,
vector<int> from,
// more code ...
vector<int> powidx,
vector<int> powarg1idx,
vector<int> powarg2idx,
// more code ...
int do_approx_hazard) : params_(params), from_(from), to_(to), count_(count),
// more code ...
powidx_(powidx),
powarg1idx_(powarg1idx),
powarg2idx_(powarg2idx),
// more code ...
// Concatenate state and params
vector<T> sp(state_.size()+params.size()+sumidx_.size()+factr_spi_.size()+powidx_.size());
// more code ...
vector<T> place_holder_pow(powidx_.size());
sp << state_, params, place_holder, place_holder_factr, place_holder_pow;
template<class Type>
void update_pow_in_sp(
vector<Type>& sp,
const vector<int>& powidx,
const vector<int>& powarg1idx,
const vector<int>& powarg2idx
)
{
for (int j=0; j<powidx.size(); j++) {
sp[powidx-1] = pow(sp[powarg1idx-1], [powarg2idx-1])
}
}
update_pow_in_sp(sp, powidx, powarg1idx, powarg2idx);