# Davis-Putnam Procedure (DP)

The Davis-Putnam procedure (DP) is a technique for deciding the satisfiability of propositional formulas in clausal form. The procedure shown here is not the Davis-Putnam-Longemann-Loveland (DPLL) procedure which is a later development.

## Algorithm

The following three rules are applied until they can be applied no more:

1. The 1-literal rule: Any clause that contains a single literal is removed along with any other clause that contains this literal. The negation of this literal is then removed from all clauses. This is also known as unit propagation.
2. The affirmative-negative rule: The set of all literals that occur either only positively or negatively is calculated. These literals are called pure. Any clause that contains a pure literal is deleted.
3. The resolution rule: A literal is chosen that occurs only positively in one clause and only negatively in another clause. We then form all resolvents for this literal and combine the result with the clauses that were not used in the resolution.

## Implementation

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

The following Prolog code in this section implements the algorithm given 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 1-literal rule

The procedure `pl_dp_one_literal_rule/2` chooses a clause that contains only one literal, removes all clauses that contain this literal (`pl_dp_one_literal_rule_remove_clauses/3`), then removes all negations of this literal from the remaining clauses (`pl_dp_one_literal_rule_remove_negated_literals/3`).

``` pl_negate(~A, A) :- !. pl_negate(A, ~A). pl_dp_one_literal_rule_remove_negated_literals([], _, []). pl_dp_one_literal_rule_remove_negated_literals([Phi|Phis], Neg, [Psi|Psis]) :- delete_all(Neg, Phi, Psi), !, pl_dp_one_literal_rule_remove_negated_literals(Phis, Neg, Psis). pl_dp_one_literal_rule_remove_clauses([], _, []). pl_dp_one_literal_rule_remove_clauses([Phi|Phis], P, Result) :- member(P, Phi), !, pl_dp_one_literal_rule_remove_clauses(Phis, P, Result). pl_dp_one_literal_rule_remove_clauses([Phi|Phis], P, [Phi|Result]) :- pl_dp_one_literal_rule_remove_clauses(Phis, P, Result). pl_dp_one_literal_rule(Phi, Psi) :- member([P], Phi), pl_negate(P, NegP), pl_dp_one_literal_rule_remove_clauses(Phi, P, Mid), pl_dp_one_literal_rule_remove_negated_literals(Mid, NegP, Psi). ```

### The affirmative-negative rule

The procedure `pl_dp_affirmative_negative_rule/2` finds all positive and negative pure literals. If there are no pure literals then the procedure fails. Otherwise, these literals are then used to filter out any clauses that contain them with `pl_affirmitive_negative_rule_remove_clauses/3`. In this implementation we make use of a library which represents sets as ordered lists. The procedure `ord_subtract/3` is from this library.

``` pl_affirmitive_negative_rule_remove_clauses([], _, []). pl_affirmitive_negative_rule_remove_clauses([Phi|Phis], Pures, Result) :- member(P, Pures), (member(P, Phi) ; member(~P, Phi)), !, pl_affirmitive_negative_rule_remove_clauses(Phis, Pures, Result). pl_affirmitive_negative_rule_remove_clauses([Phi|Phis], Pures, [Phi|Result]) :- pl_affirmitive_negative_rule_remove_clauses(Phis, Pures, Result). pl_dp_affirmative_negative_rule(Phi, Psi) :- setof(Pos, Clause^(member(Clause, Phi), member(Pos, Clause), Pos \= ~_), Positives), setof(Neg, Clause^(member(Clause, Phi), member(~Neg, Clause)), Negatives), ord_subtract(Positives, Negatives, OnlyPositives), ord_subtract(Negatives, Positives, OnlyNegatives), append(OnlyPositives, OnlyNegatives, Pures), Pures \= [], % must have them! pl_affirmitive_negative_rule_remove_clauses(Phi, Pures, Psi). ```

### The resolution rule

Here we choose a literal that occurs only positively in one clause, and only negatively in another, then we form all propositional resolvents using `combine_all_pairs/3`. As resolution can introduce clauses which are trivially satisfied, i.e., they contain both a positive and negative occurrence of a literal, we filter these out with a call to `pl_filter_trivial`. The procedure `ord_union/3` is from the same library as `ord_subtract/3` used earlier.

