use std::ops::{Deref, DerefMut};
use delegate::delegate;
use quick_xml::de::from_str;
use serde::{Deserialize, Serialize};
use crate::preconditions::{is_fully_connected, is_multi_edge, is_undirected};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct TravellingSalesmanProblemInstance {
pub name: String,
pub source: String,
pub description: String,
pub double_precision: u32,
pub ignored_digits: u32,
pub graph: Graph,
}
impl TravellingSalesmanProblemInstance {
pub fn parse_from_xml(xml: &str) -> Result<Self, quick_xml::DeError> {
let res: Result<Self, quick_xml::DeError> = from_str(xml);
if let Ok(tspi) = &res {
assert!(tspi.do_conditions_hold());
}
res
}
pub fn do_conditions_hold(&self) -> bool {
let g = &self.graph;
is_fully_connected(g) && (!is_multi_edge(g)) && is_undirected(g)
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Graph {
#[serde(rename = "$value")]
pub vertices: Vec<Vertex>,
}
impl Deref for Graph {
type Target = Vec<Vertex>;
fn deref(&self) -> &Self::Target {
&self.vertices
}
}
impl DerefMut for Graph {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.vertices
}
}
impl Graph {
delegate! {
to self.vertices {
#[call(len)]
pub fn num_vertices(&self) -> usize;
}
}
pub fn directed_edge_weight(&self) -> f64 {
self.iter()
.map(|vertex| vertex.iter().map(|edge| edge.cost).sum::<f64>())
.sum()
}
pub fn undirected_edge_weight(&self) -> f64 {
self.directed_edge_weight() * 0.5
}
pub fn add_undirected_edge(&mut self, from: usize, to: usize, cost: f64) {
debug_assert!(
from < self.num_vertices(),
"The vertex 'from' has to be a valid vertex in the graph"
);
debug_assert!(
to < self.num_vertices(),
"The vertex 'to' has to be a valid vertex in the graph"
);
let edge = Edge { to, cost };
let edge_reverse = Edge { to: from, cost };
self.vertices[from].add_edge(edge);
self.vertices[to].add_edge(edge_reverse);
}
pub fn vertex_degree(&self, vertex: usize) -> usize {
debug_assert!(
vertex < self.vertices.len(),
"The vertex {} is out of bounds, the Graph contains only the vertices 0, ..., {}",
vertex,
self.vertices.len() - 1
);
self.vertices[vertex].degree()
}
}
impl From<Vec<Vec<Edge>>> for Graph {
fn from(value: Vec<Vec<Edge>>) -> Self {
let vec = value.into_iter().map(Vertex::from).collect();
Graph { vertices: vec }
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Vertex {
#[serde(rename = "$value")]
pub edges: Vec<Edge>,
}
impl Vertex {
delegate! {
to self.edges {
pub fn iter(&self) -> std::slice::Iter<Edge>;
pub fn iter_mut(&mut self) -> std::slice::IterMut<Edge>;
#[call(push)]
pub fn add_edge(&mut self, edge: Edge);
#[call(len)]
pub fn degree(&self) -> usize;
}
}
}
impl From<Vec<Edge>> for Vertex {
fn from(value: Vec<Edge>) -> Self {
Vertex { edges: value }
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Copy, Clone)]
pub struct Edge {
#[serde(rename = "$text")]
pub to: usize,
#[serde(rename = "@cost")]
pub cost: f64,
}
#[cfg(test)]
mod test_graph_methods {
use approx::assert_abs_diff_eq;
use super::*;
#[test]
fn build_vertex_from_slice() {
let edges = vec![Edge { to: 0, cost: 1.0 }, Edge { to: 30, cost: 0.34 }];
let expected = Vertex {
edges: vec![Edge { to: 0, cost: 1.0 }, Edge { to: 30, cost: 0.34 }],
};
assert_eq!(expected, Vertex::from(edges));
}
#[test]
fn build_graph_from_slice() {
let vertices = vec![
vec![Edge { to: 0, cost: 1.0 }, Edge { to: 30, cost: 0.34 }],
vec![Edge { to: 10, cost: 5.34 }],
];
let expected = Graph {
vertices: vec![
Vertex::from(vertices[0].clone()),
Vertex::from(vertices[1].clone()),
],
};
assert_eq!(expected, Graph::from(vertices));
}
#[test]
fn directed_edge_weight() {
let graph = Graph::from(vec![
vec![Edge { to: 0, cost: 1.0 }, Edge { to: 30, cost: 0.34 }],
vec![Edge { to: 10, cost: 5.34 }],
]);
let expected = 1.0 + 0.34 + 5.34;
assert_abs_diff_eq!(expected, graph.directed_edge_weight());
}
#[test]
fn undirected_edge_weight() {
let graph = Graph::from(vec![
vec![Edge { to: 2, cost: 1.0 }, Edge { to: 1, cost: 0.34 }],
vec![Edge { to: 0, cost: 0.34 }],
vec![Edge { to: 0, cost: 1.0 }],
]);
assert_abs_diff_eq!(
graph.directed_edge_weight() / 2.0,
graph.undirected_edge_weight()
);
}
}
#[cfg(test)]
mod test_parsing {
use quick_xml::de::from_str;
use super::*;
#[test]
fn parse_edge() {
let edge_str = r#"<edge cost="2.000e+01">1</edge>"#;
let expected = Edge { to: 1, cost: 2e1 };
assert_eq!(expected, from_str(edge_str).unwrap());
}
#[test]
fn parse_vertex() {
let vertex_str =
r#"<vertex><edge cost="3.000e+01">1</edge><edge cost="2.000e+01">0</edge></vertex>"#;
let expected = Vertex {
edges: vec![Edge { to: 1, cost: 3e1 }, Edge { to: 0, cost: 2e1 }],
};
assert_eq!(expected, from_str(vertex_str).unwrap());
}
#[test]
fn parse_graph() {
let vertex_str = r#"
<graph>
<vertex><edge cost="3.000e+01">1</edge><edge cost="2.000e+01">0</edge></vertex>
<vertex><edge cost="3.000e+01">0</edge><edge cost="2.000e+01">2</edge></vertex>
</graph>"#;
let expected = Graph {
vertices: vec![
Vertex {
edges: vec![Edge { to: 1, cost: 3e1 }, Edge { to: 0, cost: 2e1 }],
},
Vertex {
edges: vec![Edge { to: 0, cost: 3e1 }, Edge { to: 2, cost: 2e1 }],
},
],
};
assert_eq!(expected, from_str(vertex_str).unwrap());
}
#[test]
fn parse_small_tsp_example() {
let xml = r#"
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<travellingSalesmanProblemInstance>
<name>test</name>
<source>Johann</source>
<description>small example</description>
<doublePrecision>15</doublePrecision>
<ignoredDigits>0</ignoredDigits>
<graph>
<vertex>
<edge cost="1.123456789012345e+00">1</edge>
</vertex>
<vertex>
<edge cost="1.000000000000000e+01">0</edge>
</vertex>
</graph>
</travellingSalesmanProblemInstance>
"#;
let expected = TravellingSalesmanProblemInstance {
name: String::from("test"),
source: String::from("Johann"),
description: String::from("small example"),
double_precision: 15,
ignored_digits: 0,
graph: Graph {
vertices: vec![
Vertex {
edges: vec![Edge {
to: 1,
cost: 1.123456789012345,
}],
},
Vertex {
edges: vec![Edge { to: 0, cost: 1e1 }],
},
],
},
};
assert_eq!(expected, from_str(xml).unwrap());
}
}