library(crmPack)
library(checkmate)
library(ggplot2)

knitr::opts_chunk$set(echo = TRUE, error = TRUE)

This design introduces prototypes for a design where a monotherapy dose escalation is directly combined with a combination dose escalation (i.e. the same molecule with different doses but on top of another fixed dose molecule).

Note that in this design doc we don't include validation functions yet.

The data: DataGrouped

The idea is that we start from Data. Even though we need the 2 parts feature due to the safety run-in requirement, we can handle that separately in the DesignGrouped. We add a slot group which is a factor with 2 levels of the same length, similar as we add the biomarker w in the class DataDual.

The helper function groupData is only used in the simulation method to combine methods. Hence the ID and cohort information is not relevant and will be arbitrarily assigned to avoid problems with the Data validation.

Definition

.DataGrouped <- setClass(
  Class = "DataGrouped",
  slots = c(
    group = "factor"
  ),
  prototype = prototype(
    group = factor(levels = c("mono", "combo"))
  ),
  contains = "Data"
)

DataGrouped <- function(group,
                        ...) {
  d <- Data(...)
  .DataGrouped(
    d,
    group = group
  )
}

groupData <- function(mono_data, combo_data) {
  assert_class(mono_data, "Data")
  assert_class(combo_data, "Data")

  # We just combine most of the slots logically, but assign ID and cohort
  # arbitrarily to avoid Data object validation issues.
  df <- data.frame(
    x = c(mono_data@x, combo_data@x),
    y = c(mono_data@y, combo_data@y),
    group = factor(
      rep(c("mono", "combo"), c(length(mono_data@x), length(combo_data@x))),
      levels = c("mono", "combo")
    )
  )

  df <- df[order(df$x), ]

  DataGrouped(
    x = df$x,
    y = df$y,
    ID = seq_along(df$x),
    cohort = as.integer(factor(df$x)),
    doseGrid = sort(unique(c(mono_data@doseGrid, combo_data@doseGrid))),
    # Here comes the group information.
    group = df$group
  )
}

Methods

Note that for the plot method to be able to use the parent method for starting the plot, we need to make sure additional arguments are passed inside to the h_plot_data_df() function initializing the ggplot2 data set.

setMethod(
  f = "update",
  signature = signature(object = "DataGrouped"),
  definition = function(object, group, ..., check = TRUE) {
    assert_character(group)
    assert_flag(check)

    # Update slots corresponding to `Data` class.
    object <- callNextMethod(object = object, ..., check = FALSE)

    # Update the group information.
    group <- factor(group, levels = levels(object@group))
    object@group <- c(object@group, group)

    if (check) {
      validObject(object)
    }

    object
  }
)

setMethod(
  f = "plot",
  signature = signature(x = "DataGrouped", y = "missing"),
  definition = function(x, y, blind = FALSE, ...) {
    assert_flag(blind)

    # Call the superclass method, to get the initial plot layout.
    # Make sure `group` is available.
    p <- callNextMethod(x, blind = blind, legend = FALSE, group = x@group, ...)

    # Now add the faceting by group.
    p + facet_wrap(vars(group), nrow = 2)
  }
)

Example

my_data <- DataGrouped(
  x = c(0.1, 0.1, 0.5, 0.5, 1.5, 1.5),
  y = c(0, 0, 0, 0, 1, 0),
  group = factor(rep(c("mono", "combo"), 3), levels = c("mono", "combo")),
  ID = 1:6,
  cohort = rep(1:3, each = 2),
  doseGrid = c(0.1, 0.5, 1.5, 3, 6, seq(from = 10, to = 80, by = 2))
)
my_ref_dose <- 0.1 # Lowest dose in dose grid, see comment below.

plot(my_data)
my_data_2 <- update(my_data, x = 3, y = c(0, 0), group = c("mono", "combo"))
plot(my_data_2)

my_mono_data <- Data(
  x = c(0.1, 0.1, 0.5, 0.5, 1.5, 1.5),
  y = c(0, 0, 0, 0, 1, 0),
  ID = 1:6,
  cohort = rep(1:3, each = 2),
  doseGrid = c(0.1, 0.5, 1.5, 3, 6, seq(from = 10, to = 80, by = 2))
)
my_combo_data <- Data(
  x = c(0.1, 0.1, 0.5, 0.5, 1.5, 1.5),
  y = c(0, 0, 0, 0, 1, 0),
  ID = 1:6,
  cohort = rep(1:3, each = 2),
  doseGrid = c(0.1, 0.5, 1.5, 3, 6, seq(from = 10, to = 80, by = 2))
)
my_grouped_data <- groupData(my_mono_data, my_combo_data)

