turnbull: Terminate search for likelihood-ratio confidence intervals based on precision
81% speedup Also resolves issue where confidence interval search would occasionally never terminate
This commit is contained in:
parent
c67965478d
commit
c9a8b5b8a5
@ -56,6 +56,10 @@ pub struct TurnbullArgs {
|
||||
/// Threshold for dropping failure probability in --se-method oim-drop-zeros
|
||||
#[arg(long, default_value="0.0001")]
|
||||
zero_tolerance: f64,
|
||||
|
||||
/// Desired precision of confidence limits in --se-method likelihood-ratio
|
||||
#[arg(long, default_value="0.01")]
|
||||
ci_precision: f64,
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Clone)]
|
||||
@ -78,7 +82,7 @@ pub fn main(args: TurnbullArgs) {
|
||||
|
||||
// Fit regression
|
||||
let progress_bar = ProgressBar::with_draw_target(Some(0), ProgressDrawTarget::term_like(Box::new(UnconditionalTermLike::stderr())));
|
||||
let result = fit_turnbull(data_times, progress_bar, args.max_iterations, args.ll_tolerance, args.se_method, args.zero_tolerance);
|
||||
let result = fit_turnbull(data_times, progress_bar, args.max_iterations, args.ll_tolerance, args.se_method, args.zero_tolerance, args.ci_precision);
|
||||
|
||||
// Display output
|
||||
match args.output {
|
||||
@ -184,7 +188,7 @@ struct Constraint {
|
||||
survival_prob: f64,
|
||||
}
|
||||
|
||||
pub fn fit_turnbull(data_times: Matrix2xX<f64>, progress_bar: ProgressBar, max_iterations: u32, ll_tolerance: f64, se_method: SEMethod, zero_tolerance: f64) -> TurnbullResult {
|
||||
pub fn fit_turnbull(data_times: Matrix2xX<f64>, progress_bar: ProgressBar, max_iterations: u32, ll_tolerance: f64, se_method: SEMethod, zero_tolerance: f64, ci_precision: f64) -> TurnbullResult {
|
||||
// ----------------------
|
||||
// Prepare for regression
|
||||
|
||||
@ -245,7 +249,7 @@ pub fn fit_turnbull(data_times: Matrix2xX<f64>, progress_bar: ProgressBar, max_i
|
||||
progress_bar.println("Computing confidence intervals by likelihood ratio test");
|
||||
|
||||
let confidence_intervals = (1..data.num_intervals()).into_par_iter()
|
||||
.map(|j| survival_prob_likelihood_ratio_ci(&data, ProgressBar::hidden(), max_iterations, ll_tolerance, &p, ll, &s, &oim_se, j))
|
||||
.map(|j| survival_prob_likelihood_ratio_ci(&data, ProgressBar::hidden(), max_iterations, ll_tolerance, ci_precision, &p, ll, &s, &oim_se, j))
|
||||
.progress_with(progress_bar.clone())
|
||||
.collect();
|
||||
|
||||
@ -607,7 +611,7 @@ fn compute_hessian(data: &TurnbullData, p: &Vec<f64>) -> DMatrix<f64> {
|
||||
return hessian;
|
||||
}
|
||||
|
||||
fn survival_prob_likelihood_ratio_ci(data: &TurnbullData, progress_bar: ProgressBar, max_iterations: u32, ll_tolerance: f64, p: &Vec<f64>, ll_model: f64, s: &Vec<f64>, oim_se: &Vec<f64>, time_index: usize) -> (f64, f64) {
|
||||
fn survival_prob_likelihood_ratio_ci(data: &TurnbullData, progress_bar: ProgressBar, max_iterations: u32, ll_tolerance: f64, ci_precision: f64, p: &Vec<f64>, ll_model: f64, s: &Vec<f64>, oim_se: &Vec<f64>, time_index: usize) -> (f64, f64) {
|
||||
// Compute lower confidence limit
|
||||
let mut ci_bound_lower = 0.0;
|
||||
let mut ci_bound_upper = s[time_index];
|
||||
@ -627,10 +631,7 @@ fn survival_prob_likelihood_ratio_ci(data: &TurnbullData, progress_bar: Progress
|
||||
let (_p, ll_test) = fit_turnbull_estimator(data, progress_bar.clone(), max_iterations, ll_tolerance, p_test, Some(Constraint { time_index: time_index, survival_prob: ci_estimate }));
|
||||
let lr_statistic = 2.0 * (ll_model - ll_test);
|
||||
|
||||
if (lr_statistic - CHI2_1DF_95).abs() < ll_tolerance {
|
||||
// Converged!
|
||||
break;
|
||||
} else if lr_statistic > CHI2_1DF_95 {
|
||||
if lr_statistic > CHI2_1DF_95 {
|
||||
// CI is too wide
|
||||
ci_bound_lower = ci_estimate;
|
||||
} else {
|
||||
@ -638,11 +639,13 @@ fn survival_prob_likelihood_ratio_ci(data: &TurnbullData, progress_bar: Progress
|
||||
ci_bound_upper = ci_estimate;
|
||||
}
|
||||
|
||||
// FIXME: Sometimes this does not converge within max_iterations - investigate why this is
|
||||
// If it is not an issue with the algorithm, then we should also terminate if width of interval is narrower than a specified tolerance
|
||||
|
||||
ci_estimate = (ci_bound_lower + ci_bound_upper) / 2.0;
|
||||
|
||||
if ci_bound_upper - ci_bound_lower <= ci_precision {
|
||||
// Desired precision has been reached
|
||||
break;
|
||||
}
|
||||
|
||||
iteration += 1;
|
||||
if iteration > max_iterations {
|
||||
panic!("Exceeded --max-iterations");
|
||||
@ -670,10 +673,7 @@ fn survival_prob_likelihood_ratio_ci(data: &TurnbullData, progress_bar: Progress
|
||||
let (_p, ll_test) = fit_turnbull_estimator(data, progress_bar.clone(), max_iterations, ll_tolerance, p_test, Some(Constraint { time_index: time_index, survival_prob: ci_estimate }));
|
||||
let lr_statistic = 2.0 * (ll_model - ll_test);
|
||||
|
||||
if (lr_statistic - CHI2_1DF_95).abs() < ll_tolerance {
|
||||
// Converged!
|
||||
break;
|
||||
} else if lr_statistic > CHI2_1DF_95 {
|
||||
if lr_statistic > CHI2_1DF_95 {
|
||||
// CI is too wide
|
||||
ci_bound_upper = ci_estimate;
|
||||
} else {
|
||||
@ -683,6 +683,11 @@ fn survival_prob_likelihood_ratio_ci(data: &TurnbullData, progress_bar: Progress
|
||||
|
||||
ci_estimate = (ci_bound_lower + ci_bound_upper) / 2.0;
|
||||
|
||||
if ci_bound_upper - ci_bound_lower <= ci_precision {
|
||||
// Desired precision has been reached
|
||||
break;
|
||||
}
|
||||
|
||||
iteration += 1;
|
||||
if iteration > max_iterations {
|
||||
panic!("Exceeded --max-iterations");
|
||||
|
Loading…
Reference in New Issue
Block a user