use std::path::PathBuf;
use clap::Parser;
#[cfg(feature = "mpi")]
use dual_threshold_optimization::run::run_multi_node;
use dual_threshold_optimization::dto::compute_population_size;
use dual_threshold_optimization::read::{
read_feature_list_from_file, read_ranked_feature_list_from_csv,
};
use dual_threshold_optimization::run::run_single_node;
use dual_threshold_optimization::run::Task;
use dual_threshold_optimization::stat_operations::empirical_pvalue;
#[derive(Parser)]
#[command(version, about, long_about = None)]
struct Cli {
#[arg(short = '1', long, value_name = "FILE")]
ranked_list1: PathBuf,
#[arg(short = '2', long, value_name = "FILE")]
ranked_list2: PathBuf,
#[arg(short, long, value_name = "FILE")]
background: Option<PathBuf>,
#[arg(short, long, value_parser, default_value_t = 1000)]
permutations: usize,
#[arg(short, long, value_parser, default_value_t = 1)]
threads: usize,
#[arg(short = 'm', long, action = clap::ArgAction::SetTrue)]
multi_node: bool,
}
fn main() {
let cli = Cli::parse();
let mut threads = cli.threads;
if threads == 0 {
eprintln!("Warning: Number of threads cannot be 0. Setting threads to 1.");
threads = 1;
}
eprintln!("Ranked list 1: {}", cli.ranked_list1.display());
eprintln!("Ranked list 2: {}", cli.ranked_list2.display());
eprintln!("Permutations: {}", cli.permutations);
eprintln!("Threads: {}", threads);
eprintln!(
"Multi-node mode: {}",
if cli.multi_node {
"enabled"
} else {
"disabled"
}
);
let ranked_feature_list1 = read_ranked_feature_list_from_csv(
cli.ranked_list1
.to_str()
.expect("Invalid file path for input1"),
);
let ranked_feature_list2 = read_ranked_feature_list_from_csv(
cli.ranked_list2
.to_str()
.expect("Invalid file path for input2"),
);
eprintln!(
"The product of the lengths of the threshold lists \
(this describes the asymptotic runtime of a single job): {}",
ranked_feature_list1.thresholds().len() * ranked_feature_list2.thresholds().len()
);
let background = cli.background.map(|background_file| {
read_feature_list_from_file(
background_file
.to_str()
.expect("Invalid file path for background"),
)
});
let population_size = compute_population_size(
&ranked_feature_list1,
&ranked_feature_list2,
background.as_ref(),
);
let mut tasks: Vec<Task> = vec![Task {
id: 0,
permute: false,
}];
tasks.extend((1..=cli.permutations).map(|id| Task { id, permute: true }));
if cli.multi_node {
#[cfg(feature = "mpi")]
{
let results = run_multi_node(
tasks,
cli.ranked_list1.to_str().unwrap(),
cli.ranked_list2.to_str().unwrap(),
population_size,
threads,
);
if !results.is_empty() {
let final_result = empirical_pvalue(results);
println!("{}", serde_json::to_string_pretty(&final_result).unwrap());
}
return;
}
#[cfg(not(feature = "mpi"))]
{
panic!("Multi-node mode requires MPI. Rebuild with the 'mpi' feature enabled.");
}
}
let results = run_single_node(
tasks,
ranked_feature_list1,
ranked_feature_list2,
population_size,
threads,
);
let final_result = empirical_pvalue(results);
println!("{}", serde_json::to_string_pretty(&final_result).unwrap());
}