The model: LogisticLogNormalGrouped

We can inherit from ModelLogNormal. Compared to LogisticLogNormal etc. we have two additional parameters in theta which are then also exponentiated to obtain the corresponding alpha parameters.

We note that here refDose should be chosen carefully. - In a scenario where the toxicity can reasonably be assumed to be higher for the combination than for the mono agent: Then the refDose should be (below or) equal to the lowest dose, such that the term log(dose / ref_dose) is never negative and hence the probability of DLT will be higher for combo than for mono agent. - In a scenario where it is the other way around, e.g. the combination with a concomitant medication is compared with the mono agent, then refDose should be (above or) equal to the highest dose. - Otherwise it can be chosen in between.

Also variants of the proposed model might not restrict delta0 and delta1 to be positive giving more flexibility of the model and then the refDose choice is less critical.

Definition

Note that this can easily be extended later (either with a flag or with another class) to not use log dose but dose instead (divided by reference dose).

.LogisticLogNormalGrouped <- setClass(
  Class = "LogisticLogNormalGrouped",
  contains = "ModelLogNormal"
)

LogisticLogNormalGrouped <- function(mean, cov, ref_dose = 1) {
  params <- ModelParamsNormal(mean, cov)
  .LogisticLogNormalGrouped(
    params = params,
    ref_dose = positive_number(ref_dose),
    priormodel = function() {
      theta ~ dmnorm(mean, prec)
      alpha0 <- theta[1]
      delta0 <- exp(theta[2])
      alpha1 <- exp(theta[3])
      delta1 <- exp(theta[4])
    },
    datamodel = function() {
      for (i in 1:nObs) {
        logit(p[i]) <- (alpha0 + is_combo[i] * delta0) +
          (alpha1 + is_combo[i] * delta1) * log(x[i] / ref_dose)
        y[i] ~ dbern(p[i])
      }
    },
    modelspecs = function(group, from_prior) {
      ms <- list(
        mean = params@mean,
        prec = params@prec
      )
      if (!from_prior) {
        ms$ref_dose <- ref_dose
        ms$is_combo <- as.integer(group == "combo")
      }
      ms
    },
    init = function() {
      list(theta = c(0, 1, 1, 1))
    },
    datanames = c("nObs", "y", "x"),
    sample = c("alpha0", "delta0", "alpha1", "delta1")
  )
}

Methods

setMethod(
  f = "prob",
  signature = signature(
    dose = "numeric",
    model = "LogisticLogNormalGrouped",
    samples = "Samples"
  ),
  definition = function(dose, model, samples, group) {
    assert_numeric(dose, lower = 0L, any.missing = FALSE, min.len = 1L)
    assert_subset(c("alpha0", "delta0", "alpha1", "delta1"), names(samples))
    assert_length(dose, len = size(samples))
    assert_factor(group, len = length(dose), levels = c("mono", "combo"))

    alpha0 <- samples@data$alpha0
    delta0 <- samples@data$delta0
    alpha1 <- samples@data$alpha1
    delta1 <- samples@data$delta1
    ref_dose <- as.numeric(model@ref_dose)
    is_combo <- as.integer(group == "combo")
    plogis((alpha0 + is_combo * delta0) + (alpha1 + is_combo * delta1) * log(dose / ref_dose))
  }
)

setMethod(
  f = "dose",
  signature = signature(
    x = "numeric",
    model = "LogisticLogNormalGrouped",
    samples = "Samples"
  ),
  definition = function(x, model, samples, group) {
    assert_probabilities(x)
    assert_subset(c("alpha0", "delta0", "alpha1", "delta1"), names(samples))
    assert_length(x, len = size(samples))
    assert_factor(group, len = length(x), levels = c("mono", "combo"))

    alpha0 <- samples@data$alpha0
    delta0 <- samples@data$delta0
    alpha1 <- samples@data$alpha1
    delta1 <- samples@data$delta1
    ref_dose <- as.numeric(model@ref_dose)
    is_combo <- as.integer(group == "combo")
    exp((logit(x) - (alpha0 + is_combo * delta0)) / (alpha1 + is_combo * delta1)) * ref_dose
  }
)

