problemreductions/models/formula/
maximum_2_satisfiability.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
8use crate::traits::Problem;
9use crate::types::Max;
10use serde::{Deserialize, Serialize};
11
12use super::{sat::validate_cnf_literals, CNFClause};
13
14inventory::submit! {
15 ProblemSchemaEntry {
16 name: "Maximum2Satisfiability",
17 display_name: "Maximum 2-Satisfiability",
18 aliases: &["MAX2SAT"],
19 dimensions: &[],
20 category: crate::registry::ProblemCategory::Formula,
21 module_path: module_path!(),
22 description: "Maximize the number of satisfied 2-literal clauses",
23 fields: &[
24 FieldInfo { name: "num_vars", type_name: "usize", description: "Number of Boolean variables" },
25 FieldInfo { name: "clauses", type_name: "Vec<CNFClause>", description: "Collection of 2-literal clauses" },
26 ],
27 }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
55#[serde(try_from = "Maximum2SatisfiabilityDef")]
56pub struct Maximum2Satisfiability {
57 num_vars: usize,
59 clauses: Vec<CNFClause>,
61}
62
63impl Maximum2Satisfiability {
64 pub fn new(num_vars: usize, clauses: Vec<CNFClause>) -> Self {
69 Self::try_new(num_vars, clauses).unwrap_or_else(|message| panic!("{message}"))
70 }
71
72 pub fn try_new(
74 num_vars: usize,
75 clauses: Vec<CNFClause>,
76 ) -> Result<Self, crate::registry::ConstructionError> {
77 validate_cnf_literals(num_vars, &clauses)?;
78 for (i, clause) in clauses.iter().enumerate() {
79 if clause.len() != 2 {
80 return Err(format!("Clause {i} has {} literals, expected 2", clause.len()).into());
81 }
82 }
83 Ok(Self { num_vars, clauses })
84 }
85
86 pub fn num_vars(&self) -> usize {
88 self.num_vars
89 }
90
91 pub fn num_clauses(&self) -> usize {
93 self.clauses.len()
94 }
95
96 pub fn clauses(&self) -> &[CNFClause] {
98 &self.clauses
99 }
100
101 pub fn count_satisfied(
103 &self,
104 assignment: &[bool],
105 ) -> Result<i64, crate::traits::EvaluationError> {
106 let count = self
107 .clauses
108 .iter()
109 .filter(|c| c.is_satisfied(assignment))
110 .count();
111 i64::try_from(count).map_err(|_| {
112 crate::traits::EvaluationError::IntegerOverflow(
113 "converting satisfied-clause count to i64".into(),
114 )
115 })
116 }
117}
118
119impl Problem for Maximum2Satisfiability {
120 const NAME: &'static str = "Maximum2Satisfiability";
121 type Solution = Vec<bool>;
122 type Value = Max<i64>;
123
124 crate::problem_parameters![("num_clauses", num_clauses), ("num_vars", num_vars),];
125
126 fn evaluate(
127 &self,
128 config: &Self::Solution,
129 ) -> Result<Max<i64>, crate::traits::EvaluationError> {
130 if config.len() != self.num_vars {
131 return Err(crate::traits::EvaluationError::InvalidConfiguration(
132 "assignment length does not match the formula variables".into(),
133 ));
134 }
135 Ok(Max(Some(self.count_satisfied(config)?)))
136 }
137
138 fn variant() -> Vec<(&'static str, &'static str)> {
139 crate::variant_params![]
140 }
141}
142
143impl crate::solvers::BruteForceProblem for Maximum2Satisfiability {
144 fn dimensions(&self) -> Vec<usize> {
145 vec![2; self.num_vars]
146 }
147}
148
149crate::declare_variants! {
150 default Maximum2Satisfiability => "2^(0.7905 * num_vars)",
151}
152
153crate::register_brute_force! {
154 Maximum2Satisfiability decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
155}
156
157#[derive(Deserialize)]
158struct Maximum2SatisfiabilityDef {
159 num_vars: usize,
160 clauses: Vec<CNFClause>,
161}
162
163impl TryFrom<Maximum2SatisfiabilityDef> for Maximum2Satisfiability {
164 type Error = crate::registry::ConstructionError;
165
166 fn try_from(value: Maximum2SatisfiabilityDef) -> Result<Self, Self::Error> {
167 Self::try_new(value.num_vars, value.clauses)
168 }
169}
170
171#[cfg(feature = "example-db")]
172pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
173 vec![crate::example_db::specs::ModelExampleSpec {
174 id: "maximum_2_satisfiability",
175 instance: Box::new(Maximum2Satisfiability::new(
176 4,
177 vec![
178 CNFClause::new(vec![1, 2]),
179 CNFClause::new(vec![1, -2]),
180 CNFClause::new(vec![-1, 3]),
181 CNFClause::new(vec![-1, -3]),
182 CNFClause::new(vec![2, 4]),
183 CNFClause::new(vec![-3, -4]),
184 CNFClause::new(vec![3, 4]),
185 ],
186 )),
187 optimal_config: serde_json::json!(vec![true, true, false, true]),
188 optimal_value: serde_json::json!(6),
189 }]
190}
191
192#[cfg(test)]
193#[path = "../../unit_tests/models/formula/maximum_2_satisfiability.rs"]
194mod tests;