problemreductions/models/graph/
minimum_edge_cost_flow.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
12use crate::topology::DirectedGraph;
13use crate::traits::Problem;
14use serde::{Deserialize, Serialize};
15
16inventory::submit! {
17 ProblemSchemaEntry {
18 name: "MinimumEdgeCostFlow",
19 display_name: "Minimum Edge-Cost Flow",
20 aliases: &["MECF"],
21 dimensions: &[],
22 category: crate::registry::ProblemCategory::Graph,
23 module_path: module_path!(),
24 description: "Integral flow minimizing the number of arcs with nonzero flow (weighted by price)",
25 fields: &[
26 FieldInfo { name: "graph", type_name: "DirectedGraph", description: "Directed graph G = (V, A)" },
27 FieldInfo { name: "prices", type_name: "Vec<i64>", description: "Price p(a) for each arc" },
28 FieldInfo { name: "capacities", type_name: "Vec<i64>", description: "Capacity c(a) for each arc" },
29 FieldInfo { name: "source", type_name: "usize", description: "Source vertex s" },
30 FieldInfo { name: "sink", type_name: "usize", description: "Sink vertex t" },
31 FieldInfo { name: "required_flow", type_name: "i64", description: "Flow requirement R" },
32 ],
33 }
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
69#[serde(try_from = "MinimumEdgeCostFlowData")]
70pub struct MinimumEdgeCostFlow {
71 graph: DirectedGraph,
73 prices: Vec<i64>,
75 capacities: Vec<i64>,
77 source: usize,
79 sink: usize,
81 required_flow: i64,
83}
84
85#[derive(Deserialize)]
86struct MinimumEdgeCostFlowData {
87 graph: DirectedGraph,
88 prices: Vec<i64>,
89 capacities: Vec<i64>,
90 source: usize,
91 sink: usize,
92 required_flow: i64,
93}
94
95impl TryFrom<MinimumEdgeCostFlowData> for MinimumEdgeCostFlow {
96 type Error = crate::registry::ConstructionError;
97 fn try_from(data: MinimumEdgeCostFlowData) -> Result<Self, Self::Error> {
98 Self::try_new(
99 data.graph,
100 data.prices,
101 data.capacities,
102 data.source,
103 data.sink,
104 data.required_flow,
105 )
106 }
107}
108
109impl MinimumEdgeCostFlow {
110 pub fn new(
131 graph: DirectedGraph,
132 prices: Vec<i64>,
133 capacities: Vec<i64>,
134 source: usize,
135 sink: usize,
136 required_flow: i64,
137 ) -> Self {
138 Self::try_new(graph, prices, capacities, source, sink, required_flow)
139 .unwrap_or_else(|error| panic!("{error}"))
140 }
141
142 fn try_new(
143 graph: DirectedGraph,
144 prices: Vec<i64>,
145 capacities: Vec<i64>,
146 source: usize,
147 sink: usize,
148 required_flow: i64,
149 ) -> Result<Self, crate::registry::ConstructionError> {
150 let n = graph.num_vertices();
151 let m = graph.num_arcs();
152 if prices.len() != m {
153 return Err(
154 format!("prices length ({}) must match num_arcs ({m})", prices.len()).into(),
155 );
156 }
157 if capacities.len() != m {
158 return Err(format!(
159 "capacities length ({}) must match num_arcs ({m})",
160 capacities.len()
161 )
162 .into());
163 }
164 if source >= n {
165 return Err(format!("source ({source}) >= num_vertices ({n})").into());
166 }
167 if sink >= n {
168 return Err(format!("sink ({sink}) >= num_vertices ({n})").into());
169 }
170 if source == sink {
171 return Err("source and sink must be distinct".into());
172 }
173 for (i, &c) in capacities.iter().enumerate() {
174 if c < 0 {
175 return Err(format!("capacity[{i}] = {c} is negative").into());
176 }
177 }
178 Ok(Self {
179 graph,
180 prices,
181 capacities,
182 source,
183 sink,
184 required_flow,
185 })
186 }
187
188 pub fn graph(&self) -> &DirectedGraph {
190 &self.graph
191 }
192
193 pub fn prices(&self) -> &[i64] {
195 &self.prices
196 }
197
198 pub fn capacities(&self) -> &[i64] {
200 &self.capacities
201 }
202
203 pub fn source(&self) -> usize {
205 self.source
206 }
207
208 pub fn sink(&self) -> usize {
210 self.sink
211 }
212
213 pub fn required_flow(&self) -> i64 {
215 self.required_flow
216 }
217
218 pub fn num_vertices(&self) -> usize {
220 self.graph.num_vertices()
221 }
222
223 pub fn num_edges(&self) -> usize {
225 self.graph.num_arcs()
226 }
227
228 pub fn max_capacity(&self) -> i64 {
230 self.capacities.iter().copied().max().unwrap_or(0)
231 }
232
233 pub fn edges(&self) -> Vec<(usize, usize)> {
235 self.graph.arcs()
236 }
237
238 pub fn is_feasible(&self, config: &[usize]) -> Result<bool, crate::traits::EvaluationError> {
245 let m = self.graph.num_arcs();
246 if config.len() != m {
247 return Ok(false);
248 }
249 let arcs = self.graph.arcs();
250
251 for (flow, cap) in config.iter().zip(self.capacities.iter()) {
253 let flow = i64::try_from(*flow).map_err(|_| {
254 crate::traits::EvaluationError::IntegerOverflow(
255 "converting edge-cost flow configuration value".to_string(),
256 )
257 })?;
258 if flow > *cap {
259 return Ok(false);
260 }
261 }
262
263 let n = self.graph.num_vertices();
265 let mut balance = vec![0_i64; n];
266 for (a, &(u, v)) in arcs.iter().enumerate() {
267 let flow = i64::try_from(config[a]).map_err(|_| {
268 crate::traits::EvaluationError::IntegerOverflow(
269 "converting edge-cost flow configuration value".to_string(),
270 )
271 })?;
272 balance[u] = balance[u].checked_sub(flow).ok_or_else(|| {
273 crate::traits::EvaluationError::IntegerOverflow(
274 "computing edge-cost flow balance".to_string(),
275 )
276 })?;
277 balance[v] = balance[v].checked_add(flow).ok_or_else(|| {
278 crate::traits::EvaluationError::IntegerOverflow(
279 "computing edge-cost flow balance".to_string(),
280 )
281 })?;
282 }
283
284 for (v, &bal) in balance.iter().enumerate() {
285 if v != self.source && v != self.sink && bal != 0 {
286 return Ok(false);
287 }
288 }
289
290 if balance[self.sink] < self.required_flow {
292 return Ok(false);
293 }
294
295 Ok(true)
296 }
297
298 pub fn edge_cost(&self, config: &[usize]) -> Result<i64, crate::traits::EvaluationError> {
301 config
302 .iter()
303 .enumerate()
304 .filter(|(_, &flow)| flow > 0)
305 .try_fold(0_i64, |total, (arc, _)| {
306 total.checked_add(self.prices[arc]).ok_or_else(|| {
307 crate::traits::EvaluationError::IntegerOverflow(
308 "summing selected edge prices".to_string(),
309 )
310 })
311 })
312 }
313}
314
315impl Problem for MinimumEdgeCostFlow {
316 const NAME: &'static str = "MinimumEdgeCostFlow";
317 type Solution = Vec<usize>;
318 type Value = crate::types::Min<i64>;
319
320 crate::problem_parameters![
321 ("max_capacity", max_capacity),
322 ("num_edges", num_edges),
323 ("num_vertices", num_vertices),
324 ];
325
326 fn evaluate(
327 &self,
328 config: &Self::Solution,
329 ) -> Result<crate::types::Min<i64>, crate::traits::EvaluationError> {
330 if config.len() != self.graph.num_arcs() {
331 return Err(crate::traits::EvaluationError::InvalidConfiguration(
332 "flow vector length does not match the graph arcs".into(),
333 ));
334 }
335 Ok({
336 if self.is_feasible(config)? {
337 crate::types::Min(Some(self.edge_cost(config)?))
338 } else {
339 crate::types::Min(None)
340 }
341 })
342 }
343
344 fn variant() -> Vec<(&'static str, &'static str)> {
345 crate::variant_params![]
346 }
347}
348
349impl crate::solvers::BruteForceProblem for MinimumEdgeCostFlow {
350 fn dimensions(&self) -> Vec<usize> {
351 self.capacities.iter().map(|&c| (c as usize) + 1).collect()
352 }
353}
354
355crate::declare_variants! {
356 default MinimumEdgeCostFlow => "(max_capacity + 1)^num_edges",
357}
358
359crate::register_brute_force! {
360 MinimumEdgeCostFlow,
361}
362
363#[cfg(feature = "example-db")]
364pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
365 vec![crate::example_db::specs::ModelExampleSpec {
366 id: "minimum_edge_cost_flow",
367 instance: Box::new(MinimumEdgeCostFlow::new(
368 crate::topology::DirectedGraph::new(
369 5,
370 vec![(0, 1), (0, 2), (0, 3), (1, 4), (2, 4), (3, 4)],
371 ),
372 vec![3, 1, 2, 0, 0, 0], vec![2, 2, 2, 2, 2, 2], 0,
375 4,
376 3,
377 )),
378 optimal_config: serde_json::json!(vec![0, 1, 2, 0, 1, 2]),
381 optimal_value: serde_json::json!(3),
382 }]
383}
384
385#[cfg(test)]
386#[path = "../../unit_tests/models/graph/minimum_edge_cost_flow.rs"]
387mod tests;