
               %  A program for Generating Prime Implicates.

:- op(900,xfy,==>).  % Simplification Rules. 
:- dynamic(clause/2).

compile :- initializeCompileCPU, clausifyAxioms,
           reportClausifyStats, resolveAll(0), reportPrimeImpStats.

clausifyAxioms :- axiom(W), processQuantifiers(W,W1),
                  simplify(W1,Simp), clausalForm(Simp,Clauses),
                  assertClauses(Clauses,0), fail.
clausifyAxioms.

processQuantifiers(W1 & W2,I1 & I2) :- processQuantifiers(W1,I1),
                                       processQuantifiers(W2,I2).
processQuantifiers(W1 v W2,I1 v I2) :- processQuantifiers(W1,I1),
                                       processQuantifiers(W2,I2).
processQuantifiers(W1 => W2,I) :- processQuantifiers(-W1 v W2,I).
processQuantifiers(W1 <=> W2,I) :-
                     processQuantifiers((W1 => W2) & (W2 => W1),I).
processQuantifiers(-W,-I) :- processQuantifiers(W,I).
processQuantifiers(some([X,T],W),I) :- processQuantifiers(-all([X,T],-W),I).
processQuantifiers(all([X,T],W),I) :- sub(X,var(X),W,WR),
          /* The substitution predicate of the GOLOG interpreter. Here, we
             rename each quantified variable X by var(X). This prevents any
             possible clash between variable and domain element names.  */
         processQuantifiers(WR,I1),
         domain(T,D), eliminateUniversal(I1,var(X),D,I).
processQuantifiers(A,A) :- isAtom(A).

eliminateUniversal(W,X,[E],I) :- sub(X,E,W,I). 
eliminateUniversal(W,X,[E1,E2 | R],I1 & I2) :- sub(X,E1,W,I1),
                                  eliminateUniversal(W,X,[E2 | R],I2).

clausalForm(L,[[L]]) :- isLiteral(L).
clausalForm(W1 & W2,Clauses) :- clausalForm(W1,C1), clausalForm(W2,C2),
                                union(C1,C2,Clauses).
clausalForm(-(-W),Clauses) :- clausalForm(W,Clauses).
clausalForm(W1 v W2,Clauses) :- clausalForm(W1,C1), clausalForm(W2,C2),
                                pairwiseUnion(C1,C2,Clauses).
clausalForm(-(W1 & W2),Clauses) :- clausalForm(-W1 v -W2,Clauses).
clausalForm(-(W1 v W2),Clauses) :- clausalForm(-W1 & -W2,Clauses).

pairwiseUnion([],Clauses,[]).
pairwiseUnion(Clauses,[],[]).
pairwiseUnion([C1 | R1],[C2 | R2],[C | Clauses]) :- union(C1,C2,C),
         pairwiseUnion([C1],R2,P1), pairwiseUnion(R1,[C2 | R2],P2),
         union(P1,P2,Clauses).

isLiteral(A) :- isAtom(A).
isLiteral(-A) :- isAtom(A).

assertClauses([],N).
assertClauses([C | R],N) :- assertClause(C,N), assertClauses(R,N).

assertClause(C,N) :- tautology(C), !.
assertClause(C,N) :- subsumed(C), !.
assertClause(C,N) :- retractSubsumedClauses(C), assert(clause(C,N)).

retractSubsumedClauses(C) :- clause(K,N), subsumes(C,K),
                             retract(clause(K,N)), fail.
retractSubsumedClauses(C).

subsumes([],C).
subsumes([L | R],C) :- member(L,C), subsumes(R,C).

% Subsumption is expensive, so optimize a little.  

subsumed([L]) :- clause([L],N).
subsumed([L1,L2 | C]) :- clause(K,N), subsumes(K,[L1,L2 | C]).

tautology([true]).
tautology([L1,L2 | C]) :- member(A,[L1,L2 | C]), isAtom(A),
                          member(-A,[L1,L2 | C]).

resolveAll(N) :- unitResolve(N), nonUnitResolve(N).
resolveAll(N) :- N1 is N + 1,
        % Was a new resolvent produced at the previous level? 
               clause(C,N1), resolveAll(N1).
resolveAll(N).

unitResolve(N) :- N1 is N + 1, 
          (clause([L],N), clause(C,J) ; 
           clause([L],J),  J < N, clause(C,N)),
          unitResolvent([L],C,R), retractSubsumedClauses(R),
          assert(clause(R,N1)), fail.
unitResolve(N).

unitResolvent([L],C,R) :- complement(L,N), member(N,C),
                          delete(N,C,R).

nonUnitResolve(N) :- N1 is N + 1,
                     clause([L1,L2 | R],N), clause([F1,F2 | T],J),
                     resolvent([L1,L2 | R],[F1,F2 | T],C),
                     assertClause(C,N1), fail.

resolvent(C1,C2,C) :- member(L1,C1), complement(L1,L2),
                      member(L2,C2), delete(L1,C1,K1),
                      delete(L2,C2,K2), union(K1,K2,C).

complement(A,-A) :- not A = -W.
complement(-A,A).

initializeCompileCPU :- cputime(T), setval(compileCPUtime,T).

reportClausifyStats :- nl, 
         write('Clausal form completed.   CPU time (sec): '),
         getval(compileCPUtime,T1), cputime(T), T2 is T - T1,
         setval(clausifyTime,T2), write(T2), countClauses(N),
         write('  Clauses: '), write(N), nl.
 
reportPrimeImpStats :- nl, 
         write('Database compiled.   CPU time (sec): '), cputime(T),
         getval(clausifyTime,T1), T2 is T - T1, write(T2),
         countClauses(N), write('   Prime implicates: '), write(N), nl.

countClauses(N) :- setval(clauseCount,0), clause(C,J),
                   incval(clauseCount), fail.
countClauses(N) :- getval(clauseCount,N).

simplify(W1 & W2,S) :- simplify(W1,S1), simplify(W2,S2),
                       simplify1(S1 & S2,S), !.
simplify(W1 v W2,S) :- simplify(W1,S1), simplify(W2,S2),
                       simplify1(S1 v S2,S), !.
simplify(-W,S) :- simplify(W,S1), simplify1(-S1,S), !.
simplify(A,S) :- simplify1(A,S).

simplify1(W,Simp) :- W ==> Simp, !.
simplify1(W,W).

%  Simplification Rules.

true & P ==> P.         P & true ==> P.       false & P ==> false.
P & false ==> false.    true v P ==> true.    P v true ==> true.
false v P ==> P.        P v false ==> P.      -true ==> false.
-false ==> true.        X = X ==> true.
X = Y ==> false :- not X = Y.
