Control, A, B, and A+B Designs with Withdrawal and Carryover

This vignette illustrates the component engine introduced in version 0.4.0. The schedule contains four assigned treatment states: Control, A, B, and A+B. Unlike a cumulative rollout, a component may be withdrawn and later restarted. The data-generating model can retain a prespecified residual component effect after withdrawal.

Schedule

The following example contains both Control -> A -> A+B -> B and Control -> B -> A+B paths. Numeric state codes are 0 for Control, 1 for A, 2 for B, and 3 for A+B.

library(stepwedgepower)

state <- rbind(
  S1 = c(0, 0, 1, 1, 3, 3, 2, 2, 2, 2),
  S2 = c(0, 0, 0, 1, 1, 3, 3, 2, 2, 2),
  S3 = c(0, 0, 2, 2, 3, 3, 3, 3, 3, 3),
  S4 = c(0, 0, 0, 2, 2, 3, 3, 3, 3, 3),
  S5 = c(0, 0, 1, 1, 1, 3, 3, 3, 3, 3),
  S6 = c(0, 0, 2, 2, 2, 3, 3, 3, 3, 3),
  S7 = c(0, 0, 0, 0, 0, 1, 1, 3, 2, 2),
  S8 = c(0, 0, 0, 0, 0, 2, 2, 3, 1, 1)
)

design <- sw_component_design(
  clusters_per_sequence = rep(5L, 8),
  state = state
)
design
#> <sw_component_design>
#>   8 sequences, 10 periods, 40 clusters total
#>   clusters per sequence: 5, 5, 5, 5, 5, 5, 5, 5 
#>   states present: Control, A, B, A+B 
#>   withdrawals: A = 3, B = 1; restarts: A = 0, B = 0
#>    Period 1 Period 2 Period 3 Period 4 Period 5 Period 6 Period 7 Period 8
#> S1 Control  Control  A        A        A+B      A+B      B        B       
#> S2 Control  Control  Control  A        A        A+B      A+B      B       
#> S3 Control  Control  B        B        A+B      A+B      A+B      A+B     
#> S4 Control  Control  Control  B        B        A+B      A+B      A+B     
#> S5 Control  Control  A        A        A        A+B      A+B      A+B     
#> S6 Control  Control  B        B        B        A+B      A+B      A+B     
#> S7 Control  Control  Control  Control  Control  A        A        A+B     
#> S8 Control  Control  Control  Control  Control  B        B        A+B     
#>    Period 9 Period 10
#> S1 B        B        
#> S2 B        B        
#> S3 A+B      A+B      
#> S4 A+B      A+B      
#> S5 A+B      A+B      
#> S6 A+B      A+B      
#> S7 B        B        
#> S8 A        A

The schedule is represented internally by separate binary assignment matrices for A and B. It is not treated as an ordinal 0–3 dose.

Effects, wash-in, and carryover

assumptions <- sw_component_assumptions(
  baseline_prob = 0.15,
  treatment_or_a = 1.35,
  treatment_or_b = 1.25,
  interaction_or = 1.10,
  delay_a = 1L,
  delay_b = 1L,
  carryover_periods_a = 2L,
  carryover_periods_b = 2L,
  carryover_weights_a = c(0.60, 0.30),
  carryover_weights_b = c(0.50, 0.25),
  restart_rule_a = "reset",
  restart_rule_b = "reset",
  interaction_mode = "effective_overlap",
  icc = 0.05,
  period_effects = log(seq(1.00, 1.10, length.out = ncol(state))),
  n_per_cluster_period = 25L
)
assumptions
#> <sw_component_assumptions>
#>   A vs Control: OR = 1.350; wash-in = 1 period(s)
#>   B vs Control: OR = 1.250; wash-in = 1 period(s)
#>   A-by-B interaction OR = 1.100; mode = effective_overlap
#>   A carryover weights: 0.600, 0.300 
#>   B carryover weights: 0.500, 0.250 
#>   restart rules: A = reset; B = reset
#>   cluster_sd = 0.416 (latent ICC = 0.050)
#>   baseline probabilities: 0.150 
#>   period effects (log-odds): 0.000, 0.011, 0.022, 0.033, 0.043, 0.054, 0.065, 0.075, 0.085, 0.095

