diff --git a/DESCRIPTION b/DESCRIPTION index b1cfb2621..b20ab90af 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: mrgsolve Title: Simulate from ODE-Based Models -Version: 1.0.3 +Version: 1.0.4 Authors@R: c(person(given = "Kyle T", family = "Baron", role = c("aut", "cre"), diff --git a/Makefile b/Makefile index 584e5d286..35e2e4010 100644 --- a/Makefile +++ b/Makefile @@ -94,6 +94,9 @@ test1: test2: Rscript -e 'testthat::test_dir("inst/maintenance/unit")' +test-cpp: + Rscript -e 'testthat::test_dir("inst/maintenance/unit-cpp")' + clean: rm src/*.o rm src/*.so diff --git a/NAMESPACE b/NAMESPACE index dcc39a29a..208da055a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -40,6 +40,8 @@ S3method(handle_spec_block,specTHETA) S3method(handle_spec_block,specTRANSIT) S3method(handle_spec_block,specVCMT) S3method(handle_spec_block,specYAML) +S3method(lctran,data.frame) +S3method(lctran,ev) S3method(length,matlist) S3method(loadso,mrgmod) S3method(merge,list) @@ -61,6 +63,8 @@ S3method(summarise,each) S3method(summarise,mrgsims) S3method(summary,mrgmod) S3method(summary,mrgsims) +S3method(uctran,data.frame) +S3method(uctran,ev) S3method(within,mrgmod) export("%>%") export("%then%") @@ -105,7 +109,9 @@ export(ev_repeat) export(ev_rx) export(ev_seq) export(evd) +export(evd_expand) export(expand.ev) +export(expand.evd) export(expand.idata) export(expand_observations) export(file_show) diff --git a/NEWS.md b/NEWS.md index c3e242075..dd9695808 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,24 @@ +# mrgsolve 1.0.4 + +- Fix bug where `as_data_frame()` was not properly working when leading event + object was `evd` type (#948, #955). + +- Add `uctran()` to convert nmtran data names to upper case (#955). + +- Both `lctran()` and `uctran()` are generic and work on `data.frame` or + event (`ev`) objects (#949, #955). + +- Fix bug where data records with `EVID = 3` were getting shifted in time by + `ALAG` (#964, #969). + +- Small negative eigenvalues from `OMEGA` or `SIGMA` are set to zero in + multivariate normal simulation of `ETA` and `EPS`, following the pattern + seen in `MASS::mvrnorm()` (#956, #957). + +- Fixed bug where template parameters (`T`) were getting modified when `nm-vars` + plugin was used in conjunction with `mrgx` plugin (#965, #968). + + # mrgsolve 1.0.3 - Removed `assert()` statement in LSODA code found by CRAN check (#943). diff --git a/R/class_evd.R b/R/class_evd.R index f9c0165ae..8fde8e23a 100644 --- a/R/class_evd.R +++ b/R/class_evd.R @@ -13,8 +13,8 @@ #' Note that `evd` isn't a separate class; it is just an `ev` object with #' a specific `case` attribute. See examples which illustrate the difference. #' -#' @param x An mrgmod object. -#' @param ... Arguments passed to [ev()]. +#' @param x an mrgmod object. +#' @param ... arguments passed to [ev()]. #' #' @examples #' a <- evd(amt = 100) @@ -51,8 +51,7 @@ setMethod("evd", "ev", function(x, ...) { set_ev_case(x, 1L) }) - -#' @param x An event object. +#' @param x an event object. #' @rdname evd #' @export as.evd <- function(x) { diff --git a/R/data_set.R b/R/data_set.R index 46e7dc3ce..78709e58b 100644 --- a/R/data_set.R +++ b/R/data_set.R @@ -167,12 +167,13 @@ setMethod("data_set", c("mrgmod", "missing"), function(x, object, ...) { #' If both lower and upper case versions of the name are present in the data #' frame, no changes will be made. #' -#' @param data A data set with nmtran-like format. -#' @param warn If `TRUE`, a warning will be issued when there are both upper +#' @param data a data set with nmtran-like format. +#' @param warn if `TRUE`, a warning will be issued when there are both upper #' and lower case versions of any nmtran-like column in the data frame. +#' @param ... for potential future use. #' #' @return -#' A data frame with possibly renamed columns. +#' A data frame or event object with possibly renamed columns. #' #' @examples #' data <- data.frame(TIME = 0, AMT = 5, II = 24, addl = 2, WT = 80) @@ -181,6 +182,9 @@ setMethod("data_set", c("mrgmod", "missing"), function(x, object, ...) { #' data <- data.frame(TIME = 0, AMT = 5, II = 24, addl = 2, wt = 80) #' uctran(data) #' +#' ev <- evd(amt = 100, evid = 3) +#' uctran(ev) +#' #' # warning #' data <- data.frame(TIME = 1, time = 2, CMT = 5) #' lctran(data) @@ -190,10 +194,10 @@ setMethod("data_set", c("mrgmod", "missing"), function(x, object, ...) { #' #' @md #' @export -lctran <- function(data, warn = TRUE) { - if(!is.data.frame(data)) { - stop("`data` must be a data.frame.") - } +lctran <- function(data, ...) UseMethod("lctran") +#' @rdname lctran +#' @export +lctran.data.frame <- function(data, warn = TRUE, ...) { n <- names(data) infrom <- n %in% GLOBALS$TRAN_UPPER haslower <- tolower(n) %in% n @@ -207,13 +211,17 @@ lctran <- function(data, warn = TRUE) { } data } - #' @rdname lctran #' @export -uctran <- function(data, warn = TRUE) { - if(!is.data.frame(data)) { - stop("`data` must be a data.frame.") - } +lctran.ev <- function(data, ...) { + as.ev(data) +} +#' @rdname lctran +#' @export +uctran <- function(data, ...) UseMethod("uctran") +#' @rdname lctran +#' @export +uctran.data.frame <- function(data, warn = TRUE, ...) { n <- names(data) infrom <- n %in% GLOBALS$TRAN_LOWER hasupper <- toupper(n) %in% n @@ -227,6 +235,11 @@ uctran <- function(data, warn = TRUE) { } data } +#' @rdname lctran +#' @export +uctran.ev <- function(data, ...) { + as.evd(data) +} data_hooks <- function(data, object, envir, param = list(), ...) { param <- as.list(param) @@ -295,7 +308,7 @@ setGeneric("as_data_set", function(x,...) standardGeneric("as_data_set")) setMethod("as_data_set", "ev", function(x, ...) { other_ev <- list(...) if(length(other_ev)==0) { - return(check_ev(x)) + return(ev_to_ds(x)) } do.call(collect_ev, c(list(x), other_ev)) }) diff --git a/R/events.R b/R/events.R index 2387eefb0..89fd2e667 100644 --- a/R/events.R +++ b/R/events.R @@ -262,6 +262,7 @@ setMethod("as.ev", "data.frame", df_to_ev) #' @rdname as.ev #' @export setMethod("as.ev", "ev", function(x, ...) { + x@case <- 0L x }) diff --git a/R/utils.R b/R/utils.R index c73aad050..b829955f6 100644 --- a/R/utils.R +++ b/R/utils.R @@ -200,38 +200,42 @@ my_str_split <- function(string,pattern,n=3,fixed=FALSE,collapse=pattern) { lapply(m,collapsen,collapse=collapse,n=n) } -##' Create template data sets for simulation -##' -##' @param ... passed to [expand.grid] -##' -##' @details -##' An ID column is added as `seq(nrow(ans))` if not supplied by the user. -##' For `expand.ev`, defaults also added include `cmt = 1`, -##' `time = 0`, `evid = 1`. If `total` is included, -##' then `addl` is derived as `total` - 1. If `tinf` is included, then -##' an infusion rate is derived for row where `tinf` is greater than -##' zero. -##' -##' @examples -##' idata <- expand.idata(CL = c(1,2,3), VC = c(10,20,30)) -##' -##' doses <- expand.ev(amt = c(300,100), ii = c(12,24), cmt = 1) -##' -##' infusion <- expand.ev(amt = 100, tinf = 2) -##' -##' @md -##' @export +#' Create template data sets for simulation +#' +#' These functions expand all combinations of arguments using +#' [expand.grid()]. The result always has only one row for one individual. +#' Use [expand.evd()] or [evd_expand()] to convert nmtran names (e.g. AMT +#' or CMT) to upper case (see [uctran()]). +#' +#' @param ... passed to [expand.grid()] +#' +#' @details +#' An ID column is added as `seq(nrow(ans))` if not supplied by the user. For +#' `expand.ev`, defaults also added include `cmt = 1`, `time = 0`, `evid = 1`. +#' If `total` is included, then `addl` is derived as `total` - 1. If `tinf` is +#' included, then an infusion rate is derived for row where `tinf` is greater +#' than zero. +#' +#' @examples +#' idata <- expand.idata(CL = c(1,2,3), VC = c(10,20,30)) +#' +#' doses <- expand.ev(amt = c(300,100), ii = c(12,24), cmt = 1) +#' +#' infusion <- expand.ev(amt = 100, tinf = 2) +#' +#' @md +#' @export expand.idata <- function(...) { - ans <- expand.grid(...,stringsAsFactors=FALSE, KEEP.OUT.ATTRS = FALSE) - ans$ID <- seq_len(nrow(ans)) - dplyr::select(ans, "ID", everything()) + ans <- expand.grid(..., stringsAsFactors = FALSE, KEEP.OUT.ATTRS = FALSE) + ans$ID <- seq(nrow(ans)) + ans[, unique(c("ID", names(ans))), drop = FALSE] } #' @export #' @rdname expand.idata expand.ev <- function(...) { - ans <- expand.grid(...,stringsAsFactors=FALSE) - ans[["ID"]] <- seq_len(nrow(ans)) + ans <- expand.grid(..., stringsAsFactors = FALSE) + ans[["ID"]] <- seq(nrow(ans)) if(!has_name("evid", ans)) ans[["evid"]] <- 1 if(!has_name("cmt", ans)) ans[["cmt"]] <- 1 if(!has_name("time", ans)) ans[["time"]] <- 0 @@ -239,20 +243,30 @@ expand.ev <- function(...) { finalize_ev(ans) } +#' @rdname expand.idata #' @export +expand.evd <- function(...) { + uctran(expand.ev(...)) +} + #' @rdname expand.idata +#' @export ev_expand <- expand.ev +#' @export +#' @rdname expand.idata +evd_expand <- expand.evd + #' Expand an event data frame across multiple ID #' #' @noRd -expand_event_object <- function(event,ID) { +expand_event_object <- function(event, ID) { event <- as.data.frame(event) out_names <- unique(c("ID", names(event))) - ind <- rep(seq(nrow(event)), times=length(ID)) - big <- dplyr::slice(event, ind) - big[["ID"]] <- rep(ID, each=nrow(event)) - big[,out_names] + ind <- rep(seq(nrow(event)), times = length(ID)) + big <- event[ind, , drop = FALSE] + big[["ID"]] <- rep(ID, each = nrow(event)) + big[, out_names, drop = FALSE] } tolist <- function(x,concat=TRUE,envir=list()) { diff --git a/inst/WORDLIST b/inst/WORDLIST index 48ee18d03..621479b1f 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -145,3 +145,4 @@ QD KA pkgdown Rcpp +df diff --git a/inst/maintenance/unit-cpp/test-cpp.R b/inst/maintenance/unit-cpp/test-cpp.R new file mode 100644 index 000000000..2ab800468 --- /dev/null +++ b/inst/maintenance/unit-cpp/test-cpp.R @@ -0,0 +1,26 @@ + +library(testthat) +library(mrgsolve) +library(dplyr) + +Sys.setenv(R_TESTS="") +options("mrgsolve_mread_quiet"=TRUE) + +context("test-cpp") + +code <- ' +[ plugin ] nm-vars, mrgx, Rcpp + +[ cmt ] @number 1 + +[ des ] +DADT(1) = -0.1 * A(1); +' + +test_that("build a model with mrgx and nm-vars", { + expect_is( + mcode("test-cpp-mrgx-nm-vars", code, quiet = TRUE), + "mrgmod" + ) +}) + diff --git a/inst/maintenance/unit/test-z-alag-f.R b/inst/maintenance/unit/test-z-alag-f.R index abfaa7728..cb785816f 100644 --- a/inst/maintenance/unit/test-z-alag-f.R +++ b/inst/maintenance/unit/test-z-alag-f.R @@ -155,3 +155,14 @@ test_that("ALAG is set from data", { }) +test_that("ALAG does not change records with EVID 3 [SLV-TEST-0007]", { + data1 <- c( + ev(amt = 100), + ev(amt = 0, evid = 3, time = 8), + ev(amt = 100, time = 12) + ) + data2 <- mutate(data1, ALAG1 = c(0, 5, 0)) + out1 <- mrgsim(mod, data1, end = 24) + out2 <- mrgsim(mod, data2, end = 24) + expect_equal(out1@data, out2@data) +}) diff --git a/inst/mrgx/mrgx.h b/inst/mrgx/mrgx.h index bb74a3a83..f8426e50f 100644 --- a/inst/mrgx/mrgx.h +++ b/inst/mrgx/mrgx.h @@ -1,4 +1,4 @@ -// Copyright (C) 2013 - 2019 Metrum Research Group, LLC +// Copyright (C) 2013 - 2022 Metrum Research Group // // This file is part of mrgsolve. // @@ -128,8 +128,8 @@ double rlognorm(const double mean, const double sd, const double lower, * @param self the model data object * @return an object from the model environment */ -template -T get(const std::string name, const databox& self) { +template +_T___ get(const std::string name, const databox& self) { Rcpp::Environment env = get_envir(self); return env[name]; } @@ -147,8 +147,8 @@ T get(const std::string name, const databox& self) { * @param name name of the R object to get * @return an object from the global environment */ -template -T get(const std::string name) { +template +_T___ get(const std::string name) { Rcpp::Environment env = Rcpp::Environment::global_env(); return env[name]; } @@ -180,10 +180,10 @@ T get(const std::string name) { * @param name name of the object to get * @return an object from the package namespace */ -template -T get(const std::string package, const std::string name) { +template +_T___ get(const std::string package, const std::string name) { Rcpp::Environment env = Rcpp::Environment::namespace_env(package); - T ans = env[name]; + _T___ ans = env[name]; return ans; } @@ -194,8 +194,8 @@ T get(const std::string package, const std::string name) { * @param filename the name of the RDS file to read * @return an object saved in the RDS file */ -template -T readRDS(const std::string filename) { +template +_T___ readRDS(const std::string filename) { Rcpp::Function readRDS = get("base", "readRDS"); return readRDS(filename); } diff --git a/inst/stories.yaml b/inst/stories.yaml new file mode 100644 index 000000000..9cd8b6f93 --- /dev/null +++ b/inst/stories.yaml @@ -0,0 +1,35 @@ +# Please add stories at the top ------------------------------------------ + +SLV-S004: + name: evid 3 with lag time + description: > + As a user, I want mrgsolve to run evid=3 reset at the given time, regardless + of the value of ALAG. + ProductRisk: low-risk + tests: + - SLV-TEST-0007 +SLV-S003: + name: expand.evd + description: > + As a user, I want to call expand / event to make data set input with all + upper case nmtran names. + ProductRisk: low-risk + tests: + - SLV-TEST-0003 +SLV-S002: + name: Handle small, negative eigenvalue + description: > + As a user, I want mrgsolve to allow small, negative eigenvalues in + OMEGA or SIGMA matrices. + ProductRisk: low-risk + tests: + - SLV-TEST-0002 +SLV-S001: + name: Change case for nmtran-specific data items + description: > + As a user, I want to convert nmtran data items to upper or lower case. + ProductRisk: low-risk + tests: + - SLV-TEST-0004 + - SLV-TEST-0005 + - SLV-TEST-0006 diff --git a/man/evd.Rd b/man/evd.Rd index aa756174d..de4826288 100644 --- a/man/evd.Rd +++ b/man/evd.Rd @@ -19,9 +19,9 @@ evd(x, ...) as.evd(x) } \arguments{ -\item{x}{An event object.} +\item{x}{an event object.} -\item{...}{Arguments passed to \code{\link[=ev]{ev()}}.} +\item{...}{arguments passed to \code{\link[=ev]{ev()}}.} } \description{ This function calls \code{\link[=ev]{ev()}} to create an event object and then sets the diff --git a/man/expand.idata.Rd b/man/expand.idata.Rd index e5d5e430a..db6000cba 100644 --- a/man/expand.idata.Rd +++ b/man/expand.idata.Rd @@ -3,28 +3,36 @@ \name{expand.idata} \alias{expand.idata} \alias{expand.ev} +\alias{expand.evd} \alias{ev_expand} +\alias{evd_expand} \title{Create template data sets for simulation} \usage{ expand.idata(...) expand.ev(...) +expand.evd(...) + ev_expand(...) + +evd_expand(...) } \arguments{ -\item{...}{passed to \link{expand.grid}} +\item{...}{passed to \code{\link[=expand.grid]{expand.grid()}}} } \description{ -Create template data sets for simulation +These functions expand all combinations of arguments using +\code{\link[=expand.grid]{expand.grid()}}. The result always has only one row for one individual. +Use \code{\link[=expand.evd]{expand.evd()}} or \code{\link[=evd_expand]{evd_expand()}} to convert nmtran names (e.g. AMT +or CMT) to upper case (see \code{\link[=uctran]{uctran()}}). } \details{ -An ID column is added as \code{seq(nrow(ans))} if not supplied by the user. -For \code{expand.ev}, defaults also added include \code{cmt = 1}, -\code{time = 0}, \code{evid = 1}. If \code{total} is included, -then \code{addl} is derived as \code{total} - 1. If \code{tinf} is included, then -an infusion rate is derived for row where \code{tinf} is greater than -zero. +An ID column is added as \code{seq(nrow(ans))} if not supplied by the user. For +\code{expand.ev}, defaults also added include \code{cmt = 1}, \code{time = 0}, \code{evid = 1}. +If \code{total} is included, then \code{addl} is derived as \code{total} - 1. If \code{tinf} is +included, then an infusion rate is derived for row where \code{tinf} is greater +than zero. } \examples{ idata <- expand.idata(CL = c(1,2,3), VC = c(10,20,30)) diff --git a/man/lctran.Rd b/man/lctran.Rd index 06907fb00..01b96c2ff 100644 --- a/man/lctran.Rd +++ b/man/lctran.Rd @@ -2,21 +2,35 @@ % Please edit documentation in R/data_set.R \name{lctran} \alias{lctran} +\alias{lctran.data.frame} +\alias{lctran.ev} \alias{uctran} +\alias{uctran.data.frame} +\alias{uctran.ev} \title{Change the case of nmtran-like data items} \usage{ -lctran(data, warn = TRUE) +lctran(data, ...) -uctran(data, warn = TRUE) +\method{lctran}{data.frame}(data, warn = TRUE, ...) + +\method{lctran}{ev}(data, ...) + +uctran(data, ...) + +\method{uctran}{data.frame}(data, warn = TRUE, ...) + +\method{uctran}{ev}(data, ...) } \arguments{ -\item{data}{A data set with nmtran-like format.} +\item{data}{a data set with nmtran-like format.} + +\item{...}{for potential future use.} -\item{warn}{If \code{TRUE}, a warning will be issued when there are both upper +\item{warn}{if \code{TRUE}, a warning will be issued when there are both upper and lower case versions of any nmtran-like column in the data frame.} } \value{ -A data frame with possibly renamed columns. +A data frame or event object with possibly renamed columns. The input data set, with select columns made lower case. } @@ -49,6 +63,9 @@ lctran(data) data <- data.frame(TIME = 0, AMT = 5, II = 24, addl = 2, wt = 80) uctran(data) +ev <- evd(amt = 100, evid = 3) +uctran(ev) + # warning data <- data.frame(TIME = 1, time = 2, CMT = 5) lctran(data) diff --git a/src/devtran.cpp b/src/devtran.cpp index b928fa769..cf75b8831 100644 --- a/src/devtran.cpp +++ b/src/devtran.cpp @@ -499,7 +499,7 @@ Rcpp::List DEVTRAN(const Rcpp::List parin, prob.rate_main(this_rec); } - if(prob.alag(this_cmtn) > mindt) { // there is a valid lagtime + if(prob.alag(this_cmtn) > mindt && this_rec->is_dose()) { // there is a valid lagtime if(this_rec->ss() > 0) { this_rec->steady(&prob, a[i], Fn,solver); diff --git a/src/mrgsolve.cpp b/src/mrgsolve.cpp index efb9bf392..9b774e9fd 100644 --- a/src/mrgsolve.cpp +++ b/src/mrgsolve.cpp @@ -130,17 +130,19 @@ arma::mat MVGAUSS(arma::mat& OMEGA, int n) { arma::vec eigval; arma::mat eigvec; - arma::eig_sym(eigval,eigvec, OMEGA); + arma::eig_sym(eigval, eigvec, OMEGA); - int ncol = OMEGA.n_cols; + if(arma::min(eigval) < 0.0) { + for(unsigned int i = 0; i < eigval.size(); ++i) { + eigval[i] = std::max(0.0, eigval[i]); + } + } - arma::mat X = arma::randn(n,ncol); + arma::mat X = arma::randn(n, OMEGA.n_cols); eigval = arma::sqrt(eigval); - - arma::mat Z = arma::diagmat(eigval); - - X = eigvec * Z * X.t(); + + X = eigvec * arma::diagmat(eigval) * X.t(); return X.t(); } diff --git a/tests/testthat/test-data_set.R b/tests/testthat/test-data_set.R index 888df1a11..165ba92d8 100644 --- a/tests/testthat/test-data_set.R +++ b/tests/testthat/test-data_set.R @@ -178,7 +178,7 @@ test_that("add position argument to expand observations issue-565", { expect_equal(dat1$time,dat2$time) }) -test_that("Convert names to lower case with lctran", { +test_that("Convert names to lower case with lctran [SLV-TEST-0004]", { data <- data.frame(time = 1, EVID = 2, ss = 2, foo = 5, BAR = 2) ans <- lctran(data) expect_equal( @@ -192,7 +192,7 @@ test_that("Convert names to lower case with lctran", { ) }) -test_that("Convert names to upper case with uctran", { +test_that("Convert names to upper case with uctran [SLV-TEST-0005]", { data <- data.frame(time = 1, EVID = 2, ss = 2, foo = 5, BAR = 2) ans <- uctran(data) expect_equal( @@ -205,3 +205,12 @@ test_that("Convert names to upper case with uctran", { regexp = "There are both upper and lower case" ) }) + +test_that("Convert event to upper or lower case [SLV-TEST-0006]", { + x <- uctran(ev(amt = 100, ii = 5)) + expect_equal(x@case, 1L) + expect_is(x, "ev") + x <- lctran(ev(amt = 100, ii = 5)) + expect_equal(x@case, 0L) + expect_is(x, "ev") +}) diff --git a/tests/testthat/test-evd.R b/tests/testthat/test-evd.R index a1d5edbd2..f21a9c4da 100644 --- a/tests/testthat/test-evd.R +++ b/tests/testthat/test-evd.R @@ -134,7 +134,7 @@ test_that("ev operations with evd objects", { d2 <- ev_rep(a, seq(3), n = 2) expect_is(d2, "data.frame") expect_equal(names(d2), toupper(names(d2))) - + d3 <- as_data_set(a, b, c) expect_is(d3, "data.frame") expect_equal(names(d3), toupper(names(d3))) @@ -144,5 +144,23 @@ test_that("ev operations with evd objects", { check <- names(d4)[-1] expect_equal(check, tolower(check)) expect_equal(names(d4)[1], "ID") + + d5 <- as_data_set(a) + expect_is(d5, "data.frame") + check <- names(d5)[-1] + expect_equal(check, toupper(check)) +}) + +test_that("test-evd expand.evd and evd_expand [SLV-TEST-0003]", { + data1 <- expand.ev(amt = 100, ii = 12, addl = 5, ss = 2) + data2 <- expand.evd(amt = 100, ii = 12, addl = 5, ss = 2) + data3 <- evd_expand(amt = 100, ii = 12, addl = 5, ss = 2) + expect_identical(data2, uctran(data1)) + expect_identical(data3, data2) +}) +test_that("test-evd coerce to ev", { + x <- evd(amt = 100, cmt = 5) + y <- as.ev(x) + expect_identical(y, ev(amt = 100, cmt = 5)) }) diff --git a/tests/testthat/test-rng.R b/tests/testthat/test-rng.R index f435b5561..5a9e2ed4a 100644 --- a/tests/testthat/test-rng.R +++ b/tests/testthat/test-rng.R @@ -51,5 +51,14 @@ test_that("Same seeds give same results with call to set.seed()", { expect_true(ident(out1,out1)) }) - - +test_that("mrgsolve simulate negative eigenvalue [SLV-TEST-0002]", { + mat <- bmat( + 1.53394, + 1.22232, 0.974014, + 2.78342 , 2.211570, 9.86881 + ) + eig <- eigen(mat) + expect_true(min(eig$value) < 0) + out <- mvgauss(mat, 1000) + expect_true(all(is.finite(out))) +})