use std::{
hash::{Hash, Hasher},
sync::{Arc, Mutex},
};
use crate::{
datastructures::{AdjacencyMatrix, NAMatrix, Path, Solution},
mst,
};
use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
#[cfg(feature = "mpi")]
use crate::datastructures::{MPICostPath, MPICostRank};
#[cfg(feature = "mpi")]
use mpi::{topology::SystemCommunicator, traits::*};
pub fn naive_solver<T>(graph_matrix: &T) -> Solution
where
T: AdjacencyMatrix,
{
let n = graph_matrix.dim();
let mut best_permutation: Path = (0..n).collect();
let mut best_cost = f64::INFINITY;
let mut current_permutation = best_permutation.clone();
while next_permutation(&mut current_permutation) {
let cost = graph_matrix.evaluate_circle(¤t_permutation);
if cost < best_cost {
best_cost = cost;
best_permutation = current_permutation.clone();
}
}
(best_cost, best_permutation)
}
pub fn first_improved_solver<T>(graph_matrix: &T) -> Solution
where
T: AdjacencyMatrix,
{
let n = graph_matrix.dim();
let mut best_permutation: Path = (0..n).collect();
let mut best_cost = f64::INFINITY;
let mut current_permutation = best_permutation.clone();
while next_permutation(&mut current_permutation[1..]) {
let cost = graph_matrix.evaluate_circle(¤t_permutation);
if cost < best_cost {
best_cost = cost;
best_permutation = current_permutation.clone();
}
}
(best_cost, best_permutation)
}
pub fn second_improved_solver<T>(graph_matrix: &T) -> Solution
where
T: AdjacencyMatrix,
{
let mut current_prefix = Vec::new();
current_prefix.reserve(graph_matrix.dim());
let mut result = (f64::INFINITY, Vec::new());
_second_improved_solver_rec(graph_matrix, &mut current_prefix, 0.0, &mut result);
result
}
fn _second_improved_solver_rec<T>(
graph_matrix: &T,
current_prefix: &mut Path,
current_cost: f64,
result: &mut Solution,
) where
T: AdjacencyMatrix,
{
let n = graph_matrix.dim();
let mut current_cost = current_cost;
if current_prefix.len() == n {
current_cost += graph_matrix.get(
*current_prefix.last().unwrap(),
*current_prefix.first().unwrap(),
);
let best_cost = result.0;
if current_cost < best_cost {
result.0 = current_cost;
result.1 = current_prefix.clone();
}
return;
}
for i in 0..n {
if current_prefix.contains(&i) {
continue;
}
current_prefix.push(i);
if current_prefix.len() == 1 {
_second_improved_solver_rec(graph_matrix, current_prefix, current_cost, result);
current_prefix.pop();
continue;
}
let from = current_prefix.len() - 2;
let to = from + 1;
let cost_last_edge = graph_matrix.get(current_prefix[from], current_prefix[to]);
current_cost += cost_last_edge;
_second_improved_solver_rec(graph_matrix, current_prefix, current_cost, result);
current_cost -= cost_last_edge;
current_prefix.pop();
}
}
pub fn third_improved_solver<T>(graph_matrix: &T) -> Solution
where
T: AdjacencyMatrix,
{
let mut current_prefix = Vec::new();
current_prefix.reserve(graph_matrix.dim());
let mut result = (f64::INFINITY, Vec::new());
_third_improved_solver_rec(graph_matrix, &mut current_prefix, 0.0, &mut result);
result
}
fn _third_improved_solver_rec<T>(
graph_matrix: &T,
current_prefix: &mut Path,
current_cost: f64,
result: &mut Solution,
) where
T: AdjacencyMatrix,
{
let n = graph_matrix.dim();
let mut current_cost = current_cost;
if current_prefix.len() == n {
current_cost += graph_matrix.get(
*current_prefix.last().unwrap(),
*current_prefix.first().unwrap(),
);
let best_cost = result.0;
if current_cost < best_cost {
result.0 = current_cost;
result.1 = current_prefix.clone();
}
return;
}
for i in 0..n {
if current_prefix.contains(&i) {
continue;
}
current_prefix.push(i);
if current_prefix.len() == 1 {
_third_improved_solver_rec(graph_matrix, current_prefix, current_cost, result);
current_prefix.pop();
continue;
}
let from = current_prefix.len() - 2;
let to = from + 1;
let cost_last_edge = graph_matrix.get(current_prefix[from], current_prefix[to]);
current_cost += cost_last_edge;
if current_cost <= result.0 {
_third_improved_solver_rec(graph_matrix, current_prefix, current_cost, result);
}
current_cost -= cost_last_edge;
current_prefix.pop();
}
}
pub fn fourth_improved_solver<T>(graph_matrix: &T) -> Solution
where
T: AdjacencyMatrix,
{
let mut current_prefix = Vec::new();
current_prefix.reserve(graph_matrix.dim());
let mut result = (f64::INFINITY, Vec::new());
_fourth_improved_solver_rec(graph_matrix, &mut current_prefix, 0.0, &mut result);
result
}
fn _fourth_improved_solver_rec<T>(
graph_matrix: &T,
current_prefix: &mut Path,
current_cost: f64,
result: &mut Solution,
) where
T: AdjacencyMatrix,
{
let n = graph_matrix.dim();
let mut current_cost = current_cost;
if current_prefix.len() == n {
current_cost += graph_matrix.get(
*current_prefix.last().unwrap(),
*current_prefix.first().unwrap(),
);
let best_cost = result.0;
if current_cost < best_cost {
result.0 = current_cost;
result.1 = current_prefix.clone();
}
return;
}
for i in 0..n {
if current_prefix.contains(&i) {
continue;
}
current_prefix.push(i);
if current_prefix.len() == 1 {
_fourth_improved_solver_rec(graph_matrix, current_prefix, current_cost, result);
current_prefix.pop();
continue;
}
let from = current_prefix.len() - 2;
let to = from + 1;
let cost_last_edge = graph_matrix.get(current_prefix[from], current_prefix[to]);
current_cost += cost_last_edge;
let lower_bound = compute_nn_of_remaining_vertices(graph_matrix, current_prefix, n);
if current_cost + lower_bound <= result.0 {
_fourth_improved_solver_rec(graph_matrix, current_prefix, current_cost, result);
}
current_cost -= cost_last_edge;
current_prefix.pop();
}
}
fn compute_nn_of_remaining_vertices<T>(graph_matrix: &T, subtour: &Path, n: usize) -> f64
where
T: AdjacencyMatrix,
{
let mut res = 0.0;
for i in 0..n {
if subtour.contains(&i) {
continue;
}
let mut shortest_edge_cost = f64::INFINITY;
for j in (i + 1)..n {
if subtour.contains(&j) {
continue;
}
let cost = graph_matrix.get(i, j);
if cost < shortest_edge_cost {
shortest_edge_cost = cost;
}
}
if shortest_edge_cost != f64::INFINITY {
res += shortest_edge_cost;
}
}
res
}
pub fn fifth_improved_solver(graph_matrix: &NAMatrix) -> Solution {
let mut current_prefix = Vec::new();
current_prefix.reserve(graph_matrix.dim());
let mut result = (f64::INFINITY, Vec::new());
_fifth_improved_solver_rec(graph_matrix, &mut current_prefix, 0.0, &mut result);
result
}
fn _fifth_improved_solver_rec(
graph_matrix: &NAMatrix,
current_prefix: &mut Path,
current_cost: f64,
result: &mut Solution,
) {
let n = graph_matrix.dim();
let mut current_cost = current_cost;
if current_prefix.len() == n {
current_cost += graph_matrix.get(
*current_prefix.last().unwrap(),
*current_prefix.first().unwrap(),
);
let best_cost = result.0;
if current_cost < best_cost {
result.0 = current_cost;
result.1 = current_prefix.clone();
}
return;
}
for i in 0..n {
if current_prefix.contains(&i) {
continue;
}
current_prefix.push(i);
if current_prefix.len() == 1 {
_fifth_improved_solver_rec(graph_matrix, current_prefix, current_cost, result);
current_prefix.pop();
continue;
}
let from = current_prefix.len() - 2;
let to = from + 1;
let cost_last_edge = graph_matrix.get(current_prefix[from], current_prefix[to]);
current_cost += cost_last_edge;
let lower_bound_graph =
mst::prim_with_excluded_node_single_threaded(graph_matrix, current_prefix);
let lower_bound = lower_bound_graph.directed_edge_weight();
if current_cost + lower_bound <= result.0 {
_fifth_improved_solver_rec(graph_matrix, current_prefix, current_cost, result);
}
current_cost -= cost_last_edge;
current_prefix.pop();
}
}
pub fn sixth_improved_solver(graph_matrix: &NAMatrix) -> Solution {
let mut current_prefix = Vec::new();
current_prefix.reserve(graph_matrix.dim());
let mut result = (f64::INFINITY, Vec::new());
let mut mst_cache: FxHashMap<u64, f64> = FxHashMap::default();
_sixth_improved_solver_rec(
graph_matrix,
&mut current_prefix,
0.0,
&mut result,
&mut mst_cache,
);
result
}
fn _sixth_improved_solver_rec(
graph_matrix: &NAMatrix,
current_prefix: &mut Path,
current_cost: f64,
result: &mut Solution,
mst_cache: &mut FxHashMap<u64, f64>,
) {
let n = graph_matrix.dim();
let mut current_cost = current_cost;
if current_prefix.len() == n {
current_cost += graph_matrix.get(
*current_prefix.last().unwrap(),
*current_prefix.first().unwrap(),
);
let best_cost = result.0;
if current_cost < best_cost {
result.0 = current_cost;
result.1 = current_prefix.clone();
}
return;
}
for i in 0..n {
if current_prefix.contains(&i) {
continue;
}
current_prefix.push(i);
if current_prefix.len() == 1 {
_sixth_improved_solver_rec(
graph_matrix,
current_prefix,
current_cost,
result,
mst_cache,
);
current_prefix.pop();
continue;
}
let from = current_prefix.len() - 2;
let to = from + 1;
let cost_last_edge = graph_matrix.get(current_prefix[from], current_prefix[to]);
current_cost += cost_last_edge;
let hash = {
let mut hasher = FxHasher::default();
current_prefix.hash(&mut hasher);
hasher.finish()
};
let lower_bound = mst_cache.entry(hash).or_insert(
mst::prim_with_excluded_node_single_threaded(graph_matrix, current_prefix)
.directed_edge_weight(),
);
if current_cost + *lower_bound <= result.0 {
_sixth_improved_solver_rec(
graph_matrix,
current_prefix,
current_cost,
result,
mst_cache,
);
}
current_cost -= cost_last_edge;
current_prefix.pop();
}
}
pub fn next_permutation<T: Ord>(array: &mut [T]) -> bool {
if array.is_empty() {
return false;
}
let mut i: usize = array.len() - 1;
while i > 0 && array[i - 1] >= array[i] {
i -= 1;
}
if i == 0 {
return false;
}
let mut j: usize = array.len() - 1;
while array[j] <= array[i - 1] {
j -= 1;
}
array.swap(i - 1, j);
array[i..].reverse();
true
}
pub fn split_up_b_ary_number_into_n_chunks(
number_digits: usize,
basis: usize,
n: usize,
k: usize,
) -> (Vec<usize>, Vec<usize>) {
let total_values = basis.pow(number_digits as u32);
assert!(total_values > n);
let chunk_size = total_values / n;
let min_value = k * chunk_size;
let max_value = min_value + chunk_size - 1;
let min_digits = convert_to_b_ary_digits(min_value, basis, number_digits);
let max_digits = convert_to_b_ary_digits(max_value, basis, number_digits);
(min_digits, max_digits)
}
fn convert_to_b_ary_digits(value: usize, basis: usize, number_digits: usize) -> Vec<usize> {
let mut digits = Vec::with_capacity(number_digits);
let mut remainder = value;
for _ in 0..number_digits {
digits.push(remainder % basis);
remainder /= basis;
}
digits.reverse();
digits
}
pub fn get_next_value(current_digits: &mut [usize], basis: usize) -> Option<Vec<usize>> {
let mut carry = 1;
for digit in current_digits.iter_mut().rev() {
*digit += carry;
carry = *digit / basis;
*digit %= basis;
if carry == 0 {
return Some(current_digits.to_vec());
}
}
None
}
pub fn threaded_solver(graph_matrix: &NAMatrix) -> Solution {
threaded_solver_generic(graph_matrix, 3, None)
}
fn threaded_solver_generic(
graph_matrix: &NAMatrix,
prefix_length: usize,
number_of_threads: Option<usize>,
) -> Solution {
let best_known_result = Arc::new(Mutex::new((f64::INFINITY, Vec::<usize>::new())));
let number_of_threads = match number_of_threads {
Some(n) => n,
None => std::thread::available_parallelism()
.expect("Could not determine number of threads!")
.into(),
};
if number_of_threads >= graph_matrix.dim() {
return fifth_improved_solver(graph_matrix);
}
rayon::scope(|s| {
for i in 0..number_of_threads {
let bkr = Arc::clone(&best_known_result);
s.spawn(move |_| {
let (min_digits, max_digits) = split_up_b_ary_number_into_n_chunks(
prefix_length,
graph_matrix.dim(),
number_of_threads,
i,
);
let mut min_digits = min_digits;
let mut current_prefix: Vec<usize> = Vec::new();
current_prefix.reserve(graph_matrix.dim());
while let Some(next_value) = get_next_value(&mut min_digits, graph_matrix.dim()) {
if next_value == max_digits {
break;
}
let is_unique =
next_value.len() == next_value.iter().collect::<FxHashSet<_>>().len();
if !is_unique {
continue;
}
for i in &next_value {
current_prefix.push(*i);
}
let starting_cost = graph_matrix.evaluate_path(¤t_prefix);
let mut result = (f64::INFINITY, Vec::new());
_fifth_improved_solver_rec(
graph_matrix,
&mut current_prefix,
starting_cost,
&mut result,
);
current_prefix.clear();
let mut global_solution = bkr.lock().unwrap();
if result.0 < global_solution.0 {
global_solution.0 = result.0;
global_solution.1 = result.1.clone();
}
}
});
}
});
Arc::try_unwrap(best_known_result)
.unwrap()
.into_inner()
.unwrap()
}
#[cfg(feature = "mpi")]
pub fn static_mpi_solver(graph_matrix: &NAMatrix) -> Solution {
static_mpi_solver_generic(graph_matrix, 3)
}
#[cfg(feature = "mpi")]
pub fn static_mpi_solver_generic(graph_matrix: &NAMatrix, prefix_length: usize) -> Solution {
let universe = mpi::initialize().unwrap();
let world = universe.world();
let rank = world.rank();
let root = world.process_at_rank(0);
let mut winner = MPICostRank(f64::INFINITY, 0);
let mut winner_path = vec![0; graph_matrix.dim()];
if rank == 0 {
winner = static_mpi_solver_generic_root(&world);
} else {
winner_path = static_mpi_solver_generic_nonroot(&world, graph_matrix, prefix_length);
}
root.broadcast_into(&mut winner);
let winner_process = world.process_at_rank(winner.1);
winner_process.broadcast_into(&mut winner_path[..]);
(winner.0, winner_path)
}
#[cfg(feature = "mpi")]
fn static_mpi_solver_generic_root(world: &SystemCommunicator) -> MPICostRank {
let size = world.size();
let mut current_winner = MPICostRank(f64::INFINITY, 0);
let mut number_of_finished_proceses = 0;
while number_of_finished_proceses < size - 1 {
let (msg, _) = world.any_process().receive::<MPICostRank>();
if msg.0 >= 0.0 {
if msg.0 < current_winner.0 {
current_winner.0 = msg.0;
current_winner.1 = msg.1;
}
world.process_at_rank(msg.1).send(&(current_winner.0));
} else {
number_of_finished_proceses += 1;
}
}
world.barrier();
current_winner
}
#[cfg(feature = "mpi")]
fn static_mpi_solver_generic_nonroot(
world: &SystemCommunicator,
graph_matrix: &NAMatrix,
prefix_length: usize,
) -> Path {
let size = world.size();
let rank = world.rank();
let root = world.process_at_rank(0);
let (min_digits, max_digits) = split_up_b_ary_number_into_n_chunks(
prefix_length,
graph_matrix.dim(),
(size as usize) - 1,
(rank as usize) - 1,
);
let mut local_solution = (f64::INFINITY, Vec::new());
let mut global_best_cost = f64::INFINITY;
let mut min_digits = min_digits;
let mut current_prefix: Vec<usize> = Vec::new();
current_prefix.reserve(graph_matrix.dim());
while let Some(next_value) = get_next_value(&mut min_digits, graph_matrix.dim()) {
if next_value == max_digits {
break;
}
let is_unique = next_value.len() == next_value.iter().collect::<FxHashSet<_>>().len();
if !is_unique {
continue;
}
for i in &next_value {
current_prefix.push(*i);
}
let starting_cost = graph_matrix.evaluate_path(¤t_prefix);
let mut result = (f64::INFINITY, Vec::new());
_mpi_improved_solver_rec(
graph_matrix,
&mut current_prefix,
starting_cost,
&mut result,
global_best_cost,
);
current_prefix.clear();
if result.0 < local_solution.0 {
local_solution.0 = result.0;
local_solution.1 = result.1.clone();
}
let sendbuf = MPICostRank(local_solution.0, rank);
root.send(&sendbuf);
let (msg, _) = root.receive::<f64>();
global_best_cost = msg;
}
root.send(&MPICostRank(-1.0, rank));
world.barrier();
local_solution.1
}
#[cfg(feature = "mpi")]
fn _mpi_improved_solver_rec(
graph_matrix: &NAMatrix,
current_prefix: &mut Path,
current_cost: f64,
result: &mut Solution,
global_best_cost: f64,
) {
let n = graph_matrix.dim();
let mut current_cost = current_cost;
if current_prefix.len() == n {
current_cost += graph_matrix.get(
*current_prefix.last().unwrap(),
*current_prefix.first().unwrap(),
);
let best_cost = result.0;
if current_cost < best_cost {
result.0 = current_cost;
result.1 = current_prefix.clone();
}
return;
}
for i in 0..n {
if current_prefix.contains(&i) {
continue;
}
current_prefix.push(i);
if current_prefix.len() == 1 {
_mpi_improved_solver_rec(
graph_matrix,
current_prefix,
current_cost,
result,
global_best_cost,
);
current_prefix.pop();
continue;
}
let from = current_prefix.len() - 2;
let to = from + 1;
let cost_last_edge = graph_matrix.get(current_prefix[from], current_prefix[to]);
current_cost += cost_last_edge;
let lower_bound_graph =
mst::prim_with_excluded_node_single_threaded(graph_matrix, current_prefix);
let lower_bound = lower_bound_graph.directed_edge_weight();
let best_known_solution = if global_best_cost < result.0 {
global_best_cost
} else {
result.0
};
if current_cost + lower_bound <= best_known_solution {
_mpi_improved_solver_rec(
graph_matrix,
current_prefix,
current_cost,
result,
global_best_cost,
);
}
current_cost -= cost_last_edge;
current_prefix.pop();
}
}
#[cfg(feature = "mpi")]
pub fn dynamic_mpi_solver(graph_matrix: &NAMatrix) -> Solution {
let universe = mpi::initialize().unwrap();
let world = universe.world();
let rank = world.rank();
let root = world.process_at_rank(0);
let mut winner = MPICostRank(f64::INFINITY, 0);
let mut winner_path = vec![0; graph_matrix.dim()];
if rank == 0 {
winner = dynamic_mpi_solver_root(&world, graph_matrix);
} else {
winner_path = dynamic_mpi_solver_nonroot(&world, graph_matrix);
}
root.broadcast_into(&mut winner);
let winner_process = world.process_at_rank(winner.1);
winner_process.broadcast_into(&mut winner_path[..]);
(winner.0, winner_path)
}
#[cfg(feature = "mpi")]
fn dynamic_mpi_solver_root(world: &SystemCommunicator, graph_matrix: &NAMatrix) -> MPICostRank {
let size = world.size();
let number_of_workers = size - 1;
const PREFIX_LENGTH: usize = 3;
let n = graph_matrix.dim();
let mut current_winner = MPICostRank(f64::INFINITY, 0);
let mut number_of_waiting_processes = 0;
let mut current_prefix: Option<[usize; PREFIX_LENGTH]> = Some([1, 2, 3]);
while number_of_waiting_processes < number_of_workers {
let (msg, _) = world.any_process().receive::<MPICostRank>();
if msg.0 >= 0.0 && msg.0 < current_winner.0 {
current_winner.0 = msg.0;
current_winner.1 = msg.1;
}
if let Some(next_value) = current_prefix {
let sendbuf = MPICostPath(current_winner.0, next_value);
world.process_at_rank(msg.1).send(&sendbuf);
loop {
current_prefix =
get_next_value(&mut current_prefix.unwrap(), n).map(|v| [v[0], v[1], v[2]]);
if current_prefix.is_none() {
break;
}
let is_unique = current_prefix.unwrap().len()
== current_prefix
.unwrap()
.iter()
.collect::<FxHashSet<_>>()
.len();
if is_unique {
break;
}
}
} else {
let sendbuf = MPICostPath(current_winner.0, [0, 0, 0]);
world.process_at_rank(msg.1).send(&sendbuf);
number_of_waiting_processes += 1;
}
}
world.barrier();
current_winner
}
#[cfg(feature = "mpi")]
fn dynamic_mpi_solver_nonroot(world: &SystemCommunicator, graph_matrix: &NAMatrix) -> Path {
let rank = world.rank();
let root = world.process_at_rank(0);
let mut local_solution = (f64::INFINITY, Vec::new());
let sendbuf = MPICostRank(local_solution.0, rank);
root.send(&sendbuf);
loop {
let (cost_path, _) = root.receive::<MPICostPath>();
let global_best_cost = cost_path.0;
let mut current_prefix = cost_path.1.to_vec();
if current_prefix.iter().all(|&x| x == 0) {
world.barrier();
return local_solution.1;
}
let starting_cost = graph_matrix.evaluate_path(¤t_prefix);
let mut result = (f64::INFINITY, Vec::new());
_mpi_improved_solver_rec(
graph_matrix,
&mut current_prefix,
starting_cost,
&mut result,
global_best_cost,
);
if result.0 < local_solution.0 {
local_solution.0 = result.0;
local_solution.1 = result.1;
}
let sendbuf = MPICostRank(local_solution.0, rank);
root.send(&sendbuf);
}
}
#[cfg(test)]
mod exact_solver {
use approx::relative_eq;
use super::*;
use crate::datastructures::{NAMatrix, VecMatrix};
use crate::parser::{Edge, Graph, Vertex};
use lazy_static::lazy_static;
lazy_static! {
static ref SMALL_FLOAT_GRAPH: Graph = Graph {
vertices: vec![
Vertex {
edges: vec![
Edge {
to: 1,
cost: 13.215648444670196,
},
Edge {
to: 2,
cost: 9.674413400408712,
},
Edge {
to: 3,
cost: 1.0970596862282833,
},
Edge {
to: 4,
cost: 16.098684067859647,
},
],
},
Vertex {
edges: vec![
Edge {
to: 0,
cost: 13.215648444670196,
},
Edge {
to: 2,
cost: 12.221639547131913,
},
Edge {
to: 3,
cost: 17.306826463341803,
},
Edge {
to: 4,
cost: 8.321138140452149,
},
],
},
Vertex {
edges: vec![
Edge {
to: 0,
cost: 9.674413400408712,
},
Edge {
to: 1,
cost: 12.221639547131913,
},
Edge {
to: 3,
cost: 4.6376150266768885,
},
Edge {
to: 4,
cost: 15.838066781407072,
},
],
},
Vertex {
edges: vec![
Edge {
to: 0,
cost: 1.0970596862282833,
},
Edge {
to: 1,
cost: 17.306826463341803,
},
Edge {
to: 2,
cost: 4.6376150266768885,
},
Edge {
to: 4,
cost: 6.102211932446107,
},
],
},
Vertex {
edges: vec![
Edge {
to: 0,
cost: 16.098684067859647,
},
Edge {
to: 1,
cost: 8.321138140452149,
},
Edge {
to: 2,
cost: 15.838066781407072,
},
Edge {
to: 3,
cost: 6.102211932446107,
},
],
},
],
};
}
lazy_static! {
static ref BIG_FLOAT_GRAPH: Graph = Graph {
vertices: vec![
Vertex {
edges: vec![
Edge {
to: 1,
cost: 5.357166694956081,
},
Edge {
to: 2,
cost: 12.673287166274285,
},
Edge {
to: 3,
cost: 15.392922519581575,
},
Edge {
to: 4,
cost: 1.8824165228898004,
},
Edge {
to: 5,
cost: 1.0673823908781577,
},
Edge {
to: 6,
cost: 8.668326879490138,
},
Edge {
to: 7,
cost: 18.956348946357103,
},
Edge {
to: 8,
cost: 5.399642479870355,
},
],
},
Vertex {
edges: vec![
Edge {
to: 0,
cost: 5.357166694956081,
},
Edge {
to: 2,
cost: 11.139733539749999,
},
Edge {
to: 3,
cost: 1.661032458795486,
},
Edge {
to: 4,
cost: 18.702631945210115,
},
Edge {
to: 5,
cost: 3.847655828276122,
},
Edge {
to: 6,
cost: 15.73510598766653,
},
Edge {
to: 7,
cost: 0.24655608854276645,
},
Edge {
to: 8,
cost: 4.321598762165737,
},
],
},
Vertex {
edges: vec![
Edge {
to: 0,
cost: 12.673287166274285,
},
Edge {
to: 1,
cost: 11.139733539749999,
},
Edge {
to: 3,
cost: 2.1803729313885345,
},
Edge {
to: 4,
cost: 16.313099247004377,
},
Edge {
to: 5,
cost: 5.585527987185975,
},
Edge {
to: 6,
cost: 8.932741722100753,
},
Edge {
to: 7,
cost: 12.6998544424725,
},
Edge {
to: 8,
cost: 9.05733402266841,
},
],
},
Vertex {
edges: vec![
Edge {
to: 0,
cost: 15.392922519581575,
},
Edge {
to: 1,
cost: 1.661032458795486,
},
Edge {
to: 2,
cost: 2.1803729313885345,
},
Edge {
to: 4,
cost: 3.340513012587236,
},
Edge {
to: 5,
cost: 1.46551068868777,
},
Edge {
to: 6,
cost: 2.6426709551798355,
},
Edge {
to: 7,
cost: 4.492948831722041,
},
Edge {
to: 8,
cost: 13.41757522658849,
},
],
},
Vertex {
edges: vec![
Edge {
to: 0,
cost: 1.8824165228898004,
},
Edge {
to: 1,
cost: 18.702631945210115,
},
Edge {
to: 2,
cost: 16.313099247004377,
},
Edge {
to: 3,
cost: 3.340513012587236,
},
Edge {
to: 5,
cost: 9.568614854660245,
},
Edge {
to: 6,
cost: 6.849461885327388,
},
Edge {
to: 7,
cost: 7.455992424446736,
},
Edge {
to: 8,
cost: 19.61866966591363,
},
],
},
Vertex {
edges: vec![
Edge {
to: 0,
cost: 1.0673823908781577,
},
Edge {
to: 1,
cost: 3.847655828276122,
},
Edge {
to: 2,
cost: 5.585527987185975,
},
Edge {
to: 3,
cost: 1.46551068868777,
},
Edge {
to: 4,
cost: 9.568614854660245,
},
Edge {
to: 6,
cost: 7.516298524772413,
},
Edge {
to: 7,
cost: 17.155030102652216,
},
Edge {
to: 8,
cost: 17.46182408314527,
},
],
},
Vertex {
edges: vec![
Edge {
to: 0,
cost: 8.668326879490138,
},
Edge {
to: 1,
cost: 15.73510598766653,
},
Edge {
to: 2,
cost: 8.932741722100753,
},
Edge {
to: 3,
cost: 2.6426709551798355,
},
Edge {
to: 4,
cost: 6.849461885327388,
},
Edge {
to: 5,
cost: 7.516298524772413,
},
Edge {
to: 7,
cost: 5.959449216135542,
},
Edge {
to: 8,
cost: 11.172366336098495,
},
],
},
Vertex {
edges: vec![
Edge {
to: 0,
cost: 18.956348946357103,
},
Edge {
to: 1,
cost: 0.24655608854276645,
},
Edge {
to: 2,
cost: 12.6998544424725,
},
Edge {
to: 3,
cost: 4.492948831722041,
},
Edge {
to: 4,
cost: 7.455992424446736,
},
Edge {
to: 5,
cost: 17.155030102652216,
},
Edge {
to: 6,
cost: 5.959449216135542,
},
Edge {
to: 8,
cost: 8.168048838216963,
},
],
},
Vertex {
edges: vec![
Edge {
to: 0,
cost: 5.399642479870355,
},
Edge {
to: 1,
cost: 4.321598762165737,
},
Edge {
to: 2,
cost: 9.05733402266841,
},
Edge {
to: 3,
cost: 13.41757522658849,
},
Edge {
to: 4,
cost: 19.61866966591363,
},
Edge {
to: 5,
cost: 17.46182408314527,
},
Edge {
to: 6,
cost: 11.172366336098495,
},
Edge {
to: 7,
cost: 8.168048838216963,
},
],
},
],
};
}
lazy_static! {
static ref SMALL_INT_GRAPH: Graph = Graph {
vertices: vec![
Vertex {
edges: vec![
Edge { to: 1, cost: 5.0 },
Edge { to: 2, cost: 4.0 },
Edge { to: 3, cost: 10.0 },
],
},
Vertex {
edges: vec![
Edge { to: 0, cost: 5.0 },
Edge { to: 2, cost: 8.0 },
Edge { to: 3, cost: 5.0 },
],
},
Vertex {
edges: vec![
Edge { to: 0, cost: 4.0 },
Edge { to: 1, cost: 8.0 },
Edge { to: 3, cost: 3.0 },
],
},
Vertex {
edges: vec![
Edge { to: 0, cost: 10.0 },
Edge { to: 1, cost: 5.0 },
Edge { to: 2, cost: 3.0 },
],
},
],
};
}
fn is_same_undirected_circle(seq1: &Path, seq2: &Path) -> bool {
if seq1.len() != seq2.len() {
return false;
}
let n = seq1.len();
let rotations = (0..n).map(|i| {
seq1[i..]
.iter()
.chain(seq1[..i].iter())
.copied()
.collect::<Path>()
});
let reversed_rotations = rotations
.clone()
.map(|xs| xs.into_iter().rev().collect::<Path>());
for rotation in rotations.chain(reversed_rotations) {
if rotation[..] == seq2[..] {
return true;
}
}
false
}
#[test]
fn test_is_same_undirected_circle() {
assert!(is_same_undirected_circle(
&vec![1, 2, 3, 4, 5, 6],
&vec![4, 3, 2, 1, 6, 5]
));
}
#[test]
fn test_not_same_undirected_circle() {
assert!(!is_same_undirected_circle(
&vec![1, 2, 3, 4, 5, 6],
&vec![4, 3, 2, 6, 1, 5]
));
}
#[test]
fn test_float_tsp_vecmatrix() {
let gm: VecMatrix = SMALL_FLOAT_GRAPH.clone().into();
for f in [
naive_solver,
first_improved_solver,
second_improved_solver,
third_improved_solver,
fourth_improved_solver,
]
.iter()
{
let (best_cost, best_permutation) = f(&gm);
assert!(relative_eq!(37.41646270666716, best_cost));
assert!(is_same_undirected_circle(
&vec![0, 3, 4, 1, 2],
&best_permutation
));
}
}
#[test]
fn test_float_tsp_namatrix() {
let gm: NAMatrix = <NAMatrix as From<&Graph>>::from(&SMALL_FLOAT_GRAPH);
for f in [
naive_solver,
first_improved_solver,
second_improved_solver,
third_improved_solver,
fourth_improved_solver,
fifth_improved_solver,
sixth_improved_solver,
threaded_solver,
]
.iter()
{
let (best_cost, best_permutation) = f(&gm);
assert!(relative_eq!(37.41646270666716, best_cost));
assert!(is_same_undirected_circle(
&vec![0, 3, 4, 1, 2],
&best_permutation
));
}
}
#[test]
fn test_big_floating_tsp_vecmatrix() {
let gm: VecMatrix = BIG_FLOAT_GRAPH.clone().into();
for f in [
naive_solver,
first_improved_solver,
second_improved_solver,
third_improved_solver,
fourth_improved_solver,
]
.iter()
{
let (best_cost, best_permutation) = f(&gm);
assert!(relative_eq!(33.03008250868411, best_cost));
assert!(is_same_undirected_circle(
&vec![0, 5, 3, 2, 8, 1, 7, 6, 4],
&best_permutation
));
}
}
#[test]
fn test_big_floating_tsp_namatrix() {
let gm: NAMatrix = <NAMatrix as From<&Graph>>::from(&BIG_FLOAT_GRAPH);
for f in [
naive_solver,
first_improved_solver,
second_improved_solver,
third_improved_solver,
fourth_improved_solver,
fifth_improved_solver,
sixth_improved_solver,
threaded_solver,
]
.iter()
{
let (best_cost, best_permutation) = f(&gm);
assert!(relative_eq!(33.03008250868411, best_cost));
assert!(is_same_undirected_circle(
&vec![0, 5, 3, 2, 8, 1, 7, 6, 4],
&best_permutation
));
}
}
#[test]
fn test_integer_tsp_vecmatrix() {
let gm: VecMatrix = SMALL_INT_GRAPH.clone().into();
for f in [
naive_solver,
first_improved_solver,
second_improved_solver,
third_improved_solver,
fourth_improved_solver,
]
.iter()
{
let (best_cost, best_permutation) = f(&gm);
assert!(relative_eq!(best_cost, 17.0));
assert!(is_same_undirected_circle(
&best_permutation,
&vec![0, 1, 3, 2]
));
}
}
#[test]
fn test_integer_tsp_namatrix() {
let gm: NAMatrix = <NAMatrix as From<&Graph>>::from(&SMALL_INT_GRAPH);
for f in [
naive_solver,
first_improved_solver,
second_improved_solver,
third_improved_solver,
fourth_improved_solver,
fifth_improved_solver,
sixth_improved_solver,
threaded_solver,
]
.iter()
{
let (best_cost, best_permutation) = f(&gm);
assert!(relative_eq!(best_cost, 17.0));
assert!(is_same_undirected_circle(
&best_permutation,
&vec![0, 1, 3, 2]
));
}
}
#[test]
fn test_get_all_permutations() {
let mut starting_vec = (0..4).collect::<Vec<i32>>();
let mut results = vec![];
results.push(starting_vec.clone());
while next_permutation(&mut starting_vec) {
results.push(starting_vec.clone());
}
let expected = vec![
vec![0, 1, 2, 3],
vec![0, 1, 3, 2],
vec![0, 2, 1, 3],
vec![0, 2, 3, 1],
vec![0, 3, 1, 2],
vec![0, 3, 2, 1],
vec![1, 0, 2, 3],
vec![1, 0, 3, 2],
vec![1, 2, 0, 3],
vec![1, 2, 3, 0],
vec![1, 3, 0, 2],
vec![1, 3, 2, 0],
vec![2, 0, 1, 3],
vec![2, 0, 3, 1],
vec![2, 1, 0, 3],
vec![2, 1, 3, 0],
vec![2, 3, 0, 1],
vec![2, 3, 1, 0],
vec![3, 0, 1, 2],
vec![3, 0, 2, 1],
vec![3, 1, 0, 2],
vec![3, 1, 2, 0],
vec![3, 2, 0, 1],
vec![3, 2, 1, 0],
];
assert_eq!(expected, results);
}
}