pdp_lns 0.1.1

Adaptive Large Neighbourhood Search solver for the Pickup and Delivery Problem with Time Windows (PDPTW)
Documentation
use pdp_lns::algorithm;
use pdp_lns::instance::Instance;
use pdp_lns::solution;
use std::path::Path;
use std::time::{Duration, Instant};

fn main() {
    let args: Vec<String> = std::env::args().collect();

    if args.len() < 2 {
        eprintln!(
            "Usage: {} <instance_file> [time_limit_secs] [seed] [initial_method]",
            args[0]
        );
        eprintln!("  instance_file: path to Li & Lim PDPTW instance file");
        eprintln!("  time_limit_secs: time limit in seconds (default: 300)");
        eprintln!("  seed: random seed (default: 42)");
        eprintln!(
            "  initial_method: legacy|lu2006|ropke2006|cluster2004|hosny2012 (default: legacy)"
        );
        std::process::exit(1);
    }

    let instance_path = &args[1];
    let time_limit_secs: u64 = args.get(2).and_then(|s| s.parse().ok()).unwrap_or(300);
    let seed: u64 = args.get(3).and_then(|s| s.parse().ok()).unwrap_or(42);
    let initial_method = if let Some(raw) = args.get(4) {
        if let Some(m) = algorithm::InitialSolutionMethod::from_str(raw) {
            m
        } else {
            eprintln!(
                "Unknown initial_method '{raw}'. Expected legacy|lu2006|ropke2006|cluster2004|hosny2012"
            );
            std::process::exit(2);
        }
    } else {
        algorithm::InitialSolutionMethod::Legacy
    };

    let process_start = Instant::now();
    eprintln!("Loading instance: {instance_path}");
    let inst = Instance::from_file(Path::new(instance_path));
    eprintln!(
        "  {} customers, {} PD pairs, capacity={}, max_vehicles={}",
        inst.n, inst.m, inst.capacity, inst.num_vehicles
    );

    eprintln!(
        "Solving with time_limit={time_limit_secs}s, seed={seed}, initial_method={}",
        initial_method.as_str()
    );
    let best = algorithm::solve_with_initial_method(
        &inst,
        Duration::from_secs(time_limit_secs),
        seed,
        None,
        initial_method,
        Some(process_start),
    );

    // Output results
    println!("=== RESULT ===");
    println!("Vehicles: {}", best.num_vehicles());
    println!("Distance: {:.2}", best.total_distance(&inst));
    println!("Routes:");
    for (i, route) in best.routes.iter().enumerate() {
        let route_str: Vec<String> = route.iter().map(std::string::ToString::to_string).collect();
        println!("  Route {}: {}", i + 1, route_str.join(" -> "));
        println!(
            "    Distance: {:.2}, Customers: {}",
            solution::route_distance(&inst, route),
            route.len() - 2
        );
    }
    println!("Feasible: {}", best.is_feasible(&inst));
}