1use crate::models::formula::Satisfiability;
12use crate::models::graph::MaximumIndependentSet;
13use crate::reduction;
14use crate::rules::traits::{ReduceTo, ReductionResult};
15use crate::topology::SimpleGraph;
16use crate::types::{Max, One, Or};
17
18#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct BoolVar {
21 pub name: usize,
23 pub neg: bool,
25}
26
27impl BoolVar {
28 pub fn new(name: usize, neg: bool) -> Self {
30 Self { name, neg }
31 }
32
33 pub fn from_literal(lit: i64) -> Self {
36 let name = lit.unsigned_abs() as usize - 1; let neg = lit < 0;
38 Self { name, neg }
39 }
40
41 pub fn is_complement(&self, other: &BoolVar) -> bool {
43 self.name == other.name && self.neg != other.neg
44 }
45}
46
47#[derive(Debug, Clone)]
55pub struct ReductionSATToIS {
56 target: MaximumIndependentSet<SimpleGraph, One>,
58 literals: Vec<BoolVar>,
60 num_source_variables: usize,
62 num_clauses: usize,
64 target_size: i64,
66}
67
68impl ReductionResult for ReductionSATToIS {
69 type Source = Satisfiability;
70 type Target = MaximumIndependentSet<SimpleGraph, One>;
71
72 fn target_problem(&self) -> &Self::Target {
73 &self.target
74 }
75
76 fn extract_solution(
82 &self,
83 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
84 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
85 let value =
86 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
87 let certificate = crate::rules::AggregateReductionResult::extract_value(self, value);
88 if !certificate.0 {
89 return Err(crate::rules::ExtractionError::invalid(
90 "target independent set does not certify satisfiability",
91 ));
92 }
93
94 let mut assignment = vec![false; self.num_source_variables];
95 for (literal, &selected) in self.literals.iter().zip(target_solution) {
96 if selected {
97 assignment[literal.name] = !literal.neg;
98 }
99 }
100 Ok(assignment)
101 }
102}
103
104impl crate::rules::AggregateReductionResult for ReductionSATToIS {
105 type Source = Satisfiability;
106 type Target = MaximumIndependentSet<SimpleGraph, One>;
107
108 fn target_problem(&self) -> &Self::Target {
109 &self.target
110 }
111
112 fn extract_value(&self, target_value: Max<i64>) -> Or {
113 Or(target_value == Max(Some(self.target_size)))
114 }
115}
116
117impl ReductionSATToIS {
118 pub fn num_clauses(&self) -> usize {
120 self.num_clauses
121 }
122
123 pub fn literals(&self) -> &[BoolVar] {
125 &self.literals
126 }
127}
128
129#[reduction(
130 aggregate = custom,
131 transform = upper_bound {
132 num_vertices = "num_literals",
133 num_edges = "num_literals^2",
134 }
135)]
136impl ReduceTo<MaximumIndependentSet<SimpleGraph, One>> for Satisfiability {
137 type Result = ReductionSATToIS;
138
139 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
140 let target_size = <Self as ReduceTo<MaximumIndependentSet<SimpleGraph, One>>>::exact_i64(
141 self.num_clauses(),
142 "representing the satisfying independent-set cardinality",
143 )?;
144 let mut literals: Vec<BoolVar> = Vec::new();
145 let mut edges: Vec<(usize, usize)> = Vec::new();
146
147 for clause in self.clauses() {
150 let clause_start = literals.len();
151
152 for &lit in &clause.literals {
154 literals.push(BoolVar::from_literal(lit));
155 }
156
157 let vertex_count = literals.len();
158 for i in clause_start..vertex_count {
160 for j in (i + 1)..vertex_count {
161 edges.push((i, j));
162 }
163 }
164 }
165
166 let vertex_count = literals.len();
167 for i in 0..vertex_count {
170 for j in (i + 1)..vertex_count {
171 if literals[i].is_complement(&literals[j]) {
172 edges.push((i, j));
173 }
174 }
175 }
176
177 let target = MaximumIndependentSet::new(
178 SimpleGraph::new(vertex_count, edges),
179 vec![One; vertex_count],
180 );
181
182 Ok(ReductionSATToIS {
183 target,
184 literals,
185 num_source_variables: self.num_vars(),
186 num_clauses: self.num_clauses(),
187 target_size,
188 })
189 }
190}
191
192#[cfg(feature = "example-db")]
193pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
194 use crate::export::SolutionPair;
195 use crate::models::formula::CNFClause;
196
197 fn sat_seven_clause_example() -> Satisfiability {
198 Satisfiability::new(
199 5,
200 vec![
201 CNFClause::new(vec![1, 2, -3]),
202 CNFClause::new(vec![-1, 3, 4]),
203 CNFClause::new(vec![2, -4, 5]),
204 CNFClause::new(vec![-2, 3, -5]),
205 CNFClause::new(vec![1, -3, 5]),
206 CNFClause::new(vec![-1, -2, 4]),
207 CNFClause::new(vec![3, -4, -5]),
208 ],
209 )
210 }
211
212 vec![crate::example_db::specs::RuleExampleSpec {
213 id: "satisfiability_to_maximumindependentset",
214 build: || {
215 crate::example_db::specs::rule_example_with_witness::<
216 _,
217 MaximumIndependentSet<SimpleGraph, One>,
218 >(
219 sat_seven_clause_example(),
220 SolutionPair {
221 source_config: serde_json::json!(vec![true, true, true, true, false]),
222 target_config: serde_json::json!(vec![
223 true, false, false, false, true, false, true, false, false, false, false,
224 true, true, false, false, false, false, true, true, false, false
225 ]),
226 },
227 )
228 },
229 }]
230}
231
232#[cfg(test)]
233#[path = "../unit_tests/rules/sat_maximumindependentset.rs"]
234mod tests;