Skip to main content

problemreductions/models/graph/
integral_flow_homologous_arcs.rs

1//! Integral Flow with Homologous Arcs problem implementation.
2//!
3//! Given a directed capacitated network with a source, sink, and pairs of arcs
4//! that must carry equal flow, determine whether an integral flow meeting the
5//! required sink inflow exists.
6
7use 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/// Integral flow with homologous arcs.
39///
40/// A configuration stores one non-negative integer flow value for each arc in
41/// the graph's arc order. The assignment is feasible when it respects arc
42/// capacities, flow conservation at non-terminal vertices, every homologous-pair
43/// equality constraint, and the required net inflow at the sink.
44#[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;