roma_lib 0.1.1

A Rust metaheuristics framework inspired by jMetal for optimization and experimentation.
Documentation
use roma_lib::{
    Algorithm,
    BinaryTournamentSelection,
    GeneticAlgorithm,
    GeneticAlgorithmParameters,
    HillClimbing,
    HillClimbingParameters,
    OrderCrossover,
    SimulatedAnnealing,
    SimulatedAnnealingParameters,
    SolutionSet,
    SwapMutation,
    TerminationCriteria,
    TerminationCriterion,
    TspProblem,
};

#[test]
fn hill_climbing_runs_on_tsp_and_returns_valid_distance() {
    let problem = TspProblem::with_distance_matrix(vec![
        vec![0.0, 8.0, 6.0, 4.0],
        vec![8.0, 0.0, 7.0, 5.0],
        vec![6.0, 7.0, 0.0, 3.0],
        vec![4.0, 5.0, 3.0, 0.0],
    ]);

    let parameters = HillClimbingParameters::new(
        SwapMutation::new(),
        0.25,
        TerminationCriteria::new(vec![TerminationCriterion::MaxIterations(50)]),
    )
    .with_seed(42);

    let mut algorithm = HillClimbing::new(parameters);
    let result = algorithm.run(&problem).expect("Hill Climbing on TSP should succeed");

    assert_eq!(result.size(), 1);
    let solution = result.get(0).expect("Expected one solution");
    assert_eq!(solution.num_variables(), 4);
    assert!(solution.quality_value().is_finite());
}

#[test]
fn simulated_annealing_runs_on_tsp_open_route() {
    let problem = TspProblem::with_distance_matrix(vec![
        vec![0.0, 9.0, 4.0, 7.0, 8.0],
        vec![9.0, 0.0, 5.0, 6.0, 3.0],
        vec![4.0, 5.0, 0.0, 2.0, 6.0],
        vec![7.0, 6.0, 2.0, 0.0, 1.0],
        vec![8.0, 3.0, 6.0, 1.0, 0.0],
    ])
    .with_open_route();

    let parameters = SimulatedAnnealingParameters::new(
        SwapMutation::new(),
        0.3,
        60.0,
        0.99,
        TerminationCriteria::new(vec![TerminationCriterion::MaxIterations(60)]),
    )
    .with_seed(7);

    let mut algorithm = SimulatedAnnealing::new(parameters);
    let result = algorithm
        .run(&problem)
        .expect("Simulated Annealing on TSP should succeed");

    assert_eq!(result.size(), 1);
    let solution = result.get(0).expect("Expected one solution");
    assert_eq!(solution.num_variables(), 5);
    assert!(solution.quality_value().is_finite());
}

#[test]
fn hill_climbing_respects_fixed_start_city_constraint() {
    let fixed_start_city = 3usize;
    let problem = TspProblem::with_distance_matrix(vec![
        vec![0.0, 8.0, 6.0, 4.0, 7.0],
        vec![8.0, 0.0, 7.0, 5.0, 9.0],
        vec![6.0, 7.0, 0.0, 3.0, 2.0],
        vec![4.0, 5.0, 3.0, 0.0, 1.0],
        vec![7.0, 9.0, 2.0, 1.0, 0.0],
    ])
    .with_fixed_start_city(fixed_start_city);

    let parameters = HillClimbingParameters::new(
        SwapMutation::new(),
        0.0,
        TerminationCriteria::new(vec![TerminationCriterion::MaxIterations(20)]),
    )
    .with_seed(99);

    let mut algorithm = HillClimbing::new(parameters);
    let result = algorithm
        .run(&problem)
        .expect("Hill Climbing with fixed-start TSP should succeed");

    let solution = result.get(0).expect("Expected one solution");
    assert_eq!(solution.get_variable(0).copied(), Some(fixed_start_city));
    assert!(solution.quality_value().is_finite());
}

#[test]
fn genetic_algorithm_runs_on_tsp_and_preserves_route_validity() {
    let problem = TspProblem::with_distance_matrix(vec![
        vec![0.0, 8.0, 6.0, 4.0, 7.0],
        vec![8.0, 0.0, 7.0, 5.0, 9.0],
        vec![6.0, 7.0, 0.0, 3.0, 2.0],
        vec![4.0, 5.0, 3.0, 0.0, 1.0],
        vec![7.0, 9.0, 2.0, 1.0, 0.0],
    ]);

    let parameters = GeneticAlgorithmParameters::new(
        24,
        0.9,
        0.15,
        OrderCrossover::new(),
        SwapMutation::new(),
        BinaryTournamentSelection::new(),
        TerminationCriteria::new(vec![TerminationCriterion::MaxIterations(25)]),
    )
    .with_elite_size(1)
    .with_seed(11)
    .sequential();

    let mut algorithm = GeneticAlgorithm::new(parameters);
    let result = algorithm.run(&problem).expect("GA on TSP should succeed");

    let best = result.best_solution(&problem).expect("Expected one best solution");
    let mut route = best.variables().to_vec();
    route.sort_unstable();

    assert_eq!(best.num_variables(), 5);
    assert_eq!(route, vec![0, 1, 2, 3, 4]);
    assert!(best.quality_value().is_finite());
}