tensorlogic_ir/resolution/
prover.rs1use std::collections::VecDeque;
9
10use super::clause::Clause;
11use super::literal::Literal;
12use super::proof::{ProofResult, ProverStats, ResolutionStep, ResolutionStrategy};
13
14pub struct ResolutionProver {
16 pub(super) clauses: Vec<Clause>,
18 strategy: ResolutionStrategy,
20 pub stats: ProverStats,
22}
23
24impl ResolutionProver {
25 pub fn new() -> Self {
27 ResolutionProver {
28 clauses: Vec::new(),
29 strategy: ResolutionStrategy::Saturation { max_clauses: 10000 },
30 stats: ProverStats::default(),
31 }
32 }
33
34 pub fn with_strategy(strategy: ResolutionStrategy) -> Self {
36 ResolutionProver {
37 clauses: Vec::new(),
38 strategy,
39 stats: ProverStats::default(),
40 }
41 }
42
43 pub fn add_clause(&mut self, clause: Clause) {
45 if !clause.is_tautology() {
47 self.clauses.push(clause);
48 } else {
49 self.stats.tautologies_removed += 1;
50 }
51 }
52
53 pub fn add_clauses(&mut self, clauses: Vec<Clause>) {
55 for clause in clauses {
56 self.add_clause(clause);
57 }
58 }
59
60 pub fn reset(&mut self) {
62 self.clauses.clear();
63 self.stats = ProverStats::default();
64 }
65
66 fn resolve(&self, c1: &Clause, c2: &Clause) -> Vec<(Clause, Literal)> {
73 let mut resolvents = Vec::new();
74
75 for lit1 in &c1.literals {
77 for lit2 in &c2.literals {
78 if lit1.is_complementary(lit2) {
79 let mut new_literals = Vec::new();
81
82 for lit in &c1.literals {
84 if lit != lit1 {
85 new_literals.push(lit.clone());
86 }
87 }
88
89 for lit in &c2.literals {
91 if lit != lit2 {
92 new_literals.push(lit.clone());
93 }
94 }
95
96 let resolvent = Clause::from_literals(new_literals);
97 resolvents.push((resolvent, lit1.clone()));
98 }
99 }
100 }
101
102 resolvents
103 }
104
105 pub fn resolve_first_order(&self, c1: &Clause, c2: &Clause) -> Vec<(Clause, Literal)> {
131 static mut RENAME_COUNTER: usize = 0;
134 let counter = unsafe {
135 RENAME_COUNTER += 1;
136 RENAME_COUNTER
137 };
138
139 let c1_renamed = c1.rename_variables(&format!("_c1_{}", counter));
141 let c2_renamed = c2.rename_variables(&format!("_c2_{}", counter));
142
143 let mut resolvents = Vec::new();
144
145 for lit1 in &c1_renamed.literals {
147 for lit2 in &c2_renamed.literals {
148 if let Some(mgu) = lit1.try_unify(lit2) {
150 let mut new_literals = Vec::new();
152
153 for lit in &c1_renamed.literals {
155 if lit != lit1 {
156 new_literals.push(lit.apply_substitution(&mgu));
157 }
158 }
159
160 for lit in &c2_renamed.literals {
162 if lit != lit2 {
163 new_literals.push(lit.apply_substitution(&mgu));
164 }
165 }
166
167 let resolvent = Clause::from_literals(new_literals);
168 let orig_lit = lit1.clone(); resolvents.push((resolvent, orig_lit));
171 }
172 }
173 }
174
175 resolvents
176 }
177
178 fn is_subsumed(&self, clause: &Clause, clause_set: &[Clause]) -> bool {
180 clause_set.iter().any(|c| c.subsumes(clause))
181 }
182
183 pub fn prove(&mut self) -> ProofResult {
185 match &self.strategy {
186 ResolutionStrategy::Saturation { max_clauses } => self.prove_saturation(*max_clauses),
187 ResolutionStrategy::SetOfSupport { max_steps } => self.prove_set_of_support(*max_steps),
188 ResolutionStrategy::UnitResolution { max_steps } => {
189 self.prove_unit_resolution(*max_steps)
190 }
191 ResolutionStrategy::Linear { max_depth } => self.prove_linear(*max_depth),
192 }
193 }
194
195 fn prove_saturation(&mut self, max_clauses: usize) -> ProofResult {
197 let mut clause_set: Vec<Clause> = self.clauses.clone();
198 let mut derivation = Vec::new();
199 let mut steps = 0;
200
201 if clause_set.iter().any(|c| c.is_empty()) {
203 self.stats.empty_clause_found = true;
204 return ProofResult::Unsatisfiable {
205 steps: 0,
206 derivation: vec![],
207 };
208 }
209
210 loop {
211 let current_clauses: Vec<Clause> = clause_set.clone();
212 let mut new_clauses = Vec::new();
213
214 for i in 0..current_clauses.len() {
216 for j in (i + 1)..current_clauses.len() {
217 let resolvents = self.resolve(¤t_clauses[i], ¤t_clauses[j]);
218
219 for (resolvent, resolved_lit) in resolvents {
220 steps += 1;
221 self.stats.resolution_steps += 1;
222
223 if resolvent.is_tautology() {
225 self.stats.tautologies_removed += 1;
226 continue;
227 }
228
229 if resolvent.is_empty() {
231 self.stats.empty_clause_found = true;
232 derivation.push(ResolutionStep {
233 parent1: current_clauses[i].clone(),
234 parent2: current_clauses[j].clone(),
235 resolvent: resolvent.clone(),
236 resolved_literal: resolved_lit,
237 });
238 return ProofResult::Unsatisfiable { steps, derivation };
239 }
240
241 if self.is_subsumed(&resolvent, ¤t_clauses) {
243 self.stats.clauses_subsumed += 1;
244 continue;
245 }
246
247 if !clause_set.contains(&resolvent) && !new_clauses.contains(&resolvent) {
249 new_clauses.push(resolvent.clone());
250 derivation.push(ResolutionStep {
251 parent1: current_clauses[i].clone(),
252 parent2: current_clauses[j].clone(),
253 resolvent,
254 resolved_literal: resolved_lit,
255 });
256 }
257 }
258 }
259 }
260
261 if new_clauses.is_empty() {
263 return ProofResult::Saturated {
264 clauses_generated: clause_set.len(),
265 };
266 }
267
268 for clause in new_clauses {
270 clause_set.push(clause);
271 self.stats.clauses_generated += 1;
272
273 if clause_set.len() >= max_clauses {
274 return ProofResult::ResourceLimitReached {
275 steps_attempted: steps,
276 };
277 }
278 }
279 }
280 }
281
282 fn prove_set_of_support(&mut self, max_steps: usize) -> ProofResult {
284 if self.clauses.is_empty() {
286 return ProofResult::Satisfiable;
287 }
288
289 let support = self
290 .clauses
291 .pop()
292 .expect("clauses must be non-empty before pop");
293 let mut sos: VecDeque<Clause> = VecDeque::new();
294 sos.push_back(support);
295
296 let usable: Vec<Clause> = self.clauses.clone();
297 let mut derivation = Vec::new();
298 let mut steps = 0;
299
300 while let Some(current) = sos.pop_front() {
301 if steps >= max_steps {
302 return ProofResult::ResourceLimitReached {
303 steps_attempted: steps,
304 };
305 }
306
307 if current.is_empty() {
308 self.stats.empty_clause_found = true;
309 return ProofResult::Unsatisfiable { steps, derivation };
310 }
311
312 for usable_clause in &usable {
314 let resolvents = self.resolve(¤t, usable_clause);
315
316 for (resolvent, resolved_lit) in resolvents {
317 steps += 1;
318 self.stats.resolution_steps += 1;
319
320 if resolvent.is_tautology() {
321 self.stats.tautologies_removed += 1;
322 continue;
323 }
324
325 if resolvent.is_empty() {
326 self.stats.empty_clause_found = true;
327 derivation.push(ResolutionStep {
328 parent1: current.clone(),
329 parent2: usable_clause.clone(),
330 resolvent: resolvent.clone(),
331 resolved_literal: resolved_lit,
332 });
333 return ProofResult::Unsatisfiable { steps, derivation };
334 }
335
336 sos.push_back(resolvent.clone());
337 self.stats.clauses_generated += 1;
338 derivation.push(ResolutionStep {
339 parent1: current.clone(),
340 parent2: usable_clause.clone(),
341 resolvent,
342 resolved_literal: resolved_lit,
343 });
344 }
345 }
346 }
347
348 ProofResult::Satisfiable
349 }
350
351 fn prove_unit_resolution(&mut self, max_steps: usize) -> ProofResult {
353 let mut clauses = self.clauses.clone();
354 let mut derivation = Vec::new();
355 let mut steps = 0;
356
357 loop {
358 if steps >= max_steps {
359 return ProofResult::ResourceLimitReached {
360 steps_attempted: steps,
361 };
362 }
363
364 let unit_clauses: Vec<Clause> =
366 clauses.iter().filter(|c| c.is_unit()).cloned().collect();
367
368 if unit_clauses.is_empty() {
369 return ProofResult::Satisfiable;
370 }
371
372 let mut new_clauses = Vec::new();
373 let mut found_new = false;
374
375 for unit in &unit_clauses {
377 for clause in &clauses {
378 if clause.is_unit() && clause == unit {
379 continue; }
381
382 let resolvents = self.resolve(unit, clause);
383
384 for (resolvent, resolved_lit) in resolvents {
385 steps += 1;
386 self.stats.resolution_steps += 1;
387
388 if resolvent.is_tautology() {
389 self.stats.tautologies_removed += 1;
390 continue;
391 }
392
393 if resolvent.is_empty() {
394 self.stats.empty_clause_found = true;
395 derivation.push(ResolutionStep {
396 parent1: unit.clone(),
397 parent2: clause.clone(),
398 resolvent: resolvent.clone(),
399 resolved_literal: resolved_lit,
400 });
401 return ProofResult::Unsatisfiable { steps, derivation };
402 }
403
404 if !clauses.contains(&resolvent) && !new_clauses.contains(&resolvent) {
405 new_clauses.push(resolvent.clone());
406 found_new = true;
407 self.stats.clauses_generated += 1;
408 derivation.push(ResolutionStep {
409 parent1: unit.clone(),
410 parent2: clause.clone(),
411 resolvent,
412 resolved_literal: resolved_lit,
413 });
414 }
415 }
416 }
417 }
418
419 if !found_new {
420 return ProofResult::Satisfiable;
421 }
422
423 clauses.extend(new_clauses);
424 }
425 }
426
427 fn prove_linear(&mut self, max_depth: usize) -> ProofResult {
429 if self.clauses.is_empty() {
431 return ProofResult::Satisfiable;
432 }
433
434 let start = self.clauses[0].clone();
435 let mut current = start.clone();
436 let mut depth = 0;
437 let mut derivation = Vec::new();
438
439 while depth < max_depth {
440 if current.is_empty() {
441 self.stats.empty_clause_found = true;
442 return ProofResult::Unsatisfiable {
443 steps: depth,
444 derivation,
445 };
446 }
447
448 let mut resolved = false;
450 for other in &self.clauses[1..] {
451 let resolvents = self.resolve(¤t, other);
452
453 if let Some((resolvent, resolved_lit)) = resolvents.first() {
454 if !resolvent.is_tautology() {
455 current = resolvent.clone();
456 depth += 1;
457 self.stats.resolution_steps += 1;
458 self.stats.clauses_generated += 1;
459 derivation.push(ResolutionStep {
460 parent1: current.clone(),
461 parent2: other.clone(),
462 resolvent: resolvent.clone(),
463 resolved_literal: resolved_lit.clone(),
464 });
465 resolved = true;
466 break;
467 }
468 }
469 }
470
471 if !resolved {
472 return ProofResult::Satisfiable;
473 }
474 }
475
476 ProofResult::ResourceLimitReached {
477 steps_attempted: depth,
478 }
479 }
480}
481
482impl Default for ResolutionProver {
483 fn default() -> Self {
484 Self::new()
485 }
486}