## This file is part of SimInf, a framework for stochastic ## disease spread simulations. ## ## Copyright (C) 2015 Pavol Bauer ## Copyright (C) 2017 -- 2019 Robin Eriksson ## Copyright (C) 2015 -- 2019 Stefan Engblom ## Copyright (C) 2015 -- 2024 Stefan Widgren ## ## SimInf is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## SimInf is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . library(SimInf) library(Matrix) library(tools) source("util/check.R") ## Specify the number of threads to use. set_num_threads(1) ## For debugging sessionInfo() ## Check that invalid arguments to mparse raises error res <- assertError( mparse(compartments = c("D", "W"), gdata = c(c1 = 0.5, c2 = 1, c3 = 0.005, c4 = 0.6), u0 = data.frame(D = 10, W = 10), tspan = 1:5)) check_error(res, "'transitions' must be specified in a character vector.") res <- assertError( mparse(transitions = 5, compartments = c("D", "W"), gdata = c(c1 = 0.5, c2 = 1, c3 = 0.005, c4 = 0.6), u0 = data.frame(D = 10, W = 10), tspan = 1:5)) check_error(res, "'transitions' must be specified in a character vector.") res <- assertError( mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), gdata = c(c1 = 0.5, c2 = 1, c3 = 0.005, c4 = 0.6), u0 = data.frame(D = 10, W = 10), tspan = 1:5)) check_error(res, "'compartments' must be specified in a character vector.") res <- assertError( mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), compartments = 5, gdata = c(c1 = 0.5, c2 = 1, c3 = 0.005, c4 = 0.6), u0 = data.frame(D = 10, W = 10), tspan = 1:5)) check_error(res, "'compartments' must be specified in a character vector.") res <- assertError( mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), compartments = c("D", "W"), gdata = letters, u0 = data.frame(D = 10, W = 10), tspan = 1:5)) check_error(res, "'gdata' must either be a 'data.frame' or a 'numeric' vector.") res <- assertError( mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), compartments = c("D", "W", "D"), gdata = c(c1 = 0.5, c2 = 1, c3 = 0.005, c4 = 0.6), u0 = data.frame(D = 10, W = 10), tspan = 1:5)) check_error(res, "'compartments' must be specified in a character vector.") res <- assertError( mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), compartments = c("D", "W"), gdata = c(c1 = 0.5, c2 = 1, c3 = 0.005, c4 = 0.6, c1 = 2), u0 = data.frame(D = 10, W = 10), tspan = 1:5)) check_error(res, "'gdata' must have non-duplicated parameter names.") res <- assertError( mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W"), compartments = c("D", "W"), gdata = c(c1 = 0.5, c2 = 1, c3 = 0.005, c4 = 0.6), u0 = data.frame(D = 10, W = 10), tspan = 1:5)) check_error(res, "Invalid transition: 'W->c4*W'.") res <- assertError( mparse(transitions = c("A->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), compartments = c("D", "W"), gdata = c(c1 = 0.5, c2 = 1, c3 = 0.005, c4 = 0.6), u0 = data.frame(D = 10, W = 10), tspan = 1:5)) check_error(res, "Unknown compartment: 'A'.") res <- assertError( mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->B"), compartments = c("D", "W"), gdata = c(c1 = 0.5, c2 = 1, c3 = 0.005, c4 = 0.6), u0 = data.frame(D = 10, W = 10), tspan = 1:5)) check_error(res, "Unknown compartment: 'B'.") res <- assertError( mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), compartments = c("D", "W"), gdata = c(c1 = 0.5, c2 = 1, c3 = 0.005, c4 = 0.6), u0 = matrix(c(10, 10), nrow = 1, ncol = 2, dimnames = list(NULL, c("A", "W"))), tspan = 1:5)) check_error(res, "Missing columns in u0.") res <- assertError( mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), compartments = c("D", "W"), ldata = 1:5, gdata = c(c1 = 0.5, c2 = 1, c3 = 0.005, c4 = 0.6), u0 = data.frame(D = rep(10, 5), W = 10), tspan = 1:5)) check_error(res, "'ldata' must either be a 'data.frame' or a 'matrix'.") res <- assertError( mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), compartments = c("D", "W"), ldata = matrix(rep(0, 10), nrow = 2, ncol = 5, dimnames = list(c("c1", "c1"))), gdata = c(c2 = 1, c3 = 0.005, c4 = 0.6), u0 = data.frame(D = rep(10, 5), W = 10), tspan = 1:5)) check_error(res, "'ldata' must have non-duplicated parameter names.") res <- assertError( mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), compartments = c("D", "W"), v0 = 1:5, gdata = c(c1 = 0.5, c2 = 1, c3 = 0.005, c4 = 0.6), u0 = data.frame(D = rep(10, 5), W = 10), tspan = 1:5)) check_error(res, "'v0' must either be a 'data.frame' or a 'matrix'.") res <- assertError( mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), compartments = c("D", "W"), v0 = matrix(rep(0, 10), nrow = 2, ncol = 5, dimnames = list(c("c1", "c1"))), gdata = c(c2 = 1, c3 = 0.005, c4 = 0.6), u0 = data.frame(D = rep(10, 5), W = 10), tspan = 1:5)) check_error(res, "'v0' must have non-duplicated parameter names.") res <- assertError( mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), compartments = c("D", "W"), ldata = matrix(1:5, nrow = 1, dimnames = list("c4", NULL)), gdata = c(c1 = 0.5, c2 = 1, c3 = 0.005, c4 = 0.6), u0 = data.frame(D = rep(10, 5), W = 10), tspan = 1:5)) check_error(res, "'u0', 'gdata', 'ldata' and 'v0' have names in common.") res <- assertError( mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), compartments = c("D", "W"), gdata = c(c1 = 0.5, c2 = 1, c3 = 0.005, c4 = 0.6), u0 = data.frame(D = 10, W = 10), tspan = 1:5, pts_fun = 5)) check_error(res, "'pts_fun' must be a character vector.") ## Check mparse m <- mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), compartments = c("D", "W"), gdata = c(c1 = 0.5, c2 = 1, c3 = 0.005, c4 = 0.6), u0 = data.frame(D = 10, W = 10), tspan = 1:5) G <- new("dgCMatrix", i = c(1L, 2L, 1L, 2L, 1L, 2L, 3L, 2L, 3L), p = c(0L, 2L, 4L, 7L, 9L), Dim = c(4L, 4L), Dimnames = list(c("@ -> c1 -> D", "D -> c2*D -> 2*D", "D + W -> c3*D*W -> 2*W", "W -> c4*W -> @"), c("1", "2", "3", "4")), x = c(1, 1, 1, 1, 1, 1, 1, 1, 1), factors = list()) stopifnot(identical(m@G, G)) S <- new("dgCMatrix", i = c(0L, 0L, 0L, 1L, 1L), p = c(0L, 1L, 2L, 4L, 5L), Dim = c(2L, 4L), Dimnames = list(c("D", "W"), c("1", "2", "3", "4")), x = c(1, 1, -1, 1, -1), factors = list()) stopifnot(identical(m@S, S)) C_code <- c( "", "#include ", "#include \"SimInf.h\"", "", "/**", " * Make sure the necessary macros are defined so that the", " * compiler can replace them when compiling the model.", " * 'SIMINF_MODEL_RUN' defines the function name of the function", " * that will be called from R to run a trajectory of the model.", " * 'SIMINF_R_INIT' is the name of the function that R will call", " * when this model is loaded into R. 'SIMINF_FORCE_SYMBOLS'", " * defines whether R allows the entry point for the run function", " * to be searched for as a character string.", " * If this file is compiled from SimInf (when calling run), the", " * macros are defined by SimInf before calling 'R CMD SHLIB'.", " * If this file is compiled as part of a package, then the", " * definitions are set in the variable 'PKG_CPPFLAGS' in", " * 'src/Makevars' and 'src/Makevars.in'.", " */", "#if !defined(SIMINF_MODEL_RUN)", "# error Definition for 'SIMINF_MODEL_RUN' is missing.", "#endif", "#if !defined(SIMINF_R_INIT)", "# error Definition for 'SIMINF_R_INIT' is missing.", "#endif", "#if !defined(SIMINF_FORCE_SYMBOLS)", "# error Definition for 'SIMINF_FORCE_SYMBOLS' is missing.", "#endif", "", "/**", " * @param u The compartment state vector in the node.", " * @param v The continuous state vector in the node.", " * @param ldata The local data vector in the node.", " * @param gdata The global data vector.", " * @param t Current time.", " * @return propensity.", " */", "static double trFun1(", " const int *u,", " const double *v,", " const double *ldata,", " const double *gdata,", " double t)", "{", " return gdata[0];", "}", "", "/**", " * @param u The compartment state vector in the node.", " * @param v The continuous state vector in the node.", " * @param ldata The local data vector in the node.", " * @param gdata The global data vector.", " * @param t Current time.", " * @return propensity.", " */", "static double trFun2(", " const int *u,", " const double *v,", " const double *ldata,", " const double *gdata,", " double t)", "{", " return gdata[1]*u[0];", "}", "", "/**", " * @param u The compartment state vector in the node.", " * @param v The continuous state vector in the node.", " * @param ldata The local data vector in the node.", " * @param gdata The global data vector.", " * @param t Current time.", " * @return propensity.", " */", "static double trFun3(", " const int *u,", " const double *v,", " const double *ldata,", " const double *gdata,", " double t)", "{", " return gdata[2]*u[0]*u[1];", "}", "", "/**", " * @param u The compartment state vector in the node.", " * @param v The continuous state vector in the node.", " * @param ldata The local data vector in the node.", " * @param gdata The global data vector.", " * @param t Current time.", " * @return propensity.", " */", "static double trFun4(", " const int *u,", " const double *v,", " const double *ldata,", " const double *gdata,", " double t)", "{", " return gdata[3]*u[1];", "}", "", "/**", " * Post time step function.", " *", " * @param v_new If a continuous state vector is used by a model,", " * this is the new continuous state vector in the node after", " * the post time step.", " * @param u The compartment state vector in the node.", " * @param v The current continuous state vector in the node.", " * @param ldata The local data vector in the node.", " * @param gdata The global data vector that is common to all nodes.", " * @param node The node index. Note the node index is zero-based,", " * i.e., the first node is 0.", " * @param t Current time in the simulation.", " * @return error code (<0), or 1 if node needs to update the", " * transition rates, or 0 when it doesn't need to update", " * the transition rates.", " */", "static int ptsFun(", " double *v_new,", " const int *u,", " const double *v,", " const double *ldata,", " const double *gdata,", " int node,", " double t)", "{", " return 0;", "}", "", "/**", " * Run a trajectory of the model.", " *", " * @param model The model.", " * @param solver The name of the numerical solver.", " * @return A model with a trajectory attached to it.", " */", "static SEXP SIMINF_MODEL_RUN(SEXP model, SEXP solver)", "{", " static SEXP(*SimInf_run)(SEXP, SEXP, TRFun*, PTSFun) = NULL;", " TRFun tr_fun[] = {&trFun1, &trFun2, &trFun3, &trFun4};", "", " if (!SimInf_run) {", " SimInf_run = (SEXP(*)(SEXP, SEXP, TRFun*, PTSFun))", " R_GetCCallable(\"SimInf\", \"SimInf_run\");", "", " if (!SimInf_run) {", " Rf_error(\"Cannot find function 'SimInf_run'.\");", " }", " }", "", " return SimInf_run(model, solver, tr_fun, &ptsFun);", "}", "", "/**", " * A NULL-terminated array of routines to register for the .Call", " * interface, see section '5.4 Registering native routines' in", " * the 'Writing R Extensions' manual.", " */", "static const R_CallMethodDef callMethods[] =", "{", " SIMINF_CALLDEF(SIMINF_MODEL_RUN, 2),", " {NULL, NULL, 0}", "};", "", "/**", " * This routine will be invoked when R loads the shared object/DLL,", " * see section '5.4 Registering native routines' in the", " * 'Writing R Extensions' manual.", " */", "void SIMINF_R_INIT(DllInfo *info)", "{", " R_registerRoutines(info, NULL, callMethods, NULL, NULL);", " R_useDynamicSymbols(info, FALSE);", " R_forceSymbols(info, SIMINF_FORCE_SYMBOLS);", "}") ## Skip first line that contains version stopifnot(identical(m@C_code[-1], C_code)) stopifnot(identical(m@C_code, C_code(m))) ## Check mparse with both gdata and ldata m <- mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), compartments = c("D", "W"), ldata = matrix(rep(0.6, 5), nrow = 1, dimnames = list("c4", NULL)), gdata = c(c1 = 0.5, c2 = 1, c3 = 0.005), u0 = data.frame(D = rep(10, 5), W = 10), tspan = 1:5) C_code <- c( "", "#include ", "#include \"SimInf.h\"", "", "/**", " * Make sure the necessary macros are defined so that the", " * compiler can replace them when compiling the model.", " * 'SIMINF_MODEL_RUN' defines the function name of the function", " * that will be called from R to run a trajectory of the model.", " * 'SIMINF_R_INIT' is the name of the function that R will call", " * when this model is loaded into R. 'SIMINF_FORCE_SYMBOLS'", " * defines whether R allows the entry point for the run function", " * to be searched for as a character string.", " * If this file is compiled from SimInf (when calling run), the", " * macros are defined by SimInf before calling 'R CMD SHLIB'.", " * If this file is compiled as part of a package, then the", " * definitions are set in the variable 'PKG_CPPFLAGS' in", " * 'src/Makevars' and 'src/Makevars.in'.", " */", "#if !defined(SIMINF_MODEL_RUN)", "# error Definition for 'SIMINF_MODEL_RUN' is missing.", "#endif", "#if !defined(SIMINF_R_INIT)", "# error Definition for 'SIMINF_R_INIT' is missing.", "#endif", "#if !defined(SIMINF_FORCE_SYMBOLS)", "# error Definition for 'SIMINF_FORCE_SYMBOLS' is missing.", "#endif", "", "/**", " * @param u The compartment state vector in the node.", " * @param v The continuous state vector in the node.", " * @param ldata The local data vector in the node.", " * @param gdata The global data vector.", " * @param t Current time.", " * @return propensity.", " */", "static double trFun1(", " const int *u,", " const double *v,", " const double *ldata,", " const double *gdata,", " double t)", "{", " return gdata[0];", "}", "", "/**", " * @param u The compartment state vector in the node.", " * @param v The continuous state vector in the node.", " * @param ldata The local data vector in the node.", " * @param gdata The global data vector.", " * @param t Current time.", " * @return propensity.", " */", "static double trFun2(", " const int *u,", " const double *v,", " const double *ldata,", " const double *gdata,", " double t)", "{", " return gdata[1]*u[0];", "}", "", "/**", " * @param u The compartment state vector in the node.", " * @param v The continuous state vector in the node.", " * @param ldata The local data vector in the node.", " * @param gdata The global data vector.", " * @param t Current time.", " * @return propensity.", " */", "static double trFun3(", " const int *u,", " const double *v,", " const double *ldata,", " const double *gdata,", " double t)", "{", " return gdata[2]*u[0]*u[1];", "}", "", "/**", " * @param u The compartment state vector in the node.", " * @param v The continuous state vector in the node.", " * @param ldata The local data vector in the node.", " * @param gdata The global data vector.", " * @param t Current time.", " * @return propensity.", " */", "static double trFun4(", " const int *u,", " const double *v,", " const double *ldata,", " const double *gdata,", " double t)", "{", " return ldata[0]*u[1];", "}", "", "/**", " * Post time step function.", " *", " * @param v_new If a continuous state vector is used by a model,", " * this is the new continuous state vector in the node after", " * the post time step.", " * @param u The compartment state vector in the node.", " * @param v The current continuous state vector in the node.", " * @param ldata The local data vector in the node.", " * @param gdata The global data vector that is common to all nodes.", " * @param node The node index. Note the node index is zero-based,", " * i.e., the first node is 0.", " * @param t Current time in the simulation.", " * @return error code (<0), or 1 if node needs to update the", " * transition rates, or 0 when it doesn't need to update", " * the transition rates.", " */", "static int ptsFun(", " double *v_new,", " const int *u,", " const double *v,", " const double *ldata,", " const double *gdata,", " int node,", " double t)", "{", " return 0;", "}", "", "/**", " * Run a trajectory of the model.", " *", " * @param model The model.", " * @param solver The name of the numerical solver.", " * @return A model with a trajectory attached to it.", " */", "static SEXP SIMINF_MODEL_RUN(SEXP model, SEXP solver)", "{", " static SEXP(*SimInf_run)(SEXP, SEXP, TRFun*, PTSFun) = NULL;", " TRFun tr_fun[] = {&trFun1, &trFun2, &trFun3, &trFun4};", "", " if (!SimInf_run) {", " SimInf_run = (SEXP(*)(SEXP, SEXP, TRFun*, PTSFun))", " R_GetCCallable(\"SimInf\", \"SimInf_run\");", "", " if (!SimInf_run) {", " Rf_error(\"Cannot find function 'SimInf_run'.\");", " }", " }", "", " return SimInf_run(model, solver, tr_fun, &ptsFun);", "}", "", "/**", " * A NULL-terminated array of routines to register for the .Call", " * interface, see section '5.4 Registering native routines' in", " * the 'Writing R Extensions' manual.", " */", "static const R_CallMethodDef callMethods[] =", "{", " SIMINF_CALLDEF(SIMINF_MODEL_RUN, 2),", " {NULL, NULL, 0}", "};", "", "/**", " * This routine will be invoked when R loads the shared object/DLL,", " * see section '5.4 Registering native routines' in the", " * 'Writing R Extensions' manual.", " */", "void SIMINF_R_INIT(DllInfo *info)", "{", " R_registerRoutines(info, NULL, callMethods, NULL, NULL);", " R_useDynamicSymbols(info, FALSE);", " R_forceSymbols(info, SIMINF_FORCE_SYMBOLS);", "}") ## Skip first line that contains version stopifnot(identical(m@C_code[-1], C_code)) stopifnot(identical(SimInf:::tokenize("beta*S*I/(S+I+R)"), c("beta", "*", "S", "*", "I", "/", "(", "S", "+", "I", "+", "R", ")"))) stopifnot( identical(SimInf:::rewrite_propensity("beta*S*I/(S+I+R)", list(), c("S", "I", "R"), NULL, "beta", NULL, FALSE), list(code = "gdata[0]*u[0]*u[1]/(u[0]+u[1]+u[2])", depends = c(1, 1, 1), G_rowname = "beta*S*I/(S+I+R)", variables = character(0)))) stopifnot( identical(SimInf:::rewrite_propensity("beta*S*I/(S+I+R)", list(), c("S", "I", "R"), NULL, "beta", NULL, TRUE), list(code = "gdata[BETA]*u[S]*u[I]/(u[S]+u[I]+u[R])", depends = c(1, 1, 1), G_rowname = "beta*S*I/(S+I+R)", variables = character(0)))) ## Check init function model <- mparse(transitions = c("S -> b*S*I/(S+I+R) -> I", "I -> g*I -> R"), compartments = c("S", "I", "R"), gdata = c(b = 0.16, g = 0.077), u0 = data.frame(S = 100, I = 1, R = 0), tspan = 1:10) C_code <- c( "", "#include ", "#include \"SimInf.h\"", "", "/**", " * Make sure the necessary macros are defined so that the", " * compiler can replace them when compiling the model.", " * 'SIMINF_MODEL_RUN' defines the function name of the function", " * that will be called from R to run a trajectory of the model.", " * 'SIMINF_R_INIT' is the name of the function that R will call", " * when this model is loaded into R. 'SIMINF_FORCE_SYMBOLS'", " * defines whether R allows the entry point for the run function", " * to be searched for as a character string.", " * If this file is compiled from SimInf (when calling run), the", " * macros are defined by SimInf before calling 'R CMD SHLIB'.", " * If this file is compiled as part of a package, then the", " * definitions are set in the variable 'PKG_CPPFLAGS' in", " * 'src/Makevars' and 'src/Makevars.in'.", " */", "#if !defined(SIMINF_MODEL_RUN)", "# error Definition for 'SIMINF_MODEL_RUN' is missing.", "#endif", "#if !defined(SIMINF_R_INIT)", "# error Definition for 'SIMINF_R_INIT' is missing.", "#endif", "#if !defined(SIMINF_FORCE_SYMBOLS)", "# error Definition for 'SIMINF_FORCE_SYMBOLS' is missing.", "#endif", "", "/**", " * @param u The compartment state vector in the node.", " * @param v The continuous state vector in the node.", " * @param ldata The local data vector in the node.", " * @param gdata The global data vector.", " * @param t Current time.", " * @return propensity.", " */", "static double trFun1(", " const int *u,", " const double *v,", " const double *ldata,", " const double *gdata,", " double t)", "{", " return gdata[0]*u[0]*u[1]/(u[0]+u[1]+u[2]);", "}", "", "/**", " * @param u The compartment state vector in the node.", " * @param v The continuous state vector in the node.", " * @param ldata The local data vector in the node.", " * @param gdata The global data vector.", " * @param t Current time.", " * @return propensity.", " */", "static double trFun2(", " const int *u,", " const double *v,", " const double *ldata,", " const double *gdata,", " double t)", "{", " return gdata[1]*u[1];", "}", "", "/**", " * Post time step function.", " *", " * @param v_new If a continuous state vector is used by a model,", " * this is the new continuous state vector in the node after", " * the post time step.", " * @param u The compartment state vector in the node.", " * @param v The current continuous state vector in the node.", " * @param ldata The local data vector in the node.", " * @param gdata The global data vector that is common to all nodes.", " * @param node The node index. Note the node index is zero-based,", " * i.e., the first node is 0.", " * @param t Current time in the simulation.", " * @return error code (<0), or 1 if node needs to update the", " * transition rates, or 0 when it doesn't need to update", " * the transition rates.", " */", "static int ptsFun(", " double *v_new,", " const int *u,", " const double *v,", " const double *ldata,", " const double *gdata,", " int node,", " double t)", "{", " return 0;", "}", "", "/**", " * Run a trajectory of the model.", " *", " * @param model The model.", " * @param solver The name of the numerical solver.", " * @return A model with a trajectory attached to it.", " */", "static SEXP SIMINF_MODEL_RUN(SEXP model, SEXP solver)", "{", " static SEXP(*SimInf_run)(SEXP, SEXP, TRFun*, PTSFun) = NULL;", " TRFun tr_fun[] = {&trFun1, &trFun2};", "", " if (!SimInf_run) {", " SimInf_run = (SEXP(*)(SEXP, SEXP, TRFun*, PTSFun))", " R_GetCCallable(\"SimInf\", \"SimInf_run\");", "", " if (!SimInf_run) {", " Rf_error(\"Cannot find function 'SimInf_run'.\");", " }", " }", "", " return SimInf_run(model, solver, tr_fun, &ptsFun);", "}", "", "/**", " * A NULL-terminated array of routines to register for the .Call", " * interface, see section '5.4 Registering native routines' in", " * the 'Writing R Extensions' manual.", " */", "static const R_CallMethodDef callMethods[] =", "{", " SIMINF_CALLDEF(SIMINF_MODEL_RUN, 2),", " {NULL, NULL, 0}", "};", "", "/**", " * This routine will be invoked when R loads the shared object/DLL,", " * see section '5.4 Registering native routines' in the", " * 'Writing R Extensions' manual.", " */", "void SIMINF_R_INIT(DllInfo *info)", "{", " R_registerRoutines(info, NULL, callMethods, NULL, NULL);", " R_useDynamicSymbols(info, FALSE);", " R_forceSymbols(info, SIMINF_FORCE_SYMBOLS);", "}") ## Skip first line that contains version stopifnot(identical(model@C_code[-1], C_code)) u0 <- structure(c(100L, 1L, 0L), .Dim = c(3L, 1L), .Dimnames = list(c("S", "I", "R"), NULL)) stopifnot(identical(model@u0, u0)) ## Check mparse with ldata and gdata as data.frames m1 <- mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), compartments = c("D", "W"), ldata = matrix(c(2L, 3L, 4L, 5L, 6L), nrow = 1, dimnames = list("c4", NULL)), gdata = c(c1 = 0.5, c2 = 1, c3 = 0.005), u0 = data.frame(D = rep(10, 5), W = 10), tspan = 1:5) m2 <- mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), compartments = c("D", "W"), ldata = data.frame(c4 = c(2L, 3L, 4L, 5L, 6L)), gdata = data.frame(c1 = 0.5, c2 = 1, c3 = 0.005), u0 = data.frame(D = rep(10, 5), W = 10), tspan = 1:5) ## Skip first line that contains version m1@C_code <- m1@C_code[-1] m2@C_code <- m2@C_code[-1] stopifnot(identical(m1, m2)) ## Check that mparse fails with gdata as a 2-row data.frame res <- assertError( mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), compartments = c("D", "W"), ldata = matrix(rep(0.6, 5), nrow = 1, dimnames = list("c4", NULL)), gdata = data.frame(c1 = rep(0.5, 2), c2 = 1, c3 = 0.005), u0 = data.frame(D = rep(10, 5), W = 10), tspan = 1:5)) check_error(res, "When 'gdata' is a data.frame, it must have one row.") ## Check mparse fails with ldata as data.frames and nrow(ldata) != nrow(u0) res <- assertError( mparse(transitions = c("@->c1->D", "D->c2*D->D+D", "D+W->c3*D*W->W+W", "W->c4*W->@"), compartments = c("D", "W"), ldata = data.frame(c4 = c(0.2, 0.3, 0.4, 0.5)), gdata = data.frame(c1 = 0.5, c2 = 1, c3 = 0.005), u0 = data.frame(D = rep(10, 5), W = 10), tspan = 1:5)) check_error(res, "The number of nodes in 'u0' and 'ldata' must match.", FALSE) ## Check 'S + S -> mu -> @' m <- mparse(transitions = "S + S -> mu -> @", compartments = c("S", "I"), gdata = c(mu = 1), u0 = data.frame(S = 100, I = 100), tspan = 1:100) S <- new("dgCMatrix", i = 0L, p = 0:1, Dim = 2:1, Dimnames = list(c("S", "I"), "1"), x = -2, factors = list()) stopifnot(identical(m@S, S)) G <- new("dgCMatrix", i = integer(0), p = c(0L, 0L), Dim = c(1L, 1L), Dimnames = list("2*S -> mu -> @", "1"), x = numeric(0), factors = list()) stopifnot(identical(m@G, G)) ## Check 'S + S -> mu -> S + S' m <- mparse(transitions = "S + S -> mu -> S + S", compartments = c("S", "I"), gdata = c(mu = 1), u0 = data.frame(S = 100, I = 100), tspan = 1:100) S <- new("dgCMatrix", i = integer(0), p = c(0L, 0L), Dim = 2:1, Dimnames = list(c("S", "I"), "1"), x = numeric(0), factors = list()) stopifnot(identical(m@S, S)) G <- new("dgCMatrix", i = integer(0), p = c(0L, 0L), Dim = c(1L, 1L), Dimnames = list("2*S -> mu -> 2*S", "1"), x = numeric(0), factors = list()) stopifnot(identical(m@G, G)) ## Check '@ -> mu-> S + S' m <- mparse(transitions = "@ -> mu-> S + S", compartments = c("S", "I"), gdata = c(mu = 1), u0 = data.frame(S = 100, I = 100), tspan = 1:100) S <- new("dgCMatrix", i = 0L, p = 0:1, Dim = 2:1, Dimnames = list(c("S", "I"), "1"), x = 2, factors = list()) stopifnot(identical(m@S, S)) G <- new("dgCMatrix", i = integer(0), p = c(0L, 0L), Dim = c(1L, 1L), Dimnames = list("@ -> mu -> 2*S", "1"), x = numeric(0), factors = list()) stopifnot(identical(m@G, G)) ## Check parsing replicates of compartments stopifnot(identical(SimInf:::parse_compartments("S + 2*S", c("S", "I")), c(3L, 0L))) ## Check mparse with a compartment name that contains '.', for ## example, '.S.S' (this is a valid column name in a data.frame). m <- mparse(transitions = ".S.S -> 1.2*.S.S -> @", compartments = c(".S.S"), u0 = data.frame(.S.S = 100), tspan = 1:100) stopifnot(identical(m@C_code[46], " return 1.2*u[0];")) ## Check mparse with a propensity that contains '->' to handle a case ## where a pointer is used in the propensity. m <- mparse(transitions = "S -> a->data[2]*1.2*S -> @", compartments = c("S"), u0 = data.frame(S = 100), tspan = 1:100) stopifnot(identical(m@C_code[46], " return a->data[2]*1.2*u[0];")) ## Check that an error is raised if the compilation fails. Define a ## model with undeclared identifiers 'betaSI' and 'gammaI'. model <- mparse(transitions = c("S -> betaSI -> I", "I -> gammaI -> R"), compartments = c("S", "I", "R"), gdata = c(beta = 0.16, gamma = 0.077), u0 = data.frame(S = 100:105, I = 1:6, R = rep(0, 6)), tspan = 1:10) assertError(run(model)) ## Test that the environmental variable PKG_CPPFLAGS is restored after ## compiling C code for a model. First set it to a known value. model <- mparse(transitions = c("S -> beta*S*I/(S+I+R) -> I", "I -> gamma*I -> R"), compartments = c("S", "I", "R"), gdata = c(beta = 0.16, gamma = 0.077), u0 = data.frame(S = 100:105, I = 1:6, R = rep(0, 6)), tspan = 1:10) Sys.setenv(PKG_CPPFLAGS = "test") set.seed(22) result <- run(model) ## Then test that PKG_CPPFLAGS is restored. stopifnot(identical(Sys.getenv("PKG_CPPFLAGS"), "test")) U_exp <- data.frame( node = c(1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L, 4L, 5L, 6L), time = c(1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 5L, 5L, 5L, 5L, 5L, 5L, 6L, 6L, 6L, 6L, 6L, 6L, 7L, 7L, 7L, 7L, 7L, 7L, 8L, 8L, 8L, 8L, 8L, 8L, 9L, 9L, 9L, 9L, 9L, 9L, 10L, 10L, 10L, 10L, 10L, 10L), S = c(100L, 101L, 102L, 103L, 103L, 103L, 100L, 101L, 101L, 103L, 99L, 103L, 100L, 101L, 101L, 103L, 98L, 102L, 100L, 101L, 101L, 103L, 98L, 100L, 100L, 100L, 100L, 102L, 98L, 99L, 100L, 100L, 100L, 102L, 98L, 97L, 100L, 100L, 99L, 102L, 96L, 96L, 100L, 100L, 99L, 102L, 92L, 94L, 100L, 99L, 98L, 102L, 91L, 94L, 100L, 99L, 98L, 102L, 87L, 94L), I = c(1L, 1L, 3L, 4L, 6L, 7L, 1L, 1L, 4L, 3L, 9L, 4L, 0L, 1L, 4L, 3L, 9L, 5L, 0L, 1L, 4L, 3L, 9L, 7L, 0L, 2L, 5L, 4L, 8L, 8L, 0L, 2L, 4L, 4L, 8L, 9L, 0L, 1L, 5L, 3L, 10L, 10L, 0L, 1L, 4L, 3L, 12L, 11L, 0L, 2L, 4L, 3L, 13L, 11L, 0L, 2L, 4L, 2L, 14L, 10L), R = c(0L, 1L, 0L, 0L, 0L, 1L, 0L, 1L, 0L, 1L, 1L, 4L, 1L, 1L, 0L, 1L, 2L, 4L, 1L, 1L, 0L, 1L, 2L, 4L, 1L, 1L, 0L, 1L, 3L, 4L, 1L, 1L, 1L, 1L, 3L, 5L, 1L, 2L, 1L, 2L, 3L, 5L, 1L, 2L, 2L, 2L, 5L, 6L, 1L, 2L, 3L, 2L, 5L, 6L, 1L, 2L, 3L, 3L, 8L, 7L)) stopifnot(identical(trajectory(result), U_exp)) ## Remove the C code and check that an error is raised when calling ## 'run'. model@C_code <- character(0) res <- assertError(run(model)) check_error(res, "The model must contain C code.") ## Check that mparse fails with invalid usage of the empty set '@'. res <- assertError( mparse(transitions = c("S -> beta*S*I/(S+I+R) -> I", "I -> gamma*I -> @+R"), compartments = c("S", "I", "R"), gdata = c(beta = 0.16, gamma = 0.077), u0 = data.frame(S = 100:105, I = 1:6, R = rep(0, 6)), tspan = 1:10)) check_error(res, "Invalid usage of the empty set '@'.") res <- assertError( mparse(transitions = c("S -> beta*S*I/(S+I+R) -> I", "I -> gamma*I -> @+@"), compartments = c("S", "I", "R"), gdata = c(beta = 0.16, gamma = 0.077), u0 = data.frame(S = 100:105, I = 1:6, R = rep(0, 6)), tspan = 1:10)) check_error(res, "Invalid usage of the empty set '@'.") res <- assertError( mparse(transitions = c("S -> beta*S*I/(S+I+R) -> I", "I -> gamma*I -> 2*@"), compartments = c("S", "I", "R"), gdata = c(beta = 0.16, gamma = 0.077), u0 = data.frame(S = 100:105, I = 1:6, R = rep(0, 6)), tspan = 1:10)) check_error(res, "Invalid usage of the empty set '@'.") res <- assertError( SimInf:::parse_variable(x = "3N <- S + I + R", compartments = c("S", "I", "R"), ldata_names = character(0), gdata_names = character(0), v0_names = character(0), use_enum = FALSE)) check_error(res, "Invalid variable: '3N <- S + I + R'.") res <- assertError( SimInf:::parse_variable(x = "N <- S + I + R", compartments = c("S", "I", "R"), ldata_names = "N", gdata_names = character(0), v0_names = character(0), use_enum = FALSE)) check_error( res, "Variable name already exists in 'u0', 'gdata', 'ldata' or 'v0'.") stopifnot(identical( SimInf:::parse_variable(x = "N <- S + I + R", compartments = c("S", "I", "R"), ldata_names = character(0), gdata_names = character(0), v0_names = character(0), use_enum = FALSE), list(variable = "N", tokens = c("u[0]", "+", "u[1]", "+", "u[2]"), type = "double", compartments = c("S", "I", "R")))) stopifnot(identical( SimInf:::parse_variable(x = "N <- S + I + R", compartments = c("S", "I", "R"), ldata_names = character(0), gdata_names = character(0), v0_names = character(0), use_enum = TRUE), list(variable = "N", tokens = c("u[S]", "+", "u[I]", "+", "u[R]"), type = "double", compartments = c("S", "I", "R")))) res <- assertError( SimInf:::topological_sort( matrix(c(1, 0, 0, 1, 0, 0, 0, 1, 0), nrow = 3, ncol = 3, dimnames = list(c("A", "B", "C"), c("A", "B", "C"))))) check_error(res, "Invalid dependencies between variables.") res <- assertError( SimInf:::topological_sort( matrix(c(0, 0, 0, 1, 0, 0, 0, 1, 1), nrow = 3, ncol = 3, dimnames = list(c("A", "B", "C"), c("A", "B", "C"))))) check_error(res, "Invalid dependencies between variables.") stopifnot(identical( SimInf:::topological_sort( matrix(c(0, 1, 0, 1, 0, 0, 0, 0, 0), nrow = 3, ncol = 3, dimnames = list(c("A", "B", "C"), c("C", "B", "A")))), matrix(c(0, 0, 0, 1, 0, 0, 0, 1, 0), nrow = 3, ncol = 3, dimnames = list(c("A", "B", "C"), c("A", "B", "C"))))) ## Check to generate C code for variables. propensity <- list(code = "N>0?beta*u[0]*u[1]/N:0", depends = c(1, 1, 0), S = c(-1L, 1L, 0L), G_rowname = "S -> N>0?beta*S*I/N:0 -> I", variables = "N") variables <- list(N1 = list(variable = "N1", type = "double", depends = character(0), code = "u[0]"), N2 = list(variable = "N2", type = "double", depends = "N1", code = "N1+u[1]"), N = list(variable = "N", type = "double", depends = "N2", code = "N2+u[2]")) stopifnot(identical( SimInf:::C_variables(propensity, variables), c(" const double N1 = u[0];", " const double N2 = N1+u[1];", " const double N = N2+u[2];", ""))) stopifnot(identical( SimInf:::C_enum(compartment = c("S", "I", "R"), ldata_names = c("beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta", "iota", "kappa", "lambda"), gdata_names = character(0), v0_names = character(0), use_enum = TRUE), c("/* Enumeration constants for indicies in the 'u' vector. */", "enum {S, I, R};", "", "/* Enumeration constants for indicies in the 'ldata' vector. */", "enum {BETA, GAMMA, DELTA, EPSILON, ZETA, ETA, THETA, IOTA, KAPPA,", " LAMBDA};", ""))) ## Check dependecies to compartments via variables. model <- mparse(transitions = c("S -> beta*NS*NI/N -> E", "E -> gamma*NE -> I", "I -> delta*NI -> R", "NS <- S", "NE <- E", "NI <- I", "N <- NS+NE+NI+NR"), compartments = c("S", "E", "I", "R"), ldata = c(beta = 1, gamma = 2), u0 = c(S = 100, E = 0, I = 10, R = 0), v0 = c(delta = 3), tspan = 1:100) G_expected <- new("dgCMatrix", i = c(0L, 1L, 0L, 1L, 2L, 0L, 2L), p = c(0L, 2L, 5L, 7L), Dim = c(3L, 3L), Dimnames = list(c("S -> beta*NS*NI/N -> E", "E -> gamma*NE -> I", "I -> delta*NI -> R"), c("1", "2", "3")), x = c(1, 1, 1, 1, 1, 1, 1), factors = list()) stopifnot(identical(model@G, G_expected))