Connect++ 0.5.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
27//----------------------------------------------------------------------
28UnificationOutcome Unifier::operator()(Term* term1, Term* term2) {
29 to_do.clear();
30 to_do.push_front(UPair(term1, term2));
31 return complete_unification();
32}
33//----------------------------------------------------------------------
34UnificationOutcome Unifier::operator()(const vector<Term*>& t1s,
35 const vector<Term*>& t2s) {
36 if (t1s.size() != t2s.size())
37 return UnificationOutcome::ConflictFail;
38 to_do.clear();
39 auto i = t2s.begin();
40 for (Term* term1 : t1s) {
41 to_do.push_front(UPair(term1, *i));
42 i++;
43 }
44 return complete_unification();
45}
46//----------------------------------------------------------------------
47UnificationOutcome Unifier::operator()(Literal* l1, Literal* l2) {
48 if (!l1->is_compatible_with(l2)) {
49 cerr << "ERROR" << ": ";
50 cerr << "You're trying to unify non-compatible literals." << endl;
51 cerr << "ERROR" << ": " << *l1 << endl;
52 cerr << "ERROR" << ": " << *l2 << endl;
53 }
54 const vector<Term*>& args1 = l1->get_args();
55 const vector<Term*>& args2 = l2->get_args();
56 return operator()(args1, args2);
57}
58//----------------------------------------------------------------------
59UnificationOutcome Unifier::operator()(const Literal& l1,
60 const Literal& l2) {
61 if (!l1.is_compatible_with(&l2)) {
62 cerr << "ERROR" << ": ";
63 cerr << "You're trying to unify non-compatible literals." << endl;
64 cerr << "ERROR" << ": " << l1 << endl;
65 cerr << "ERROR" << ": " << l2 << endl;
66 }
67 const vector<Term*>& args1 = l1.get_args();
68 const vector<Term*>& args2 = l2.get_args();
69 return operator()(args1, args2);
70}
71//----------------------------------------------------------------------
72UnificationOutcome Unifier::complete_unification() {
73 s.clear();
74 while (to_do.size() > 0) {
75 // Get the next thing from the queue and split it up.
76 UPair upair(to_do.front());
77 Term* t1(upair.first);
78 Term* t2(upair.second);
79 // Make sure you do this! Many long debugging sessions
80 // await if you ignore my advice.
81 t1 = t1->skip_leading_variables();
82 t2 = t2->skip_leading_variables();
83 to_do.pop_front();
84 // Work out exactly what everything is.
85 bool t1_is_fun = t1->subbed_is_function();
86 bool t2_is_fun = t2->subbed_is_function();
87 bool t1_is_var = t1->subbed_is_variable();
88 bool t2_is_var = t2->subbed_is_variable();
89 Variable* t1v = nullptr;
90 if (t1_is_var)
91 t1v = t1->get_v()->subbed_variable();
92
93 // Delete
94 if (t1->subbed_equal(t2)) {
95 continue;
96 }
97 // Swap
98 if (t1_is_fun && t2_is_var) {
99 to_do.push_back(UPair(t2, t1));
100 continue;
101 }
102 // Occurs
103 if (t1_is_var && t2_is_fun && t2->contains_variable(t1v)) {
104 backtrack();
105 return UnificationOutcome::OccursFail;
106 }
107 // Eliminate
108 if (t1_is_var && !t2->contains_variable(t1v)) {
109 s.push_back(t1v, t2);
110 t1v->substitute(t2);
111 continue;
112 }
113 // Decompose/Conflict
114 if (t1_is_fun && t2_is_fun) {
115 // Conflict
116 if ((t1->get_subbed_f() != t2->get_subbed_f()) ||
117 (t1->get_subbed_arity() != t2->get_subbed_arity())) {
118 backtrack();
119 return UnificationOutcome::ConflictFail;
120 }
121 // Decompose
122 else {
123 size_t n_args = t1->arity();
124 for (size_t i = 0; i < n_args; i++) {
125 to_do.push_back(UPair((*t1)[i], (*t2)[i]));
126 }
127 continue;
128 }
129 }
130 }
131 return UnificationOutcome::Succeed;
132}
133//----------------------------------------------------------------------
135 s.backtrack();
136 s.clear();
137}
138//----------------------------------------------------------------------
139ostream& operator<<(ostream& out, const UnificationOutcome& o) {
140 switch(o) {
141 case UnificationOutcome::Succeed:
142 out << "Unification: success.";
143 break;
144 case UnificationOutcome::ConflictFail:
145 out << "Unification: failed due to conflict.";
146 break;
147 case UnificationOutcome::OccursFail:
148 out << "Unification: failed due to occurs check.";
149 break;
150 default:
151
152 break;
153 }
154 return out;
155}
156//----------------------------------------------------------------------
157ostream& operator<<(ostream& out, const Unifier& u) {
158 out << u.to_string();
159 return out;
160}
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:67
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
Function * get_subbed_f() const
Taking substitution into account, what function do we actually have?
Definition Term.cpp:138
Variable * get_v() const
Self-explanatory access function.
Definition Term.hpp:106
bool subbed_equal(Term *) const
Equality test, taking substitution into account.
Definition Term.cpp:72
Term * skip_leading_variables() const
It may be that there is a chain of substitutions attached to a variable.
Definition Term.cpp:228
bool subbed_is_variable() const
Is this term a variable, taking substitution into accoumt?
Definition Term.cpp:112
Arity get_subbed_arity() const
Taking substitution into account, what arity do we actually have?
Definition Term.cpp:145
bool contains_variable(Variable *) const
Taking substitution into account, does the term include the variable passed?
Definition Term.cpp:128
Arity arity() const
Self-explanatory access function.
Definition Term.hpp:122
bool subbed_is_function() const
Is this term a function, taking substitution into accoumt?
Definition Term.cpp:105
Wrap up various applications of unificiation into a single class: all the unification you need to do ...
Definition Unifier.hpp:83
void backtrack()
Apply the backtracking process to the substitution that has been constructed.
Definition Unifier.cpp:134
string to_string(bool subbed=false) const
Make a nice string representation.
Definition Unifier.hpp:164
UnificationOutcome operator()(Term *, Term *)
Implementation of unification for a pair of Terms.
Definition Unifier.cpp:28
UnificationOutcome complete_unification()
Implementation of unification: all the real work happens here.
Definition Unifier.cpp:72
deque< UPair > to_do
Queue used by complete_unification method.
Definition Unifier.hpp:92
Substitution s
Build the resulting substitution here.
Definition Unifier.hpp:88
Basic representation of variables.
Definition Variable.hpp:58
Variable * subbed_variable() const
If the variable is unsubbed then return "this"; otherwise a pointer to the variable at the end of a c...
Definition Term.cpp:303
void substitute(Term *t)
Self-explanatory, but be careful!
Definition Variable.hpp:142