aima.core.logic.propositional.inference.PLResolution Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aima-core Show documentation
Show all versions of aima-core Show documentation
AIMA-Java Core Algorithms from the book Artificial Intelligence a Modern Approach 3rd Ed.
The newest version!
package aima.core.logic.propositional.inference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import aima.core.logic.propositional.kb.KnowledgeBase;
import aima.core.logic.propositional.kb.data.Clause;
import aima.core.logic.propositional.kb.data.Literal;
import aima.core.logic.propositional.parsing.ast.ComplexSentence;
import aima.core.logic.propositional.parsing.ast.Connective;
import aima.core.logic.propositional.parsing.ast.PropositionSymbol;
import aima.core.logic.propositional.parsing.ast.Sentence;
import aima.core.logic.propositional.visitors.ConvertToConjunctionOfClauses;
import aima.core.util.SetOps;
/**
* Artificial Intelligence A Modern Approach (3rd Edition): page 255.
*
*
*
*
* function PL-RESOLUTION(KB, α) returns true or false
* inputs: KB, the knowledge base, a sentence in propositional logic
* α, the query, a sentence in propositional logic
*
* clauses ← the set of clauses in the CNF representation of KB ∧ ¬α
* new ← {}
* loop do
* for each pair of clauses Ci, Cj in clauses do
* resolvents ← PL-RESOLVE(Ci, Cj)
* if resolvents contains the empty clause then return true
* new ← new ∪ resolvents
* if new ⊆ clauses then return false
* clauses ← clauses ∪ new
*
*
*
* Figure 7.12 A simple resolution algorithm for propositional logic. The
* function PL-RESOLVE returns the set of all possible clauses obtained by
* resolving its two inputs.
*
* Note: Optional optimization added to implementation whereby tautological
* clauses can be removed during processing of the algorithm - see pg. 254 of
* AIMA3e:
* Inspection of Figure 7.13 reveals that many resolution steps are
* pointless. For example, the clause B1,1 ∨ ¬B1,1
* ∨ P1,2 is equivalent to True ∨ P1,2 which
* is equivalent to True. Deducing that True is true is not very
* helpful. Therefore, any clauses in which two complementary literals appear
* can be discarded.
*
* @see Clause#isTautology()
*
* @author Ciaran O'Reilly
* @author Ravi Mohan
* @author Mike Stampone
*/
public class PLResolution {
/**
* PL-RESOLUTION(KB, α)
* A simple resolution algorithm for propositional logic.
*
* @param kb
* the knowledge base, a sentence in propositional logic.
* @param alpha
* the query, a sentence in propositional logic.
* @return true if KB |= α, false otherwise.
*/
public boolean plResolution(KnowledgeBase kb, Sentence alpha) {
// clauses <- the set of clauses in the CNF representation
// of KB & ~alpha
Set clauses = setOfClausesInTheCNFRepresentationOfKBAndNotAlpha(
kb, alpha);
// new <- {}
Set newClauses = new LinkedHashSet();
// loop do
do {
// for each pair of clauses C_i, C_j in clauses do
List clausesAsList = new ArrayList(clauses);
for (int i = 0; i < clausesAsList.size() - 1; i++) {
Clause ci = clausesAsList.get(i);
for (int j = i + 1; j < clausesAsList.size(); j++) {
Clause cj = clausesAsList.get(j);
// resolvents <- PL-RESOLVE(C_i, C_j)
Set resolvents = plResolve(ci, cj);
// if resolvents contains the empty clause then return true
if (resolvents.contains(Clause.EMPTY)) {
return true;
}
// new <- new U resolvents
newClauses.addAll(resolvents);
}
}
// if new is subset of clauses then return false
if (clauses.containsAll(newClauses)) {
return false;
}
// clauses <- clauses U new
clauses.addAll(newClauses);
} while (true);
}
/**
* PL-RESOLVE(Ci, Cj)
* Calculate the set of all possible clauses by resolving its two inputs.
*
* @param ci
* clause 1
* @param cj
* clause 2
* @return the set of all possible clauses obtained by resolving its two
* inputs.
*/
public Set plResolve(Clause ci, Clause cj) {
Set resolvents = new LinkedHashSet();
// The complementary positive literals from C_i
resolvePositiveWithNegative(ci, cj, resolvents);
// The complementary negative literals from C_i
resolvePositiveWithNegative(cj, ci, resolvents);
return resolvents;
}
//
// SUPPORTING CODE
//
private boolean discardTautologies = true;
/**
* Default constructor, which will set the algorithm to discard tautologies
* by default.
*/
public PLResolution() {
this(true);
}
/**
* Constructor.
*
* @param discardTautologies
* true if the algorithm is to discard tautological clauses
* during processing, false otherwise.
*/
public PLResolution(boolean discardTautologies) {
setDiscardTautologies(discardTautologies);
}
/**
* @return true if the algorithm will discard tautological clauses during
* processing.
*/
public boolean isDiscardTautologies() {
return discardTautologies;
}
/**
* Determine whether or not the algorithm should discard tautological
* clauses during processing.
*
* @param discardTautologies
*/
public void setDiscardTautologies(boolean discardTautologies) {
this.discardTautologies = discardTautologies;
}
//
// PROTECTED
//
protected Set setOfClausesInTheCNFRepresentationOfKBAndNotAlpha(
KnowledgeBase kb, Sentence alpha) {
// KB & ~alpha;
Sentence isContradiction = new ComplexSentence(Connective.AND,
kb.asSentence(), new ComplexSentence(Connective.NOT, alpha));
// the set of clauses in the CNF representation
Set clauses = new LinkedHashSet(
ConvertToConjunctionOfClauses.convert(isContradiction)
.getClauses());
discardTautologies(clauses);
return clauses;
}
protected void resolvePositiveWithNegative(Clause c1, Clause c2,
Set resolvents) {
// Calculate the complementary positive literals from c1 with
// the negative literals from c2
Set complementary = SetOps.intersection(
c1.getPositiveSymbols(), c2.getNegativeSymbols());
// Construct a resolvent clause for each complement found
for (PropositionSymbol complement : complementary) {
List resolventLiterals = new ArrayList();
// Retrieve the literals from c1 that are not the complement
for (Literal c1l : c1.getLiterals()) {
if (c1l.isNegativeLiteral()
|| !c1l.getAtomicSentence().equals(complement)) {
resolventLiterals.add(c1l);
}
}
// Retrieve the literals from c2 that are not the complement
for (Literal c2l : c2.getLiterals()) {
if (c2l.isPositiveLiteral()
|| !c2l.getAtomicSentence().equals(complement)) {
resolventLiterals.add(c2l);
}
}
// Construct the resolvent clause
Clause resolvent = new Clause(resolventLiterals);
// Discard tautological clauses if this optimization is turned on.
if (!(isDiscardTautologies() && resolvent.isTautology())) {
resolvents.add(resolvent);
}
}
}
// Utility routine for removing the tautological clauses from a set (in
// place).
protected void discardTautologies(Set clauses) {
if (isDiscardTautologies()) {
Set toDiscard = new HashSet();
for (Clause c : clauses) {
if (c.isTautology()) {
toDiscard.add(c);
}
}
clauses.removeAll(toDiscard);
}
}
}