Clinical Trial Simulation¶
NeoPKPD provides comprehensive clinical trial simulation capabilities for study design, power analysis, and decision-making.
Overview¶
Clinical trial simulation enables:
- Virtual trial execution before enrollment
- Power and sample size estimation
- Study design optimization
- Risk assessment and mitigation
graph TB
A[Define Design] --> B[Generate Population]
B --> C[Apply Dosing]
C --> D[Simulate PK/PD]
D --> E[Evaluate Endpoints]
E --> F[Statistical Analysis]
F --> G[Decision Metrics]
Trial Types¶
-
Parallel Design
Independent treatment groups
-
Crossover Design
Within-subject comparisons
-
Dose Escalation
3+3, mTPI, CRM designs
-
Power Analysis
Sample size and power estimation
Quick Start¶
Parallel Design¶
using NeoPKPD
# Define trial design
design = ParallelDesign(
n_arms = 3,
arm_names = ["Placebo", "Low Dose", "High Dose"],
randomization_ratio = [1, 1, 1]
)
# Define dosing regimens
regimens = [
DosingRegimen(0.0, 28, 24.0), # Placebo
DosingRegimen(50.0, 28, 24.0), # 50 mg QD
DosingRegimen(100.0, 28, 24.0) # 100 mg QD
]
# Generate virtual population
population = generate_population(
n = 150, # Total subjects
spec = HealthyVolunteerSpec(),
seed = 42
)
# Define trial specification
trial = TrialSpec(
name = "Phase 2 Dose Finding",
design = design,
regimens = regimens,
population = population,
pk_model = OneCompIVBolus(),
pk_params = OneCompIVBolusParams(5.0, 50.0),
omega = OmegaMatrix([0.09 0.0; 0.0 0.04])
)
# Run simulation
result = simulate_trial(trial, seed = 12345)
# Analyze results
for arm in result.arms
println("$(arm.name): n=$(arm.n_completed), mean_auc=$(arm.mean_auc)")
end
Crossover Design¶
# 2×2 crossover
design = CrossoverDesign(
n_periods = 2,
n_sequences = 2,
sequences = [["A", "B"], ["B", "A"]],
washout_days = 14
)
# Generate balanced population
population = generate_population(
n = 24, # 12 per sequence
spec = HealthyVolunteerSpec(),
seed = 42
)
trial = TrialSpec(
name = "BE Crossover",
design = design,
population = population,
...
)
result = simulate_trial(trial, seed = 12345)
Population Generation¶
Demographics¶
# Healthy volunteer specification
spec = DemographicSpec(
age_mean = 35.0,
age_sd = 10.0,
age_min = 18.0,
age_max = 55.0,
weight_mean = 75.0,
weight_sd = 12.0,
weight_min = 50.0,
weight_max = 100.0,
female_fraction = 0.5,
race_distribution = Dict(
"white" => 0.7,
"black" => 0.15,
"asian" => 0.10,
"other" => 0.05
)
)
pop = generate_population(n = 100, spec = spec, seed = 42)
Patient Population¶
# Renal impairment study
spec = DemographicSpec(
age_mean = 65.0,
age_sd = 12.0,
age_min = 18.0,
age_max = 85.0,
egfr_distribution = Dict(
"normal" => 0.0, # eGFR ≥ 90
"mild" => 0.33, # eGFR 60-89
"moderate" => 0.34, # eGFR 30-59
"severe" => 0.33 # eGFR 15-29
)
)
Dosing Regimens¶
# Once daily
regimen = dosing_qd(dose = 100.0, days = 28)
# Twice daily
regimen = dosing_bid(dose = 50.0, days = 14)
# Custom schedule
regimen = DosingRegimen(
doses = [100.0, 100.0, 100.0],
times = [0.0, 8.0, 16.0],
repeat_days = 7
)
# Titration
regimen = TitrationRegimen(
start_dose = 25.0,
target_dose = 100.0,
steps = [25, 50, 75, 100],
days_per_step = 7
)
Power Analysis¶
Analytical Power¶
# Calculate power for given sample size
power = estimate_power(
n_per_arm = 50,
effect_size = 0.5, # Cohen's d
sd = 1.0,
alpha = 0.05,
test = :two_sample_t
)
println("Power: ", power.power)
println("Effect size: ", power.effect_size)
Sample Size Estimation¶
# Find n for target power
result = estimate_sample_size(
target_power = 0.80,
effect_size = 0.5,
sd = 1.0,
alpha = 0.05
)
println("Required n per arm: ", result.n_per_arm)
println("Achieved power: ", result.achieved_power)
Simulation-Based Power¶
# Power via simulation (more flexible)
power = estimate_power_simulation(
trial_spec = trial,
n_simulations = 1000,
endpoint = :auc_comparison,
success_criterion = auc_diff -> auc_diff > 0 && pvalue < 0.05,
seed = 42
)
println("Simulated power: ", power.power)
println("95% CI: ", power.ci)
Statistical Analysis¶
Arm Comparison¶
# Compare treatment to control
comparison = compare_arms(
treatment = result.arms["High Dose"].auc_values,
control = result.arms["Placebo"].auc_values,
test = :ttest
)
println("Difference: ", comparison.difference)
println("95% CI: ", comparison.ci)
println("p-value: ", comparison.pvalue)
Bioequivalence Analysis¶
# 90% CI for geometric mean ratio
be_result = bioequivalence_analysis(
test = result.arms["Test"].auc_values,
reference = result.arms["Reference"].auc_values
)
println("GMR: ", be_result.gmr)
println("90% CI: ", be_result.ci_90)
println("BE conclusion: ", be_result.is_bioequivalent)
Trial Result Structure¶
struct TrialResult
# Design info
name::String
design::TrialDesign
# Arm results
arms::Dict{String, ArmResult}
# Population summary
n_enrolled::Int
n_completed::Int
dropout_rate::Float64
# Timing
enrollment_duration::Float64
study_duration::Float64
end
struct ArmResult
name::String
n_enrolled::Int
n_completed::Int
# PK endpoints
auc_values::Vector{Float64}
cmax_values::Vector{Float64}
# Summary statistics
mean_auc::Float64
sd_auc::Float64
cv_auc::Float64
end
Next Steps¶
- Parallel Design - Detailed parallel trial guide
- Crossover - Crossover study design
- Power Analysis - Sample size determination
- Python Trial Module - Python interface