problemreductions/models/formula/
non_tautology.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
8use crate::traits::Problem;
9use serde::{Deserialize, Serialize};
10
11inventory::submit! {
12 ProblemSchemaEntry {
13 name: "NonTautology",
14 display_name: "Non-Tautology",
15 aliases: &[],
16 dimensions: &[],
17 module_path: module_path!(),
18 description: "Find a falsifying assignment for a DNF formula (proving it is not a tautology)",
19 fields: &[
20 FieldInfo { name: "num_vars", type_name: "usize", description: "Number of Boolean variables" },
21 FieldInfo { name: "disjuncts", type_name: "Vec<Vec<i32>>", description: "Disjuncts (each a conjunction of literals) in disjunctive normal form" },
22 ],
23 }
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct NonTautology {
55 num_vars: usize,
57 disjuncts: Vec<Vec<i32>>,
60}
61
62impl NonTautology {
63 pub fn new(num_vars: usize, disjuncts: Vec<Vec<i32>>) -> Self {
68 for (i, disjunct) in disjuncts.iter().enumerate() {
69 for &lit in disjunct {
70 let var = lit.unsigned_abs() as usize;
71 assert!(
72 var >= 1 && var <= num_vars,
73 "Disjunct {} contains literal {} referencing variable {} outside range [1, {}]",
74 i,
75 lit,
76 var,
77 num_vars
78 );
79 }
80 }
81 Self {
82 num_vars,
83 disjuncts,
84 }
85 }
86
87 pub fn num_vars(&self) -> usize {
89 self.num_vars
90 }
91
92 pub fn num_disjuncts(&self) -> usize {
94 self.disjuncts.len()
95 }
96
97 pub fn disjuncts(&self) -> &[Vec<i32>] {
99 &self.disjuncts
100 }
101
102 fn literal_is_true(lit: i32, assignment: &[bool]) -> bool {
104 let var = lit.unsigned_abs() as usize - 1;
105 let value = assignment.get(var).copied().unwrap_or(false);
106 if lit > 0 {
107 value
108 } else {
109 !value
110 }
111 }
112
113 pub fn is_falsifying(&self, assignment: &[bool]) -> bool {
119 self.disjuncts.iter().all(|disjunct| {
120 !disjunct
122 .iter()
123 .all(|&lit| Self::literal_is_true(lit, assignment))
124 })
125 }
126}
127
128impl Problem for NonTautology {
129 const NAME: &'static str = "NonTautology";
130 type Value = crate::types::Or;
131
132 fn dims(&self) -> Vec<usize> {
133 vec![2; self.num_vars]
134 }
135
136 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
137 crate::types::Or({
138 let assignment = super::config_to_assignment(config);
139 self.is_falsifying(&assignment)
140 })
141 }
142
143 fn variant() -> Vec<(&'static str, &'static str)> {
144 crate::variant_params![]
145 }
146}
147
148crate::declare_variants! {
149 default NonTautology => "1.307^num_variables",
150}
151
152#[cfg(feature = "example-db")]
153pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
154 vec![crate::example_db::specs::ModelExampleSpec {
155 id: "non_tautology",
156 instance: Box::new(NonTautology::new(3, vec![vec![1, 2, 3], vec![-1, -2, -3]])),
157 optimal_config: vec![1, 0, 0],
158 optimal_value: serde_json::json!(true),
159 }]
160}
161
162#[cfg(test)]
163#[path = "../../unit_tests/models/formula/non_tautology.rs"]
164mod tests;