Note that for the fit method we need to make sure that the ... are passed down to prob, such that we can pass down the group argument.

We have added a corresponding unit test already in this PR.

Note that in production we will need to modify the doseFunction and probFunction method definitions accordingly to allow for the passing of the group (or other) arguments as well.

Example

my_model <- LogisticLogNormalGrouped(
  mean = rep(0, 4),
  cov = diag(rep(1, 4)),
  ref_dose = my_ref_dose
)

my_options <- McmcOptions()
my_samples <- mcmc(my_data, my_model, my_options)
str(my_samples)

mean(my_samples@data$delta0)
mean(my_samples@data$delta1)
mean(prob(dose = 5, my_model, my_samples, factor("mono", levels = c("mono", "combo"))))
mean(prob(dose = 5, my_model, my_samples, factor("combo", levels = c("mono", "combo"))))

one_sample <- Samples(
  data = list(alpha0 = -0.5, delta0 = 0.1, alpha1 = 0.3, delta1 = 0.5),
  options = McmcOptions(samples = 1L)
)

td50_mono <- dose(x = 0.5, my_model, one_sample, factor("mono", levels = c("mono", "combo")))
td50_combo <- dose(x = 0.5, my_model, one_sample, factor("combo", levels = c("mono", "combo")))

prob(dose = td50_mono, my_model, one_sample, factor("mono", levels = c("mono", "combo")))
prob(dose = td50_combo, my_model, one_sample, factor("combo", levels = c("mono", "combo")))

fit_mono <- fit(one_sample, my_model, my_data, group = factor("mono", levels = c("mono", "combo")))
fit_combo <- fit(one_sample, my_model, my_data, group = factor("combo", levels = c("mono", "combo")))
matplot(x = fit_mono$dose, y = cbind(fit_mono$middle, fit_combo$middle), type = "l")

Prior elicitation

As usual we can sample from the prior and look at the fit results. For this to work well we also need to pass additional arguments in the plot method.

We have added a unit test for the plot method of the Samples class already as well.

# Need to choose the prior parameters here.
my_model <- LogisticLogNormalGrouped(
  mean = rep(0, 4),
  cov = diag(rep(1, 4)),
  ref_dose = my_ref_dose
)

# Create empty data.
my_empty_data <- DataGrouped(
  doseGrid = c(0.1, 0.5, 1.5, 3, 6, seq(from = 10, to = 80, by = 2)),
  group = factor(levels = c("mono", "combo"))
)

# Sample from the prior.
my_options <- McmcOptions()
my_prior_samples <- mcmc(my_empty_data, my_model, my_options)
str(my_prior_samples)

# Look at fit results.
plot(
  my_prior_samples, my_model, my_empty_data,
  group = factor("mono", levels = c("mono", "combo"))
)
plot(
  my_prior_samples, my_model, my_empty_data,
  group = factor("combo", levels = c("mono", "combo"))
)

So we can see here that the design is very informative, because the credible intervals are pretty narrow. We also expect a high toxicity already at low dose levels. Let's therefore modify the parameters.

# Need to choose the prior parameters here.
my_model <- LogisticLogNormalGrouped(
  mean = c(-4, -4, -4, -4),
  cov = diag(rep(6, 4)),
  ref_dose = my_ref_dose
)
my_prior_samples <- mcmc(my_empty_data, my_model, my_options)
plot(
  my_prior_samples, my_model, my_empty_data,
  group = factor("mono", levels = c("mono", "combo"))
)
plot(
  my_prior_samples, my_model, my_empty_data,
  group = factor("combo", levels = c("mono", "combo"))
)

This looks more reasonable.

The design: DesignGrouped

It seems easiest to combine two Design objects here in one - so we have one for the mono agent and one for the combo group, because then we get all the rules at once. Note that his implicitly handles the randomization ratio as the ratio between the cohort sizes, but allows for much more flexibility. If missing in the user constructor, we assume that the same rule as for the mono agent is followed. Note that for the part 1 handling with fewer patients per cohort the design class does not need to know about it, because the data and the size rules handle this already.

Here we add the flag first_cohort_mono_only. When turned on, this means that we first test one mono agent cohort. Once that DLT data has been collected, we proceed from the second cohort onwards with concurrent mono and combo cohorts.

We also add a flag same_dose_for_all to specify whether the lower dose of the separately determined mono and combo doses should be used as the next dose for both mono and combo. This might or might not be desired in the given situation.

