use std::{fs::File, path::Path, time::{Duration, Instant}};
use clap::Parser;
use ddo::{Completion, TimeBudget, NoDupFringe, MaxUB, Solution, SimpleDominanceChecker, Problem, DefaultCachingSolver, Solver};
use dominance::TsptwDominance;
use heuristics::{TsptwWidth, TsptwRanking};
use instance::TsptwInstance;
use model::Tsptw;
use relax::TsptwRelax;
mod instance;
mod state;
mod model;
mod relax;
mod heuristics;
mod dominance;
#[cfg(test)]
mod tests;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
instance: String,
#[clap(short, long)]
width: Option<usize>,
#[clap(short, long)]
threads: Option<usize>,
#[clap(short, long)]
duration: Option<u64>,
}
fn main() {
let args = Args::parse();
let inst = TsptwInstance::from(File::open(&args.instance).unwrap());
let pb = Tsptw::new(inst);
let relax = TsptwRelax::new(&pb);
let width = TsptwWidth::new(pb.nb_variables(), args.width.unwrap_or(1));
let dominance = SimpleDominanceChecker::new(TsptwDominance, pb.nb_variables());
let cutoff = TimeBudget::new(Duration::from_secs(args.duration.unwrap_or(u64::MAX)));
let mut fringe = NoDupFringe::new(MaxUB::new(&TsptwRanking));
let mut solver = DefaultCachingSolver::custom(
&pb,
&relax,
&TsptwRanking,
&width,
&dominance,
&cutoff,
&mut fringe,
args.threads.unwrap_or(num_cpus::get())
);
let start = Instant::now();
let outcome = solver.maximize();
let finish = Instant::now();
let instance = instance_name(&args.instance);
let nb_vars = pb.nb_variables();
let lb = objective(solver.best_lower_bound());
let ub = objective(solver.best_upper_bound());
let solution = solver.best_solution();
let duration = finish - start;
print_solution(&instance, nb_vars, outcome, &lb, &ub, duration, solution);
}
fn print_solution(name: &str, n: usize, completion: Completion, lb: &str, ub: &str, duration: Duration, solution: Option<Solution>) {
println!("instance : {name}");
println!("status : {}", status(completion));
println!("lower bnd: {lb}");
println!("upper bnd: {ub}");
println!("duration : {}", duration.as_secs_f32());
println!("solution : {}", solution_to_string(n, solution));
}
fn instance_name<P: AsRef<Path>>(fname: P) -> String {
let name = fname.as_ref().file_name().unwrap().to_str().unwrap();
let bench= fname.as_ref().parent().unwrap().file_name().unwrap().to_str().unwrap();
format!("{}/{}", bench, name)
}
fn objective(x: isize) -> String {
match x {
isize::MIN => "+inf".to_string(),
isize::MAX => "-inf".to_string(),
_ => format!("{:.2}", -(x as f32 / 10_000.0_f32))
}
}
fn status(completion: Completion) -> &'static str {
if completion.is_exact {
"Proved"
} else {
"Timeout"
}
}
fn solution_to_string(nb_vars: usize, solution: Option<Solution>) -> String {
match solution {
None => "No feasible solution found".to_string(),
Some(s)=> {
let mut perm = vec![0; nb_vars];
for d in s.iter() {
perm[d.variable.id()] = d.value;
}
let mut txt = String::new();
for v in perm {
txt = format!("{} {}", txt, v);
}
txt
}
}
}