aima.core.logic.fol.inference.FOLBCAsk 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.
package aima.core.logic.fol.inference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import aima.core.logic.fol.inference.proof.Proof;
import aima.core.logic.fol.inference.proof.ProofFinal;
import aima.core.logic.fol.inference.proof.ProofStepBwChGoal;
import aima.core.logic.fol.kb.FOLKnowledgeBase;
import aima.core.logic.fol.kb.data.Clause;
import aima.core.logic.fol.kb.data.Literal;
import aima.core.logic.fol.parsing.ast.AtomicSentence;
import aima.core.logic.fol.parsing.ast.Sentence;
import aima.core.logic.fol.parsing.ast.Term;
import aima.core.logic.fol.parsing.ast.Variable;
/**
* Artificial Intelligence A Modern Approach (2nd Edition): Figure 9.6, page
* 288.
*
*
*
* function FOL-BC-ASK(KB, goals, theta) returns a set of substitutions
* input: KB, a knowledge base
* goals, a list of conjuncts forming a query (theta already applied)
* theta, the current substitution, initially the empty substitution {}
* local variables: answers, a set of substitutions, initially empty
*
* if goals is empty then return {theta}
* qDelta <- SUBST(theta, FIRST(goals))
* for each sentence r in KB where STANDARDIZE-APART(r) = (p1 ˆ ... ˆ pn => q)
* and thetaDelta <- UNIFY(q, qDelta) succeeds
* new_goals <- [p1,...,pn|REST(goals)]
* answers <- FOL-BC-ASK(KB, new_goals, COMPOSE(thetaDelta, theta)) U answers
* return answers
*
*
* Figure 9.6 A simple backward-chaining algorithm.
*
* @author Ciaran O'Reilly
* @author Mike Stampone
*/
public class FOLBCAsk implements InferenceProcedure {
public FOLBCAsk() {
}
//
// START-InferenceProcedure
/**
* Returns a set of substitutions
*
* @param KB
* a knowledge base
* @param query
* goals, a list of conjuncts forming a query
*
* @return a set of substitutions
*/
public InferenceResult ask(FOLKnowledgeBase KB, Sentence query) {
// Assertions on the type queries this Inference procedure
// supports
if (!(query instanceof AtomicSentence)) {
throw new IllegalArgumentException(
"Only Atomic Queries are supported.");
}
List goals = new ArrayList();
goals.add(new Literal((AtomicSentence) query));
BCAskAnswerHandler ansHandler = new BCAskAnswerHandler();
List> allProofSteps = folbcask(KB, ansHandler,
goals, new HashMap());
ansHandler.setAllProofSteps(allProofSteps);
return ansHandler;
}
// END-InferenceProcedure
//
//
// PRIVATE METHODS
//
/**
*
* function FOL-BC-ASK(KB, goals, theta) returns a set of substitutions
* input: KB, a knowledge base
* goals, a list of conjuncts forming a query (theta already applied)
* theta, the current substitution, initially the empty substitution {}
*
*/
private List> folbcask(FOLKnowledgeBase KB,
BCAskAnswerHandler ansHandler, List goals,
Map theta) {
List> thisLevelProofSteps = new ArrayList>();
// local variables: answers, a set of substitutions, initially empty
// if goals is empty then return {theta}
if (goals.isEmpty()) {
thisLevelProofSteps.add(new ArrayList());
return thisLevelProofSteps;
}
// qDelta <- SUBST(theta, FIRST(goals))
Literal qDelta = KB.subst(theta, goals.get(0));
// for each sentence r in KB where
// STANDARDIZE-APART(r) = (p1 ^ ... ^ pn => q)
for (Clause r : KB.getAllDefiniteClauses()) {
r = KB.standardizeApart(r);
// and thetaDelta <- UNIFY(q, qDelta) succeeds
Map thetaDelta = KB.unify(r.getPositiveLiterals()
.get(0).getAtomicSentence(), qDelta.getAtomicSentence());
if (null != thetaDelta) {
// new_goals <- [p1,...,pn|REST(goals)]
List newGoals = new ArrayList(
r.getNegativeLiterals());
newGoals.addAll(goals.subList(1, goals.size()));
// answers <- FOL-BC-ASK(KB, new_goals, COMPOSE(thetaDelta,
// theta)) U answers
Map composed = compose(KB, thetaDelta, theta);
List> lowerLevelProofSteps = folbcask(
KB, ansHandler, newGoals, composed);
ansHandler.addProofStep(lowerLevelProofSteps, r, qDelta,
composed);
thisLevelProofSteps.addAll(lowerLevelProofSteps);
}
}
// return answers
return thisLevelProofSteps;
}
// Artificial Intelligence A Modern Approach (2nd Edition): page 288.
// COMPOSE(delta, tau) is the substitution whose effect is identical to
// the effect of applying each substitution in turn. That is,
// SUBST(COMPOSE(theta1, theta2), p) = SUBST(theta2, SUBST(theta1, p))
private Map compose(FOLKnowledgeBase KB,
Map theta1, Map theta2) {
Map composed = new HashMap();
// So that it behaves like:
// SUBST(theta2, SUBST(theta1, p))
// There are two steps involved here.
// See: http://logic.stanford.edu/classes/cs157/2008/notes/chap09.pdf
// for a detailed discussion:
// 1. Apply theta2 to the range of theta1.
for (Variable v : theta1.keySet()) {
composed.put(v, KB.subst(theta2, theta1.get(v)));
}
// 2. Adjoin to delta all pairs from tau with different
// domain variables.
for (Variable v : theta2.keySet()) {
if (!theta1.containsKey(v)) {
composed.put(v, theta2.get(v));
}
}
return cascadeSubstitutions(KB, composed);
}
// See:
// http://logic.stanford.edu/classes/cs157/2008/miscellaneous/faq.html#jump165
// for need for this.
private Map cascadeSubstitutions(FOLKnowledgeBase KB,
Map theta) {
for (Variable v : theta.keySet()) {
Term t = theta.get(v);
theta.put(v, KB.subst(theta, t));
}
return theta;
}
class BCAskAnswerHandler implements InferenceResult {
private List proofs = new ArrayList();
public BCAskAnswerHandler() {
}
//
// START-InferenceResult
public boolean isPossiblyFalse() {
return proofs.size() == 0;
}
public boolean isTrue() {
return proofs.size() > 0;
}
public boolean isUnknownDueToTimeout() {
return false;
}
public boolean isPartialResultDueToTimeout() {
return false;
}
public List getProofs() {
return proofs;
}
// END-InferenceResult
//
public void setAllProofSteps(List> allProofSteps) {
for (List steps : allProofSteps) {
ProofStepBwChGoal lastStep = steps.get(steps.size() - 1);
Map theta = lastStep.getBindings();
proofs.add(new ProofFinal(lastStep, theta));
}
}
public void addProofStep(
List> currentLevelProofSteps,
Clause toProve, Literal currentGoal,
Map bindings) {
if (currentLevelProofSteps.size() > 0) {
ProofStepBwChGoal predecessor = new ProofStepBwChGoal(toProve,
currentGoal, bindings);
for (List steps : currentLevelProofSteps) {
if (steps.size() > 0) {
steps.get(0).setPredecessor(predecessor);
}
steps.add(0, predecessor);
}
}
}
}
}