problemreductions/rules/
minimummaximalmatching_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
11use crate::models::graph::MinimumMaximalMatching;
12use crate::reduction;
13use crate::rules::traits::{ReduceTo, ReductionResult};
14use crate::topology::{Graph, SimpleGraph};
15
16#[derive(Debug, Clone)]
25pub struct ReductionMMMToILP {
26 target: ILP<bool>,
27}
28
29impl ReductionResult for ReductionMMMToILP {
30 type Source = MinimumMaximalMatching<SimpleGraph>;
31 type Target = ILP<bool>;
32
33 fn target_problem(&self) -> &ILP<bool> {
34 &self.target
35 }
36
37 fn extract_solution(
42 &self,
43 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
44 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
45 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
46
47 Ok(target_solution.iter().map(|&value| value == 1).collect())
48 }
49}
50
51#[reduction(
52 transform = exact {
53 num_vars = "num_edges",
54 num_constraints = "num_vertices + num_edges",
55 },
56 unavailable = {
57 num_nonzeros = "the exact target parameter is not represented by this reduction's symbolic transform",
58 }
59)]
60impl ReduceTo<ILP<bool>> for MinimumMaximalMatching<SimpleGraph> {
61 type Result = ReductionMMMToILP;
62
63 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
64 let edges = self.graph().edges();
65 let num_vars = edges.len();
66 let mut constraints = Vec::new();
67
68 let n = self.graph().num_vertices();
71 let mut v2e: Vec<Vec<usize>> = vec![Vec::new(); n];
72 for (idx, &(u, v)) in edges.iter().enumerate() {
73 v2e[u].push(idx);
74 v2e[v].push(idx);
75 }
76 for incident in &v2e {
77 if !incident.is_empty() {
78 let terms: Vec<(usize, i64)> = incident.iter().map(|&e| (e, 1)).collect();
79 constraints.push(LinearConstraint::le(terms, 1));
80 }
81 }
82
83 for (j, &(uj, vj)) in edges.iter().enumerate() {
87 let mut neighbors: Vec<usize> = vec![j];
89 for &i in v2e[uj].iter().chain(v2e[vj].iter()) {
90 if i != j && !neighbors.contains(&i) {
91 neighbors.push(i);
92 }
93 }
94 let terms: Vec<(usize, i64)> = neighbors.iter().map(|&i| (i, 1)).collect();
95 constraints.push(LinearConstraint::ge(terms, 1));
96 }
97
98 let objective: Vec<(usize, i64)> = (0..num_vars).map(|i| (i, 1)).collect();
100
101 let target = ILP::new(num_vars, constraints, objective, ObjectiveSense::Minimize)
102 .map_err(Self::target_construction)?;
103 Ok(ReductionMMMToILP { target })
104 }
105}
106
107#[cfg(feature = "example-db")]
108pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
109 vec![crate::example_db::specs::RuleExampleSpec {
110 id: "minimummaximalmatching_to_ilp",
111 build: || {
112 let source = MinimumMaximalMatching::new(SimpleGraph::new(
114 6,
115 vec![(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)],
116 ));
117 crate::example_db::specs::rule_example_via_ilp::<_, bool>(source)
118 },
119 }]
120}
121
122#[cfg(test)]
123#[path = "../unit_tests/rules/minimummaximalmatching_ilp.rs"]
124mod tests;