Connect++ 0.6.0
A fast, readable connection prover for first-order logic.
Loading...
Searching...
No Matches
FOF.cpp
1/*
2
3Copyright © 2023-24 Sean Holden. All rights reserved.
4
5*/
6/*
7
8This file is part of Connect++.
9
10Connect++ is free software: you can redistribute it and/or modify it
11under the terms of the GNU General Public License as published by the
12Free Software Foundation, either version 3 of the License, or (at your
13option) any later version.
14
15Connect++ is distributed in the hope that it will be useful, but WITHOUT
16ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18more details.
19
20You should have received a copy of the GNU General Public License along
21with Connect++. If not, see <https://www.gnu.org/licenses/>.
22
23*/
24
25#include "FOF.hpp"
26
29TermIndex* FOF::term_index = nullptr;
31
32//---------------------------------------------------------------------------
33// Simple methods for Variables.
34//---------------------------------------------------------------------------
36 switch (type) {
37 case FOFType::Empty:
38 cerr << "Don't do this with an Empty FOF!" << endl;
39 return false;
40 break;
41 case FOFType::Atom:
42 return pred.contains_variable(v);
43 case FOFType::Neg:
44 return (sub_formulas[0].has_free_variable(v));
45 break;
46 case FOFType::And:
47 case FOFType::Or:
48 for (size_t i = 0; i < sub_formulas.size(); i++) {
50 return true;
51 }
52 }
53 return false;
54 break;
55 case FOFType::Imp:
56 case FOFType::Iff:
57 return (sub_formulas[0].has_free_variable(v) ||
59 break;
60 case FOFType::A:
61 case FOFType::E:
62 if (var == v)
63 return false;
64 return sub_formulas[0].has_free_variable(v);
65 break;
66 default:
67 break;
68 }
69 return false;
70}
71//---------------------------------------------------------------------------
72void FOF::replace_variable(Variable* new_var, Variable* old_var) {
73 if (type == FOFType::Empty) {
74 cerr << "Don't do this with an Empty FOF!" << endl;
75 return;
76 }
77 if (type == FOFType::Atom) {
78 pred.replace_variable(new_var, old_var, term_index);
79 }
80 else {
81 for (FOF& f : sub_formulas)
82 f.replace_variable(new_var, old_var);
83 }
84}
85//---------------------------------------------------------------------------
87 set<Term*> a = this->find_free_variables();
88 vector<Term*> args;
89 for (Term* p : a)
90 args.push_back(p);
92 Literal lit(name, args, args.size(), true);
93 return FOF(lit);
94}
95//---------------------------------------------------------------------------
97 // For literals there is nothing to do.
98 if (is_literal()) {
99 return false;
100 }
101 // If we're at an AND, or an OR with no AND immediately under it,
102 // then we're just going to deal with the subformulas.
103 size_t and_location = find_and();
104 size_t s = sub_formulas.size();
105 if (type == FOFType::And ||
106 ((type == FOFType::Or) && (and_location == s))) {
107 bool result = false;
108 // OK, so it's not, strictly speaking, just "once" but
109 // we don't really mind do we?
110 for (size_t i = 0; i < s; i++) {
112 result = true;
113 }
114 return result;
115 }
116 // It must be an OR with an AND immediately under it. That means
117 // we can distribute. First get the AND's subformulas and
118 // remove it.
119 vector<FOF> and_fof_subs = sub_formulas[and_location].sub_formulas;
120 if (and_location != (s-1)) {
121 sub_formulas[and_location] = sub_formulas[s-1];
122 }
123 sub_formulas.pop_back();
124 // Now you've saved the sub-formulas for the AND, and
125 // removed it. The last remaining child of the OR is
126 // now going to be combined using OR with all
127 // the saved subformulas.
128 FOF f = sub_formulas[s-2];
129 size_t s2 = and_fof_subs.size();
130 vector<FOF> new_sub_formulas_2;
131 for (int i = 0; i < s2; i++) {
132 vector<FOF> new_sub_formulas;
133 new_sub_formulas.push_back(f);
134 new_sub_formulas.push_back(and_fof_subs[i]);
135 new_sub_formulas_2.push_back(FOF::make_or(new_sub_formulas));
136 }
137 // You may have started with an OR with only two children. If so
138 // then you can now move the AND upwards.
139 if (s == 2) {
140 type = FOFType::And;
141 sub_formulas = new_sub_formulas_2;
142 }
143 else {
144 sub_formulas[s-2] = FOF::make_and(new_sub_formulas_2);
145 }
146 return true;
147}
148//---------------------------------------------------------------------------
150 // For literals there is nothing to do.
151 if (is_literal()) {
152 return false;
153 }
154 // If we're at an OR, or an AND with no OR immediately under it,
155 // then we're just going to deal with the subformulas.
156 size_t or_location = find_or();
157 size_t s = sub_formulas.size();
158 if (type == FOFType::Or ||
159 ((type == FOFType::And) && (or_location == s))) {
160 bool result = false;
161 // OK, so it's not, strictly speaking, just "once" but
162 // we don't really mind do we?
163 for (size_t i = 0; i < s; i++) {
165 result = true;
166 }
167 return result;
168 }
169 // It must be an AND with an OR immediately under it. That means
170 // we can distribute. First get the OR's subformulas and
171 // remove it.
172 vector<FOF> or_fof_subs = sub_formulas[or_location].sub_formulas;
173 if (or_location != (s-1)) {
174 sub_formulas[or_location] = sub_formulas[s-1];
175 }
176 sub_formulas.pop_back();
177 // Now you've saved the sub-formulas for the OR, and
178 // removed it. The last remaining child of the AND is
179 // now going to be combined using AND with all
180 // the saved subformulas.
181 FOF f = sub_formulas[s-2];
182 size_t s2 = or_fof_subs.size();
183 vector<FOF> new_sub_formulas_2;
184 for (int i = 0; i < s2; i++) {
185 vector<FOF> new_sub_formulas;
186 new_sub_formulas.push_back(f);
187 new_sub_formulas.push_back(or_fof_subs[i]);
188 new_sub_formulas_2.push_back(FOF::make_and(new_sub_formulas));
189 }
190 // You may have started with an AND with only two children. If so
191 // then you can now move the OR upwards.
192 if (s == 2) {
193 type = FOFType::Or;
194 sub_formulas = new_sub_formulas_2;
195 }
196 else {
197 sub_formulas[s-2] = FOF::make_or(new_sub_formulas_2);
198 }
199 return true;
200}
201//---------------------------------------------------------------------------
202// Skolemization
203//---------------------------------------------------------------------------
204Term* FOF::make_skolem_function(const vector<Term*>& args) {
205 Function* sk = fun_index->make_skolem_function(args.size());
206 Term* sk_t = term_index->add_function_term(sk, args);
207 return sk_t;
208}
209//---------------------------------------------------------------------------
211 if (type == FOFType::Empty) {
212 cerr << "Don't do this with an Empty FOF!" << endl;
213 return;
214 }
215 if (type == FOFType::Atom) {
216 pred.replace_variable_with_term(new_term, old_var, term_index);
217 }
218 else {
219 for (FOF& f : sub_formulas)
220 f.replace_variable_with_term(new_term, old_var);
221 }
222}
223//---------------------------------------------------------------------------
224void FOF::skolemize_main(vector<Term*> skolem_arguments) {
225 Term* sk_t;
226 FOF f(FOFType::Atom);
227 switch (type) {
228 case FOFType::Atom:
229 break;
230 case FOFType::Neg:
231 sub_formulas[0].skolemize_main(skolem_arguments);
232 break;
233 case FOFType::And:
234 case FOFType::Or:
235 case FOFType::Imp:
236 case FOFType::Iff:
237 for (FOF& g : sub_formulas)
238 g.skolemize_main(skolem_arguments);
239 break;
240 case FOFType::A:
241 skolem_arguments.push_back(term_index->add_variable_term(var));
242 sub_formulas[0].skolemize_main(skolem_arguments);
243 break;
244 case FOFType::E:
245 sk_t = make_skolem_function(skolem_arguments);
246 sub_formulas[0].replace_variable_with_term(sk_t, var);
247 sub_formulas[0].skolemize_main(skolem_arguments);
248 f = sub_formulas[0];
249 *this = f;
250 break;
251 default:
252 break;
253 }
254}
255//---------------------------------------------------------------------------
256void FOF::skolemize_universals_main(vector<Term*> skolem_arguments) {
257 Term* sk_t;
258 FOF f(FOFType::Atom);
259 switch (type) {
260 case FOFType::Atom:
261 break;
262 case FOFType::Neg:
263 sub_formulas[0].skolemize_universals_main(skolem_arguments);
264 break;
265 case FOFType::And:
266 case FOFType::Or:
267 case FOFType::Imp:
268 case FOFType::Iff:
269 for (FOF& g : sub_formulas)
270 g.skolemize_universals_main(skolem_arguments);
271 break;
272 case FOFType::E:
273 skolem_arguments.push_back(term_index->add_variable_term(var));
274 sub_formulas[0].skolemize_universals_main(skolem_arguments);
275 break;
276 case FOFType::A:
277 sk_t = make_skolem_function(skolem_arguments);
278 sub_formulas[0].replace_variable_with_term(sk_t, var);
279 sub_formulas[0].skolemize_universals_main(skolem_arguments);
280 f = sub_formulas[0];
281 *this = f;
282 break;
283 default:
284 break;
285 }
286}
287//---------------------------------------------------------------------------
288// Miniscoping
289//---------------------------------------------------------------------------
291 vector<FOF>& free,
292 vector<FOF>& absent) {
293 // Remember that at this point you have a NNF with
294 // unique variables. So whatever is in the subformula
295 // is AND, OR, Atom or Neg (Literal).
296 vector<FOF>& subs = sub_formulas[0].sub_formulas;
297 for (size_t i = 0; i < subs.size(); i++) {
298 FOF f(subs[i]);
299 if (f.has_free_variable(v))
300 free.push_back(f);
301 else
302 absent.push_back(f);
303 }
304}
305//---------------------------------------------------------------------------
307 for (size_t i = 0; i < sub_formulas.size(); i++ ) {
308 sub_formulas[i].miniscope();
309 }
310}
311//---------------------------------------------------------------------------
312// Constructors.
313//---------------------------------------------------------------------------
314FOF::FOF(const Literal& lit)
315: sub_formulas()
316, var(nullptr)
317{
318 if (lit.is_positive()) {
319 type = FOFType::Atom;
320 pred = lit;
321 }
322 else {
323 pred.clear();
324 type = FOFType::Neg;
325 Literal lit_copy = lit;
326 lit_copy.make_positive();
327 FOF f(lit_copy);
328 sub_formulas.push_back(f);
329 }
330}
331//---------------------------------------------------------------------------
332FOF::FOF(FOFType t, const vector<FOF>& sf, Variable* v)
333: type(t)
334, pred()
335, sub_formulas(sf)
336, var(v)
337{
338 switch (t) {
339 case FOFType::Atom:
340 cerr << "Stop it! You can't make an atom with this constructor!" << endl;
341 break;
342 case FOFType::Neg:
343 case FOFType::And:
344 case FOFType::Or:
345 case FOFType::Imp:
346 case FOFType::Iff:
347 // Easy as the work gets done above.
348 if (var != nullptr) {
349 cerr << "Are you sure you want to associate a variable with something that's not a quantifier?" << endl;
350 var = nullptr;
351 }
352 break;
353 case FOFType::A:
354 case FOFType::E:
355 if (sf.size() != 1) {
356 cerr << "Careful! A quantifier should only have one subformula." << endl;
357 }
358 if (v == nullptr) {
359 cerr << "Careful! A quantifier needs a variable." << endl;
360 }
361 break;
362 default:
363 break;
364 }
365}
366//---------------------------------------------------------------------------
367// Basic tests.
368//---------------------------------------------------------------------------
369bool FOF::is_literal() const {
370 return (type == FOFType::Atom) ||
371 (type == FOFType::Neg && sub_formulas[0].type == FOFType::Atom);
372}
373//---------------------------------------------------------------------------
374bool FOF::is_clause() const {
375 if (is_literal()) {
376 return true;
377 }
378 if (!(type == FOFType::Or)) {
379 return false;
380 }
381 for (size_t i = 0; i < sub_formulas.size(); i++) {
382 if (!sub_formulas[i].is_literal()) {
383 return false;
384 }
385 }
386 return true;
387}
388//---------------------------------------------------------------------------
389// Standard simplifications.
390//---------------------------------------------------------------------------
392 if (type == FOFType::Neg) {
393 FOF f = sub_formulas[0];
394 *this = f;
395 }
396}
397//---------------------------------------------------------------------------
399 if (type == FOFType::Empty) {
400 cerr << "Don't do this with an Empty FOF!" << endl;
401 return;
402 }
403 if (type == FOFType::Imp) {
404 type = FOFType::And;
405 sub_formulas[1].negate();
406 }
407 else if (type == FOFType::Neg) {
409 }
410 else {
411 FOF f = *this;
412 type = FOFType::Neg;
413 pred.clear();
414 var = nullptr;
415 sub_formulas.clear();
416 sub_formulas.push_back(f);
417 }
418}
419//---------------------------------------------------------------------------
421 FOF f = *this;
422 type = FOFType::Neg;
423 pred.clear();
424 var = nullptr;
425 sub_formulas.clear();
426 sub_formulas.push_back(f);
427}
428//---------------------------------------------------------------------------
430 if (type == FOFType::Atom) {
432 return;
433 }
434 if (type == FOFType::Neg && sub_formulas[0].type == FOFType::Atom) {
436 return;
437 }
438 cerr << "Don't use this with a non-literal." << endl;
439}
440//---------------------------------------------------------------------------
442 if (type == FOFType::Empty) {
443 cerr << "Don't do this with an Empty FOF!" << endl;
444 return;
445 }
446 // End of recursion.
447 if (type == FOFType::Atom)
448 return;
449 // Do the relevant transformation.
450 if (type == FOFType::Iff) {
451 FOF lhs = *this;
452 FOF rhs = *this;
453 lhs.type = FOFType::Imp;
454 rhs.type = FOFType::Imp;
455 FOF store = rhs.sub_formulas[1];
456 rhs.sub_formulas[1] = rhs.sub_formulas[0];
457 rhs.sub_formulas[0] = store;
458 type = FOFType::And;
459 sub_formulas.clear();
460 sub_formulas.push_back(lhs);
461 sub_formulas.push_back(rhs);
462 }
463 // Finish the job. Also applies the transformation to other FOF types.
464 for (FOF& f : sub_formulas)
465 f.remove_iff();
466}
467//---------------------------------------------------------------------------
469 if (type == FOFType::Empty) {
470 cerr << "Don't do this with an Empty FOF!" << endl;
471 return;
472 }
473 // End of recursion.
474 if (type == FOFType::Atom)
475 return;
476 // Do the relevant transformation.
477 if (type == FOFType::Imp) {
478 type = FOFType::Or;
479 sub_formulas[0].negate();
480 }
481 // Finish the job. Also applies the transformation to other FOF types.
482 for (FOF& f : sub_formulas)
483 f.remove_imp();
484}
485//---------------------------------------------------------------------------
487 Variable* new_var;
488 switch (type) {
489 case FOFType::Atom:
490 break;
491 case FOFType::Neg:
492 case FOFType::And:
493 case FOFType::Or:
494 case FOFType::Imp:
495 case FOFType::Iff:
496 for (FOF& sf : sub_formulas)
497 sf.make_unique_bound_variables();
498 break;
499 case FOFType::A:
500 case FOFType::E:
501 sub_formulas[0].make_unique_bound_variables();
502 new_var = var_index->add_unique_var();
503 sub_formulas[0].replace_variable(new_var, var);
504 var = new_var;
505 break;
506 default:
507 break;
508 }
509}
510//---------------------------------------------------------------------------
512 Variable* new_var;
513 switch (type) {
514 case FOFType::Atom:
515 break;
516 case FOFType::Neg:
517 case FOFType::And:
518 case FOFType::Or:
519 case FOFType::Imp:
520 case FOFType::Iff:
521 for (FOF& sf : sub_formulas)
522 sf.remove_redundant_quantifiers();
523 break;
524 case FOFType::A:
525 case FOFType::E:
526 sub_formulas[0].remove_redundant_quantifiers();
528 vector<FOF> sf(sub_formulas);
529 type = sub_formulas[0].type;
530 pred = sub_formulas[0].pred;
531 var = sub_formulas[0].var;
532 sub_formulas = sf[0].sub_formulas;
533 }
534 break;
535 default:
536 break;
537 }
538}
539//---------------------------------------------------------------------------
541 switch (type) {
542 case FOFType::Neg:
543 switch (sub_formulas[0].type) {
544 case FOFType::Neg:
547 push_negs();
548 break;
549 case FOFType::Atom:
550 break;
551 case FOFType::And:
553 type = FOFType::Or;
554 for (FOF& f : sub_formulas) {
555 f.negate();
556 f.push_negs();
557 }
558 break;
559 case FOFType::Or:
561 type = FOFType::And;
562 for (FOF& f : sub_formulas) {
563 f.negate();
564 f.push_negs();
565 }
566 break;
567 case FOFType::A:
569 type = FOFType::E;
570 sub_formulas[0].negate();
571 sub_formulas[0].push_negs();
572 break;
573 case FOFType::E:
575 type = FOFType::A;
576 sub_formulas[0].negate();
577 sub_formulas[0].push_negs();
578 break;
579 case FOFType::Imp:
580 case FOFType::Iff:
581 cerr << "Stop it!!! You don't push negations through -> or <->." << endl;
582 break;
583 default:
584 break;
585 }
586 break;
587 case FOFType::Atom:
588 break;
589 case FOFType::And:
590 case FOFType::Or:
591 case FOFType::A:
592 case FOFType::E:
593 for (FOF& f : sub_formulas)
594 f.push_negs();
595 break;
596 case FOFType::Imp:
597 case FOFType::Iff:
598 cerr << "Stop it!!! You don't push negations through -> or <->." << endl;
599 break;
600 default:
601 break;
602 }
603}
604//---------------------------------------------------------------------------
606 if (type == FOFType::Empty) {
607 cerr << "Don't do this with an Empty FOF!" << endl;
608 return;
609 }
610 remove_iff();
611 remove_imp();
612 push_negs();
613}
614//---------------------------------------------------------------------------
616 vector<FOF> free;
617 vector<FOF> absent;
618 FOF new_sub(FOFType::Empty);
619 vector<FOF> new_subs;
620 Variable* new_var;
621 switch (type) {
622 case FOFType::Empty:
623 cerr << "Don't do this with an empty formula" << endl;
624 break;
625 case FOFType::Atom:
626 case FOFType::Neg:
627 break;
628 case FOFType::And:
629 case FOFType::Or:
631 break;
632 case FOFType::Imp:
633 case FOFType::Iff:
634 cerr << "Don't do this unless you've removed -> and <->" << endl;
635 break;
636 // Remember you're only dealing with stuff in NNF. You
637 // do however have to handle nested quantifiers.
638 case FOFType::A:
639 sub_formulas[0].miniscope();
640 if (!(sub_formulas[0].type == FOFType::And ||
641 sub_formulas[0].type == FOFType::Or)) {
642 return;
643 }
644 // You have \forall followed by (miniscoped) AND or OR.
645 miniscope_split(var, free, absent);
646 // If the quantifier is followed by AND you can just
647 // make a \forall for every subformula involving the
648 // variable.
649 if (sub_formulas[0].type == FOFType::And) {
650 type = FOFType::And;
651 // If everything is bound then make a new forall everywhere
652 // with a new variable.
653 if (free.size() == sub_formulas[0].sub_formulas.size()) {
654 for (size_t i = 0; i < free.size(); i++) {
655 new_var = var_index->add_unique_var();
656 new_sub = free[i];
657 new_sub.replace_variable(new_var, var);
658 new_sub = FOF::make_forall(new_sub, new_var);
659 new_subs.push_back(new_sub);
660 }
661 sub_formulas = new_subs;
662 }
663 // If any subformula doesn't have the variable then just
664 // move the forall in. Remember you need to consider the
665 // possibility that the quantifier can just be removed.
666 else if (free.size() > 0) {
667 sub_formulas = absent;
668 if (free.size() == 1)
669 new_sub = free[0];
670 else
671 new_sub = FOF::make_and(free);
672 new_sub = FOF::make_forall(new_sub, var);
673 sub_formulas.push_back(new_sub);
674 }
675 // You're just going to remove the quantifier.
676 else {
677 sub_formulas = sub_formulas[0].sub_formulas;
678 }
679 var = nullptr;
681 return;
682 }
683 if (sub_formulas[0].type == FOFType::Or) {
684 // You have a \forall followed by an OR.
685 // If eveything is bound there's nothing to do.
686 if (absent.size() == 0)
687 return;
688 else if (free.size() > 0) {
689 type = FOFType::Or;
690 sub_formulas = absent;
691 if (free.size() == 1)
692 new_sub = free[0];
693 else
694 new_sub = FOF::make_or(free);
695 new_sub = FOF::make_forall(new_sub, var);
696 sub_formulas.push_back(new_sub);
697 var = nullptr;
699 }
700 // You can just drop the quantifier.
701 else {
702 type = FOFType::Or;
703 vector<FOF> sf(sub_formulas);
704 sub_formulas = sf[0].sub_formulas;
705 var = nullptr;
706 }
707 return;
708 }
709 break;
710 // You have an \exists.
711 case FOFType::E:
712 sub_formulas[0].miniscope();
713 if (!(sub_formulas[0].type == FOFType::And ||
714 sub_formulas[0].type == FOFType::Or)) {
715 return;
716 }
717 // You have an \exists followed by AND or OR.
718 miniscope_split(var, free, absent);
719 if (sub_formulas[0].type == FOFType::Or) {
720 type = FOFType::Or;
721 // If everything is bound then make a new exists everywhere
722 // with a new variable.
723 if (free.size() == sub_formulas[0].sub_formulas.size()) {
724 for (size_t i = 0; i < free.size(); i++) {
725 new_var = var_index->add_unique_var();
726 new_sub = free[i];
727 new_sub.replace_variable(new_var, var);
728 new_sub = FOF::make_exists(new_sub, new_var);
729 new_subs.push_back(new_sub);
730 }
731 sub_formulas = new_subs;
732 }
733 // If any subformula doesn't have the variable then just
734 // move the \exists in.
735 else if (free.size() > 0) {
736 sub_formulas = absent;
737 if (free.size() == 1)
738 new_sub = free[0];
739 else
740 new_sub = FOF::make_or(free);
741 new_sub = FOF::make_exists(new_sub, var);
742 sub_formulas.push_back(new_sub);
743 }
744 // We're just going to drop the quantifier.
745 else {
746 sub_formulas = sub_formulas[0].sub_formulas;
747 }
748 var = nullptr;
750 return;
751 }
752 // You have an \exists followed by and AND.
753 if (sub_formulas[0].type == FOFType::And) {
754 // If eveything is bound there's nothing to do.
755 if (absent.size() == 0)
756 return;
757 else if (free.size() != sub_formulas[0].sub_formulas.size()) {
758 type = FOFType::And;
759 sub_formulas = absent;
760 if (free.size() == 1)
761 new_sub = free[0];
762 else
763 new_sub = FOF::make_and(free);
764 new_sub = FOF::make_exists(new_sub, var);
765 sub_formulas.push_back(new_sub);
766 var = nullptr;
768 }
769 // We can just drop the quantifier.
770 else {
771 type = FOFType::And;
772 vector<FOF> sf(sub_formulas);
773 sub_formulas = sf[0].sub_formulas;
774 var = nullptr;
775 }
776 return;
777 }
778 break;
779 default:
780 break;
781 }
782}
783//---------------------------------------------------------------------------
784void FOF::convert_to_cnf_clauses(vector<Clause>& cs) {
785 if (type == FOFType::Empty) {
786 cerr << "Don't do this with an Empty FOF!" << endl;
787 return;
788 }
789#ifdef DEBUGOUTPUT
790 cout << "Remove iff:" << endl;
791#endif
792 remove_iff();
793#ifdef DEBUGOUTPUT
794 cout << *this << endl;
795 cout << "Remove imp" << endl;
796#endif
797 remove_imp();
798#ifdef DEBUGOUTPUT
799 cout << *this << endl;
800 cout << "Push negs:" << endl;
801#endif
802 push_negs();
803#ifdef DEBUGOUTPUT
804 cout << *this << endl;
805 cout << "Make unique vars:" << endl;
806#endif
808#ifdef DEBUGOUTPUT
809 cout << *this << endl;
810 cout << "Remove redundant quantifiers:" << endl;
811#endif
813#ifdef DEBUGOUTPUT
814 cout << *this << endl;
815 cout << "Miniscope:" << endl;
816#endif
817 if (params::miniscope) {
818 miniscope();
819 }
820#ifdef DEBUGOUTPUT
821 cout << *this << endl;
822 cout << "Skolemize:" << endl;
823#endif
824 skolemize();
825#ifdef DEBUGOUTPUT
826 cout << *this << endl;
827 cout << "Remove universal quantifiers:" << endl;
828#endif
830#ifdef DEBUGOUTPUT
831 cout << *this << endl;
832 cout << "Distribute or:" << endl;
833#endif
835#ifdef DEBUGOUTPUT
836 cout << *this << endl;
837 cout << "Convert to clauses:" << endl;
838#endif
840#ifdef DEBUGOUTPUT
841 cout << *this << endl;
842#endif
843}
844//---------------------------------------------------------------------------
845void FOF::split_sub_formulas(vector<FOF>& left_sub, vector<FOF>& right_sub) {
846 left_sub.clear();
847 right_sub.clear();
848 size_t s = sub_formulas.size();
849 if (s == 2) {
850 left_sub.push_back(sub_formulas[0]);
851 right_sub.push_back(sub_formulas[1]);
852 return;
853 }
854 if (s == 3) {
855 right_sub.push_back(sub_formulas[2]);
856 left_sub = sub_formulas;
857 left_sub.pop_back();
858 return;
859 }
860 size_t left_size = s / 2;
861 for (size_t i = 0; i < left_size; i++) {
862 left_sub.push_back(sub_formulas[i]);
863 }
864 for (size_t i = left_size; i < s; i++) {
865 right_sub.push_back(sub_formulas[i]);
866 }
867}
868//---------------------------------------------------------------------------
869void FOF::find_definitional_tuple(FOF& f, vector<FOF>& d, bool in_conjunction) {
870 f.clear();
871 d.clear();
872 // Literals are straightforward.
873 if (is_literal()) {
874 f = *this;
875 vector<FOF> new_d;
876 d = new_d;
877 return;
878 }
879 // You must have an And or an Or because we're in NNF.
880 // Let's split half and half, just for the sake of symmetry.
881 vector<FOF> left;
882 vector<FOF> right;
883 split_sub_formulas(left, right);
884 FOF l = left[0];
885 FOF r = right[0];
886 if (left.size() > 1) {
887 l = FOF(type, left, nullptr);
888 }
889 if (right.size() > 1) {
890 r = FOF(type, right, nullptr);
891 }
892 // Somewhere to store results.
893 FOF new_f_1(FOFType::Empty);
894 FOF new_f_2(FOFType::Empty);
895 vector<FOF> new_d_1;
896 vector<FOF> new_d_2;
897 // The tricky case is an Or inside an And.
898 if (in_conjunction && type == FOFType::Or) {
899 l.find_definitional_tuple(new_f_1, new_d_1, false);
900 r.find_definitional_tuple(new_f_2, new_d_2, false);
902 f = def;
903 def.simple_negate();
904 vector<FOF> args1;
905 args1.push_back(def);
906 args1.push_back(new_f_1);
907 d.push_back(FOF::make_and(args1));
908 vector<FOF> args2;
909 args2.push_back(def);
910 args2.push_back(new_f_2);
911 d.push_back(FOF::make_and(args2));
912 for (const FOF& g : new_d_1) {
913 d.push_back(g);
914 }
915 for (const FOF& g : new_d_2) {
916 d.push_back(g);
917 }
918 return;
919 }
920 // The final case is straightforward.
921 bool is_and(type == FOFType::And);
922 l.find_definitional_tuple(new_f_1, new_d_1, is_and);
923 r.find_definitional_tuple(new_f_2, new_d_2, is_and);
924 vector<FOF> new_args;
925 new_args.push_back(new_f_1);
926 new_args.push_back(new_f_2);
927 FOF new_f(type, new_args, nullptr);
928 f = new_f;
929 d = new_d_1;
930 for (const FOF& g : new_d_2){
931 d.push_back(g);
932 }
933}
934//---------------------------------------------------------------------------
936 if (type == FOFType::Empty) {
937 cerr << "Don't do this with an Empty FOF!" << endl;
938 return;
939 }
940 /*
941 * Note that the description in the restricted backtracking paper produces a
942 * DNF and starts with a Skolemized NNF of the original formula, *not* its
943 * negation. As the difference between this and the current representation, for
944 * connection calculus is just that all the literals get negated, this could
945 * be done more efficiently, but for now I'm going to just do a direct
946 * implementation of the procedure described in the paper. So: negate
947 * first, produce a DNF, then negate again.
948 */
950 remove_iff();
951 remove_imp();
952 push_negs();
955 /*
956 * As we're producing a DNF, we skolemize universals and then remove existentials.
957 */
960 /*
961 * Now we do the definitional magic.
962 */
963 FOF f(FOFType::Empty);
964 vector<FOF> d;
965 find_definitional_tuple(f, d, false);
966 /*
967 * f is a DNF so we can invert it and flatten it to
968 * the required CNF.
969 */
971 /*
972 * Each element of d now needs to be converted using
973 * a standard conversion, then inverted and transformed
974 * to CNF.
975 */
976 for (FOF& formula : d) {
977 vector<Clause> new_cs;
978 formula.distribute_and();
979 formula.dnf_invert_and_convert_to_clauses(new_cs);
980 for (const Clause& new_c : new_cs) {
981 cs.push_back(new_c);
982 }
983 }
984}
985//---------------------------------------------------------------------------
987 vector<Term*> skolem_arguments;
988 skolemize_main(skolem_arguments);
989}
990//---------------------------------------------------------------------------
992 vector<Term*> skolem_arguments;
993 skolemize_universals_main(skolem_arguments);
994}
995//---------------------------------------------------------------------------
997 FOF f(FOFType::Atom);
998 switch (type) {
999 case FOFType::Atom:
1000 break;
1001 case FOFType::Neg:
1002 sub_formulas[0].remove_universal_quantifiers();
1003 break;
1004 case FOFType::And:
1005 case FOFType::Or:
1006 case FOFType::Imp:
1007 case FOFType::Iff:
1008 for (FOF& g : sub_formulas)
1009 g.remove_universal_quantifiers();
1010 break;
1011 case FOFType::A:
1012 sub_formulas[0].remove_universal_quantifiers();
1013 f = sub_formulas[0];
1014 *this = f;
1015 break;
1016 case FOFType::E:
1017 sub_formulas[0].remove_universal_quantifiers();
1018 break;
1019 default:
1020 break;
1021 }
1022}
1023//---------------------------------------------------------------------------
1025 FOF f(FOFType::Atom);
1026 switch (type) {
1027 case FOFType::Atom:
1028 break;
1029 case FOFType::Neg:
1030 sub_formulas[0].remove_existential_quantifiers();
1031 break;
1032 case FOFType::And:
1033 case FOFType::Or:
1034 case FOFType::Imp:
1035 case FOFType::Iff:
1036 for (FOF& g : sub_formulas)
1037 g.remove_existential_quantifiers();
1038 break;
1039 case FOFType::E:
1040 sub_formulas[0].remove_existential_quantifiers();
1041 f = sub_formulas[0];
1042 *this = f;
1043 break;
1044 case FOFType::A:
1045 sub_formulas[0].remove_existential_quantifiers();
1046 break;
1047 default:
1048 break;
1049 }
1050}
1051//---------------------------------------------------------------------------
1052void FOF::to_Literal(Literal& new_lit) const {
1053 if (type == FOFType::Atom) {
1054 new_lit = pred;
1055 }
1056 else {
1057 new_lit = sub_formulas[0].pred;
1058 new_lit.make_negative();
1059 }
1060}
1061//---------------------------------------------------------------------------
1062void FOF::convert_to_clauses(vector<Clause>& cs) const {
1063 cs.clear();
1064 if (is_literal()) {
1065 Literal new_lit;
1066 to_Literal(new_lit);
1067 Clause new_c;
1068 new_c.add_lit(new_lit);
1069 cs.push_back(new_c);
1070 return;
1071 }
1072 if (type == FOFType::Or) {
1073 vector<Clause> c;
1074 Clause new_c;
1075 cs.push_back(new_c);
1076 for (size_t i = 0; i < sub_formulas.size(); i++) {
1077 sub_formulas[i].convert_to_clauses(c);
1078 for (size_t j = 0; j < c[0].size(); j++) {
1079 cs[0].add_lit((c[0])[j]);
1080 }
1081 }
1082 return;
1083 }
1084 if (type == FOFType::And) {
1085 vector<Clause> c;
1086 for (size_t i = 0; i < sub_formulas.size(); i++) {
1087 sub_formulas[i].convert_to_clauses(c);
1088 for (size_t j = 0; j < c.size();j++) {
1089 cs.push_back(c[j]);
1090 }
1091 }
1092 }
1093}
1094//---------------------------------------------------------------------------
1095void FOF::dnf_invert_and_convert_to_clauses(vector<Clause>& cs) const {
1096 cs.clear();
1097 if (is_literal()) {
1098 Literal new_lit;
1099 to_Literal(new_lit);
1100 // This is where the inversion happens.
1101 new_lit.invert();
1102 Clause new_c;
1103 new_c.add_lit(new_lit);
1104 cs.push_back(new_c);
1105 return;
1106 }
1107 // Anything under here is essentially ANDed literals.
1108 if (type == FOFType::And) {
1109 vector<Clause> c;
1110 Clause new_c;
1111 cs.push_back(new_c);
1112 for (size_t i = 0; i < sub_formulas.size(); i++) {
1113 sub_formulas[i].dnf_invert_and_convert_to_clauses(c);
1114 for (size_t j = 0; j < c[0].size(); j++) {
1115 cs[0].add_lit((c[0])[j]);
1116 }
1117 }
1118 return;
1119 }
1120 // Anything below here essentially converts to another DNF.
1121 if (type == FOFType::Or) {
1122 vector<Clause> c;
1123 for (size_t i = 0; i < sub_formulas.size(); i++) {
1124 sub_formulas[i].dnf_invert_and_convert_to_clauses(c);
1125 for (size_t j = 0; j < c.size();j++) {
1126 cs.push_back(c[j]);
1127 }
1128 }
1129 }
1130}
1131//---------------------------------------------------------------------------
1132size_t FOF::find_and() const {
1133 size_t s = sub_formulas.size();
1134 for (size_t i = 0; i < s; i++) {
1135 if (sub_formulas[i].type == FOFType::And) {
1136 return i;
1137 }
1138 }
1139 return s;
1140}
1141//---------------------------------------------------------------------------
1142size_t FOF::find_or() const {
1143 size_t s = sub_formulas.size();
1144 for (size_t i = 0; i < s; i++) {
1145 if (sub_formulas[i].type == FOFType::Or) {
1146 return i;
1147 }
1148 }
1149 return s;
1150}
1151//---------------------------------------------------------------------------
1153 while (distribute_or_once()) {};
1154}
1155//---------------------------------------------------------------------------
1157 while (distribute_and_once()) {};
1158}
1159//---------------------------------------------------------------------------
1160string FOF::to_string() const {
1161 string result;
1162 size_t s = sub_formulas.size();;
1163 size_t i = 1;
1164 switch (type) {
1165 case FOFType::Atom:
1166 result = pred.to_string();
1167 break;
1168 case FOFType::Neg:
1169 result = unicode_symbols::LogSym::neg;
1170 result += "( ";
1171 result += sub_formulas[0].to_string();
1172 result += " )";
1173 break;
1174 case FOFType::And:
1175 result = "( ";
1176 for (const FOF& g : sub_formulas) {
1177 result += g.to_string();
1178 if (i < s) {
1179 result += " ";
1180 result += unicode_symbols::LogSym::and_sym;
1181 result += " ";
1182 }
1183 i++;
1184 }
1185 result += " )";
1186 break;
1187 case FOFType::Or:
1188 result = "( ";
1189 for (const FOF& g : sub_formulas) {
1190 result += g.to_string();
1191 if (i < s) {
1192 result += " ";
1193 result += unicode_symbols::LogSym::or_sym;
1194 result += " ";
1195 }
1196 i++;
1197 }
1198 result += " )";
1199 break;
1200 case FOFType::Imp:
1201 result = "( ";
1202 result += sub_formulas[0].to_string();
1203 result += " ";
1204 result += unicode_symbols::LogSym::ifthen;
1205 result += " ";
1206 result += sub_formulas[1].to_string();
1207 result += " )";
1208 break;
1209 case FOFType::Iff:
1210 result = "( ";
1211 result += sub_formulas[0].to_string();
1212 result += " ";
1213 result += unicode_symbols::LogSym::iff;
1214 result += " ";
1215 result += sub_formulas[1].to_string();
1216 result += " )";
1217 break;
1218 case FOFType::A:
1219 result = unicode_symbols::LogSym::forall;
1220 result += var->to_string();
1221 result += " . [ ";
1222 result += sub_formulas[0].to_string();
1223 result += " ]";
1224 break;
1225 case FOFType::E:
1226 result = unicode_symbols::LogSym::exists;
1227 result += var->to_string();
1228 result += " . [ ";
1229 result += sub_formulas[0].to_string();
1230 result += " ]";
1231 break;
1232 default:
1233 break;
1234 }
1235 return result;
1236}
1237//---------------------------------------------------------------------------
1238SimplificationResult FOF::simplify_cnf(vector<Clause>& cnf,
1239 vector<string>& roles) {
1240 vector<Clause> new_cnf;
1241 vector<string> new_roles;
1242 bool cnf_is_contradiction = false;
1243 size_t i = 0;
1244 for (Clause& c : cnf) {
1245 SimplificationResult result = c.simplify();
1246 if (result == SimplificationResult::Tautology) {
1247 i++;
1248 continue;
1249 }
1250 if (result == SimplificationResult::Contradiction) {
1251 i++;
1252 cnf_is_contradiction = true;
1253 continue;
1254 }
1255 new_cnf.push_back(c);
1256 new_roles.push_back(roles[i]);
1257 i++;
1258 }
1259 cnf = new_cnf;
1260 roles = new_roles;
1261 if (cnf_is_contradiction)
1262 return SimplificationResult::Contradiction;
1263 if (cnf.size() == 0)
1264 return SimplificationResult::Tautology;
1265 return SimplificationResult::OK;
1266}
1267//---------------------------------------------------------------------------
1268SimplificationResult FOF::simplify_cnf(vector<Clause>& cnf) {
1269 vector<Clause> new_cnf;
1270 bool cnf_is_contradiction = false;
1271 for (Clause& c : cnf) {
1272 SimplificationResult result = c.simplify();
1273 if (result == SimplificationResult::Tautology)
1274 continue;
1275 if (result == SimplificationResult::Contradiction) {
1276 cnf_is_contradiction = true;
1277 continue;
1278 }
1279 new_cnf.push_back(c);
1280 }
1281 cnf = new_cnf;
1282 if (cnf_is_contradiction)
1283 return SimplificationResult::Contradiction;
1284 if (cnf.size() == 0)
1285 return SimplificationResult::Tautology;
1286 return SimplificationResult::OK;
1287}
1288//---------------------------------------------------------------------------
1289set<Term*> FOF::find_free_variables() const {
1290 set<Term*> result;
1291 set<Term*> free;
1292 Term* to_remove;
1293 switch (type) {
1294 case FOFType::Empty:
1295 cerr << "You shouldn't be doing this with an FOFType::Empty!" << endl;
1296 break;
1297 case FOFType::Atom:
1298 for (const Term* p : pred.get_args()) {
1299 free = p->all_variables();
1300 result.insert(free.begin(), free.end());
1301 }
1302 break;
1303 case FOFType::Neg:
1304 case FOFType::And:
1305 case FOFType::Or:
1306 case FOFType::Imp:
1307 case FOFType::Iff:
1308 for (const FOF& f : sub_formulas) {
1309 free = f.find_free_variables();
1310 result.insert(free.begin(), free.end());
1311 }
1312 break;
1313 case FOFType::A:
1314 case FOFType::E:
1315 result = sub_formulas[0].find_free_variables();
1316 to_remove = term_index->add_variable_term(var);
1317 result.erase(to_remove);
1318 break;
1319 default:
1320 break;
1321 }
1322 return result;
1323}
1324//---------------------------------------------------------------------------
1325ostream& operator<<(ostream& out, const FOF& f) {
1326 out << f.to_string();
1327 return out;
1328}
Representation of clauses.
Definition Clause.hpp:52
void add_lit(const Literal &)
Add a literal, making sure you don't duplicate.
Definition Clause.cpp:96
Representation of first order formulas.
Definition FOF.hpp:57
void skolemize()
Skolemize the given formula.
Definition FOF.cpp:986
void remove_iff()
Replace <-> throughout using ->.
Definition FOF.cpp:441
static FOF make_or(const vector< FOF > &fs)
Directly make a disjunction.
Definition FOF.hpp:302
void miniscope_all()
Apply miniscoping to all subformulas.
Definition FOF.cpp:306
size_t find_or() const
Does the collection of subformulas contain an OR? If so, find it.
Definition FOF.cpp:1142
static VariableIndex * var_index
Access to the index: static because all FOFs should share it.
Definition FOF.hpp:80
void convert_to_nnf()
Convert to Negation Normal Form.
Definition FOF.cpp:605
void clear()
Make an FOFType::Empty.
Definition FOF.hpp:265
void dnf_invert_and_convert_to_clauses(vector< Clause > &) const
Assuming you have a DNF, invert it and convert it to a collection of CNF clauses.
Definition FOF.cpp:1095
set< Term * > find_free_variables() const
Find the free variables in the formula.
Definition FOF.cpp:1289
void convert_to_cnf_clauses(vector< Clause > &)
Convert to Conjunctive Normal Form.
Definition FOF.cpp:784
void remove_redundant_quantifiers()
Remove quantifiers if they don't bind anything in their scope.
Definition FOF.cpp:511
void miniscope_split(Variable *, vector< FOF > &, vector< FOF > &)
Helper for miniscope.
Definition FOF.cpp:290
void split_sub_formulas(vector< FOF > &, vector< FOF > &)
Split subformulas in half.
Definition FOF.cpp:845
void make_unique_bound_variables()
Replace all bound variables with unique new ones.
Definition FOF.cpp:486
static SimplificationResult simplify_cnf(vector< Clause > &)
Simplify the clauses in a CNF using the method in Clause.
Definition FOF.cpp:1268
void skolemize_main(vector< Term * >)
Main helper function called by FOF::skolemize().
Definition FOF.cpp:224
void remove_universal_quantifiers()
Self-explanatory.
Definition FOF.cpp:996
Variable * var
Associate a Variable with a quantifier.
Definition FOF.hpp:75
bool distribute_or_once()
Find an OR that can be distributed and deal with it. Indicate if there isn't one.
Definition FOF.cpp:96
FOFType type
Used for all FOFs to denote what kind it it.
Definition FOF.hpp:62
static FOF make_and(const vector< FOF > &fs)
Directly make a conjunction.
Definition FOF.hpp:295
void miniscope()
Apply the rules for miniscoping.
Definition FOF.cpp:615
void push_negs()
Push all negations in.
Definition FOF.cpp:540
bool distribute_and_once()
Find an AND that can be distributed and deal with it. Indicate if there isn't one.
Definition FOF.cpp:149
bool has_free_variable(Variable *)
Does this formula contain the specified variable unbound?
Definition FOF.cpp:35
void definitional_convert_to_cnf_clauses(vector< Clause > &)
Convert to Conjunctive Normal Form using a form of definitional conversion.
Definition FOF.cpp:935
void skolemize_universals()
Skolemize the universal quantifiers in the given formula.
Definition FOF.cpp:991
void replace_variable(Variable *, Variable *)
Self-explanatory.
Definition FOF.cpp:72
vector< FOF > sub_formulas
Most FOFs will have subformulas.
Definition FOF.hpp:71
void to_Literal(Literal &) const
Assuming the FOF is a literal, convert it to something of type Literal.
Definition FOF.cpp:1052
FOF make_definitional_predicate() const
When doing definitional clause conversion you need to be able to make unique new predicates....
Definition FOF.cpp:86
static FOF make_exists(const FOF &f, Variable *v)
Directly make an existentially quantified FOF.
Definition FOF.hpp:338
static TermIndex * term_index
Access to the index: static because all FOFs should share it.
Definition FOF.hpp:90
void remove_negation()
Remove the leading negation from an arbitrary FOF, if it has one.
Definition FOF.cpp:391
static PredicateIndex * pred_index
Access to the index: static because all FOFs should share it.
Definition FOF.hpp:95
static FOF make_forall(const FOF &f, Variable *v)
Directly make a universally quantified FOF.
Definition FOF.hpp:329
void convert_to_clauses(vector< Clause > &) const
Assuming you have a CNF, convert it to a collection of Clauses.
Definition FOF.cpp:1062
void skolemize_universals_main(vector< Term * >)
The dual of skolemize_main.
Definition FOF.cpp:256
void remove_imp()
Replace A->B throughout with neg A v B.
Definition FOF.cpp:468
size_t find_and() const
Does the collection of subformulas contain an AND? If so, find it.
Definition FOF.cpp:1132
void find_definitional_tuple(FOF &, vector< FOF > &, bool)
Heavy-lifting for conversion to definitional clause form.
Definition FOF.cpp:869
static FunctionIndex * fun_index
Access to the index: static because all FOFs should share it.
Definition FOF.hpp:85
FOF()=delete
You don't want this constructor.
void distribute_and()
Distribute ANDs to the maximum extent possible.
Definition FOF.cpp:1156
Term * make_skolem_function(const vector< Term * > &)
Make a new Skolem function.
Definition FOF.cpp:204
void distribute_or()
Distribute ORs to the maximum extent possible.
Definition FOF.cpp:1152
void simple_negate()
Negate an FOF without applying any simplifications.
Definition FOF.cpp:420
bool is_literal() const
Check if an FOF is a literal.
Definition FOF.cpp:369
string to_string() const
Convert a FOF into a nice-looking string.
Definition FOF.cpp:1160
void negate()
Negate an FOF, applying obvious simplifications if it's already negated or an implication.
Definition FOF.cpp:398
void remove_existential_quantifiers()
Self-explanatory.
Definition FOF.cpp:1024
void invert_literal()
Self-explanatory!
Definition FOF.cpp:429
bool is_clause() const
Check if something is a clause.
Definition FOF.cpp:374
Literal pred
FOFType::Atom can store one of these to represent a Literal.
Definition FOF.hpp:67
void replace_variable_with_term(Term *, Variable *)
Replace a Variable with a Term.
Definition FOF.cpp:210
Basic representation of functions.
Definition Function.hpp:54
Mechanism for looking after functions.
Function * make_skolem_function(Arity)
Add a new Skolem function. Naming is automatic.
Basic representation of literals, bundling together (pointers to) a Predicate, a collection of argume...
Definition Literal.hpp:50
void make_positive()
Basic manipulation - entirely self-explanatory.
Definition Literal.hpp:99
void replace_variable_with_term(Term *, Variable *, TermIndex *)
Replace one variable with a term throughout.
Definition Literal.cpp:264
const vector< Term * > & get_args() const
Basic get method.
Definition Literal.hpp:83
string to_string(bool=false) const
Full conversion of Literal to string.
Definition Literal.cpp:166
bool contains_variable(Variable *) const
Does this Literal contain the specified variable?
Definition Literal.cpp:121
void replace_variable(Variable *, Variable *, TermIndex *)
Replace one variable with another throughout.
Definition Literal.cpp:253
bool is_positive() const
Basic manipulation - entirely self-explanatory.
Definition Literal.hpp:91
void invert()
Basic manipulation - entirely self-explanatory.
Definition Literal.hpp:107
void clear()
Basic manipulation - entirely self-explanatory.
Definition Literal.hpp:119
void make_negative()
Basic manipulation - entirely self-explanatory.
Definition Literal.hpp:103
Basic representation of predicates: here just names, ids and arities.
Definition Predicate.hpp:51
Management of Predicate objects.
Predicate * make_definitional_predicate(Arity arity)
Make a new, unique definitional predicate.
General representation of terms.
Definition Term.hpp:62
Look after terms, using hash consing to avoid storing copies of terms.
Definition TermIndex.hpp:54
Term * add_function_term(Function *, const vector< Term * > &)
Self-explanatory: add a Term containing a function to the index.
Definition TermIndex.cpp:56
Term * add_variable_term(Variable *)
Self-explanatory: add a Term containing a variable to the index.
Definition TermIndex.cpp:41
Basic representation of variables.
Definition Variable.hpp:58
string to_string(bool=false) const
Convert to a (coloured) string.
Definition Term.cpp:319
Storage of named variables, and management of new, anonymous and unique variables.
Variable * add_unique_var()
Add a unique variable when converting to CNF.