problemreductions/models/graph/
integral_flow_homologous_arcs.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: "IntegralFlowHomologousArcs",
15 display_name: "Integral Flow with Homologous Arcs",
16 aliases: &[],
17 dimensions: &[],
18 module_path: module_path!(),
19 description: "Integral flow feasibility with arc-pair equality constraints",
20 fields: &[
21 FieldInfo { name: "graph", type_name: "DirectedGraph", description: "Directed graph G = (V, A)" },
22 FieldInfo { name: "capacities", type_name: "Vec<u64>", description: "Capacity c(a) for each arc" },
23 FieldInfo { name: "source", type_name: "usize", description: "Source vertex s" },
24 FieldInfo { name: "sink", type_name: "usize", description: "Sink vertex t" },
25 FieldInfo { name: "requirement", type_name: "u64", description: "Required net inflow R at the sink" },
26 FieldInfo { name: "homologous_pairs", type_name: "Vec<(usize, usize)>", description: "Arc-index pairs (a, a') with f(a) = f(a')" },
27 ],
28 }
29}
30
31inventory::submit! {
32 ProblemSizeFieldEntry {
33 name: "IntegralFlowHomologousArcs",
34 fields: &["num_vertices", "num_arcs"],
35 }
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct IntegralFlowHomologousArcs {
46 graph: DirectedGraph,
47 capacities: Vec<u64>,
48 source: usize,
49 sink: usize,
50 requirement: u64,
51 homologous_pairs: Vec<(usize, usize)>,
52}
53
54impl IntegralFlowHomologousArcs {
55 pub fn new(
56 graph: DirectedGraph,
57 capacities: Vec<u64>,
58 source: usize,
59 sink: usize,
60 requirement: u64,
61 homologous_pairs: Vec<(usize, usize)>,
62 ) -> Self {
63 let num_vertices = graph.num_vertices();
64 let num_arcs = graph.num_arcs();
65
66 assert_eq!(
67 capacities.len(),
68 num_arcs,
69 "capacities length must match graph.num_arcs()"
70 );
71 assert!(
72 source < num_vertices,
73 "source ({source}) must be less than num_vertices ({num_vertices})"
74 );
75 assert!(
76 sink < num_vertices,
77 "sink ({sink}) must be less than num_vertices ({num_vertices})"
78 );
79
80 for &(a, b) in &homologous_pairs {
81 assert!(a < num_arcs, "homologous arc index {a} out of range");
82 assert!(b < num_arcs, "homologous arc index {b} out of range");
83 }
84
85 for &capacity in &capacities {
86 assert!(
87 usize::try_from(capacity)
88 .ok()
89 .and_then(|value| value.checked_add(1))
90 .is_some(),
91 "capacities must fit into usize for dims()"
92 );
93 }
94
95 Self {
96 graph,
97 capacities,
98 source,
99 sink,
100 requirement,
101 homologous_pairs,
102 }
103 }
104
105 pub fn graph(&self) -> &DirectedGraph {
106 &self.graph
107 }
108
109 pub fn capacities(&self) -> &[u64] {
110 &self.capacities
111 }
112
113 pub fn source(&self) -> usize {
114 self.source
115 }
116
117 pub fn sink(&self) -> usize {
118 self.sink
119 }
120
121 pub fn requirement(&self) -> u64 {
122 self.requirement
123 }
124
125 pub fn homologous_pairs(&self) -> &[(usize, usize)] {
126 &self.homologous_pairs
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 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
142 self.evaluate(config).0
143 }
144
145 fn domain_size(capacity: u64) -> usize {
146 usize::try_from(capacity)
147 .ok()
148 .and_then(|value| value.checked_add(1))
149 .expect("capacity already validated to fit into usize")
150 }
151}
152
153impl Problem for IntegralFlowHomologousArcs {
154 const NAME: &'static str = "IntegralFlowHomologousArcs";
155 type Value = crate::types::Or;
156
157 fn variant() -> Vec<(&'static str, &'static str)> {
158 crate::variant_params![]
159 }
160
161 fn dims(&self) -> Vec<usize> {
162 self.capacities
163 .iter()
164 .map(|&capacity| Self::domain_size(capacity))
165 .collect()
166 }
167
168 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
169 crate::types::Or({
170 if config.len() != self.num_arcs() {
171 return crate::types::Or(false);
172 }
173
174 for &(a, b) in &self.homologous_pairs {
175 if config[a] != config[b] {
176 return crate::types::Or(false);
177 }
178 }
179
180 let mut balances = vec![0_i128; self.num_vertices()];
181 for (arc_index, ((u, v), &capacity)) in self
182 .graph
183 .arcs()
184 .into_iter()
185 .zip(self.capacities.iter())
186 .enumerate()
187 {
188 let Ok(flow) = u64::try_from(config[arc_index]) else {
189 return crate::types::Or(false);
190 };
191 if flow > capacity {
192 return crate::types::Or(false);
193 }
194 let flow = i128::from(flow);
195 balances[u] -= flow;
196 balances[v] += flow;
197 }
198
199 for (vertex, &balance) in balances.iter().enumerate() {
200 if vertex != self.source && vertex != self.sink && balance != 0 {
201 return crate::types::Or(false);
202 }
203 }
204
205 balances[self.sink] >= i128::from(self.requirement)
206 })
207 }
208}
209
210crate::declare_variants! {
211 default IntegralFlowHomologousArcs => "(max_capacity + 1)^num_arcs",
212}
213
214#[cfg(feature = "example-db")]
215pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
216 vec![crate::example_db::specs::ModelExampleSpec {
217 id: "integral_flow_homologous_arcs",
218 instance: Box::new(IntegralFlowHomologousArcs::new(
219 DirectedGraph::new(
220 6,
221 vec![
222 (0, 1),
223 (0, 2),
224 (1, 3),
225 (2, 3),
226 (1, 4),
227 (2, 4),
228 (3, 5),
229 (4, 5),
230 ],
231 ),
232 vec![1; 8],
233 0,
234 5,
235 2,
236 vec![(2, 5), (4, 3)],
237 )),
238 optimal_config: vec![1, 1, 1, 0, 0, 1, 1, 1],
239 optimal_value: serde_json::json!(true),
240 }]
241}
242
243#[cfg(test)]
244#[path = "../../unit_tests/models/graph/integral_flow_homologous_arcs.rs"]
245mod tests;