Note that we deliberately ignore information in Design, including the model and the placebo cohort size information in there. This is not super clean, but ok at least for this first prototype.

Definition

.DesignGrouped <- setClass(
  Class = "DesignGrouped",
  slots = c(
    model = "LogisticLogNormalGrouped",
    mono = "Design",
    combo = "Design",
    first_cohort_mono_only = "logical",
    same_dose_for_all = "logical"
  )
)

DesignGrouped <- function(model,
                          mono,
                          combo,
                          first_cohort_mono_only,
                          same_dose_for_all,
                          ...) {
  if (missing(combo)) combo <- mono

  .DesignGrouped(
    model = model,
    mono = mono,
    combo = combo,
    first_cohort_mono_only = first_cohort_mono_only,
    same_dose_for_all = same_dose_for_all
  )
}

Methods

Let's for now just look at the simulation method. In production we also need the examine method though, but it is going to follow a similar logic.

For simplicity we don't support the firstSeparate argument here for now. Since we have two groups now, we also have two true dose-toxicity relationships now. We therefore add the combo_truth argument.

Note that in order for the nextBest method to work out of the box, we just need to pass again the ... arguments down to e.g. the prob method used inside. We do this in this PR already for the NextBestNCRM method as illustration.

The same applies for the stopTrial methods. We here do it for StoppingTargetProb as illustration.

For both methods updates we have unit tests in this PR already.

