TrialSimulator 1.33.0
New features
- New method
trial$conditionalPower()computes conditional power at a triggered interim milestone for every treatment-vs-placebo comparison of a time-to-event endpoint, under a group sequential design with one interim and one final analysis. It pulls the milestone’s locked data automatically and callsfitLogrank()internally to obtain the observed z statistic and number of events of each comparison. Conditional power can be evaluated at the interim trend (default), under the null, or at a user-specified hazard ratio, which is converted internally using the allocation ratio in effect at the milestone. Itsalphaargument is the one-sided nominal significance level corresponding to the planned final critical boundary, not the total design alpha or alpha spent at the final look. The calculation assumes the trial continues as designed (constant allocation ratio of the compared arms, planned final statistic and boundary); it is users’ responsibility to call it only when legitimate, e.g., not after the allocation ratio of the compared arms was adapted, asTrialSimulatordoes not verify this. Results are validated againstrpactandgsDesignin unit tests.
TrialSimulator 1.31.0
Updates
-
controller$run()can no longer be called twice withoutreset()in between. Previously, a secondrun()silently continued the already-executed trial (all milestones already triggered, snapshots partially overwritten) instead of starting a new simulation. To define a milestone whose triggering condition or action depends on interim results, register it upfront and revise it within an action function throughupdate_milestone(). - A milestone that would lock data at a time earlier than the current trial time now raises an informative error before any trial state is modified, naming both milestones involved and the seed to debug with. Previously the violation was caught by
save_milestone_time()only after the trial clock had been moved backwards and the locked snapshot stored; that check remains as an internal assertion, andset_current_time()gains a backstop against backward clock moves. -
listener$add_milestones()now raises an error when a milestone with the same name is already registered. Previously it warned and silently over-wrote the registered milestone. To modify a not-yet-triggered milestone within an action function, useupdate_milestone().
TrialSimulator 1.30.1
Updates
- Continue minimizing the public API of R6 classes:
Arms$get_number_endpoints(),Endpoints$get_readout()andEndpoints$get_type()are now private. The machinery methods ofArms(get_name(),get_endpoints_name(),has_endpoint()andupdate_endpoint_generator()) and ofEndpoints(get_uid()andupdate_generator()), which stay public only because trials and arms invoke them, now carry a bold warning that users should not call them directly. - Polish class documentation: user-facing methods of
Controllers,ArmsandEndpointsnow come with brief usage notes in the class description; exploratory methods (test_generator(),get_generator()andget_name()ofEndpoints;generate_data()andget_endpoints()ofArms) are documented as helpful for understanding the classes but not needed in formal simulation.
TrialSimulator 1.30.0
Updates
- Continue minimizing the public API of R6 classes:
Controllers$get_trial(),Controllers$get_listener(),Controllers$mute(),Milestones$execute_action(),Milestones$get_type(),Milestones$get_trigger_condition(),Milestones$get_action(),Regimens$get_number_time_selector()andRegimens$get_number_data_modifier()are now private. They were used only internally by their own classes. - The remaining public methods of
Milestonesand all public methods ofRegimensare invoked on their objects by other components of the package (listeners and trials) and stay public only for that reason; they now carry a bold warning that users should not call them directly, in the same style as theTrialsandListenersclasses.
TrialSimulator 1.29.0
Updates
- Minimize the public API of the
Listenersclass:get_milestones()is now private. The machinery methodsmonitor(),mute()andreset(), which stay public only because the controller invokes them, now carry a bold warning that users should not call them directly, in the same style as theTrialsclass.
TrialSimulator 1.28.0
New Feature
- New adaptation method
trial$update_milestone()(wrapperupdate_milestone()): update the triggering condition and/or the action of a not-yet-triggered milestone from within an action function. The update takes effect right after the current action function returns, and the as-designed milestone is restored between simulation replicates.
TrialSimulator 1.27.0
Updates
- Minimize the public API of the
Trialsclass: 38 methods used only internally are now private (includingcensor_trial_data(),roll_back(),enroll_patients(),get_trial_data()andindependentIncrement()), leaving 34 public members whose definitions and documentation are reorganized into sections: adaptation methods, methods callable within action functions (data access and manipulation, trial status queries, statistical testing), trial setup (add_regimen(), which must not be called within action functions), and internal machinery. Machinery methods that stay public only because other components of the package invoke them now carry a bold warning that users should not call them directly. -
make_snapshot()distinguishes private methods from data fields viabindingIsLocked()(R6 locks the binding of every member defined as a function) instead of a hardcoded name list, so snapshot/reset stay correct as methods are added.
TrialSimulator 1.26.2
Updates
- Replace deprecated
.data$in tidyselect contexts (rename()/select()in the correlated PFS/OS generators,PiecewiseConstantExponentialRNG(), and endpoint naming) with string literals, eliminating the tidyselect deprecation warnings that flooded test output..data$remains in data-masking contexts (filter(),mutate(),aes()), where it is still the recommended idiom;R CMD checkstays clean. -
set_dropout()documentation no longer claims adaptive use: dropout times are generated at enrollment, so updating the generator within an action function would not apply to enrolled patients. - Ignore the knitr vignette cache in
.gitignoreand exclude.DS_Storefrom the build via.Rbuildignore. - Fix the remaining test-time warnings:
event_plot()now selects the endpoint column viaall_of()(tidyselect external-vector deprecation), andplot.three_state_model()zooms withcoord_cartesian()instead ofxlim(), so the dashed guide segments anchored at the axes are clipped and rendered rather than dropped with a ggplot2 warning. - Fix the root cause of a stray
Rplots.pdfappearing after test runs:summarizeDataFrame()restoredpar()settings viaon.exit()after itspng()device was already closed, which implicitly opened the default device.par()settings are local to the device and need no restore.
TrialSimulator 1.26.1
Bug Fix
-
make_snapshot()silently droppedNULL-valued private fields from the snapshot (assigningNULLwith[[<-deletes a list entry);reset()’s explicit re-null list masked this for existing fields. The snapshot now preservesNULLfields, so any future field is restored across simulation replicates by construction. - Fix an error message in
dunnettTest()that referenced an undefined variable. - Fix a summary line in the
doseRangingvignette that compared a string literal instead of thedecisioncolumn.
Updates
- Regenerate the precomputed outputs of the
doseRangingandfixedDesignvignettes under the 1.25.2 enrollment convention; thefixedDesignoutput now contains the 1,000 replicates its code states (previously 100). - Add an
R CMD checkGitHub Actions workflow (macOS/Windows/Ubuntu; R release/devel/oldrel). - Add
\valuesections to exported help topics, a grouped reference index for the pkgdown site, and mention the newest adaptations in theactionFunctionsvignette introduction. - Remove the unused
graphicalMCPentry fromSuggests.
TrialSimulator 1.26.0
New Feature
- New adaptation method
trial$update_accrual_rate()(wrapperupdate_accrual_rate()): update the accrual rate of the recruitment curve at a milestone, e.g., to revise recruitment after dose selection or enrichment, or to pause it for a period after an interim decision.end_timeof the newaccrual_rateis measured from the milestone; patients not yet enrolled (and the enrollment reserves used byresize()) are re-planned and re-randomized under the new schedule, while enrolled patients are left unchanged. Like other adaptations, it must be called within an action function, after a milestone has been triggered.
TrialSimulator 1.25.2
Updates
-
StaggeredRecruiter()now enrolls patientkwhen the planned cumulative accrual reachesk(previouslyk - 1): under a constant raterthen-th patient enrolls exactly atn / r, so a milestone triggered byenrollment(n)occurs exactly at the planned accrual time. The first patient enrolls at1 / piecewise_rateinstead of time 0, and all enrollment times shift by one inter-arrival accordingly; simulation results under a fixed seed differ slightly from 1.25.1. The precomputed output of theadaptiveDesignvignette is regenerated under the new convention.
TrialSimulator 1.25.1
Bug Fix
- Fix an infinite loop in patient enrollment when fractional sample ratios are combined with stratification factors: the pool labels now match the unstratified randomization queue in that case.
Updates
- Warn when fractional sample ratios are used in a trial with stratification factors: stratified randomization is not supported in that case and unenrolled patients are randomized by
sample()without stratification. The warning is suppressed whensilent = TRUE.
TrialSimulator 1.25.0
New Feature
- New adaptation method
trial$stop_followup()(wrapperstop_followup()): stop follow-up of a subset of enrolled patients at a milestone, optionally after extra follow-up time (additional_followup). Patients are selected by conditions compatible withdplyr::filter; affected time-to-event endpoints are censored and non-time-to-event readouts are set to missing. This adaptation supports, e.g., treatment discontinuation and enrichment design. Like other adaptations, it must be called within an action function, after a milestone has been triggered.
Updates
-
censor_trial_data()gains...to further restrict the patients to be censored by conditions compatible withdplyr::filter, in addition toselected_armsandenrolled_before. Internal callers are unchanged and benchmark at parity with 1.24.0; simulation outputs are identical.
TrialSimulator 1.23.0
Updates
-
trial()now defaultsenrollertoStaggeredRecruiterand accepts no other enroller: any non-StaggeredRecruitervalue is rejected with an informative error. Theenrollerargument is retained for backward compatibility, so existing code that passesenroller = StaggeredRecruiterexplicitly is unaffected, and code that omits it now gets the default.
TrialSimulator 1.22.0
New Feature
-
StaggeredRecruiter()now supports recruitment pauses: a window withpiecewise_rate = 0enrolls no one while calendar time still advances, so accrual resumes at the window’send_time. Pauses may occur in the first window or span several consecutive windows (e.g., a safety hold, a site not yet activated, or a seasonal gap).
Updates
-
StaggeredRecruiter()enrollment times are now the deterministic inverse of the cumulative accrual intensity: the cumulative accrual capacity increases by window length ×piecewise_rateacross each window (no per-window truncation), so an integer-capacity window holds exactly that many patients. Single open-ended schedules and integer-capacity windows are unchanged; schedules with fractional per-window capacity now yield slightly different (more accurate) times. -
StaggeredRecruiter()input validation is stricter: the lastend_timemust beInfwith a positive rate (so the schedule can always supply the patients the engine requests, including the inflated count used for adaptive resizing); a positive rate too low to enroll even one patient (window length ×piecewise_rate< 1) is now an error pointing topiecewise_rate = 0; andnmust be a positive integer.
TrialSimulator 1.21.0
Bug Fix
-
GraphicalTesting$test()no longer errors withalpha_spent should be monotonically increasingwhen a hypothesis is first tested only at a later (or final) look. This happens when a hypothesis holds zero or very small allocated alpha at early looks and inherits alpha after another hypothesis is rejected: those looks yield a degenerate (flat, near-zero) reconstructed cumulative alpha-spent sequence. Such entries are now truncated and floored to a strictly increasing sequence before being passed to the group sequential boundary computation.
Unit Tests
- Add regression tests for the above fix (alpha inherited only at the final look; tiny-alpha early looks with degenerate spending boundaries), plus tests that re-including an already-rejected hypothesis at a later look is ignored safely and that
computeCumulativeAlphaSpenttreatsInfboundaries as zero increments.
TrialSimulator 1.20.0
New Feature
-
endpoint()now acceptstype = "baseline"for a non-tte endpoint observed at randomization (e.g., a baseline covariate, biomarker, or subgroup indicator). Its readout is0by definition and must be omitted fromreadout. For forward compatibility, a non-tte endpoint observed at randomization can still be defined the existing way withreadout = 0.Endpoints$new()continues to recognize only"tte"and"non-tte"; the conversion happens inendpoint().
TrialSimulator 1.19.1
Documentation
- Add the “Crossover at milestone” vignette demonstrating
crossover(). - Update the
regimen()/crossover()examples and help pages to use the naturalifelse(condition, new_value, original)idiom inhow()and to return only the switching patients inwhat(), rather than relying onNAto mark unchanged cells or non-switchers. The engine still acceptsNAfor backward compatibility.
TrialSimulator 1.19.0
New Feature
- Add
crossover()for milestone-triggered treatment crossover. Called inside a milestone’s action function, it lets patients still in the trial switch treatment at (or after) the milestone, altering only their post-switch endpoint values. Regimens are unified under one contract keyed off an earliest crossover time; the classic enrollment regimen added viaadd_regimen()is theT = 0case.
Updates
- A regimen’s
how()may now only modify post-switch outcomes: returning a value that differs from the original for an endpoint whose readout/event is at or beforeswitch_time(a pre-switch or already-observed outcome) raises an error. Guard suchhow()functions accordingly, e.g.os = ifelse(os > switch_time, new_os, os).
TrialSimulator 1.18.4
CRAN release: 2026-05-13
Bug Fix
- Re-export
Surv()andstrata()from thesurvivalpackage so that user-supplied formulas in action functions (e.g.Surv(os, os_event) ~ arm) work afterlibrary(TrialSimulator)alone, without requiringlibrary(survival)or thesurvival::prefix. This fixes errors in parallel runs (n_workers > 1) where each worker only attachesTrialSimulator(reported in #14).
TrialSimulator 1.18.0
New Feature
- Accelerate milestone trigger evaluation via C++ helpers (Rcpp) for
eventNumber()andenrollment()conditions. Wall-time reduction ranges from a few percent to ~20% depending on how condition-evaluation-heavy the design is, with no change to results. Setoptions(trialsimulator.use_cpp = FALSE)to fall back to the original R implementation.
TrialSimulator 1.17.0
New Feature
- Add
CorrelatedPfsAndOs2()to simulate correlated PFS and OS using a Gumbel copula while targeting marginal PFS/OS medians and Kendall’s tau between observed, uncensored PFS and OS times.
Updates
- Add a vignette for simulating correlated PFS and OS using the Gumbel copula method, alongside the existing illness-death model vignette.
- Skip an unnecessary
dplyr::filter()call inTrials$get_event_tables()when no filter expression is provided, reducing total simulation wall time by roughly 5-10% in trials with frequent milestone condition checks.
TrialSimulator 1.16.0
CRAN release: 2026-04-13
New Feature
- Add
n_switchescolumn to locked data returned byget_locked_data(), counting the number of treatment switches per patient within the data lock window. - Add
expandRegimen()to expand theregimen_trajectorycolumn in locked data into a long-format data frame with one row per regimen segment per patient.
TrialSimulator 1.15.0
Bug Fix
- Fix a bug in
regimen()to support both single or multiple switching.
TrialSimulator 1.14.0
New Feature
- Support multiple rounds of dynamic treatment switching through
regimen().
TrialSimulator 1.13.0
Bug Fix
- Fix a minor bug displaying incorrect event count when using
enrollment()to define milestone. This bug does not affect milestone triggering.
TrialSimulator 1.11.0
New Feature
- Add function
solvePiecewiseConstantExponentialDistribution()to compute event rates in time windows given survival probabilities at changepoints. - Add
qPiecewiseExponent(), the quantile function of piecewise exponential distribution. This function is useful to simulate time-to-event endpoint that is correlated to other endpoints using the copula method. For example, thesimdatapackage needs marginal quantile functions.
TrialSimulator 1.10.0
CRAN release: 2026-02-15
New Feature
- Support parallelization in
Controllers$run()through new argumentn_workers. The packagemiraiis used. Althoughmiraiadvocates the use of L’Ecuyer-CMRG streams to maintain independence between multiple streams, however,TrialSimulatorresets it to be Mersenne-Twister streams to force identical behavior betweenn_workers = 1andn_workers > 1. This enables debuggability and reproduciability under single-process mode by setting seed that causes issues under multi-process mode.
TrialSimulator 1.9.0
New Feature
- Support wrapper functions for adaptation, including
remove_arms(),add_arms(),update_sample_ratio(),set_duration(),resize()andupdate_generator(). - More informative message is prompted when error is throwed from an action function; milestone’s name is printed.
TrialSimulator 1.7.0
CRAN release: 2025-12-19
TrialSimulator 1.3.0
CRAN release: 2025-09-26
Major Updates
- Action function no longer needs argument
milestone_name. Now action function only requires argumenttrialand supports optional arguments. -
milestone()now support...to pass arguments to action functions. - Documents and vignettes are updated.
TrialSimulator 1.2.0
New Feature
-
enrollment()now supportsmin_treatment_durationto ensure minimum treatment duration received by patients at a milestone. With its default value 0, milestone is triggered when a specific number of patients are enrolled. - No longer print the return value of action function, thus
invisible(NULL)is no longer recommended as return value of action function.
TrialSimulator 0.97.0
Bug Fix
- Fix a bug in function
event_plot()for plot of cumulative events number when endpoint name is"ep". This is due to data masking indplyr. - Fix issues in unit tests caused by new dropout mechanisum.
- use Bonferroni method in unit test of
update_sample_ratio. This test is probably broken by randomness (it is okay).
TrialSimulator 0.93.0
New Feature
-
update_sample_ratio()now supports updating multiple arms simultaneously. When ratio is not a whole number,sample()is used to replace the permuted block algorithm to randomize patients to arms. This enable response-adaptive design. - Add a vignette of response-adaptive design using
update_sample_ratio().
TrialSimulator 0.91.0
TrialSimulator 0.90.0
TrialSimulator 0.82.0
New Feature
- The function
fitCoxphnow supportsformulato compute log hazard ratio or hazard ratio as main effect ofarm. It will detect whether arm’s main effect is specified in formula. It allows covariates and interaction between covariates and arm. However, only the main effect of arm is tested and returned. - Add unit tests for
fitCoxph.
TrialSimulator 0.68.0
Updates
- Rename trial event as milestone. This a major update. Relevant codes and documents are updated accordingly.
- Add executable examples for
controller()as per suggestion from CRAN team.
TrialSimulator 0.67.0
Updates
- Print event counts at trial events using
message()so that Shiny app can display it properly.
TrialSimulator 0.58.0
TrialSimulator 0.57.0
Updates
- Move vignette of comparison between
GraphicalTestingandgraphicalMCPto repository TrialSimulatorDocuments.
TrialSimulator 0.55.0
TrialSimulator 0.35.0
TrialSimulator 0.33.0
New Features
- Provide a default action function
do_nothing()if users have no intent to do anything at a triggered event. This function can be passed to the argumentactionwhen creating a new event, e.g.,Event$new(name = 'interim', trigger_condition = TriggerByCalendarTime, action = do_nothing, calendar_time = 64).