1use crate::models::decision::Decision;
18use crate::models::formula::Satisfiability;
19use crate::models::graph::MinimumDominatingSet;
20use crate::reduction;
21use crate::rules::sat_maximumindependentset::BoolVar;
22use crate::rules::traits::{ReduceTo, ReductionResult};
23use crate::topology::SimpleGraph;
24use std::collections::BTreeMap;
25
26#[derive(Debug, Clone)]
33pub struct ReductionSATToDS {
34 target: Decision<MinimumDominatingSet<SimpleGraph, i64>>,
36 num_literals: usize,
38 num_clauses: usize,
40 variables: BTreeMap<usize, usize>,
42}
43
44impl ReductionResult for ReductionSATToDS {
45 type Source = Satisfiability;
46 type Target = Decision<MinimumDominatingSet<SimpleGraph, i64>>;
47
48 fn target_problem(&self) -> &Self::Target {
49 &self.target
50 }
51
52 fn extract_solution(
60 &self,
61 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
62 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
63 crate::rules::traits::validate_target_witness(
64 self.target_problem(),
65 target_solution,
66 |value| value.0,
67 "target dominating set does not certify satisfiability",
68 )?;
69
70 let mut assignment = vec![false; self.num_literals];
71 for (&variable, &gadget) in &self.variables {
72 assignment[variable] = target_solution[3 * gadget];
73 }
74
75 Ok(assignment)
76 }
77}
78
79#[crate::aggregate_reduction(identity)]
80impl crate::rules::AggregateReductionResult for ReductionSATToDS {}
81
82impl ReductionSATToDS {
83 fn target_dimensions(
85 num_variables: usize,
86 num_clauses: usize,
87 ) -> Result<(usize, i64), crate::rules::ReductionError> {
88 let num_vertices = num_variables
89 .checked_mul(3)
90 .and_then(|base| base.checked_add(num_clauses))
91 .ok_or_else(|| {
92 crate::rules::ReductionError::integer_overflow::<
93 Satisfiability,
94 Decision<MinimumDominatingSet<SimpleGraph, i64>>,
95 >("counting dominating-set vertices")
96 })?;
97 <Satisfiability as ReduceTo<Decision<MinimumDominatingSet<SimpleGraph, i64>>>>::exact_i64(
100 num_vertices,
101 "representing all dominating-set weights",
102 )?;
103 let target_size = <Satisfiability as ReduceTo<
104 Decision<MinimumDominatingSet<SimpleGraph, i64>>,
105 >>::exact_i64(
106 num_variables,
107 "representing the satisfying dominating-set cardinality",
108 )?;
109 Ok((num_vertices, target_size))
110 }
111
112 pub fn num_literals(&self) -> usize {
114 self.num_literals
115 }
116
117 pub fn num_clauses(&self) -> usize {
119 self.num_clauses
120 }
121}
122
123#[reduction(
124 transform = upper_bound {
125 num_vertices = "3 * num_vars + num_clauses",
126 num_edges = "3 * num_vars + num_literals",
127 }
128)]
129impl ReduceTo<Decision<MinimumDominatingSet<SimpleGraph, i64>>> for Satisfiability {
130 type Result = ReductionSATToDS;
131
132 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
133 let mut variables: BTreeMap<usize, usize> = self
136 .clauses()
137 .iter()
138 .flat_map(|clause| &clause.literals)
139 .map(|&literal| (BoolVar::from_literal(literal).name, 0))
140 .collect();
141 for (index, gadget) in variables.values_mut().enumerate() {
142 *gadget = index;
143 }
144 let num_variables = variables.len();
145 let num_clauses = self.num_clauses();
146 let (num_vertices, target_size) =
147 ReductionSATToDS::target_dimensions(num_variables, num_clauses)?;
148
149 let mut edges: Vec<(usize, usize)> = Vec::new();
150
151 for i in 0..num_variables {
158 let base = 3 * i;
159 edges.push((base, base + 1));
161 edges.push((base, base + 2));
162 edges.push((base + 1, base + 2));
163 }
164
165 for (j, clause) in self.clauses().iter().enumerate() {
168 let clause_vertex = 3 * num_variables + j;
169
170 for &lit in &clause.literals {
171 let var = BoolVar::from_literal(lit);
172 let literal_vertex = 3 * variables[&var.name] + usize::from(var.neg);
175 edges.push((literal_vertex, clause_vertex));
176 }
177 }
178
179 let target = MinimumDominatingSet::new(
180 SimpleGraph::new(num_vertices, edges),
181 vec![1i64; num_vertices],
182 );
183
184 Ok(ReductionSATToDS {
185 target: Decision::new(target, target_size),
186 num_literals: self.num_vars(),
187 num_clauses,
188 variables,
189 })
190 }
191}
192
193#[cfg(feature = "example-db")]
194pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
195 use crate::export::SolutionPair;
196 use crate::models::formula::CNFClause;
197
198 vec![crate::example_db::specs::RuleExampleSpec {
199 id: "satisfiability_to_minimumdominatingset",
200 build: || {
201 let source = Satisfiability::new(
202 5,
203 vec![
204 CNFClause::new(vec![1, 2, -3]),
205 CNFClause::new(vec![-1, 3, 4]),
206 CNFClause::new(vec![2, -4, 5]),
207 CNFClause::new(vec![-2, 3, -5]),
208 CNFClause::new(vec![1, -3, 5]),
209 CNFClause::new(vec![-1, -2, 4]),
210 CNFClause::new(vec![3, -4, -5]),
211 ],
212 );
213 crate::example_db::specs::rule_example_with_witness::<
214 _,
215 Decision<MinimumDominatingSet<SimpleGraph, i64>>,
216 >(
217 source,
218 SolutionPair {
219 source_config: serde_json::json!(vec![true, false, true, true, true]),
220 target_config: serde_json::json!(vec![
221 true, false, false, false, true, false, true, false, false, true, false,
222 false, true, false, false, false, false, false, false, false, false, false
223 ]),
224 },
225 )
226 },
227 }]
228}
229
230#[cfg(test)]
231#[path = "../unit_tests/rules/sat_minimumdominatingset.rs"]
232mod tests;