package aima.core.logic.propositional.algorithms;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import aima.core.logic.propositional.parsing.PEParser;
import aima.core.logic.propositional.parsing.ast.BinarySentence;
import aima.core.logic.propositional.parsing.ast.Sentence;
import aima.core.logic.propositional.parsing.ast.Symbol;
import aima.core.logic.propositional.parsing.ast.SymbolComparator;
import aima.core.logic.propositional.parsing.ast.UnarySentence;
import aima.core.logic.propositional.visitors.CNFClauseGatherer;
import aima.core.logic.propositional.visitors.CNFTransformer;
import aima.core.logic.propositional.visitors.SymbolClassifier;
import aima.core.util.Converter;
import aima.core.util.SetOps;
/**
* Artificial Intelligence A Modern Approach (3rd Edition): page 255.<br>
* <br>
*
* <pre>
* <code>
* 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 C<sub>i</sub>, C<sub>j</sub> in clauses do
* resolvents ← PL-RESOLVE(C<sub>i</sub>, C<sub>j</sub>)
* if resolvents contains the empty clause then return true
* new ← new ∪ resolvents
* if new ⊆ clauses then return false
* clauses ← clauses ∪ new
* </code>
* </pre>
*
* 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.
*
* @author Ravi Mohan
* @author Mike Stampone
*/
public class PLResolution {
/**
* Returns the answer to the specified question using PL-Resolution.
*
* @param kb
* the knowledge base, a sentence in propositional logic
* @param alpha
* the query, a sentence in propositional logic
*
* @return the answer to the specified question using PL-Resolution.
*/
public boolean plResolution(KnowledgeBase kb, String alpha) {
return plResolution(kb, (Sentence) new PEParser().parse(alpha));
}
/**
* Returns the answer to the specified question using PL-Resolution.
*
* @param kb
* the knowledge base, a sentence in propositional logic
* @param alpha
* the query, a sentence in propositional logic
*
* @return the answer to the specified question using PL-Resolution.
*/
public boolean plResolution(KnowledgeBase kb, Sentence alpha) {
Sentence kBAndNotAlpha = new BinarySentence("AND", kb.asSentence(),
new UnarySentence(alpha));
Set<Sentence> clauses = new CNFClauseGatherer()
.getClausesFrom(new CNFTransformer().transform(kBAndNotAlpha));
clauses = filterOutClausesWithTwoComplementaryLiterals(clauses);
Set<Sentence> newClauses = new HashSet<Sentence>();
while (true) {
List<List<Sentence>> pairs = getCombinationPairs(new Converter<Sentence>()
.setToList(clauses));
for (int i = 0; i < pairs.size(); i++) {
List<Sentence> pair = pairs.get(i);
// System.out.println("pair number" + i+" of "+pairs.size());
Set<Sentence> resolvents = plResolve(pair.get(0), pair.get(1));
resolvents = filterOutClausesWithTwoComplementaryLiterals(resolvents);
if (resolvents.contains(new Symbol("EMPTY_CLAUSE"))) {
return true;
}
newClauses = SetOps.union(newClauses, resolvents);
// System.out.println("clauseslist size = " +clauses.size());
}
if (SetOps.intersection(newClauses, clauses).size() == newClauses
.size()) {// subset test
return false;
}
clauses = SetOps.union(newClauses, clauses);
clauses = filterOutClausesWithTwoComplementaryLiterals(clauses);
}
}
public Set<Sentence> plResolve(Sentence clause1, Sentence clause2) {
Set<Sentence> resolvents = new HashSet<Sentence>();
ClauseSymbols cs = new ClauseSymbols(clause1, clause2);
Iterator<Symbol> iter = cs.getComplementedSymbols().iterator();
while (iter.hasNext()) {
Symbol symbol = iter.next();
resolvents.add(createResolventClause(cs, symbol));
}
return resolvents;
}
public boolean plResolution(String kbs, String alphaString) {
KnowledgeBase kb = new KnowledgeBase();
kb.tell(kbs);
Sentence alpha = (Sentence) new PEParser().parse(alphaString);
return plResolution(kb, alpha);
}
//
// PRIVATE METHODS
//
private Set<Sentence> filterOutClausesWithTwoComplementaryLiterals(
Set<Sentence> clauses) {
Set<Sentence> filtered = new HashSet<Sentence>();
SymbolClassifier classifier = new SymbolClassifier();
Iterator<Sentence> iter = clauses.iterator();
while (iter.hasNext()) {
Sentence clause = iter.next();
Set<Symbol> positiveSymbols = classifier
.getPositiveSymbolsIn(clause);
Set<Symbol> negativeSymbols = classifier
.getNegativeSymbolsIn(clause);
if ((SetOps.intersection(positiveSymbols, negativeSymbols).size() == 0)) {
filtered.add(clause);
}
}
return filtered;
}
private Sentence createResolventClause(ClauseSymbols cs, Symbol toRemove) {
List<Symbol> positiveSymbols = new Converter<Symbol>().setToList(SetOps
.union(cs.clause1PositiveSymbols, cs.clause2PositiveSymbols));
List<Symbol> negativeSymbols = new Converter<Symbol>().setToList(SetOps
.union(cs.clause1NegativeSymbols, cs.clause2NegativeSymbols));
if (positiveSymbols.contains(toRemove)) {
positiveSymbols.remove(toRemove);
}
if (negativeSymbols.contains(toRemove)) {
negativeSymbols.remove(toRemove);
}
Collections.sort(positiveSymbols, new SymbolComparator());
Collections.sort(negativeSymbols, new SymbolComparator());
List<Sentence> sentences = new ArrayList<Sentence>();
for (int i = 0; i < positiveSymbols.size(); i++) {
sentences.add(positiveSymbols.get(i));
}
for (int i = 0; i < negativeSymbols.size(); i++) {
sentences.add(new UnarySentence(negativeSymbols.get(i)));
}
if (sentences.size() == 0) {
return new Symbol("EMPTY_CLAUSE"); // == empty clause
} else {
return LogicUtils.chainWith("OR", sentences);
}
}
private List<List<Sentence>> getCombinationPairs(List<Sentence> clausesList) {
//int odd = clausesList.size() % 2;
//int midpoint = 0;
//if (odd == 1) {
// midpoint = (clausesList.size() / 2) + 1;
//} else {
// midpoint = (clausesList.size() / 2);
//}
List<List<Sentence>> pairs = new ArrayList<List<Sentence>>();
for (int i = 0; i < clausesList.size(); i++) {
for (int j = i; j < clausesList.size(); j++) {
List<Sentence> pair = new ArrayList<Sentence>();
Sentence first = clausesList.get(i);
Sentence second = clausesList.get(j);
if (!(first.equals(second))) {
pair.add(first);
pair.add(second);
pairs.add(pair);
}
}
}
return pairs;
}
class ClauseSymbols {
Set<Symbol> clause1Symbols, clause1PositiveSymbols,
clause1NegativeSymbols;
Set<Symbol> clause2Symbols, clause2PositiveSymbols,
clause2NegativeSymbols;
Set<Symbol> positiveInClause1NegativeInClause2,
negativeInClause1PositiveInClause2;
public ClauseSymbols(Sentence clause1, Sentence clause2) {
SymbolClassifier classifier = new SymbolClassifier();
clause1Symbols = classifier.getSymbolsIn(clause1);
clause1PositiveSymbols = classifier.getPositiveSymbolsIn(clause1);
clause1NegativeSymbols = classifier.getNegativeSymbolsIn(clause1);
clause2Symbols = classifier.getSymbolsIn(clause2);
clause2PositiveSymbols = classifier.getPositiveSymbolsIn(clause2);
clause2NegativeSymbols = classifier.getNegativeSymbolsIn(clause2);
positiveInClause1NegativeInClause2 = SetOps.intersection(
clause1PositiveSymbols, clause2NegativeSymbols);
negativeInClause1PositiveInClause2 = SetOps.intersection(
clause1NegativeSymbols, clause2PositiveSymbols);
}
public Set<Symbol> getComplementedSymbols() {
return SetOps.union(positiveInClause1NegativeInClause2,
negativeInClause1PositiveInClause2);
}
}
}