vrp-cli 1.25.0

A command line interface for VRP solver
Documentation
use super::*;
use std::fs::File;
use vrp_core::models::examples::create_example_problem;

#[test]
fn can_read_full_config() {
    let file = File::open("../examples/data/config/config.full.json").expect("cannot read config from file");

    let config = read_config(BufReader::new(file)).unwrap();

    let telemetry = config.telemetry.expect("no telemetry config");
    let logging = telemetry.progress.expect("no logging config");
    assert!(logging.enabled);
    assert_eq!(logging.log_best, Some(100));
    assert_eq!(logging.log_population, Some(1000));
    let metrics = telemetry.metrics.unwrap();
    assert!(!metrics.enabled);
    assert_eq!(metrics.track_population, Some(1000));

    let evolution_config = config.evolution.expect("no evolution config");

    let initial = evolution_config.initial.expect("no initial population config");

    match initial.method {
        RecreateMethod::Cheapest { weight: 1 } => {}
        _ => unreachable!(),
    }
    assert_eq!(initial.alternatives.methods.len(), 7);
    assert_eq!(initial.alternatives.max_size, 4);
    assert_eq!(initial.alternatives.quota, 0.05);

    match evolution_config.population.expect("no population config") {
        PopulationType::Rosomaxa {
            selection_size,
            max_elite_size,
            max_node_size,
            spread_factor,
            distribution_factor,
            rebalance_memory,
            exploration_ratio,
        } => {
            assert_eq!(selection_size, Some(8));
            assert_eq!(max_elite_size, Some(2));
            assert_eq!(max_node_size, Some(2));
            assert_eq!(spread_factor, Some(0.75));
            assert_eq!(distribution_factor, Some(0.75));
            assert_eq!(rebalance_memory, Some(100));
            assert_eq!(exploration_ratio, Some(0.9));
        }
        _ => unreachable!(),
    }

    let hyper_config = config.hyper.expect("cannot get hyper");
    match hyper_config {
        HyperType::StaticSelective { operators } => {
            let operators = operators.expect("cannot get operators");
            assert_eq!(operators.len(), 4);
            match operators.first().unwrap() {
                SearchOperatorType::Decomposition { routes, repeat, probability } => {
                    assert_eq!(*repeat, 4);
                    assert_eq!(routes.min, 2);
                    assert_eq!(routes.max, 4);
                    match probability {
                        OperatorProbabilityType::Context { threshold, phases } => {
                            assert_eq!(threshold.jobs, 300);
                            assert_eq!(threshold.routes, 10);
                            assert_eq!(phases.len(), 2);
                        }
                        _ => unreachable!(),
                    }
                }
                _ => unreachable!(),
            }

            match operators.get(1).unwrap() {
                SearchOperatorType::LocalSearch { probability, times, operators: inners } => {
                    assert_eq!(as_scalar_probability(probability), 0.05);
                    assert_eq!(*times, MinMaxConfig { min: 1, max: 2 });
                    assert_eq!(inners.len(), 4);
                }
                _ => unreachable!(),
            }

            match operators.get(2).unwrap() {
                SearchOperatorType::RuinRecreate { probability, ruins, recreates } => {
                    assert_eq!(as_scalar_probability(probability), 1.);
                    assert_eq!(ruins.len(), 7);
                    assert_eq!(recreates.len(), 12);
                }
                _ => unreachable!(),
            }

            match operators.last().unwrap() {
                SearchOperatorType::LocalSearch { probability, times, operators: inners } => {
                    assert_eq!(as_scalar_probability(probability), 0.01);
                    assert_eq!(*times, MinMaxConfig { min: 1, max: 2 });
                    assert_eq!(inners.len(), 4);
                }
                _ => unreachable!(),
            }
        }
        HyperType::DynamicSelective => unreachable!(),
    }

    let termination = config.termination.expect("no termination config");
    assert_eq!(termination.max_time, Some(300));
    assert_eq!(termination.max_generations, Some(3000));

    let environment = config.environment.expect("no environment config");
    assert_eq!(environment.is_experimental, Some(false));

    let parallelism = environment.parallelism.expect("no parallelism config");
    assert_eq!(parallelism.num_thread_pools, 6);
    assert_eq!(parallelism.threads_per_pool, 8);

    let logging = environment.logging.expect("no logging config");
    assert!(logging.enabled);
    assert_eq!(logging.prefix, Some("[config.full]".to_string()));

    let output_cfg = config.output.expect("cannot read output config");
    assert_eq!(output_cfg.include_geojson, Some(true));
}

#[test]
fn can_create_default_config() {
    let config = Config::default();

    assert!(config.evolution.is_none());
    assert!(config.hyper.is_none());
    assert!(config.termination.is_none());
    assert!(config.telemetry.is_none());
}

#[test]
fn can_configure_telemetry_metrics() {
    let config = Config {
        evolution: None,
        hyper: None,
        termination: Some(TerminationConfig { max_time: None, max_generations: Some(100), variation: None }),
        environment: None,
        telemetry: Some(TelemetryConfig {
            progress: None,
            metrics: Some(MetricsConfig { enabled: true, track_population: Some(10) }),
        }),
        output: None,
    };

    let solution = create_builder_from_config(create_example_problem(), Vec::default(), &config)
        .and_then(|config_builder| config_builder.build())
        .map(|evolution_config| Solver::new(create_example_problem(), evolution_config))
        .and_then(|solver| solver.solve())
        .unwrap();

    let metrics = solution.telemetry.expect("no metrics");
    assert_eq!(metrics.generations, 100);
    assert_eq!(metrics.evolution.len(), 10 + 1);
}

fn as_scalar_probability(probability: &OperatorProbabilityType) -> Float {
    match probability {
        OperatorProbabilityType::Scalar { scalar } => *scalar,
        _ => unreachable!(),
    }
}