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 module_path: module_path!(),
23 description: "Integral flow minimizing the number of arcs with nonzero flow (weighted by price)",
24 fields: &[
25 FieldInfo { name: "graph", type_name: "DirectedGraph", description: "Directed graph G = (V, A)" },
26 FieldInfo { name: "prices", type_name: "Vec<i64>", description: "Price p(a) for each arc" },
27 FieldInfo { name: "capacities", type_name: "Vec<i64>", description: "Capacity c(a) for each arc" },
28 FieldInfo { name: "source", type_name: "usize", description: "Source vertex s" },
29 FieldInfo { name: "sink", type_name: "usize", description: "Sink vertex t" },
30 FieldInfo { name: "required_flow", type_name: "i64", description: "Flow requirement R" },
31 ],
32 }
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct MinimumEdgeCostFlow {
69 graph: DirectedGraph,
71 prices: Vec<i64>,
73 capacities: Vec<i64>,
75 source: usize,
77 sink: usize,
79 required_flow: i64,
81}
82
83impl MinimumEdgeCostFlow {
84 pub fn new(
105 graph: DirectedGraph,
106 prices: Vec<i64>,
107 capacities: Vec<i64>,
108 source: usize,
109 sink: usize,
110 required_flow: i64,
111 ) -> Self {
112 let n = graph.num_vertices();
113 let m = graph.num_arcs();
114 assert_eq!(
115 prices.len(),
116 m,
117 "prices length ({}) must match num_arcs ({m})",
118 prices.len()
119 );
120 assert_eq!(
121 capacities.len(),
122 m,
123 "capacities length ({}) must match num_arcs ({m})",
124 capacities.len()
125 );
126 assert!(source < n, "source ({source}) >= num_vertices ({n})");
127 assert!(sink < n, "sink ({sink}) >= num_vertices ({n})");
128 assert_ne!(source, sink, "source and sink must be distinct");
129 for (i, &c) in capacities.iter().enumerate() {
130 assert!(c >= 0, "capacity[{i}] = {c} is negative");
131 }
132 Self {
133 graph,
134 prices,
135 capacities,
136 source,
137 sink,
138 required_flow,
139 }
140 }
141
142 pub fn graph(&self) -> &DirectedGraph {
144 &self.graph
145 }
146
147 pub fn prices(&self) -> &[i64] {
149 &self.prices
150 }
151
152 pub fn capacities(&self) -> &[i64] {
154 &self.capacities
155 }
156
157 pub fn source(&self) -> usize {
159 self.source
160 }
161
162 pub fn sink(&self) -> usize {
164 self.sink
165 }
166
167 pub fn required_flow(&self) -> i64 {
169 self.required_flow
170 }
171
172 pub fn num_vertices(&self) -> usize {
174 self.graph.num_vertices()
175 }
176
177 pub fn num_edges(&self) -> usize {
179 self.graph.num_arcs()
180 }
181
182 pub fn max_capacity(&self) -> i64 {
184 self.capacities.iter().copied().max().unwrap_or(0)
185 }
186
187 pub fn edges(&self) -> Vec<(usize, usize)> {
189 self.graph.arcs()
190 }
191
192 pub fn is_feasible(&self, config: &[usize]) -> bool {
199 let m = self.graph.num_arcs();
200 if config.len() != m {
201 return false;
202 }
203 let arcs = self.graph.arcs();
204
205 for (flow, cap) in config.iter().zip(self.capacities.iter()) {
207 if (*flow as i64) > *cap {
208 return false;
209 }
210 }
211
212 let n = self.graph.num_vertices();
214 let mut balance = vec![0_i64; n];
215 for (a, &(u, v)) in arcs.iter().enumerate() {
216 let flow = config[a] as i64;
217 balance[u] -= flow;
218 balance[v] += flow;
219 }
220
221 for (v, &bal) in balance.iter().enumerate() {
222 if v != self.source && v != self.sink && bal != 0 {
223 return false;
224 }
225 }
226
227 if balance[self.sink] < self.required_flow {
229 return false;
230 }
231
232 true
233 }
234
235 pub fn edge_cost(&self, config: &[usize]) -> i64 {
238 config
239 .iter()
240 .enumerate()
241 .filter(|(_, &f)| f > 0)
242 .map(|(a, _)| self.prices[a])
243 .sum()
244 }
245}
246
247impl Problem for MinimumEdgeCostFlow {
248 const NAME: &'static str = "MinimumEdgeCostFlow";
249 type Value = crate::types::Min<i64>;
250
251 fn dims(&self) -> Vec<usize> {
252 self.capacities.iter().map(|&c| (c as usize) + 1).collect()
253 }
254
255 fn evaluate(&self, config: &[usize]) -> crate::types::Min<i64> {
256 if self.is_feasible(config) {
257 crate::types::Min(Some(self.edge_cost(config)))
258 } else {
259 crate::types::Min(None)
260 }
261 }
262
263 fn variant() -> Vec<(&'static str, &'static str)> {
264 crate::variant_params![]
265 }
266}
267
268crate::declare_variants! {
269 default MinimumEdgeCostFlow => "(max_capacity + 1)^num_edges",
270}
271
272#[cfg(feature = "example-db")]
273pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
274 vec![crate::example_db::specs::ModelExampleSpec {
275 id: "minimum_edge_cost_flow",
276 instance: Box::new(MinimumEdgeCostFlow::new(
277 crate::topology::DirectedGraph::new(
278 5,
279 vec![(0, 1), (0, 2), (0, 3), (1, 4), (2, 4), (3, 4)],
280 ),
281 vec![3, 1, 2, 0, 0, 0], vec![2, 2, 2, 2, 2, 2], 0,
284 4,
285 3,
286 )),
287 optimal_config: vec![0, 1, 2, 0, 1, 2],
290 optimal_value: serde_json::json!(3),
291 }]
292}
293
294#[cfg(test)]
295#[path = "../../unit_tests/models/graph/minimum_edge_cost_flow.rs"]
296mod tests;