setMethod("simulate",
  signature =
    signature(
      object = "DesignGrouped",
      nsim = "ANY",
      seed = "ANY"
    ),
  def =
    function(object,
             nsim = 1L,
             seed = NULL,
             truth,
             combo_truth,
             args = NULL,
             mcmcOptions = McmcOptions(),
             parallel = FALSE,
             nCores = min(parallelly::availableCores(), 5),
             ...) {
      ## checks and extracts
      assert_function(truth)
      assert_function(combo_truth)
      assert_count(nsim, positive = TRUE)
      assert_flag(parallel)
      assert_count(nCores, positive = TRUE)

      args <- as.data.frame(args)
      nArgs <- max(nrow(args), 1L)

      ## seed handling
      RNGstate <- setSeed(seed)

      ## from this,
      ## generate the individual seeds for the simulation runs
      simSeeds <- sample.int(n = 2147483647, size = nsim)

      ## the function to produce the run a mono simulation
      ## with index "iterSim"
      runSim <- function(iterSim) {
        ## set the seed for this run
        set.seed(simSeeds[iterSim])

        ## what is now the argument for the truth?
        ## (appropriately recycled)
        thisArgs <- args[(iterSim - 1) %% nArgs + 1, , drop = FALSE]

        ## so this truth for mono agent is...
        this_mono_truth <- function(dose) {
          do.call(truth, c(dose, thisArgs))
        }

        ## and for combo similarly:
        this_combo_truth <- function(dose) {
          do.call(combo_truth, c(dose, thisArgs))
        }

        ## start the simulated data with the provided one
        this_mono_data <- object@mono@data
        this_combo_data <- object@combo@data

        ## shall we stop the trial? separately for mono and combo.
        ## First, we want to continue with the starting dose.
        ## This variable is updated after each cohort in the loop.
        stop_mono <- stop_combo <- FALSE

        ## are we in the first cohort? This is to support the staggering feature
        first_cohort <- TRUE

        ## what are the next doses to be used?
        ## initialize with starting doses
        if (object@mono@startingDose < object@combo@startingDose) {
          warning("combo starting dose usually not higher than mono starting dose")
        }
        if (object@same_dose_for_all) {
          this_mono_dose <- this_combo_dose <- min(
            object@mono@startingDose,
            object@combo@startingDose
          )
        } else {
          this_mono_dose <- object@mono@startingDose
          this_combo_dose <- object@combo@startingDose
        }

        ## inside this loop we simulate the whole trial, until stopping
        while (!(stop_mono && stop_combo)) {
          if (!stop_mono) {
            ## what is the probability for tox. at this dose?
            this_mono_prob <- this_mono_truth(this_mono_dose)

            ## what is the mono cohort size at this dose?
            this_mono_size <- size(
              object@mono@cohort_size,
              dose = this_mono_dose,
              data = this_mono_data
            )
            ## we can dose the mono patients
            this_mono_dlts <- rbinom(
              n = this_mono_size,
              size = 1,
              prob = this_mono_prob
            )
            ## update the mono data with this cohort
            this_mono_data <- update(
              object = this_mono_data,
              x = this_mono_dose,
              y = this_mono_dlts
            )
          }

          ## Check if we also dose combo patients now
          if (!stop_combo && (!first_cohort || !object@first_cohort_mono_only)) {
            this_combo_prob <- this_combo_truth(this_combo_dose)

            ## what is the combo cohort size at this dose?
            this_combo_size <- size(
              object@combo@cohort_size,
              dose = this_combo_dose,
              data = this_combo_data
            )
            ## we can dose the combo patients
            this_combo_dlts <- rbinom(
              n = this_combo_size,
              size = 1,
              prob = this_combo_prob
            )
            ## update the data with this cohort
            this_combo_data <- update(
              object = this_combo_data,
              x = this_combo_dose,
              y = this_combo_dlts
            )
          }

          ## update first cohort flag
          if (first_cohort) {
            first_cohort <- FALSE
          }

          ## join the data together
          grouped_data <- groupData(
            this_mono_data,
            this_combo_data
          )

          ## generate samples from the joint model
          thisSamples <- mcmc(
            data = grouped_data,
            model = object@model,
            options = mcmcOptions
          )

          if (!stop_mono) {
            mono_dose_limit <- maxDose(
              object@mono@increments,
              data = this_mono_data
            )

            ## => what is the next best dose for mono?
            this_mono_dose <- nextBest(
              object@mono@nextBest,
              doselimit = mono_dose_limit,
              samples = thisSamples,
              model = object@model,
              data = grouped_data,
              group = factor("mono", levels = c("mono", "combo"))
            )$value

            stop_mono <- stopTrial(
              object@mono@stopping,
              dose = this_mono_dose,
              samples = thisSamples,
              model = object@model,
              data = this_mono_data,
              group = factor("mono", levels = c("mono", "combo"))
            )

            stop_mono_results <- crmPack:::h_unpack_stopit(stop_mono)
          }

          if (!stop_combo) {
            combo_dose_limit <- if (is.na(this_mono_dose)) {
              0
            } else {
              combo_max_dose <- maxDose(
                object@combo@increments,
                data = this_combo_data
              )
              min(
                combo_max_dose,
                this_mono_dose,
                na.rm = TRUE
              )
            }

            this_combo_dose <- nextBest(
              object@combo@nextBest,
              doselimit = combo_dose_limit,
              samples = thisSamples,
              model = object@model,
              data = grouped_data,
              group = factor("combo", levels = c("mono", "combo"))
            )$value

            stop_combo <- stopTrial(
              object@combo@stopping,
              dose = this_combo_dose,
              samples = thisSamples,
              model = object@model,
              data = this_combo_data,
              group = factor("combo", levels = c("mono", "combo"))
            )

            stop_combo_results <- crmPack:::h_unpack_stopit(stop_combo)

            if (object@same_dose_for_all) {
              this_mono_dose <- this_combo_dose <- min(
                this_mono_dose,
                this_combo_dose
              )
            }
          }
        }

        ## get the fit, separately for mono and for combo
        fit_mono <- fit(
          object = thisSamples,
          model = object@model,
          data = grouped_data,
          group = factor("mono", levels = c("mono", "combo"))
        )
        fit_combo <- fit(
          object = thisSamples,
          model = object@model,
          data = grouped_data,
          group = factor("combo", levels = c("mono", "combo"))
        )

        ## return the results
        thisResult <- list(
          mono = list(
            data = this_mono_data,
            dose = this_mono_dose,
            fit = subset(fit_mono, select = -dose),
            stop = attr(stop_mono, "message"),
            report_results = stop_mono_results
          ),
          combo = list(
            data = this_combo_data,
            dose = this_combo_dose,
            fit = subset(fit_combo, select = -dose),
            stop = attr(stop_combo, "message"),
            report_results = stop_combo_results
          )
        )

        return(thisResult)
      }

      resultList <- crmPack:::getResultList(
        fun = runSim,
        nsim = nsim,
        vars =
          c(
            "simSeeds",
            "args",
            "nArgs",
            "truth",
            "object",
            "mcmcOptions"
          ),
        parallel = if (parallel) nCores else NULL
      )

      ## now we have a list with each element containing mono and combo,
      ## but we want it now the other way around, i.e. a list with 2 elements
      ## mono and combo and the iterations inside.
      resultList <- list(
        mono = lapply(resultList, "[[", "mono"),
        combo = lapply(resultList, "[[", "combo")
      )

      ## put everything in a list with both mono and combo Simulations:
      lapply(resultList, function(this_list) {
        data_list <- lapply(this_list, "[[", "data")
        recommended_doses <- as.numeric(sapply(this_list, "[[", "dose"))
        fit_list <- lapply(this_list, "[[", "fit")
        stop_reasons <- lapply(this_list, "[[", "stop")
        report_results <- lapply(this_list, "[[", "report_results")
        stop_report <- as.matrix(do.call(rbind, report_results))

        Simulations(
          data = data_list,
          doses = recommended_doses,
          fit = fit_list,
          stop_reasons = stop_reasons,
          stop_report = stop_report,
          seed = RNGstate
        )
      })
    }
)

