aima.core.logic.propositional.inference.PLFCEntails 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.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import aima.core.logic.propositional.kb.KnowledgeBase;
import aima.core.logic.propositional.kb.data.Clause;
import aima.core.logic.propositional.parsing.ast.PropositionSymbol;
import aima.core.logic.propositional.visitors.ConvertToConjunctionOfClauses;
import aima.core.logic.propositional.visitors.SymbolCollector;
/**
* Artificial Intelligence A Modern Approach (3rd Edition): page 258.
*
*
*
*
* function PL-FC-ENTAILS?(KB, q) returns true or false
* inputs: KB, the knowledge base, a set of propositional definite clauses
* q, the query, a proposition symbol
* count ← a table, where count[c] is the number of symbols in c's premise
* inferred ← a table, where inferred[s] is initially false for all symbols
* agenda ← a queue of symbols, initially symbols known to be true in KB
*
* while agenda is not empty do
* p ← Pop(agenda)
* if p = q then return true
* if inferred[p] = false then
* inferred[p] ← true
* for each clause c in KB where p is in c.PREMISE do
* decrement count[c]
* if count[c] = 0 then add c.CONCLUSION to agenda
* return false
*
*
*
* Figure 7.15 the forward-chaining algorithm for propositional logic. The
* agenda keeps track of symbols known to be true but not yet
* "processed". The count table keeps track of how many premises of each
* implication are as yet unknown. Whenever a new symbol p from the agenda is
* processed, the count is reduced by one for each implication in whose premise
* p appears (easily identified in constant time with appropriate indexing.) If
* a count reaches zero, all the premises of the implication are known, so its
* conclusion can be added to the agenda. Finally, we need to keep track of
* which symbols have been processed; a symbol that is already in the set of
* inferred symbols need not be added to the agenda again. This avoids redundant
* work and prevents loops caused by implications such as P ⇒ Q and Q
* ⇒ P.
*
* @author Ciaran O'Reilly
* @author Ravi Mohan
* @author Mike Stampone
*/
public class PLFCEntails {
/**
* PL-FC-ENTAILS?(KB, q)
* The forward-chaining algorithm for propositional logic.
*
* @param kb
* the knowledge base, a set of propositional definite clauses.
* @param q
* q, the query, a proposition symbol
* @return true if KB |= q, false otherwise.
* @throws IllegalArgumentException
* if KB contains any non-definite clauses.
*/
public boolean plfcEntails(KnowledgeBase kb, PropositionSymbol q) {
// count <- a table, where count[c] is the number of symbols in c's
// premise
Map count = initializeCount(kb);
// inferred <- a table, where inferred[s] is initially false for all
// symbols
Map inferred = initializeInferred(kb);
// agenda <- a queue of symbols, initially symbols known to be true in
// KB
Queue agenda = initializeAgenda(count);
// Note: an index for p to the clauses where p appears in the premise
Map> pToClausesWithPInPremise = initializeIndex(
count, inferred);
// while agenda is not empty do
while (!agenda.isEmpty()) {
// p <- Pop(agenda)
PropositionSymbol p = agenda.remove();
// if p = q then return true
if (p.equals(q)) {
return true;
}
// if inferred[p] = false then
if (inferred.get(p).equals(Boolean.FALSE)) {
// inferred[p] <- true
inferred.put(p, true);
// for each clause c in KB where p is in c.PREMISE do
for (Clause c : pToClausesWithPInPremise.get(p)) {
// decrement count[c]
decrement(count, c);
// if count[c] = 0 then add c.CONCLUSION to agenda
if (count.get(c) == 0) {
agenda.add(conclusion(c));
}
}
}
}
// return false
return false;
}
//
// SUPPORTING CODE
//
//
// PROTECTED
//
protected Map initializeCount(KnowledgeBase kb) {
// count <- a table, where count[c] is the number of symbols in c's
// premise
Map count = new HashMap();
Set clauses = ConvertToConjunctionOfClauses.convert(
kb.asSentence()).getClauses();
for (Clause c : clauses) {
if (!c.isDefiniteClause()) {
throw new IllegalArgumentException(
"Knowledge Base contains non-definite clauses:" + c);
}
// Note: # of negative literals is equivalent to the number of
// symbols in c's premise
count.put(c, c.getNumberNegativeLiterals());
}
return count;
}
protected Map initializeInferred(KnowledgeBase kb) {
// inferred <- a table, where inferred[s] is initially false for all
// symbols
Map inferred = new HashMap();
for (PropositionSymbol p : SymbolCollector.getSymbolsFrom(kb
.asSentence())) {
inferred.put(p, false);
}
return inferred;
}
// Note: at the point of calling this routine, count will contain all the
// clauses in KB.
protected Queue initializeAgenda(Map count) {
// agenda <- a queue of symbols, initially symbols known to be true in
// KB
Queue agenda = new LinkedList();
for (Clause c : count.keySet()) {
// No premise just a conclusion, then we know its true
if (c.getNumberNegativeLiterals() == 0) {
agenda.add(conclusion(c));
}
}
return agenda;
}
// Note: at the point of calling this routine, count will contain all the
// clauses in KB while inferred will contain all the proposition symbols.
protected Map> initializeIndex(
Map count, Map inferred) {
Map> pToClausesWithPInPremise = new HashMap>();
for (PropositionSymbol p : inferred.keySet()) {
Set clausesWithPInPremise = new HashSet();
for (Clause c : count.keySet()) {
// Note: The negative symbols comprise the premise
if (c.getNegativeSymbols().contains(p)) {
clausesWithPInPremise.add(c);
}
}
pToClausesWithPInPremise.put(p, clausesWithPInPremise);
}
return pToClausesWithPInPremise;
}
protected void decrement(Map count, Clause c) {
int currentCount = count.get(c);
// Note: a definite clause can just be a fact (i.e. 1 positive literal)
// However, we only decrement those where the symbol is in the premise
// so we don't need to worry about going < 0.
count.put(c, currentCount - 1);
}
protected PropositionSymbol conclusion(Clause c) {
// Note: the conclusion is from the single positive
// literal in the definite clause (which we are
// restricted to).
return c.getPositiveSymbols().iterator().next();
}
}