Index

Tableau Procedure

The tableau procedure is a refutation technique which processes a formula as a tree. Each node in the tree is a set of formulas and each branch is a conjunction of its nodes. The subtrees of a node form a disjunction. A refutation is a tree where all branches contain a contradiction.

In the following we describe the tableau procedure for propositional logic.

The procedure involves systematically growing branches on the tree then checking for contradictions. Starting with a single node containing the formula to be refuted, a tree is grown in accordance with the tableau rules.

Other connectives, and the negation of compound formulas, can be first rewritten to an equivalent in terms of ¬, , and , then made subject to the rules just given. Essentially, we are rewriting to Negation Normal Form (NNF). The table below shows the rewrite rules used. Note that is rewritten in terms of . It is intended that this is in turn rewritten to remove .

Formula Equivalent
F1 ⇒ F2 ¬F1 ∨ F2
F1 ⇔ F2 (F1 ⇒ F2) ∧ (F2 ⇒ F1)
¬(F1 ∧ F2) ¬F1 ∨ ¬F2
¬(F1 ∨ F2) ¬F1 ∧ ¬F2
¬(F1 ⇒ F2) F1 ∧ ¬F2
¬(F1 ⇔ F2) F1 ⇔ ¬F2

After expanding the tree we can close branches which give a contradiction. If the only connectives used are ¬, , and , then the only possible contradictions are branches with a node containing false, and branches with a node containing some formula F and a node containing ¬F, since F ∧ ¬F is a contradiction. If we can close all the branches in the tree, then we have a refutation.

Implementation

The following code can be found in the examples directory of the Barry's Prolog distribution.

We'll represent the tree as a list of branches, each branch being a list of the formulas at all of the nodes on the branch. We don't need to store all of the nodes separately, and the order of the formulas in the list is irrelevant as these formulas are conjuncts and conjunction is an associative and commutative operation. The procedure description given above talks of rewriting nodes, but due to the tree representation just described, we'll actually rewrite branches.

The initial tree for input formula F will be [[F]], i.e. a disjunction with a single disjunct which is the conjunction of the single conjunct F. When we create the two new subtrees with the rewrite rule for , we'll replace the branch being rewritten with two branches and any formula common to both branches occurs in each branch — there is no sharing. When we expand a branch by rewriting one of its formulas, we will remove the formula from that branch. We are allowed to do this as we don't need to use that formula in a given branch more than once. Examples:

The following Prolog code in this section implements the procedure given earlier using the tree representation described above. First we fix a set of operators which we will need to represent the formulas of propositional logic.

:-op(200, fy, ~).                   % Negation
:-op(0, yfx, (/\)).
:-op(400, xfy, (/\)).               % Conjunction (not ISO associativity which is yfx)
:-op(0, yfx, (\/)).
:-op(500, xfy, (\/)).               % Disjunction (not ISO associativity which is yfx)
:-op(600, xfy, =>).                 % Implication
:-op(700, xfy, <=>).                % Equivalence
         

The procedure pl_negate(A, B) succeeds if A is the negation of B.

pl_negate(true, false)  :- !.
pl_negate(false, true)  :- !.
pl_negate(~A, A) :- !.
pl_negate(A, ~A).
	

The procedure pl_tableau_transform(TreeIn, TreeOut) transforms TreeIn into TreeOut. The representation of a tree is a list of complete branches. Each branch is a conjunction of its node's formulas represented as a list of these formulas. It is here that we rewrite formulas to NNF.

pl_tableau_transform([Branch|Branches], NewBranches) :-
	del(~ true, Branch, Rest),
	NewBranches = [[false|Rest]|Branches].
pl_tableau_transform([Branch|Branches], NewBranches) :-
	del(~ false, Branch, Rest),
	NewBranches = [[true|Rest]|Branches].
pl_tableau_transform([Branch|Branches], NewBranches) :-
	del(~(~A), Branch, Rest),
	NewBranches = [[A|Rest]|Branches].
pl_tableau_transform([Branch|Branches], [NewBranch|Branches]) :-
	del(A /\ B, Branch, Rest),
	NewBranch = [A, B|Rest].
pl_tableau_transform([Branch|Branches], NewBranches) :-
	del(A \/ B, Branch, Rest),
	NewBranches = [[A|Rest], [B|Rest]|Branches].
pl_tableau_transform([Branch|Branches], NewBranches) :-
	del(A => B, Branch, Rest),
	pl_tableau_transform([[~A \/ B|Rest]|Branches], NewBranches).
pl_tableau_transform([Branch|Branches], NewBranches) :-
	del(A <=> B, Branch, Rest),
	pl_tableau_transform([[(A => B) /\ (B => A)|Rest]|Branches], NewBranches).
pl_tableau_transform([Branch|Branches], NewBranches) :-
	del(~(A /\ B), Branch, Rest),
	pl_tableau_transform([[~A \/ ~B|Rest]|Branches], NewBranches).
pl_tableau_transform([Branch|Branches], NewBranches) :-
	del(~(A \/ B), Branch, Rest),
	pl_tableau_transform([[~A /\ ~B|Rest]|Branches], NewBranches).
pl_tableau_transform([Branch|Branches], NewBranches) :-
	del(~(A => B), Branch, Rest),
	pl_tableau_transform([[A /\ ~B|Rest]|Branches], NewBranches).
pl_tableau_transform([Branch|Branches], NewBranches) :-
	del(~(A <=> B), Branch, Rest),
	pl_tableau_transform([[A <=> ~B|Rest]|Branches], NewBranches).
	

The procedure pl_close_branches(TreeIn, TreeOut) creates TreeOut which is TreeIn with all closed branches removed. A closed branch is a contradiction which in our case means either false, or, F /\ ~F for any formula F. A tree where all branches have been closed will be represented by the empty list [].

pl_close_branches([], []).
pl_close_branches([H|T], Result) :-
	member(false, H),
	!,
	pl_close_branches(T, Result).
pl_close_branches([H|T], Result) :-
	member(A, H),
	pl_negate(A, NegA),
	member(NegA, H),
	!,
	pl_close_branches(T, Result).
pl_close_branches([H|T], [H|Result]) :-
	pl_close_branches(T, Result).
	

The procedure pl_tableau_loop(Tree) succeeds if Tree can have all of its branches closed.

pl_tableau_loop([]) :- !.
pl_tableau_loop(Tree0) :- 
	pl_tableau_transform(Tree0, Tree1),
	!,
	pl_close_branches(Tree1, Tree2),
	pl_tableau_loop(Tree2).
pl_tableau_loop(Tree1) :- 
	pl_close_branches(Tree1, Tree2),
	pl_tableau_loop(Tree2).
	

The procedure pl_tableau(Formula) succeeds if Formula can be refuted.

pl_tableau(Formula) :-
	pl_tableau_loop([[Formula]]).
	

The procedure pl_tableau_tautology(Formula) succeeds if Formula is a tautology.

pl_tableau_tautology(Formula) :-
	pl_tableau(~Formula).
	

References

Fitting, Melvin. First-Order Logic and Automated Theorem Proving. Springer, 1990.

Index