Nothing
#' @title Simulation of a seamless phase II/III design with treatment
#' selection based on a short-term endpoint
#' @description Simulates a two-stage seamless phase II/III trial in which
#' several doses are compared with a common control. At the end of phase II
#' a single dose is carried forward based on the posterior benefit-risk
#' tradeoff of a binary short-term efficacy endpoint and a binary toxicity
#' endpoint. The confirmatory phase III analysis is performed on a
#' time-to-event long-term endpoint whose hazard depends on the short-term
#' response status, and the type I error rate is protected by a closed
#' testing procedure combined across the two stages.
#'
#' @param phase2SampleSizePerArm The number of subjects per arm enrolled in
#' phase II (stage 1).
#' @param phase3SampleSizePerArmMin The smallest number of subjects per arm
#' enrolled in phase III (stage 2). Operating characteristics are reported
#' for every stage 2 sample size from this value to
#' \code{phase3SampleSizePerArmMax}.
#' @param phase3SampleSizePerArmMax The largest number of subjects per arm
#' enrolled in phase III (stage 2).
#' @param responseProbControl The probability of a short-term response in the
#' control arm.
#' @param responseProbTreatments A vector of length \code{M} giving the
#' probability of a short-term response for each of the \code{M} doses under
#' investigation. Its length determines \code{M}.
#' @param toxicityProbTreatments A vector of length \code{M} giving the
#' probability of toxicity for each dose under investigation.
#' @param corrEfficacyToxicity The correlation between the bivariate latent
#' normal variables used to generate the binary efficacy and toxicity
#' endpoints. This is the correlation on the latent scale, not the
#' correlation of the observed binary endpoints. Use 0 for independent
#' endpoints.
#' @param hazardRateControl A vector of length 2 giving the hazard rate of the
#' long-term endpoint in the control arm for short-term nonresponders and
#' responders, respectively.
#' @param hazardRateTreatments An \code{M} by 2 matrix giving the hazard rate
#' of the long-term endpoint for each dose, with the first column for
#' short-term nonresponders and the second column for responders.
#' @param studyDurationPhase3 The duration of phase III, measured from the
#' first phase III enrollment to the final analysis.
#' @param toxicityWeight The weight placed on the posterior mean toxicity rate
#' in the benefit-risk tradeoff used for dose selection. Use 0 to select on
#' efficacy alone.
#' @param toxicityUpperLimit The prespecified upper limit for the toxicity
#' rate used in the safety criterion. Use 1 when the safety criterion is not
#' applied.
#' @param efficacyThreshold The threshold for the posterior probability that a
#' dose is superior to the control in short-term response. Use 0 when the
#' efficacy criterion is not applied.
#' @param safetyThreshold The threshold for the posterior probability that the
#' toxicity rate of a dose is below \code{toxicityUpperLimit}. Use 0 when
#' the safety criterion is not applied.
#' @param useUniformPrior Whether to use the uniform Beta(1,1) prior (the
#' default) or the Jeffreys Beta(0.5,0.5) prior for the beta-binomial
#' posterior used in dose selection.
#' @param methods A character vector naming the testing procedures to evaluate
#' for the confirmatory analysis. Any subset of \code{"ctdunnett"},
#' \code{"ctsimes"}, \code{"ctpooled"}, \code{"cer"}, \code{"TSSSD.k"},
#' \code{"TSSSD.uk"}, \code{"TSSSD.k.rank"}, \code{"TSSSD.uk.rank"},
#' \code{"TSSSD.k.ce"}, \code{"TSSSD.uk.ce"},
#' \code{"TSSSD.k.rank.ce"}, \code{"TSSSD.uk.rank.ce"},
#' \code{"naive"}, and \code{"ph3only"}. Restricting the set skips the
#' corresponding computation entirely, which matters because the methods
#' differ by orders of magnitude in cost.
#' @param accrualRatePhase2 The accrual rate per arm during phase II. Arrival
#' times follow a homogeneous Poisson process.
#' @param accrualRatePhase3 The accrual rate per arm during phase III.
#' @param followupTimePhase2 The follow-up time after the last phase II
#' enrollment across all arms. Phase III enrollment opens at that point. Use
#' 0 when dose selection occurs immediately after the last phase II
#' enrollment.
#' @param maxNumberOfIterations The number of simulated trials.
#' @param seed The seed for the random number generator.
#' @param nthreads The number of threads to use. The default, 0, leaves the
#' \code{RcppParallel} setting unchanged.
#'
#' @return A list of operating characteristics. Let \code{ngrid} denote
#' \code{phase3SampleSizePerArmMax - phase3SampleSizePerArmMin + 1}, the
#' number of stage 2 sample sizes examined, and let \code{M} denote the
#' number of doses. The list contains
#'
#' * \code{n1}, \code{n2}, \code{numberOfIterations}, \code{trueOBD}: The
#' design inputs echoed back, with \code{n2} the vector of stage 2 sample
#' sizes examined.
#'
#' * \code{selectionProb}: A vector of length \code{M} giving the probability
#' that each dose is selected at the end of phase II.
#'
#' * \code{pcs}: The percentage of simulated trials selecting the dose with
#' the largest true benefit-risk tradeoff \code{responseProbTreatments -
#' toxicityWeight * toxicityProbTreatments}.
#'
#' * \code{ave.event}: An \code{ngrid} by 3 matrix of the average number of
#' events in the selected dose and the control arm combined, in stage 1,
#' stage 2, and overall.
#'
#' * \code{methods}: The testing procedures evaluated, in canonical order.
#'
#' * \code{byMethod}: A named list with one element per evaluated method,
#' each containing
#'
#' - \code{gpower}: A vector of length \code{ngrid} giving the generalized
#' power, the probability of both selecting the true best dose and
#' rejecting its null hypothesis.
#'
#' - \code{prob.rej.each}: An \code{ngrid} by \code{M} matrix of the
#' probability of rejecting the null hypothesis for each dose conditional
#' on that dose being selected.
#'
#' - \code{prob.rej.any}: A vector of length \code{ngrid} giving the
#' probability of rejecting any null hypothesis.
#'
#' The method names are
#' \code{ctdunnett}, \code{ctsimes}, and \code{ctpooled} for the closed
#' testing procedure with the inverse normal combination of stage 1 and
#' stage 2 p-values, using the Dunnett, Simes, and pooled log-rank local
#' tests respectively; \code{cer} for the conditional error rate method;
#' \code{TSSSD.k} and \code{TSSSD.uk} for the original two-stage seamless
#' design boundaries with known and unknown correlation; \code{TSSSD.k.rank}
#' and \code{TSSSD.uk.rank} for rank-adjusted versions of those boundaries;
#' \code{TSSSD.k.ce}, \code{TSSSD.uk.ce}, \code{TSSSD.k.rank.ce}, and
#' \code{TSSSD.uk.rank.ce} for conditional-error updates that start from
#' nominal boundaries based on \code{n1/(n1+n2)} and then update boundaries
#' using the observed stage 1 z-statistic and observed information fraction;
#' \code{naive} for the
#' unadjusted log-rank test on the combined stage 1 and stage 2 data; and
#' \code{ph3only} for the unadjusted log-rank test on the stage 2 data only.
#' Because dose selection uses stage 1 data only, \code{ph3only} is based on
#' data independent of the selection and still controls the familywise error
#' rate, at the cost of discarding the stage 1 information. \code{naive}
#' reuses the selection data and is anticonservative; it is reported for
#' reference.
#'
#' @details
#' For each subject on a treatment arm, a bivariate latent normal vector
#' \eqn{(z_T, z_E)} with mean zero, unit variances, and correlation
#' \code{corrEfficacyToxicity} is drawn. The binary toxicity and efficacy
#' endpoints are obtained as \eqn{Y_T = I\{z_T \le \Phi^{-1}(p_T(d))\}} and
#' \eqn{Y_E = I\{z_E \le \Phi^{-1}(p_E(d))\}}, so that the marginal
#' probabilities are \eqn{p_T(d)} and \eqn{p_E(d)} while the two endpoints are
#' correlated. The long-term endpoint is exponential with a rate determined by
#' the realized short-term response status.
#'
#' Dose selection uses a beta-binomial model with independent priors, either
#' uniform Beta(1,1) or Jeffreys Beta(0.5,0.5) depending on
#' \code{useUniformPrior}. A dose enters the acceptable set when
#' the posterior probability that its
#' response rate exceeds that of the control is above \code{efficacyThreshold}
#' and the posterior probability that its toxicity rate is below
#' \code{toxicityUpperLimit} is above \code{safetyThreshold}. Among the
#' acceptable doses, the one maximizing the posterior mean benefit-risk
#' tradeoff is selected. When both thresholds are 0, all doses are acceptable.
#'
#' Phase III enrollment opens \code{followupTimePhase2} after the last phase
#' II enrollment across all arms, and the final analysis occurs
#' \code{studyDurationPhase3} later. Subjects whose long-term endpoint has not
#' occurred by then are censored at the analysis time.
#'
#' @references
#' Liyun Jiang and Ying Yuan. Seamless phase II/III design: a useful strategy
#' to reduce the sample size for dose optimization. Journal of the National
#' Cancer Institute. 2023, 115(9):1092-1098.
#'
#' Ping Gao and Yingqiu Li. Adaptive two-stage seamless sequential design for
#' clinical trials. Journal of Biopharmaceutical Statistics. 2025, 35(4),
#' 565-587.
#'
#' Cyrus Mehta, Ajoy Mukhopadhyay, and Martin Posch. Graph Based, Adaptive,
#' Multiarm, Multiple Endpoint, Two-Stage Designs. Statistics in Medicine.
#' 2025.
#'
#' @author Kaifeng Lu, \email{kaifenglu@@gmail.com}
#'
#' @examples
#'
#' # hazard rates in the nonresponse and response groups of the control arm
#' the0 <- c(log(2)/12, log(2)/24)
#'
#' # response rates of the two doses under investigation
#' pe <- c(0.6, 0.5)
#'
#' # hazard ratio versus control within each response group
#' hr <- rbind(c(0.75, 0.75), c(0.75, 0.75))
#' the1 <- t(sapply(1:2, function(k) hr[k,]*the0))
#'
#' sim <- lrsim_bmTrtSel(
#' phase2SampleSizePerArm = 50,
#' phase3SampleSizePerArmMin = 113,
#' phase3SampleSizePerArmMax = 118,
#' responseProbControl = 0.4,
#' responseProbTreatments = pe,
#' toxicityProbTreatments = c(0, 0),
#' corrEfficacyToxicity = 0,
#' hazardRateControl = the0,
#' hazardRateTreatments = the1,
#' studyDurationPhase3 = 42.1,
#' toxicityWeight = 0,
#' toxicityUpperLimit = 1,
#' efficacyThreshold = 0,
#' safetyThreshold = 0,
#' methods = c("ctdunnett", "ctsimes", "ctpooled",
#' "cer", "naive", "ph3only"),
#' accrualRatePhase2 = 3,
#' accrualRatePhase3 = 6,
#' followupTimePhase2 = 6,
#' maxNumberOfIterations = 100,
#' seed = 314159,
#' nthreads = 1)
#'
#' sim$pcs
#' sim$byMethod$ctdunnett$gpower
#'
#' @export
lrsim_bmTrtSel <- function(
phase2SampleSizePerArm = NA_integer_,
phase3SampleSizePerArmMin = NA_integer_,
phase3SampleSizePerArmMax = NA_integer_,
responseProbControl = NA_real_,
responseProbTreatments = NA_real_,
toxicityProbTreatments = NA_real_,
corrEfficacyToxicity = 0,
hazardRateControl = NA_real_,
hazardRateTreatments = matrix(),
studyDurationPhase3 = NA_real_,
toxicityWeight = NA_real_,
toxicityUpperLimit = NA_real_,
efficacyThreshold = 0,
safetyThreshold = 0,
useUniformPrior = TRUE,
methods = c("ctdunnett", "ctsimes", "ctpooled", "cer",
"TSSSD.k", "TSSSD.uk", "TSSSD.k.rank", "TSSSD.uk.rank",
"TSSSD.k.ce", "TSSSD.uk.ce",
"TSSSD.k.rank.ce", "TSSSD.uk.rank.ce",
"naive", "ph3only"),
accrualRatePhase2 = NA_real_,
accrualRatePhase3 = NA_real_,
followupTimePhase2 = 0,
maxNumberOfIterations = 1000,
seed = 0,
nthreads = 0) {
# Respect user-requested number of threads (best effort)
if (nthreads > 0) {
n_physical_cores <- parallel::detectCores(logical = FALSE)
RcppParallel::setThreadOptions(min(nthreads, n_physical_cores))
}
lrsim_bmTrtSel_Rcpp(
phase2SampleSizePerArm = phase2SampleSizePerArm,
phase3SampleSizePerArmMin = phase3SampleSizePerArmMin,
phase3SampleSizePerArmMax = phase3SampleSizePerArmMax,
responseProbControl = responseProbControl,
responseProbTreatments = responseProbTreatments,
toxicityProbTreatments = toxicityProbTreatments,
corrEfficacyToxicity = corrEfficacyToxicity,
hazardRateControl = hazardRateControl,
hazardRateTreatments = hazardRateTreatments,
studyDurationPhase3 = studyDurationPhase3,
toxicityWeight = toxicityWeight,
toxicityUpperLimit = toxicityUpperLimit,
efficacyThreshold = efficacyThreshold,
safetyThreshold = safetyThreshold,
useUniformPrior = useUniformPrior,
methods = methods,
accrualRatePhase2 = accrualRatePhase2,
accrualRatePhase3 = accrualRatePhase3,
followupTimePhase2 = followupTimePhase2,
maxNumberOfIterations = maxNumberOfIterations,
seed = seed)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.