Connect++ 0.6.0
A fast, readable connection prover for first-order logic.
Loading...
Searching...
No Matches
Unifier.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 "Unifier.hpp"
26
27UnificationOutcome Unifier::operator()(Term* term1, Term* term2) {
28 if (params::poly_unification) {
29 to_do.clear();
30 to_do.push_back(UPair(term1, term2));
31 return complete_unification();
32 }
33 else {
34 return unify_terms(term1, term2);
35 }
36}
37//----------------------------------------------------------------------
38UnificationOutcome Unifier::unify(Term* t1, Term* t2) {
39 UnificationOutcome outcome = unify_terms(t1, t2);
40 if (outcome != UnificationOutcome::Succeed) {
41 backtrack();
42 }
43 return outcome;
44}
45//----------------------------------------------------------------------
46// Figure 1, page 454, Handbook of Automated Reasoning Volume 1.
47//----------------------------------------------------------------------
48UnificationOutcome Unifier::unify_terms(Term* t1, Term* t2) {
49 bool t1_is_var;
50 Variable* t1v;
51 Function* t1f;
52 size_t t1a;
53 t1 = t1->skip_leading_variables_for_unification(t1_is_var, t1v, t1f, t1a);
54
55 bool t2_is_var;
56 Variable* t2v;
57 Function* t2f;
58 size_t t2a;
59 t2 = t2->skip_leading_variables_for_unification(t2_is_var, t2v, t2f, t2a);
60
61 if (t1_is_var && t1v == t2v) {}
62 else if (!t1_is_var && !t2_is_var) {
63 if (t1f == t2f && t1a == t2a) {
64 for (size_t i = 0; i < t1a; i++) {
65 UnificationOutcome outcome = unify_terms((*t1)[i], (*t2)[i]);
66 if (outcome != UnificationOutcome::Succeed) {
67 return outcome;
68 }
69 }
70 }
71 else {
72 return UnificationOutcome::ConflictFail;
73 }
74 }
75 else if (!t1_is_var) {
76 return unify_terms(t2, t1);
77 }
78 else if (t2->contains_variable(t1v)) {
79 return UnificationOutcome::OccursFail;
80 }
81 else {
82 s.push_back(t1v, t2);
83 t1v->substitute(t2);
84 }
85 return UnificationOutcome::Succeed;
86}
87//----------------------------------------------------------------------
88UnificationOutcome Unifier::operator()(const vector<Term*>& t1s,
89 const vector<Term*>& t2s) {
90 if (t1s.size() != t2s.size())
91 return UnificationOutcome::ConflictFail;
92 if (params::poly_unification) {
93 to_do.clear();
94 auto i = t2s.begin();
95 for (Term* term1 : t1s) {
96 to_do.push_back(UPair(term1, *i));
97 i++;
98 }
99 return complete_unification();
100 }
101 else {
102 auto i = t2s.begin();
103 for (Term* term1 : t1s) {
104 UnificationOutcome outcome = unify_terms(term1, *i);
105 if (outcome != UnificationOutcome::Succeed) {
106 backtrack();
107 return outcome;
108 }
109 i++;
110 }
111 return UnificationOutcome::Succeed;
112 }
113}
114//----------------------------------------------------------------------
115UnificationOutcome Unifier::operator()(Literal* l1, Literal* l2) {
116 if (!l1->is_compatible_with(l2)) {
117 cerr << "ERROR" << ": ";
118 cerr << "You're trying to unify non-compatible literals." << endl;
119 cerr << "ERROR" << ": " << *l1 << endl;
120 cerr << "ERROR" << ": " << *l2 << endl;
121 }
122 const vector<Term*>& args1 = l1->get_args();
123 const vector<Term*>& args2 = l2->get_args();
124 return operator()(args1, args2);
125}
126//----------------------------------------------------------------------
127UnificationOutcome Unifier::complete_unification() {
128 s.clear();
129 while (to_do.size() > 0) {
130 // Get the next thing from the queue and split it up.
131 UPair upair(to_do.back());
132 Term* t1(upair.first);
133 Term* t2(upair.second);
134 to_do.pop_back();
135
136 bool t1_is_var;
137 Variable* t1v;
138 Function* t1f;
139 size_t t1a;
140 t1 = t1->skip_leading_variables_for_unification(t1_is_var, t1v, t1f, t1a);
141
142 bool t2_is_var;
143 Variable* t2v;
144 Function* t2f;
145 size_t t2a;
146 t2 = t2->skip_leading_variables_for_unification(t2_is_var, t2v, t2f, t2a);
147
148 // Swap
149 if (!t1_is_var && t2_is_var) {
150 to_do.push_back(UPair(t2, t1));
151 continue;
152 }
153
154 // Delete
155 if (t1->subbed_equal(t2)) {
156 continue;
157 }
158
159 // Decompose/Conflict
160 if (!t1_is_var && !t2_is_var) {
161 // Conflict
162 if ((t1f != t2f) ||
163 (t1a != t2a)) {
164 backtrack();
165 return UnificationOutcome::ConflictFail;
166 }
167 // Decompose
168 else {
169 size_t n_args = t1->arity();
170 for (size_t i = 0; i < n_args; i++) {
171 to_do.push_back(UPair((*t1)[i], (*t2)[i]));
172 }
173 continue;
174 }
175 }
176
177 bool contains = t2->contains_variable(t1v);
178
179 // Eliminate
180 if (t1_is_var && !contains) {
181 s.push_back(t1v, t2);
182 t1v->substitute(t2);
183 continue;
184 }
185
186 // Occurs
187 if (t1_is_var && !t2_is_var && contains) {
188 backtrack();
189 return UnificationOutcome::OccursFail;
190 }
191 }
192 return UnificationOutcome::Succeed;
193}
194//----------------------------------------------------------------------
196 s.backtrack();
197 s.clear();
198}
199//----------------------------------------------------------------------
200ostream& operator<<(ostream& out, const UnificationOutcome& o) {
201 switch(o) {
202 case UnificationOutcome::Succeed:
203 out << "Unification: success.";
204 break;
205 case UnificationOutcome::ConflictFail:
206 out << "Unification: failed due to conflict.";
207 break;
208 case UnificationOutcome::OccursFail:
209 out << "Unification: failed due to occurs check.";
210 break;
211 default:
212
213 break;
214 }
215 return out;
216}
217//----------------------------------------------------------------------
218ostream& operator<<(ostream& out, const Unifier& u) {
219 out << u.to_string();
220 return out;
221}
Basic representation of functions.
Definition Function.hpp:54
Basic representation of literals, bundling together (pointers to) a Predicate, a collection of argume...
Definition Literal.hpp:50
const vector< Term * > & get_args() const
Basic get method.
Definition Literal.hpp:83
bool is_compatible_with(const Literal *) const
Literals can only be unified if the polarity and actual predicate are the same.
Definition Literal.cpp:74
void backtrack() const
Remove a substitution everywhere.
void push_back(Variable *, Term *)
Add a pair to a substitution.
void clear()
Clear a substitution.
General representation of terms.
Definition Term.hpp:62
bool subbed_equal(Term *) const
Equality test, taking substitution into account.
Definition Term.cpp:84
Term * skip_leading_variables_for_unification(bool &, Variable *&, Function *&, size_t &) const
It may be that there is a chain of substitutions attached to a variable.
Definition Term.cpp:255
bool contains_variable(Variable *) const
Taking substitution into account, does the term include the variable passed?
Definition Term.cpp:143
Arity arity() const
Self-explanatory access function.
Definition Term.hpp:102
Wrap up various applications of unificiation into a single class: all the unification you need to do ...
Definition Unifier.hpp:89
void backtrack()
Apply the backtracking process to the substitution that has been constructed.
Definition Unifier.cpp:195
UnificationOutcome unify(Term *, Term *)
Implementation of unification for a pair of Terms.
Definition Unifier.cpp:38
string to_string(bool subbed=false) const
Make a nice string representation.
Definition Unifier.hpp:200
UnificationOutcome operator()(Term *, Term *)
Implementation of unification for a pair of Terms.
Definition Unifier.cpp:27
vector< UPair > to_do
Queue used by complete_unification method.
Definition Unifier.hpp:98
UnificationOutcome unify_terms(Term *, Term *)
Main method implementing the traditional unification algorithm.
Definition Unifier.cpp:48
UnificationOutcome complete_unification()
Implementation of unification: for the polynomial-time approach. All the real work happens here.
Definition Unifier.cpp:127
Substitution s
Build the resulting substitution here.
Definition Unifier.hpp:94
Basic representation of variables.
Definition Variable.hpp:58
void substitute(Term *t)
Self-explanatory, but be careful!
Definition Variable.hpp:129