Skip to contents

The Trial class coordinates one or more Population objects, a Timer, and a list of Condition objects to simulate a clinical trial.

At each unique time defined in the trial's Timer, the Trial:

  • applies enrollment and dropout updates to each Population

  • builds a snapshot of all currently enrolled subjects

  • evaluates each Condition in self$conditions against the snapshot

  • stores both the snapshot (locked_data) and the analysis outputs (results)

Use run() to execute the simulation. Trigger conditions are built with Condition$new() (or helpers condition_calendar_time() / condition_enrollment_fraction()) and stored in trial$conditions.

Public fields

name

character Unique trial identifier.

seed

numeric or NULL Random seed for reproducibility.

timer

Timer object with timepoints.

population

list of Population objects, one per arm.

conditions

list of Condition objects evaluated at each timepoint.

locked_data

list Snapshots at each timepoint.

results

list Analysis outputs per condition.

adaptive

logical When FALSE (default), uses the fixed fast path: enroll/drop times are precomputed deterministically before iteration, and snapshots are cheap prefix slices. When TRUE, uses the adaptive loop: enrollment and dropout are sampled incrementally at each timepoint, supporting designs where the schedule may change mid-trial.

Methods


Trial$new()

Create a new Trial instance.

Usage

Trial$new(
  name,
  seed = NULL,
  timer = NULL,
  population = list(),
  locked_data = list(),
  conditions = list(),
  results = list(),
  adaptive = FALSE
)

Arguments

name

character Unique identifier for the trial.

seed

numeric or NULL Optional random seed for reproducibility.

timer

Timer object defining timepoints.

population

list of Population objects, one per arm.

locked_data

list Generated at each $run() call.

conditions

list of Condition objects to evaluate at each timepoint.

results

list Analysis outputs generated at each $run() call.

adaptive

logical When FALSE (default), uses the fixed fast path (deterministic precompute, prefix snapshots). When TRUE, uses the adaptive loop (incremental random sampling at each timepoint).

Returns

A new Trial instance.

Examples

t <- Timer$new(name="simple_timer")
pop <- Population$new(
  name = "simple_pop",
  data = as_population_data(rnorm(5))
)
pop$set_enrolled(5, 1)
Trial$new(name = "simple_trial", timer=t, population = list(pop))


Trial$run()

Execute a trial simulation.

Dispatches to the fixed fast path (adaptive = FALSE, default) or the adaptive loop (adaptive = TRUE). Both paths update locked_data and results fields with the same structure.

Fixed path (adaptive = FALSE): enroll/drop times are precomputed deterministically before iteration. Snapshots are cheap prefix slices of a single combined data frame. Conditions are evaluated with per-condition exhausted-skip.

Adaptive loop (adaptive = TRUE): enrollment and dropout are sampled randomly at each timepoint, supporting designs where the schedule may change mid-trial based on interim results.

Usage

Trial$run()

Returns

Updates locked_data and results fields; returns self invisibly.

Examples

# Create two populations
popA <- Population$new("A", data = as_population_data(rnorm(10)))
popB <- Population$new("B", data = as_population_data(rnorm(12)))

# Create a timer and add timepoints
t <- Timer$new("Timer")
t$add_schedule(data.frame(time = 1, arm = "A", drop = 0L, enroll = 4L))
t$add_schedule(data.frame(time = 1, arm = "B", drop = 0L, enroll = 5L))
t$add_schedule(data.frame(time = 2, arm = "A", drop = 1L, enroll = 2L))
t$add_schedule(data.frame(time = 2, arm = "B", drop = 2L, enroll = 3L))

# Create a trial
trial <- Trial$new(
  name = "ExampleTrial",
  seed = 123,
  timer = t,
  population = list(popA, popB)
)

# Run the simulation
trial$run()

collect_results(trial)


Trial$clone()

The objects of this class are cloneable with this method.

Usage

Trial$clone(deep = FALSE)

Arguments

deep

Whether to make a deep clone.

Examples

# Create two populations
popA <- Population$new("A", data = as_population_data(rnorm(10)))
popB <- Population$new("B", data = as_population_data(rnorm(12)))

# Create a timer and add timepoints
t <- Timer$new("Timer")
t$add_schedule(data.frame(time = 1, arm = "A", drop = 0L, enroll = 4L))
t$add_schedule(data.frame(time = 1, arm = "B", drop = 0L, enroll = 5L))
t$add_schedule(data.frame(time = 2, arm = "A", drop = 1L, enroll = 2L))
t$add_schedule(data.frame(time = 2, arm = "B", drop = 2L, enroll = 3L))

# Build a condition: fire at time >= 2 and count enrolled rows
cond <- Condition$new(
  where    = calendar_trigger(2),
  analysis = function(df, current_time) nrow(df),
  name     = "final"
)

# Create a trial
trial <- Trial$new(
  name       = "ExampleTrial",
  seed       = 123,
  timer      = t,
  population = list(popA, popB),
  conditions = list(cond)
)

# Run the simulation
trial$run()

collect_results(trial)
#>   replicate timepoint analysis X14L
#> 1         1         2    final   14


## ------------------------------------------------
## Method `Trial$new()`
## ------------------------------------------------

t <- Timer$new(name="simple_timer")
pop <- Population$new(
  name = "simple_pop",
  data = as_population_data(rnorm(5))
)
pop$set_enrolled(5, 1)
Trial$new(name = "simple_trial", timer=t, population = list(pop))
#> <Trial>
#>   Public:
#>     adaptive: FALSE
#>     clone: function (deep = FALSE) 
#>     conditions: list
#>     initialize: function (name, seed = NULL, timer = NULL, population = list(), 
#>     locked_data: list
#>     name: simple_trial
#>     population: list
#>     results: list
#>     run: function () 
#>     seed: NULL
#>     timer: Timer, R6
#>   Private:
#>     build_full_snapshot: function () 
#>     precompute_population: function (p, plan_df) 
#>     run_adaptive: function () 
#>     run_fixed: function () 

## ------------------------------------------------
## Method `Trial$run()`
## ------------------------------------------------

# Create two populations
popA <- Population$new("A", data = as_population_data(rnorm(10)))
popB <- Population$new("B", data = as_population_data(rnorm(12)))

# Create a timer and add timepoints
t <- Timer$new("Timer")
t$add_schedule(data.frame(time = 1, arm = "A", drop = 0L, enroll = 4L))
t$add_schedule(data.frame(time = 1, arm = "B", drop = 0L, enroll = 5L))
t$add_schedule(data.frame(time = 2, arm = "A", drop = 1L, enroll = 2L))
t$add_schedule(data.frame(time = 2, arm = "B", drop = 2L, enroll = 3L))

# Create a trial
trial <- Trial$new(
  name = "ExampleTrial",
  seed = 123,
  timer = t,
  population = list(popA, popB)
)

# Run the simulation
trial$run()

collect_results(trial)
#> # A tibble: 0 × 0