A one-period delay means that a component first contributes its full effect in the second consecutive assigned period. After A is withdrawn, 60 percent and 30 percent of its full log-odds effect persist for two periods. With interaction_mode = "effective_overlap", the interaction weight is the product of the effective A and B weights, so interaction carryover is possible while both effective components overlap.

Design audit

audit <- audit_component_design(design, assumptions)
audit
#> <sw_component_audit>
#>   fixed-effect rank: 13 of 13 (full)
#>   requested contrasts:
#>       contrast              label estimable
#>   A_vs_control       A vs Control      TRUE
#>   B_vs_control       B vs Control      TRUE
#>  AB_vs_control     A+B vs Control      TRUE
#>        AB_vs_A           A+B vs A      TRUE
#>        AB_vs_B           A+B vs B      TRUE
#>    interaction A-by-B interaction      TRUE
#>   withdrawals: A = 3, B = 1; restarts: A = 0, B = 0
#>   no structural cautions detected

The audit checks the rank of the intended fixed-effect model and formal estimability of six standard contrasts. It also reports concurrent state comparisons and notes any withdrawal for which immediate washout has been assumed.

One simulated trial

one_trial <- simulate_component_swcrt(design, assumptions, seed = 123)
fit <- fit_component_model(
  one_trial,
  multiplicity = "holm",
  multiplicity_family = c("A_vs_control", "B_vs_control", "interaction")
)
fit$contrasts
#>        contrast              label  estimate  std_error  z_value      p_value
#> 1  A_vs_control       A vs Control 0.2816765 0.12624300 2.231224 2.566626e-02
#> 2  B_vs_control       B vs Control 0.2481387 0.13065989 1.899119 5.754882e-02
#> 3 AB_vs_control     A+B vs Control 0.7497071 0.16120678 4.650593 3.309825e-06
#> 4       AB_vs_A           A+B vs A 0.4680306 0.12204716 3.834834 1.256492e-04
#> 5       AB_vs_B           A+B vs B 0.5015684 0.09744443 5.147225 2.643682e-07
#> 6   interaction A-by-B interaction 0.2198919 0.14437878 1.523021 1.277535e-01
#>       conf_low conf_high odds_ratio p_adjusted reject reject_adjusted
#> 1  0.034244740 0.5291082   1.325350 0.07699879   TRUE           FALSE
#> 2 -0.007949991 0.5042274   1.281638 0.11509765  FALSE           FALSE
#> 3  0.433747592 1.0656666   2.116380         NA   TRUE              NA
#> 4  0.228822557 0.7072387   1.596846         NA   TRUE              NA
#> 5  0.310580825 0.6925560   1.651309         NA   TRUE              NA
#> 6 -0.063085297 0.5028691   1.245942 0.12775345  FALSE           FALSE

The standard contrasts are:

  • A versus Control;
  • B versus Control;
  • A+B versus Control;
  • A+B versus A;
  • A+B versus B; and
  • the A-by-B interaction.

The interaction coefficient is the departure from additivity on the log-odds scale. A B-only state is required to separate the B main effect from the interaction when the interaction is fitted.

Power and global-null calibration

A short exploratory run is shown below. A final analysis should use a simulation count chosen from a prespecified Monte Carlo precision target and should also run the global-null design through type1_component_swcrt().

power <- power_component_swcrt(
  design,
  assumptions,
  nsim = 5000,
  multiplicity = "holm",
  multiplicity_family = c("A_vs_control", "B_vs_control", "interaction"),
  nAGQ = 1,
  n_cores = 4,
  seed = 2026
)
summary(power)

type1 <- type1_component_swcrt(
  design,
  assumptions,
  nsim = 5000,
  multiplicity = "holm",
  multiplicity_family = c("A_vs_control", "B_vs_control", "interaction"),
  nAGQ = 1,
  n_cores = 4,
  seed = 22026
)
summary(type1)

Both conditional power and failure-aware power are reported. Their difference shows how much the apparent operating performance depends on excluding failed or non-estimable analyses.