``` pl_filter_trivial([], []). pl_filter_trivial([Clause|Clauses], Result) :- member(Lit, Clause), pl_negate(Lit, NegLit), member(NegLit, Clause), !, pl_filter_trivial(Clauses, Result). pl_filter_trivial([Clause|Clauses], [Clause|Result]) :- pl_filter_trivial(Clauses, Result). combine_all_pairs([], S, S) :- !. combine_all_pairs(S, [], S) :- !. combine_all_pairs(S1, S2, S4) :- combine_all_pairs(S1, S2, [], S4). combine_all_pairs([], _, Result, Result). combine_all_pairs([H|T], S2, Acc, Result) :- combine(S2, H, P), ord_union(P, Acc, NewAcc), combine_all_pairs(T, S2, NewAcc, Result). combine([], _, []). combine([H|T], E, [Set|Sets]) :- ord_union(E, H, Set), combine(T, E, Sets). pl_dp_resolution_clauses([], _, _, [], [], []). pl_dp_resolution_clauses([Clause|Clauses], Lit, NegLit, [Clause1|ResultPos], ResultNeg, Others) :- member(Lit, Clause), \+ member(NegLit, Clause), delete_all(Lit, Clause, Clause1), !, pl_dp_resolution_clauses(Clauses, Lit, NegLit, ResultPos, ResultNeg, Others). pl_dp_resolution_clauses([Clause|Clauses], Lit, NegLit, ResultPos, [Clause1|ResultNeg], Others) :- member(NegLit, Clause), \+ member(Lit, Clause), delete_all(NegLit, Clause, Clause1), !, pl_dp_resolution_clauses(Clauses, Lit, NegLit, ResultPos, ResultNeg, Others). pl_dp_resolution_clauses([Clause|Clauses], Lit, NegLit, ResultPos, ResultNeg, [Clause|Others]) :- pl_dp_resolution_clauses(Clauses, Lit, NegLit, ResultPos, ResultNeg, Others). pl_dp_resolution_rule(Clauses0, Result) :- del(C0, Clauses0, Clauses1), % Choose a clause C0. del(Lit, C0, Alpha), % Choose a literal from C0. pl_negate(Lit, NegLit), % Negate this literal. \+ member(NegLit, Alpha), % The negated literal cannot occur in C0. del(C1, Clauses1, _), % Choose another clause C1. del(NegLit, C1, Beta), % The negated literal must occur in C1. \+ member(Lit, Beta), % The original literal cannot occur in C1. !, pl_dp_resolution_clauses(Clauses0, Lit, NegLit, ClausesPos, ClausesNeg, ClausesOthers), combine_all_pairs(ClausesPos, ClausesNeg, Resolvents), % Form all resolvants for Lit. ord_union(Resolvents, ClausesOthers, Mid), pl_filter_trivial(Mid, Result). ```

### Entry-point

The driver for the procedure is `pl_dp_loop/2`. In each loop we try the 1-literal rule before the affirmative-negative rule, and we try the affirmative-negative rule before the resolution rule. We stop when no rule can be applied.

``` pl_dp_loop(ClausesIn, ClausesOut) :- pl_dp_one_literal_rule(ClausesIn, ClausesMid), !, pl_dp_loop(ClausesMid, ClausesOut). pl_dp_loop(ClausesIn, ClausesOut) :- pl_dp_affirmative_negative_rule(ClausesIn, ClausesMid), !, pl_dp_loop(ClausesMid, ClausesOut). pl_dp_loop(ClausesIn, ClausesOut) :- pl_dp_resolution_rule(ClausesIn, ClausesMid), !, pl_dp_loop(ClausesMid, ClausesOut). pl_dp_loop(Clauses, Clauses). ```

### Misc.

The procedure `pl_dp_sat/1` succeeds if the argument, which is a formula, is satisfiable. The procedure `cnf_transform/2` transforms a formula into conjunctive normal form (CNF). The procedure `pl_clausal_form/2` transforms a formula in CNF into clausal form.

``` pl_dp_sat(Phi) :- cnf_transform(Phi, CNF), pl_clausal_form(CNF, Clauses), pl_dp_loop(Clauses, []). ```

The procedure `pl_dp_tautology/1` succeeds if the argument is a tautology, i.e. the negation of the argument is unsatisfiable.

``` pl_dp_tautology(Phi) :- \+ pl_dp_sat(~Phi). ```

## References

Chang, C-L, Lee, R C-T. Symbolic Logic and Mechanical Theorem Proving. Academic Press, 1973.
Fitting, Melvin. First-Order Logic and Automated Theorem Proving. Springer, 1990.
Harrison, John. Handbook of Practical Logic and Automated Reasoning. Cambridge, 2009.