use super::{DimacsReader, Error, Result};
use crate::builder::{Buildable, Builder};
use crate::traits::IndexDigraph;
use num_traits::Zero;
use std::fmt::Display;
use std::io::{Read, Write};
use std::str::FromStr;
pub struct Instance<G, T> {
pub graph: G,
pub balances: Vec<T>,
pub lower: Vec<T>,
pub upper: Vec<T>,
pub costs: Vec<T>,
}
pub fn read<R: Read, G, T>(reader: R) -> Result<Instance<G, T>>
where
G: IndexDigraph + Buildable,
T: FromStr + Zero + Clone,
T::Err: Display,
{
let mut reader = DimacsReader::new(reader);
let mut pline = reader.expect_line('p')?;
pline.expect("min")?;
let nnodes = pline.number()?;
let nedges = pline.number()?;
pline.end()?;
let mut builder = G::Builder::new();
let mut balances = vec![T::zero(); nnodes];
let mut costs = Vec::with_capacity(nedges);
let mut lower = Vec::with_capacity(nedges);
let mut upper = Vec::with_capacity(nedges);
let nodes = builder.add_nodes(nnodes);
while let Some((d, mut toks)) = reader.read_one_line_of(&["n", "a"])? {
#[allow(clippy::branches_sharing_code)]
if d == "n" {
let u: usize = toks.number()?;
if u < 1 || u > nnodes {
return Err(Error::Data {
line: toks.line,
msg: format!("invalid node id {} (must be in 1..{})", u, nnodes),
});
}
balances[u - 1] = toks.number()?;
} else {
let u: usize = toks.number()?;
let v: usize = toks.number()?;
let lb: T = toks.number()?;
let ub: T = toks.number()?;
let c: T = toks.number()?;
if u < 1 || u > nnodes {
return Err(Error::Data {
line: toks.line,
msg: format!("invalid source node id {} (must be in 1..{})", u, nnodes),
});
}
if v < 1 || v > nnodes {
return Err(Error::Data {
line: toks.line,
msg: format!("invalid sink node id {} (must be in 1..{})", u, nnodes),
});
}
if u == v {
return Err(Error::Data {
line: toks.line,
msg: format!("invalid loop ({},{}) in edge", u, u),
});
}
if upper.len() == nedges {
return Err(Error::Data {
line: toks.line,
msg: format!("unexpected 'a' line (expected exactly {} arcs)", nedges),
});
}
builder.add_edge(nodes[u - 1], nodes[v - 1]);
lower.push(lb);
upper.push(ub);
costs.push(c);
}
toks.end()?;
}
Ok(Instance {
graph: builder.into_graph(),
balances,
lower,
upper,
costs,
})
}
pub fn read_from_file<G, T>(filename: &str) -> Result<Instance<G, T>>
where
G: IndexDigraph + Buildable,
T: FromStr + Zero + Clone,
T::Err: Display,
{
read(std::fs::File::open(filename)?)
}
pub fn write<W, G, T>(mut w: W, instance: &Instance<G, T>) -> std::io::Result<()>
where
W: Write,
G: IndexDigraph,
T: Zero + Display + Clone,
{
let g = &instance.graph;
writeln!(w, "p min {} {}", g.num_nodes(), g.num_edges())?;
for u in g.nodes() {
let b = instance.balances[g.node_id(u)].clone();
if !b.is_zero() {
writeln!(w, "n {} {}", g.node_id(u) + 1, b)?;
}
}
for e in g.edges() {
let eid = g.edge_id(e);
writeln!(
w,
"a {} {} {} {} {}",
g.node_id(g.src(e)) + 1,
g.node_id(g.snk(e)) + 1,
instance.lower[eid],
instance.upper[eid],
instance.costs[eid]
)?;
}
Ok(())
}
pub fn write_to_file<G, T>(filename: &str, instance: &Instance<G, T>) -> std::io::Result<()>
where
G: IndexDigraph,
T: Zero + Display + Clone,
{
write(&mut std::fs::File::create(filename)?, instance)
}
pub fn write_solution<'a, W, G, T, Fs>(mut w: W, g: &'a G, flow: Fs, value: T) -> std::io::Result<()>
where
W: Write,
G: IndexDigraph,
T: Display + Zero,
Fs: Fn(G::Edge<'a>) -> T,
{
writeln!(w, "s {}", value)?;
for e in g.edges() {
let fl = (flow)(e);
if !fl.is_zero() {
writeln!(w, "f {} {} {}", g.node_id(g.src(e)) + 1, g.node_id(g.snk(e)) + 1, fl)?;
}
}
Ok(())
}
pub fn write_solution_to_file<'a, G, T, Fs>(filename: &str, g: &'a G, flow: Fs, value: T) -> std::io::Result<()>
where
G: IndexDigraph,
T: Display + Zero,
Fs: Fn(G::Edge<'a>) -> T,
{
write_solution(&mut std::fs::File::create(filename)?, g, flow, value)
}
#[allow(clippy::type_complexity)]
pub fn read_solution<R, T>(r: R) -> Result<(T, Vec<(usize, usize, T)>)>
where
R: Read,
T: FromStr,
T::Err: Display,
{
let mut reader = DimacsReader::new(r);
let mut flows = vec![];
let mut sol = None;
while let Some((d, mut toks)) = reader.read_one_line_of(&["f", "s"])? {
if d == "f" {
flows.push((toks.number::<usize>()? - 1, toks.number::<usize>()? - 1, toks.number()?));
} else {
if sol.is_some() {
return Err(Error::Format {
line: toks.line,
msg: "The solution value must be specified exactly once".to_string(),
});
}
sol = Some(toks.number()?);
}
toks.end()?;
}
Ok((
sol.ok_or_else(|| Error::Format {
line: 0,
msg: "Missing solution value".to_string(),
})?,
flows,
))
}
#[allow(clippy::type_complexity)]
pub fn read_solution_from_file<T>(filename: &str) -> Result<(T, Vec<(usize, usize, T)>)>
where
T: FromStr,
T::Err: Display,
{
read_solution(std::fs::File::open(filename)?)
}
#[cfg(test)]
mod tests {
use crate::dimacs;
use crate::{traits::*, Buildable, Builder, VecGraph};
use std::io::{self, Cursor};
#[test]
fn parse_file_test() {
let file = "c this is a test file
p min 8 11
n 1 10
n 2 20
n 4 -5
n 7 -15
n 8 -10
c there might be empty lines
a 1 4 0 15 2
a 2 1 0 10 1
a 2 3 0 10 0
a 2 6 0 10 6
a 3 4 0 5 1
a 3 5 0 10 4
a 4 7 0 10 5
a 5 6 0 20 2
a 5 7 0 15 7
a 6 8 0 10 8
a 7 8 0 15 9
c end of the file
";
let instance = dimacs::min::read::<_, _, isize>(io::Cursor::new(file)).unwrap();
let g: VecGraph = instance.graph;
let balances = instance.balances;
let lower = instance.lower;
let upper = instance.upper;
let costs = instance.costs;
assert_eq!(g.num_nodes(), 8);
assert_eq!(g.num_edges(), 11);
assert_eq!(balances, vec![10, 20, 0, -5, 0, 0, -15, -10]);
assert_eq!(lower, vec![0; g.num_edges()]);
assert_eq!(upper, vec![15, 10, 10, 10, 5, 10, 10, 20, 15, 10, 15]);
assert_eq!(costs, vec![2, 1, 0, 6, 1, 4, 5, 2, 7, 8, 9]);
let edges = (0..g.num_edges())
.map(|i| g.id2edge(i))
.map(|e| (g.node_id(g.src(e)) + 1, g.node_id(g.snk(e)) + 1))
.collect::<Vec<_>>();
assert_eq!(
edges,
vec![
(1, 4),
(2, 1),
(2, 3),
(2, 6),
(3, 4),
(3, 5),
(4, 7),
(5, 6),
(5, 7),
(6, 8),
(7, 8),
]
);
}
#[test]
fn write_test_file() {
let g = VecGraph::<u32>::new_with(|b| {
let nodes = b.add_nodes(4);
b.add_edge(nodes[0], nodes[1]);
b.add_edge(nodes[0], nodes[2]);
b.add_edge(nodes[1], nodes[2]);
b.add_edge(nodes[1], nodes[3]);
b.add_edge(nodes[2], nodes[3]);
});
let balances = vec![4, 0, 0, -4];
let lower = vec![0; g.num_edges()];
let upper = vec![4, 2, 2, 3, 5];
let costs = vec![2, 2, 1, 3, 1];
let mut buf = Cursor::new(Vec::new());
dimacs::min::write(
&mut buf,
&dimacs::min::Instance {
graph: &g,
balances,
lower,
upper,
costs,
},
)
.unwrap();
assert_eq!(
String::from_utf8(buf.into_inner()).unwrap(),
"p min 4 5
n 1 4
n 4 -4
a 1 2 0 4 2
a 1 3 0 2 2
a 2 3 0 2 1
a 2 4 0 3 3
a 3 4 0 5 1
"
);
}
#[test]
fn write_solution_file() -> std::result::Result<(), Box<dyn std::error::Error>> {
let g = VecGraph::<u32>::new_with(|b| {
let nodes = b.add_nodes(4);
b.add_edge(nodes[0], nodes[1]);
b.add_edge(nodes[0], nodes[2]);
b.add_edge(nodes[1], nodes[2]);
b.add_edge(nodes[1], nodes[3]);
b.add_edge(nodes[2], nodes[3]);
});
let flow = vec![2, 2, 2, 0, 4];
let mut buf = Cursor::new(Vec::new());
dimacs::min::write_solution(&mut buf, &g, |e| flow[g.edge_id(e)], 14)?;
let soltxt = String::from_utf8(buf.into_inner())?;
assert_eq!(
soltxt,
"s 14
f 1 2 2
f 1 3 2
f 2 3 2
f 3 4 4
"
);
let (value, flows) = dimacs::min::read_solution(Cursor::new(soltxt))?;
assert_eq!(value, 14);
assert_eq!(flows, vec![(0, 1, 2), (0, 2, 2), (1, 2, 2), (2, 3, 4)]);
Ok(())
}
}