Example

my_stopping <- StoppingTargetProb(target = c(0.2, 0.35), prob = 0.5) |
  StoppingMinPatients(20) |
  StoppingMissingDose()
my_increments <- IncrementsDoseLevels(levels = 3L)
my_next_best <- NextBestNCRM(
  target = c(0.2, 0.3),
  overdose = c(0.3, 1),
  max_overdose_prob = 0.3
)
my_cohort_size <- CohortSizeConst(3)
empty_data <- Data(doseGrid = c(0.1, 0.5, 1.5, 3, 6, seq(from = 10, to = 80, by = 2)))

my_design <- DesignGrouped(
  model = my_model,
  mono = Design(
    model = .ModelLogNormal(), # is not used
    stopping = my_stopping,
    increments = my_increments,
    nextBest = my_next_best,
    cohort_size = my_cohort_size,
    data = empty_data,
    startingDose = 0.1
  ),
  combo = Design(
    model = .ModelLogNormal(), # is not used
    stopping = my_stopping,
    increments = my_increments,
    nextBest = my_next_best,
    cohort_size = my_cohort_size,
    data = empty_data,
    startingDose = 0.1
  ),
  first_cohort_mono_only = TRUE,
  same_dose_for_all = FALSE
)

my_model@datamodel
my_truth <- function(x) plogis(-4 + 0.2 * log(x / 0.1))
my_combo_truth <- function(x) plogis(-4 + 0.5 * log(x / 0.1))
matplot(
  x = empty_data@doseGrid,
  y = cbind(
    mono = my_truth(empty_data@doseGrid),
    combo = my_combo_truth(empty_data@doseGrid)
  ),
  type = "l",
  ylab = "true DLT prob",
  xlab = "dose"
)
legend("topright", c("mono", "combo"), lty = c(1, 2), col = c(1, 2))

my_sims <- simulate(
  my_design,
  nsim = 20,
  seed = 123,
  truth = my_truth,
  combo_truth = my_combo_truth
)

Let's have a look at the simulation results.

plot(my_sims$mono)
plot(my_sims$combo)

mono_sims_sum <- summary(my_sims$mono, truth = my_truth)
combo_sims_sum <- summary(my_sims$combo, truth = my_combo_truth)

mono_sims_sum
combo_sims_sum

plot(mono_sims_sum)
plot(combo_sims_sum)

So this seems to work nicely because we are now back to "normal" simulation results.

We only needed to add a condition inside the Simulations plot method to make sure that if there is no patients dosed at all (which can happen here for combo when mono is too toxic already) that it still works.

cbind(
  mono = my_sims$mono@doses,
  combo = my_sims$combo@doses
)

plot(my_sims$mono@data[[1]])
plot(my_sims$combo@data[[1]])

So here we have allowed the mono and combo doses to be different for each cohort. And we can now also try out forcing the same dose for the mono and combo cohorts:

my_design2 <- my_design
my_design@same_dose_for_all <- TRUE

my_sims_same_dose <- simulate(
  my_design2,
  nsim = 1,
  seed = 123,
  truth = my_truth,
  combo_truth = my_combo_truth
)

plot(my_sims_same_dose$mono)
plot(my_sims_same_dose$combo)

plot(my_sims_same_dose$mono@data[[1]])
plot(my_sims_same_dose$combo@data[[1]])

This looks ok. Note that when we have staggered the first cohort as here and limit the sample size for mono and combo at the same maximum then we might stop mono earlier than combo.



Roche/crmPack documentation built on June 30, 2024, 1:31 a.m.