problemreductions/rules/
minimumvertexcover_comparativecontainment.rs1use crate::models::decision::Decision;
13use crate::models::graph::MinimumVertexCover;
14use crate::models::set::ComparativeContainment;
15use crate::reduction;
16use crate::rules::traits::{ReduceTo, ReductionResult};
17use crate::topology::{Graph, SimpleGraph};
18
19#[derive(Debug, Clone)]
21pub struct ReductionDecisionMVCToComparativeContainment {
22 target: ComparativeContainment<i64>,
23}
24
25impl ReductionResult for ReductionDecisionMVCToComparativeContainment {
26 type Source = Decision<MinimumVertexCover<SimpleGraph, i64>>;
27 type Target = ComparativeContainment<i64>;
28
29 fn target_problem(&self) -> &Self::Target {
30 &self.target
31 }
32
33 fn extract_solution(
34 &self,
35 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
36 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
37 if !crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?
38 .0
39 {
40 return Err(crate::rules::ExtractionError::invalid(
41 "containment inequality is not satisfied",
42 ));
43 }
44 Ok(target_solution.clone())
45 }
46}
47
48#[reduction(
49 transform = upper_bound {
50 universe_size = "num_vertices",
51 num_r_sets = "num_vertices + 1",
52 num_s_sets = "num_vertices + num_edges + 1",
53 }
54)]
55impl ReduceTo<ComparativeContainment<i64>> for Decision<MinimumVertexCover<SimpleGraph, i64>> {
56 type Result = ReductionDecisionMVCToComparativeContainment;
57
58 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
59 let overflow = |operation| {
60 crate::rules::ReductionError::integer_overflow::<Self, ComparativeContainment<i64>>(
61 operation,
62 )
63 };
64 let weights = self.inner().weights();
65 let n = self.inner().graph().num_vertices();
66 let mut positive_total = 0i64;
67 let mut negative_total = 0i64;
68 for &weight in weights {
69 if weight > 0 {
70 positive_total = positive_total
71 .checked_add(weight)
72 .ok_or_else(|| overflow("summing positive vertex weights"))?;
73 } else {
74 negative_total = negative_total
75 .checked_add(weight)
76 .ok_or_else(|| overflow("summing negative vertex weights"))?;
77 }
78 }
79 let total = positive_total + negative_total;
82 let bound = (*self.bound()).min(positive_total);
83 let constant = bound
84 .checked_sub(total)
85 .ok_or_else(|| overflow("computing the containment budget term"))?;
86 let penalty = positive_total
87 .checked_sub(negative_total)
88 .and_then(|span| span.checked_add(1))
89 .ok_or_else(|| overflow("computing a strict uncovered-edge penalty"))?;
90
91 let mut r_sets = Vec::new();
92 let mut r_weights = Vec::new();
93 let mut s_sets = Vec::new();
94 let mut s_weights = Vec::new();
95 for (set, coefficient) in weights
97 .iter()
98 .enumerate()
99 .filter(|(_, w)| **w != 0)
100 .map(|(v, &w)| (complement_singleton(n, v), w))
101 .chain((constant != 0).then(|| ((0..n).collect(), constant)))
102 {
103 if coefficient > 0 {
104 r_sets.push(set);
105 r_weights.push(coefficient);
106 } else if coefficient < 0 {
107 s_sets.push(set);
108 s_weights.push(
109 coefficient
110 .checked_neg()
111 .ok_or_else(|| overflow("negating a containment coefficient"))?,
112 );
113 }
114 }
115 for (u, v) in self.inner().graph().edges() {
116 s_sets.push(complement_pair(n, u, v));
117 s_weights.push(penalty);
118 }
119 for family in [&r_weights, &s_weights] {
122 family
123 .iter()
124 .try_fold(0i64, |sum, &weight| sum.checked_add(weight))
125 .ok_or_else(|| overflow("summing a containment weight family"))?;
126 }
127 let target = ComparativeContainment::with_weights(n, r_sets, s_sets, r_weights, s_weights)
128 .map_err(
129 crate::rules::ReductionError::construction::<Self, ComparativeContainment<i64>>,
130 )?;
131 Ok(ReductionDecisionMVCToComparativeContainment { target })
132 }
133}
134
135fn complement_singleton(n: usize, v: usize) -> Vec<usize> {
136 (0..n).filter(|&x| x != v).collect()
137}
138
139fn complement_pair(n: usize, u: usize, v: usize) -> Vec<usize> {
140 (0..n).filter(|&x| x != u && x != v).collect()
141}
142
143#[cfg(feature = "example-db")]
144pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
145 use crate::export::SolutionPair;
146
147 vec![crate::example_db::specs::RuleExampleSpec {
148 id: "decisionminimumvertexcover_to_comparativecontainment",
149 build: || {
150 let inner = MinimumVertexCover::new(
152 SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)]),
153 vec![1i64; 4],
154 );
155 let source = Decision::new(inner, 2);
156 crate::example_db::specs::rule_example_with_witness::<_, ComparativeContainment<i64>>(
157 source,
158 SolutionPair {
159 source_config: serde_json::json!(vec![false, true, true, false]),
160 target_config: serde_json::json!(vec![false, true, true, false]),
161 },
162 )
163 },
164 }]
165}
166
167#[cfg(test)]
168#[path = "../unit_tests/rules/minimumvertexcover_comparativecontainment.rs"]
169mod tests;