problemreductions/rules/
minimumfeedbackvertexset_ilp.rs1use crate::models::algebraic::{Bounded, IntegerVariable, LinearConstraint, ObjectiveSense, ILP};
10use crate::models::graph::MinimumFeedbackVertexSet;
11use crate::reduction;
12use crate::rules::traits::{ReduceTo, ReductionResult};
13
14#[derive(Debug, Clone)]
23pub struct ReductionMFVSToILP {
24 target: ILP<i64, i64, Bounded>,
25 num_vertices: usize,
27}
28
29impl ReductionResult for ReductionMFVSToILP {
30 type Source = MinimumFeedbackVertexSet<i64>;
31 type Target = ILP<i64, i64, Bounded>;
32
33 fn target_problem(&self) -> &ILP<i64, i64, Bounded> {
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[..self.num_vertices]
48 .iter()
49 .map(|&value| value == 1)
50 .collect())
51 }
52}
53
54#[reduction(transform = {
55 exact {
56 num_vars = "2 * num_vertices",
57 num_constraints = "num_arcs + 2 * num_vertices",
58 },
59 upper_bound {
60 max_constraint_magnitude_bits = "2 * num_vertices + 1",
61 num_nonzeros = "(2 * num_vertices) * (num_arcs + 2 * num_vertices)",
62 },
63})]
64impl ReduceTo<ILP<i64, i64, Bounded>> for MinimumFeedbackVertexSet<i64> {
65 type Result = ReductionMFVSToILP;
66
67 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
68 let n = self.graph().num_vertices();
69 let arcs = self.graph().arcs();
70 let num_vars = 2 * n;
71
72 let mut constraints = Vec::new();
77 let n_i64 = <Self as ReduceTo<ILP<i64, i64, Bounded>>>::exact_i64(
78 n,
79 "encoding the topological order",
80 )?;
81
82 for i in 0..n {
84 constraints.push(LinearConstraint::le(vec![(i, 1)], 1));
85 }
86
87 for i in 0..n {
89 constraints.push(LinearConstraint::le(vec![(n + i, 1)], n_i64 - 1));
90 }
91
92 for &(u, v) in &arcs {
96 let terms = vec![
97 (n + v, 1), (n + u, -1), (u, n_i64), (v, n_i64), ];
102 constraints.push(LinearConstraint::ge(terms, 1));
103 }
104
105 let objective: Vec<(usize, i64)> = self
107 .weights()
108 .iter()
109 .enumerate()
110 .map(|(vertex, &weight)| (vertex, weight))
111 .collect();
112
113 let mut variables = vec![IntegerVariable::binary(); num_vars];
114 variables[n..].fill(
115 IntegerVariable::new(Some(0), Some((n_i64 - 1).max(0)))
116 .map_err(<Self as ReduceTo<ILP<i64, i64, Bounded>>>::target_construction)?,
117 );
118
119 let target =
120 ILP::with_variables(variables, constraints, objective, ObjectiveSense::Minimize)
121 .map_err(<Self as ReduceTo<ILP<i64, i64, Bounded>>>::target_construction)?;
122
123 Ok(ReductionMFVSToILP {
124 target,
125 num_vertices: n,
126 })
127 }
128}
129
130#[cfg(feature = "example-db")]
131pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
132 use crate::topology::DirectedGraph;
133
134 vec![crate::example_db::specs::RuleExampleSpec {
135 id: "minimumfeedbackvertexset_to_ilp",
136 build: || {
137 let graph = DirectedGraph::new(3, vec![(0, 1), (1, 2), (2, 0)]);
139 let source = MinimumFeedbackVertexSet::new(graph, vec![1i64; 3]);
140 crate::example_db::specs::rule_example_via_bounded_ilp::<_>(source)
141 },
142 }]
143}
144
145#[cfg(test)]
146#[path = "../unit_tests/rules/minimumfeedbackvertexset_ilp.rs"]
147mod tests;