problemreductions/models/graph/
integral_flow_with_multipliers.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, ProblemSizeFieldEntry};
8use crate::topology::DirectedGraph;
9use crate::traits::Problem;
10use serde::{Deserialize, Serialize};
11
12inventory::submit! {
13 ProblemSchemaEntry {
14 name: "IntegralFlowWithMultipliers",
15 display_name: "Integral Flow With Multipliers",
16 aliases: &[],
17 dimensions: &[],
18 module_path: module_path!(),
19 description: "Integral flow feasibility on a directed graph with multiplier-scaled conservation at non-terminal vertices",
20 fields: &[
21 FieldInfo { name: "graph", type_name: "DirectedGraph", description: "Directed graph G = (V, A)" },
22 FieldInfo { name: "source", type_name: "usize", description: "Source vertex s" },
23 FieldInfo { name: "sink", type_name: "usize", description: "Sink vertex t" },
24 FieldInfo { name: "multipliers", type_name: "Vec<u64>", description: "Vertex multipliers h(v) in vertex order; source/sink entries are ignored" },
25 FieldInfo { name: "capacities", type_name: "Vec<u64>", description: "Arc capacities c(a) in graph arc order" },
26 FieldInfo { name: "requirement", type_name: "u64", description: "Required net inflow R at the sink" },
27 ],
28 }
29}
30
31inventory::submit! {
32 ProblemSizeFieldEntry {
33 name: "IntegralFlowWithMultipliers",
34 fields: &["num_vertices", "num_arcs", "max_capacity", "requirement"],
35 }
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct IntegralFlowWithMultipliers {
40 graph: DirectedGraph,
41 source: usize,
42 sink: usize,
43 multipliers: Vec<u64>,
44 capacities: Vec<u64>,
45 requirement: u64,
46}
47
48impl IntegralFlowWithMultipliers {
49 pub fn new(
50 graph: DirectedGraph,
51 source: usize,
52 sink: usize,
53 multipliers: Vec<u64>,
54 capacities: Vec<u64>,
55 requirement: u64,
56 ) -> Self {
57 assert_eq!(
58 capacities.len(),
59 graph.num_arcs(),
60 "capacities length must match graph num_arcs"
61 );
62 assert_eq!(
63 multipliers.len(),
64 graph.num_vertices(),
65 "multipliers length must match graph num_vertices"
66 );
67
68 let num_vertices = graph.num_vertices();
69 assert!(
70 source < num_vertices,
71 "source ({source}) must be less than num_vertices ({num_vertices})"
72 );
73 assert!(
74 sink < num_vertices,
75 "sink ({sink}) must be less than num_vertices ({num_vertices})"
76 );
77 assert_ne!(source, sink, "source and sink must be distinct");
78
79 for (vertex, &multiplier) in multipliers.iter().enumerate() {
80 if vertex != source && vertex != sink {
81 assert!(multiplier > 0, "non-terminal multipliers must be positive");
82 }
83 }
84
85 for &capacity in &capacities {
86 let domain = usize::try_from(capacity)
87 .ok()
88 .and_then(|value| value.checked_add(1));
89 assert!(
90 domain.is_some(),
91 "arc capacities must fit into usize for dims()"
92 );
93 }
94
95 Self {
96 graph,
97 source,
98 sink,
99 multipliers,
100 capacities,
101 requirement,
102 }
103 }
104
105 pub fn graph(&self) -> &DirectedGraph {
106 &self.graph
107 }
108
109 pub fn source(&self) -> usize {
110 self.source
111 }
112
113 pub fn sink(&self) -> usize {
114 self.sink
115 }
116
117 pub fn multipliers(&self) -> &[u64] {
118 &self.multipliers
119 }
120
121 pub fn capacities(&self) -> &[u64] {
122 &self.capacities
123 }
124
125 pub fn requirement(&self) -> u64 {
126 self.requirement
127 }
128
129 pub fn num_vertices(&self) -> usize {
130 self.graph.num_vertices()
131 }
132
133 pub fn num_arcs(&self) -> usize {
134 self.graph.num_arcs()
135 }
136
137 pub fn max_capacity(&self) -> u64 {
138 self.capacities.iter().copied().max().unwrap_or(0)
139 }
140
141 fn domain_size(capacity: u64) -> usize {
142 usize::try_from(capacity)
143 .ok()
144 .and_then(|value| value.checked_add(1))
145 .expect("capacity already validated to fit into usize")
146 }
147
148 pub fn is_feasible(&self, config: &[usize]) -> bool {
149 if config.len() != self.num_arcs() {
150 return false;
151 }
152
153 let num_vertices = self.num_vertices();
154 let mut inflow = vec![0_i128; num_vertices];
155 let mut outflow = vec![0_i128; num_vertices];
156
157 for (arc_index, ((u, v), &capacity)) in self
158 .graph
159 .arcs()
160 .into_iter()
161 .zip(self.capacities.iter())
162 .enumerate()
163 {
164 let Some(flow_usize) = config.get(arc_index).copied() else {
165 return false;
166 };
167 let Ok(flow_u64) = u64::try_from(flow_usize) else {
168 return false;
169 };
170 if flow_u64 > capacity {
171 return false;
172 }
173 let flow = i128::from(flow_u64);
174 outflow[u] += flow;
175 inflow[v] += flow;
176 }
177
178 for vertex in 0..num_vertices {
179 if vertex == self.source || vertex == self.sink {
180 continue;
181 }
182 let multiplier = i128::from(self.multipliers[vertex]);
183 let Some(expected_outflow) = inflow[vertex].checked_mul(multiplier) else {
184 return false;
185 };
186 if expected_outflow != outflow[vertex] {
187 return false;
188 }
189 }
190
191 let sink_net_flow = inflow[self.sink] - outflow[self.sink];
192 sink_net_flow >= i128::from(self.requirement)
193 }
194}
195
196impl Problem for IntegralFlowWithMultipliers {
197 const NAME: &'static str = "IntegralFlowWithMultipliers";
198 type Value = crate::types::Or;
199
200 fn dims(&self) -> Vec<usize> {
201 self.capacities
202 .iter()
203 .map(|&capacity| Self::domain_size(capacity))
204 .collect()
205 }
206
207 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
208 crate::types::Or(self.is_feasible(config))
209 }
210
211 fn variant() -> Vec<(&'static str, &'static str)> {
212 crate::variant_params![]
213 }
214}
215
216crate::declare_variants! {
217 default IntegralFlowWithMultipliers => "(max_capacity + 1)^num_arcs",
218}
219
220#[cfg(feature = "example-db")]
221pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
222 vec![crate::example_db::specs::ModelExampleSpec {
223 id: "integral_flow_with_multipliers",
224 instance: Box::new(IntegralFlowWithMultipliers::new(
225 DirectedGraph::new(
226 8,
227 vec![
228 (0, 1),
229 (0, 2),
230 (0, 3),
231 (0, 4),
232 (0, 5),
233 (0, 6),
234 (1, 7),
235 (2, 7),
236 (3, 7),
237 (4, 7),
238 (5, 7),
239 (6, 7),
240 ],
241 ),
242 0,
243 7,
244 vec![1, 2, 3, 4, 5, 6, 4, 1],
245 vec![1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 4],
246 12,
247 )),
248 optimal_config: vec![1, 0, 1, 0, 1, 0, 2, 0, 4, 0, 6, 0],
249 optimal_value: serde_json::json!(true),
250 }]
251}
252
253#[cfg(test)]
254#[path = "../../unit_tests/models/graph/integral_flow_with_multipliers.rs"]
255mod tests;