// OO jDREW - An Object Oriented extension of the Java Deductive Reasoning Engine for the Web
// Copyright (C) 2005 Marcel Ball
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
package org.ruleml.oojdrew.TopDown;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Stack;
import java.util.Vector;
import javax.swing.tree.DefaultMutableTreeNode;
import org.ruleml.oojdrew.Reasoner;
import org.ruleml.oojdrew.Builtins.AbsBuiltin;
import org.ruleml.oojdrew.Builtins.AddBuiltin;
import org.ruleml.oojdrew.Builtins.Builtin;
import org.ruleml.oojdrew.Builtins.CeilingBuiltin;
import org.ruleml.oojdrew.Builtins.ContainsBuiltin;
import org.ruleml.oojdrew.Builtins.ContainsIgnoreCaseBuiltin;
import org.ruleml.oojdrew.Builtins.CosBuiltin;
import org.ruleml.oojdrew.Builtins.DateBuiltin;
import org.ruleml.oojdrew.Builtins.DateTimeBuiltin;
import org.ruleml.oojdrew.Builtins.DivideBuiltin;
import org.ruleml.oojdrew.Builtins.EndsWithBuiltin;
import org.ruleml.oojdrew.Builtins.EqualBuiltin;
import org.ruleml.oojdrew.Builtins.FloorBuiltin;
import org.ruleml.oojdrew.Builtins.GreaterThanBuiltin;
import org.ruleml.oojdrew.Builtins.GreaterThanOrEqualBuiltin;
import org.ruleml.oojdrew.Builtins.IntegerDivideBuiltin;
import org.ruleml.oojdrew.Builtins.LessThanBuiltin;
import org.ruleml.oojdrew.Builtins.LessThanOrEqualBuiltin;
import org.ruleml.oojdrew.Builtins.ModBuiltin;
import org.ruleml.oojdrew.Builtins.MultiplyBuiltin;
import org.ruleml.oojdrew.Builtins.NotEqualBuiltin;
import org.ruleml.oojdrew.Builtins.PowBuiltin;
import org.ruleml.oojdrew.Builtins.ReplaceBuiltin;
import org.ruleml.oojdrew.Builtins.RoundBuiltin;
import org.ruleml.oojdrew.Builtins.SinBuiltin;
import org.ruleml.oojdrew.Builtins.StartsWithBuiltin;
import org.ruleml.oojdrew.Builtins.StringConcatBuiltin;
import org.ruleml.oojdrew.Builtins.StringEqualIgnoreCaseBuiltin;
import org.ruleml.oojdrew.Builtins.StringLengthBuiltin;
import org.ruleml.oojdrew.Builtins.StringLowerCaseBuiltin;
import org.ruleml.oojdrew.Builtins.StringUpperCaseBuiltin;
import org.ruleml.oojdrew.Builtins.SubstringAfterBuiltin;
import org.ruleml.oojdrew.Builtins.SubstringBeforeBuiltin;
import org.ruleml.oojdrew.Builtins.SubstringBuiltin;
import org.ruleml.oojdrew.Builtins.SubtractBuiltin;
import org.ruleml.oojdrew.Builtins.TanBuiltin;
import org.ruleml.oojdrew.Builtins.TimeBuiltin;
import org.ruleml.oojdrew.Builtins.UnaryMinusBuiltin;
import org.ruleml.oojdrew.Builtins.UnaryPlusBuiltin;
import org.ruleml.oojdrew.TopDown.Builtins.AssertBuiltin;
import org.ruleml.oojdrew.TopDown.Builtins.RegisterBuiltin;
import org.ruleml.oojdrew.TopDown.Builtins.TDBuiltin;
import org.ruleml.oojdrew.parsing.POSLParser;
import org.ruleml.oojdrew.util.DefiniteClause;
import org.ruleml.oojdrew.util.EngineException;
import org.ruleml.oojdrew.util.SymbolTable;
import org.ruleml.oojdrew.util.Term;
/**
* Based upon the backward reasoner from the original jDREW by Bruce Spencer.
*
* <p>Title: OO jDREW</p>
*
* <p>Description: Reasoning Engine for the Semantic Web - Supporting OO RuleML
* 0.88</p>
*
* <p>Copyright: Copyright (c) 2005</p>
*
* @author Marcel A. Ball
* @version 0.89
*/
public class BackwardReasoner implements Reasoner {
/**
*
*/
private static final int REPORT_EVERY_N_INFERENCES = 5000;
/**
*
*/
public Hashtable clauses;
public Hashtable oids;
public int count = 0;
/**
*
*/
private Hashtable builtins;
public Hashtable getBuiltins(){
return builtins;
}
/**
*
*/
private Stack choicePoints;
/**
*
*/
private int nExtensions;
/**
*
*/
private int nInferences;
/**
*
*/
private GoalList top;
/**
*
*/
//Logger logger = Logger.getLogger("jdrew.oo.td.BackwardReasoner");
/**
*
*/
public BackwardReasoner() {
this(new Hashtable(), new Hashtable());
oids.put(-1, new Vector());
}
/**
*
* @param clauses Hashtable
*/
public BackwardReasoner(Hashtable clauses, Hashtable oids) {
this.clauses = clauses;
this.oids = oids;
choicePoints = new Stack();
nExtensions = 0;
nInferences = 0;
builtins = new Hashtable();
registerBuiltins();
}
/**
*
*/
public void registerBuiltins() {
this.registerBuiltin(new AssertBuiltin(this));
this.registerBuiltin(new RegisterBuiltin(this, "registerBuiltin"));
this.registerBuiltin(new AbsBuiltin());
this.registerBuiltin(new AddBuiltin());
this.registerBuiltin(new CeilingBuiltin());
this.registerBuiltin(new ContainsBuiltin());
this.registerBuiltin(new ContainsIgnoreCaseBuiltin());
this.registerBuiltin(new CosBuiltin());
this.registerBuiltin(new DivideBuiltin());
this.registerBuiltin(new EndsWithBuiltin());
this.registerBuiltin(new EqualBuiltin());
this.registerBuiltin(new FloorBuiltin());
this.registerBuiltin(new GreaterThanBuiltin());
this.registerBuiltin(new GreaterThanOrEqualBuiltin());
this.registerBuiltin(new IntegerDivideBuiltin());
this.registerBuiltin(new LessThanBuiltin());
this.registerBuiltin(new LessThanOrEqualBuiltin());
this.registerBuiltin(new ModBuiltin());
this.registerBuiltin(new MultiplyBuiltin());
this.registerBuiltin(new NotEqualBuiltin());
this.registerBuiltin(new PowBuiltin());
this.registerBuiltin(new RoundBuiltin());
this.registerBuiltin(new SinBuiltin());
this.registerBuiltin(new StartsWithBuiltin());
this.registerBuiltin(new StringConcatBuiltin());
this.registerBuiltin(new StringEqualIgnoreCaseBuiltin());
this.registerBuiltin(new StringLengthBuiltin());
this.registerBuiltin(new StringLowerCaseBuiltin());
this.registerBuiltin(new StringUpperCaseBuiltin());
this.registerBuiltin(new SubstringBuiltin());
this.registerBuiltin(new SubtractBuiltin());
this.registerBuiltin(new TanBuiltin());
this.registerBuiltin(new DateBuiltin());
this.registerBuiltin(new TimeBuiltin());
this.registerBuiltin(new DateTimeBuiltin());
this.registerBuiltin(new SubstringAfterBuiltin());
this.registerBuiltin(new SubstringBeforeBuiltin());
this.registerBuiltin(new ReplaceBuiltin());
this.registerBuiltin(new UnaryPlusBuiltin());
this.registerBuiltin(new UnaryMinusBuiltin());
}
/**
*
* @param handler Builtin
*/
public void registerBuiltin(Builtin handler) {
registerBuiltin(new TDBuiltin(handler));
}
/**
*
* @param handler TDBuiltin
*/
public void registerBuiltin(TDBuiltin handler) {
Integer sym = handler.getSymbol();
this.builtins.put(sym, handler);
}
/**
*
* @param it
*/
public void loadClauses(Iterator it) {
while (it.hasNext()) {
DefiniteClause dc = (DefiniteClause) it.next();
//logger.debug("Loaded clause: " + dc.toPOSLString());
Integer sym = dc.atoms[0].getSymbol();
if(!dc.atoms[0].subTerms[0].isExpr()){
int ioid = dc.atoms[0].subTerms[0].getSymbol();
if(ioid < 0) ioid = -1;
//logger.debug("Loading oid: " + ioid);
Integer oid = ioid;
if (oids.containsKey(oid)) {
//if(ioid != -1)
//logger.warn("Duplicate OID: " + SymbolTable.symbol(oid.intValue()));
Vector v = (Vector) oids.get(oid);
v.add(dc);
} else {
Vector v = new Vector();
v.add(dc);
oids.put(oid, v);
}
}
if (clauses.containsKey(sym)) {
Vector v = (Vector) clauses.get(sym);
v.add(dc);
} else {
Vector v = new Vector();
v.add(dc);
clauses.put(sym, v);
}
}
}
public void clearClauses()
{
clauses.clear();
}
/**
*
* @return
*/
public String toString() {
StringBuffer b = new StringBuffer();
b.append("Size " + nExtensions + "\n");
toString(1, top, b);
return b.toString();
}
/**
*
* @param indent int
* @param gl GoalList
* @param b StringBuffer
*/
private void toString(int indent, GoalList gl, StringBuffer b) {
for (int i = 0; i < indent; i++) {
b.append(" ");
}
b.append(gl.toString());
b.append("\n");
for (int i = 1; i < gl.atomCount; i++) {
if (gl.memberGoals[i].state == Goal.HAS_SUBGOALLIST_STATE) {
toString(indent + 2, gl.memberGoals[i].subGoalList, b);
}
}
}
/**
* Methods to generate a DefaulteMultibleTreeNode representation of the
* solution tree. This can be displayed in a JTree
* @return DefaultMutableTreeNode
*/
public DefaultMutableTreeNode toTree() {
return toTree(top);
}
/**
*
* @param gl GoalList
* @return DefaultMutableTreeNode
*/
private DefaultMutableTreeNode toTree(GoalList gl) {
//System.out.println(gl.toString());
DefaultMutableTreeNode tn = new DefaultMutableTreeNode();
tn.setUserObject(gl.toString());
for (int i = 1; i < gl.atomCount; i++) {
if (gl.memberGoals[i].state == Goal.HAS_SUBGOALLIST_STATE) {
tn.add(toTree(gl.memberGoals[i].subGoalList));
}
}
return tn;
}
/**
*
* <p>Title: OO jDREW</p>
*
* <p>Description: Reasoning Engine for the Semantic Web - Supporting OO RuleML
* 0.88</p>
*
* <p>Copyright: Copyright (c) 2005</p>
*
* @author Marcel A. Ball
* @version 0.89
*/
public class DepthFirstSolutionIterator implements Iterator {
boolean foundNext;
boolean failed;
boolean enteringFirstTime;
//Constructor Takes a goal list.
DepthFirstSolutionIterator(GoalList goalList) {
foundNext = false;
failed = false;
enteringFirstTime = true;
top = goalList;
top.init();
}
//Constructor takes a Definite Clause for a query
DepthFirstSolutionIterator(DefiniteClause queryClause) {
this(new GoalList(queryClause));
}
//Check to see if there is a next goal
public boolean hasNext() {
if (foundNext) {
return true;
} else if (failed) {
foundNext = false;
return false;
} else {
if (!enteringFirstTime) {
if (!chronologicalBacktrack()) {
failed = true;
}
}
boolean succeeded = false;
while (!failed && !succeeded) {
Goal g = firstOpenGoal();
//if (g != null) {
//logger.debug("New Goal is " + g + " in state " + g.stateToString());
//}
if (g == null) {
succeeded = true;
} else if (g.hasMoreChoices()) {
if (g == null) {
System.err.println("GEE IS NULL");
}
if (choicePoints == null) {
System.err.println("CHOICEPOINTS IS NULL");
}
choicePoints.push(g);
g.nextChoice(Goal.PROPAGATE_WHEN_SOLVED);
} else {
g.refreshChoices();
if (!chronologicalBacktrack()) {
failed = true;
}
}
}
foundNext = succeeded;
return foundNext;
}
}
/**
*
* @return Object
*/
//Top is a goal list
public Object next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
foundNext = false;
enteringFirstTime = false;
return top;
}
/**
*
* @throws UnsupportedOperationException
*/
public void remove() throws UnsupportedOperationException {
throw new UnsupportedOperationException
("DepthFirstSolutionIterator does not allow remove");
}
/**
*
* @return boolean
*/
private boolean chronologicalBacktrack() {
//go back to most recent choicepoint that has something to try
//unbind any variables that were done since that choice
boolean foundSomethingToRetry = false;
while (!choicePoints.empty() && !foundSomethingToRetry) {
Goal g = (Goal) choicePoints.pop();
//logger.debug("BTing - Removing " + g.subGoalList +
// " from " + g);
g.undoChoice();
if (g.hasMoreChoices()) {
foundSomethingToRetry = true;
// logger.debug("BTing - found something to retry: " + g);
} else {
g.refreshChoices();
}
}
return foundSomethingToRetry;
}
}
/**
*
* <p>Title: OO jDREW</p>
*
* <p>Description: Reasoning Engine for the Semantic Web - Supporting OO RuleML
* 0.88</p>
*
* <p>Copyright: Copyright (c) 2005</p>
*
* @author Marcel A. Ball
* @version 0.89
*/
public class IterativeDepthFirstSolutionIterator implements Iterator {
private boolean foundNext;
private boolean failed;
private boolean enteringFirstTime;
private int max;
private int by;
private boolean bumpedIntoLimit;
IterativeDepthFirstSolutionIterator(GoalList goalList, int max, int by) {
foundNext = false;
failed = false;
enteringFirstTime = true;
this.max = max;
this.by = by;
bumpedIntoLimit = false;
top = goalList;
top.init();
}
IterativeDepthFirstSolutionIterator(DefiniteClause queryClause, int max, int by) {
this(new GoalList(queryClause), max, by);
}
public boolean hasNext() {
if (foundNext) {
return true;
} else if (failed && !bumpedIntoLimit) {
foundNext = false;
return false;
} else {
if (!enteringFirstTime) {
if (!iterativeDepthChronologicalBacktrack()) {
failed = true;
}
}
boolean succeeded = false;
while (!(failed && !bumpedIntoLimit) && !succeeded) {
//failure in this search method means failure without
//having hit the depth limit, hence "!failed" is
//replaced by "!(failed && !bumpedIntoLimit)"
Goal g = firstOpenGoal();
///if (g != null) {
// logger.debug("New Goal is " + g + " in state " + g.stateToString());
//}
if (g == null) {
if (max - by < nExtensions && nExtensions <= max) {
succeeded = true;
} else {
if (!iterativeDepthChronologicalBacktrack()) {
failed = true;
}
}
} else if (g.hasMoreChoices()) {
int nextChoiceSize = g.nextChoiceSize();
choicePoints.push(g);
g.nextChoice(Goal.PROPAGATE_WHEN_SOLVED);
//logger.debug("NExtensions = " + nExtensions + "\nnextChoiceSize = " + nextChoiceSize + "\nmax = " + max);
if (nExtensions + nextChoiceSize > max) {
//uses the number of new subgoals as an admissible heuristic in an a* search
bumpedIntoLimit = true;
if (!iterativeDepthChronologicalBacktrack()) {
failed = true;
}
}
} else {
g.refreshChoices();
if (!iterativeDepthChronologicalBacktrack()) {
failed = true;
}
}
}
foundNext = succeeded;
return foundNext;
}
}
/**
*
* @return Object
*/
public Object next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
foundNext = false;
enteringFirstTime = false;
return top;
}
/**
*
* @throws UnsupportedOperationException
*/
public void remove() throws UnsupportedOperationException {
throw new UnsupportedOperationException
("DepthFirstSolutionIterator does not allow remove");
}
/**
*
* @return boolean
*/
private boolean iterativeDepthChronologicalBacktrack() {
//go back to most recent choicepoint that has something to try
//unbind any variables that were done since that choice
boolean foundSomethingToRetry = false;
while (!choicePoints.empty() && !foundSomethingToRetry) {
Goal g = (Goal) choicePoints.pop();
// logger.debug("BTing - Removing " + g.subGoalList + " from " + g);
g.undoChoice();
if (g.hasMoreChoices()) {
foundSomethingToRetry = true;
//logger.debug("BTing - found something to retry: " + g);
} else {
g.refreshChoices();
}
}
if (!foundSomethingToRetry) {
if (bumpedIntoLimit) {
max += by;
//logger.debug("Increasing the limit from " + (max - by) + " to " + max);
bumpedIntoLimit = false;
foundSomethingToRetry = true;
}
}
return foundSomethingToRetry;
}
}
/**
*
* @param queryClause
* @return
*/
public Iterator depthFirstSolutionIterator(DefiniteClause queryClause) {
return new DepthFirstSolutionIterator(queryClause);
}
/**
*
* @param goalList
* @return
*/
public Iterator depthFirstSolutionIterator(GoalList goalList) {
return new DepthFirstSolutionIterator(goalList);
}
/**
*
* @param queryClause
* @param max
* @param by
* @return
*/
public Iterator iterativeDepthFirstSolutionIterator(DefiniteClause queryClause, int max, int by) {
return new IterativeDepthFirstSolutionIterator(queryClause, max, by);
}
/**
*
* @param queryClause - the queryClause contains the internal representation of the query to be executed
* @return Iterator the Iterator contains all the solutions to the query
*/
public Iterator iterativeDepthFirstSolutionIterator(DefiniteClause queryClause) {
return new IterativeDepthFirstSolutionIterator(queryClause, 1, 1);
}
/**
*
* @param goalList
* @param max
* @param by
* @return
*/
public Iterator iterativeDepthFirstSolutionIterator(GoalList goalList, int max, int by) {
return new IterativeDepthFirstSolutionIterator(goalList, max, by);
}
/**
*
* @param goalList
* @return
*/
public Iterator iterativeDepthFirstSolutionIterator(GoalList goalList) {
return new IterativeDepthFirstSolutionIterator(goalList, 1, 1);
}
/**
*
* @return
*/
public Goal firstOpenGoal() {
//This is a shortcut to finding the first open goal
// we will develop a better strategy involving an openGoals iterator
return top.firstOpenGoal();
}
/**
* Goal - contains an atom (atomic formula) to be solved. Each Goal object
* belongs to exactly one GoalList. Also each Goal can have at most one
* SubGoalList attached to it at a given time. The SubGoalList is attached by
* calling attachSubGoalList, and this GoalList forms the children of the Goal
* in the GoalTree. The possible GoalLists relevant for this Goal are created
* by first constructing the SubGoalListIterator and then calling next() on
* this Iterator to generate successively the SubGoalLists. By relevant, we
* mean that the Goal is unifiable with the head atom of a Definite Clause in
* the BackwardReasoner's input clauses.
*
* <p> A Goal may be partially or fully solved. A Goal with an attached
* SubGoalList is fully solved if that SubGoalList is empty, or if all of the
* Goals in that SubGoalList are solved. In this case we also say that the
* SubGoalList is solved. Solved is synonymous with fully solved. A Goal or
* its attached SubGoalList is partially solved if not fully solved and not
* failed (see below).
*
* <p> A Goal may also become failed, which means that there are no
* SubGoalLists available for it, or all SubGoalLists available for it are
* failed GoalLists. A failed GoalList contains at least one failed Goal. A
* Goal informs the GoalList of which it is a member that is a failed Goal by
* calling failed(this) on the GoalList. Recall that a Goal knows this only
* after all of its SubGoalLists have been found to be failed GoalLists, and
* since this is under programmer control, it is up to the programmer to make
* the call to failed. Depending on the client program, this method may or
* may not be used as it is sets variables that are provided only for the
* user's convenience of record keeping, and do not initiate any other
* activity in the BackwardReasoner.
*
* <p>Title: OO jDREW</p>
* <p>Description: A deductive reasoning engine for Object-Oriented Knowledge Representation in OO RuleML</p>
* <p>Copyright: Copyright (c) 2003</p>
* <p>Company: National Research Council of Canada</p>
* @author Marcel Ball
* @version 0.83
*/
public class Goal {
public static final int NO_AUTO_PROPAGATE = 0;
public static final int PROPAGATE_WHEN_SOLVED = 1;
public static final int PROPAGATE_EAGERLY = 2;
public static final int INITIAL_STATE = 0;
public static final int HAS_SUBGOALLIST_STATE = 1;
public static final int HAS_NO_SUBGOALLIST_STATE = 2;
public static final int HAS_BUILTIN_SOLUTION_STATE = 3;
public static final int HAS_NO_BUILTIN_SOLUTION_STATE = 4;
public static final int HAS_NAF_SOLUTION_STATE = 5;
public static final int HAS_NO_NAF_SOLUTION_STATE = 6;
public static final int HAS_UNCONSUMED_NAF_SOLUTION_STATE = 7;
public static final int PROVE_BY_SUBGOALLIST = 0;
public static final int PROVE_BY_BUILTIN = 1;
public static final int PROVE_BY_NAF = 2;
SubGoalListIterator sglit;
GoalList goalList;
int literalIndex;
int symbolIndex;
int propagateMode;
int state;
int proveByType;
boolean solved;
GoalList subGoalList;
/**
*
* @param goalList GoalList
* @param literalIndex int
*/
Goal(GoalList goalList, int literalIndex) {
this.goalList = goalList;
this.literalIndex = literalIndex;
solved = false;
subGoalList = null;
state = INITIAL_STATE;
}
/**
*
* @return String
*/
String stateToString() {
switch (state) {
case INITIAL_STATE: {
return "INITIAL_STATE";
}
case HAS_SUBGOALLIST_STATE: {
return "HAS_SUBGOALLIST_STATE";
}
case HAS_NO_SUBGOALLIST_STATE: {
return "HAS_NO_SUBGOALLIST_STATE";
}
case HAS_BUILTIN_SOLUTION_STATE: {
return "HAS_BUILTIN_SOLUTION_STATE";
}
case HAS_NO_BUILTIN_SOLUTION_STATE: {
return "HAS_NO_BUILTIN_SOLUTION_STATE";
}
case HAS_NAF_SOLUTION_STATE: {
return "HAS_NAF_SOLUTION_STATE";
}
case HAS_NO_NAF_SOLUTION_STATE: {
return "HAS_NO_NAF_SOLUTION_STATE";
}
case HAS_UNCONSUMED_NAF_SOLUTION_STATE: {
return "HAS_UNCONSUMED_NAF_SOLUTION_STATE";
}
}
return "No string for this state " + state;
}
/**
*
* @return boolean
*/
boolean nafGoal() {
return goalList.atoms[symbolIndex].getSymbol() == SymbolTable.INAF;
}
/**
*
* @return boolean
*/
boolean hasMoreChoices() {
// logger.debug("Entering hasMoreChoices()");
if (proveByType == PROVE_BY_SUBGOALLIST) {
return hasMoreSubGoalLists();
} else if (proveByType == PROVE_BY_NAF) {
if ((state == HAS_NO_NAF_SOLUTION_STATE) || (state == HAS_NAF_SOLUTION_STATE)) {
return false;
} else if (state == INITIAL_STATE) {
// logger.debug("Proving by NAF:");
Term nafAtom = this.goalList.atoms[this.symbolIndex];
String goalString = "";
Term[] nafAtomterms = nafAtom.getSubTerms();
for (int i = 0; i < nafAtomterms.length; i++) {
goalString += nafAtomterms[i].toPOSLString(this.goalList.variableNames);
if ((i + 1) < nafAtomterms.length) {
goalString += ",";
}
}
goalString += ".";
// logger.debug("Naf goal string: " + goalString);
BackwardReasoner nafTree = null;
DefiniteClause nafQueryClause = null;
try {
POSLParser dcfp = new POSLParser();
try {
nafQueryClause = dcfp.parseQueryString(goalString);
} catch (antlr.RecognitionException ex1) {
// logger.error("Cannot parse naf clause: " +
// goalString);
// logger.error(
// "This should never happen as this is a generated clause string.");
return false;
} catch (antlr.TokenStreamException ex1) {
// logger.error("IO Exception parsing naf clause.");
return false;
}
// logger.debug("New BackwardReasoner for naf clause: "
// + goalString);
nafTree = new BackwardReasoner(clauses, oids);
} catch (RuntimeException e) {
// logger.error("Runtime exception creating naf clause.");
return false;
}
Iterator nafSolver = nafTree.depthFirstSolutionIterator(nafQueryClause);
if (nafSolver.hasNext()) {
// logger.debug("NO NAF solution");
state = HAS_NO_NAF_SOLUTION_STATE;
} else {
// logger.debug("NAF soltuion");
state = HAS_UNCONSUMED_NAF_SOLUTION_STATE;
}
}
return state == HAS_UNCONSUMED_NAF_SOLUTION_STATE;
}
// else if (provedByType == PROVED_BY_BUILTIN){
else {
return false; // Can't happen unless proveByType is missing
}
}
/**
*
* @param propagateMode int
*/
void nextChoice(int propagateMode) {
//logger.debug("Trying next choice for " + this);
if (!hasMoreChoices()) {
throw new EngineException("Attempted next choice for " +
this +", but no more choices exist");
} else if (proveByType == PROVE_BY_SUBGOALLIST) {
attachNextSubGoalList(propagateMode);
} else if (proveByType == PROVE_BY_NAF) {
solved = true;
state = HAS_NAF_SOLUTION_STATE;
goalList.notifySolved(this);
}
// else if (provedByType == PROVED_BY_BUILTIN){
nExtensions++;
nInferences++;
if (BackwardReasoner.REPORT_EVERY_N_INFERENCES > 0) {
if (nInferences % BackwardReasoner.REPORT_EVERY_N_INFERENCES == 0) {
// logger.info("Performed " + nInferences +
// " inferences");
}
}
}
/**
*
* @return int
*/
int nextChoiceSize() {
if (!hasMoreChoices()) {
throw new EngineException("Attempted next choice for " + this + ", but no more choices exist");
} else if (proveByType == PROVE_BY_SUBGOALLIST) {
return sglit.nextGoalListSize() - 1;
} else if (proveByType == PROVE_BY_NAF) {
return 0;
} else {
return 1;
}
}
/**
*
*/
void undoChoice() {
//logger.debug("Undoing choice for " + this);
if (proveByType == PROVE_BY_SUBGOALLIST) {
removeSubGoalList();
} else if (proveByType == PROVE_BY_NAF) {
removeNAFSolve();
state = HAS_NO_NAF_SOLUTION_STATE;
}
//else if (proveByType == PROVED_BY_BUILTIN)
nExtensions--;
}
/**
*
*/
void refreshChoices() {
//logger.debug("refreshing choices for " + this);
if (proveByType == PROVE_BY_SUBGOALLIST) {
state = INITIAL_STATE;
} else if (proveByType == PROVE_BY_NAF) {
state = INITIAL_STATE;
//else if (proveByType == PROVED_BY_BUILTIN)
}
}
/**
*
*/
void removeNAFSolve() {
if (solved) {
//should be solved. If not we should not be here
goalList.notifyUnsolved(this);
solved = false;
}
}
/**
*
* @return boolean
*/
boolean hasMoreSubGoalLists() {
if (state == INITIAL_STATE) {
state = HAS_NO_SUBGOALLIST_STATE;
sglit = new SubGoalListIterator(this, SubGoalListIterator.APPLY_TO_GOALLIST);
//if (true) {
// SubGoalListIterator sglit_1 = new SubGoalListIterator(this,
// SubGoalListIterator.APPLY_TO_GOALLIST);
//logger.debug("Choices for Goal " + this +" are ");
// int count = 0;
// while (sglit_1.hasNext()) {
// count++;
//logger.debug(count + " " + sglit_1.next());
// }
//logger.debug("Total of " + count + " choices");
//}
}
return sglit.hasNext();
}
/**
*
* @param propagateMode int
*/
void attachNextSubGoalList(int propagateMode) {
//have already checked that a new subgoalist exists
//first undo any bindings that might already have been propagated
if (state == HAS_SUBGOALLIST_STATE) {
removeSubGoalList();
}
GoalList subGoalList = (GoalList) sglit.next();
//logger.debug("Adding " + subGoalList + " to goal " + this);
this.subGoalList = subGoalList;
this.propagateMode = propagateMode;
subGoalList.setParent(this);
state = HAS_SUBGOALLIST_STATE;
if (subGoalList.solved()) {
solved = true;
subGoalList.propagateBindingsToParent();
goalList.notifySolved(this);
}
if (propagateMode == PROPAGATE_EAGERLY) {
subGoalList.propagateBindingsToParent();
}
}
/**
*
*/
void removeSubGoalList() {
//logger.debug("Removing " + this.subGoalList + " from goal " + this);
if (solved) {
goalList.notifyUnsolved(this);
solved = false;
}
state = HAS_NO_SUBGOALLIST_STATE;
subGoalList = null;
}
/**
*
*/
public void setSymbolIndex() {
goalList.setSymbolIndex();
}
/**
*
* @return String
*/
public String toString() {
StringBuffer b = new StringBuffer();
setSymbolIndex();
b.append(this.goalList.atoms[this.symbolIndex].toPOSLString(this.
goalList.variableNames));
return b.toString();
}
} //Goal
/**
* GoalList - contains a list of Goals and is attached to a specific Goal
* which is called its parent. If and when a Goal in the list becomes solved,
* or partially solved, the effect is to bind variables in the atomic formula.
* These bindings are immediately applied to the sibling Goals in this
* SubGoalList. They may also be propogated to the parent Goal, by calling
* subGoalListSolved() on the parent when the parent is attached to this
* SubGoalList. By default, this call must be done by the programmmer, but
* the default behaviour can be set to PROPAGATE_FULLY_SOLVED, which changes
* it so that subGoalSolved is called on the parent when the SubGoalList is
* fully solved. The default behaviour can also be set to PROPAGATE_EAGERLY
* which will propagate eagerly: subGoalSolved is called automatically
* whenever a GoalList becomes more fully solved by the solution of one more
* of its member Goals.
*
* <p> Because a Goal may be attached to a failed SubGoalList, but other
* SubGoalLists are still available for it, it is important to be able undo
* the effects of this failed SubGoalList on the Goal, and consequently
* effects that have may been propagated throughout the BackwardReasoner. Thus each Goal
* and each GoalList has a backup facility that makes it possible to retrieve
* a previous version, replacing the old values the variable bindings. In the
* case of the GoalList, the backup facility also retrieves a records of what
* member Goals have been solved. The backup facility is used through the
* createBackup() and restoreBackup() routines. Thus backups are done
* manually, not automatically, and are based on a stack; N calls to
* createBackup() followed by M calls to restoreBackup(), when N<=M, will
* restore to the point when N-M call to createBackup() was made. If M>N, an
* exception is thrown. (We may consider automatic backups through the tree
* but the problem is that backups need to be synchronized so that the tree is
* brought back to a consistent overall state. This is also the reason why
* propagation from GoalLists to Goals is not automatic by default. One can
* keep the effects of changes to the Clause tree local in a GoalList until
* that GoalList is fully solved, and only then propagate it. This removes
* the need to undo the results of propagating partially solved GoalLists
* through the tree.)
*
* <p>Title: OO jDREW</p>
* <p>Description: A deductive reasoning engine for Object-Oriented Knowledge Representation in OO RuleML</p>
* <p>Copyright: Copyright (c) 2003</p>
* <p>Company: National Research Council of Canada</p>
* @author Marcel Ball
* @version 0.83
*/
public class GoalList {
public Hashtable varBindings;
public int varCount;
public String[] variableNames;
public boolean hasVariableNames;
public int atomCount = 0;
public Term[] atoms;
public Goal parent;
public Goal[] memberGoals;
boolean hasParent = false;
boolean solved = false;
private Stack s;
/**
*
* @param dc DefiniteClause
*/
GoalList(DefiniteClause dc) {
atoms = dc.atoms;
varCount = dc.variableNames.length;
hasVariableNames = true;
variableNames = dc.variableNames;
varBindings = new Hashtable();
s = new Stack();
}
/**
*
*/
void init() {
countAtoms();
memberGoals = new Goal[atomCount];
memberGoals[0] = null;
for (int i = 1; i < atomCount; i++) {
memberGoals[i] = new Goal(this, i);
}
setSymbolIndex();
for (int i = 1; i < atomCount; i++) {
if (memberGoals[i].nafGoal()) {
memberGoals[i].proveByType = Goal.PROVE_BY_NAF;
} else {
memberGoals[i].proveByType = Goal.PROVE_BY_SUBGOALLIST;
}
}
}
/**
*
*/
void countAtoms() {
atomCount = atoms.length;
}
/**
*
*/
void setSymbolIndex() {
int symbolIndex = 0;
for (int i = 1; i < atomCount; i++) {
symbolIndex++;
memberGoals[i].symbolIndex = symbolIndex;
}
}
/**
*
* @param goal Goal
*/
void setParent(Goal goal) {
parent = goal;
hasParent = true;
}
/**
*
* @param g Goal
*/
void notifySolved(Goal g) {
if (solved()) {
if (hasParent) {
parent.solved = true;
if (parent.propagateMode == Goal.PROPAGATE_WHEN_SOLVED) {
propagateBindingsToParent();
}
parent.goalList.notifySolved(parent);
}
}
}
/**
*
* @return boolean
*/
boolean solved() {
int i = 1;
while (i < atomCount) {
if (!memberGoals[i].solved) {
return false;
} else {
i++;
}
}
return true;
}
/**
*
* @param g Goal
*/
void notifyUnsolved(Goal g) {
// logger.debug("Unsolving goal " + g);
if (g.propagateMode == Goal.PROPAGATE_WHEN_SOLVED) {
if (g.proveByType == Goal.PROVE_BY_SUBGOALLIST) {
g.goalList.restoreBackup();
// logger.debug("Restoring unsolved goal to " + g);
}
}
if (solved()) {
if (hasParent) {
parent.goalList.notifyUnsolved(parent);
}
}
g.solved = false;
}
/**
*
*/
public void propagateBindingsToParent() {
if (!hasParent) {
//logger.warn(this +" has no parent.");
//Should we throw an exception here?
} else {
//logger.debug("Moving variable bindings of " + this +" to " + parent);
Unifier u = new Unifier(parent, this, Unifier.DCTREE_MODE);
if (u.unified) {
parent.goalList.createBackup();
u.applyToGoal();
//this.setSymbolIndex();
//logger.debug("Variable bindings moved to " + parent);
} else {
throw new EngineException("Could not propagate bindings");
}
}
}
/**
* Used to display the Proof Tree in the GUI
*
* @return String
*/
public String toString() {
StringBuffer b = new StringBuffer();
//need this
b.append(this.atoms[0].toPOSLString(this.variableNames)); //top
//Reconstructing POSL in the goal list
//System.out.println("Test Line 1214 BR:");
//System.out.println(this.atoms[0].toPOSLString(this.variableNames));
if (this.atoms.length > 1) {
//and this
b.append(":-");
for (int i = 1; i < this.atoms.length; i++) {
b.append(this.atoms[i].toPOSLString(this.variableNames));
if (i + 1 < this.atoms.length) {
b.append(",");
}
}
}
b.append(".");
return b.toString();
}
public String toStringAll() {
StringBuffer b = new StringBuffer();
//need this
// b.append(this.atoms[0].toPOSLString(this.variableNames)); //top
//Reconstructing POSL in the goal list
//System.out.println("Test Line 1214 BR:");
//System.out.println(this.atoms[0].toPOSLString(this.variableNames));
if (this.atoms.length > 1) {
//and this
//b.append(":-");
for (int i = 1; i < this.atoms.length; i++) {
b.append(this.atoms[i].toPOSLStringAll(this.variableNames,false,true));
if (i + 1 < this.atoms.length) {
b.append(",");
}
}
}
b.append(".");
return b.toString();
}
/**
*
* @return Goal
*/
public Goal firstOpenGoal() {
for (int i = 1; i < atomCount; i++) {
Goal g = memberGoals[i];
if (g.proveByType == Goal.PROVE_BY_SUBGOALLIST) {
if (g.state != Goal.HAS_SUBGOALLIST_STATE &&
!g.solved) {
return g;
} else if (!g.solved) {
return g.subGoalList.firstOpenGoal();
}
} else if (g.proveByType == Goal.PROVE_BY_NAF) {
if (g.state == Goal.INITIAL_STATE) {
return g;
}
}
//else if (g.proveByType == Goal.PROVE_BY_BUILTIN)
//if(g.state == INITIAL) return g;
}
return null; //means no open goals
}
/**
*
* @return Goal
*/
public Goal head() {
return new Goal(this, 0);
}
/**
*
* @param idx int
* @return Term
*/
public Term getAtom(int idx) {
if ((idx < 0) || (idx >= this.atoms.length)) {
return null;
} else {
return this.atoms[idx].deepCopy();
}
}
/**
*
*/
public void createBackup() {
s.push(new StackFrame(atoms, varCount, hasVariableNames, variableNames));
}
/**
*
*/
public void restoreBackup() {
if (s.empty()) {
//logger.error("Popped empty stack");
} else {
StackFrame sf = (StackFrame) s.pop();
atoms = sf.atoms;
varCount = sf.varCount;
hasVariableNames = sf.hasVariableNames;
variableNames = sf.variableNames;
}
}
/**
*
*/
public void purgeBackup() {
if (s.empty()) {
throw new RuntimeException("Popped empty stack");
}
StackFrame sf = (StackFrame) s.pop();
}
/**
*
* <p>Title: OO jDREW</p>
*
* <p>Description: Reasoning Engine for the Semantic Web - Supporting OO RuleML
* 0.88</p>
*
* <p>Copyright: Copyright (c) 2005</p>
*
* @author Marcel A. Ball
* @version 0.89
*/
class StackFrame {
Term[] atoms;
int varCount;
boolean hasVariableNames;
String[] variableNames;
/**
*
* @param terms Term[]
* @param side int
* @return Term[]
*/
private Term[] cloneTermArray(Term[] terms, int side) {
Term[] nterms = new Term[terms.length];
for (int i = 0; i < nterms.length; i++) {
nterms[i] = terms[i].deepCopy(side);
}
return nterms;
}
/**
*
* @param atoms Term[]
* @param varCount int
* @param hasVariableNames boolean
* @param variableNames String[]
*/
StackFrame(Term[] atoms, int varCount, boolean hasVariableNames, String[] variableNames) {
this.atoms = cloneTermArray(atoms, 0);
this.varCount = varCount;
this.hasVariableNames = hasVariableNames;
if (hasVariableNames) {
this.variableNames = new String[varCount];
System.arraycopy(variableNames, 0, this.variableNames, 0, varCount);
}
}
}
/**
*
* @return String[]
*/
public String[] getVariableNames() {
return this.variableNames;
}
} //BackwardReasoner.GoalList
/**
*
* <p>Title: OO jDREW</p>
*
* <p>Description: Reasoning Engine for the Semantic Web - Supporting OO RuleML
* 0.88</p>
*
* <p>Copyright: Copyright (c) 2005</p>
*
* @author Marcel A. Ball
* @version 0.89
*/
public class SubGoalListIterator implements Iterator {
public static final int APPLY_TO_GOALLIST = 0;
public static final int APPLY_TO_GOAL_AND_GOALLIST = 1;
public static final int APPLY_TO_GOAL = 2;
private int mode;
public Iterator uit;
private GoalList nextGoalList;
private Goal goal;
private boolean foundNext = false;
/**
*
* @param goal Goal
* @param mode int
*/
SubGoalListIterator(Goal goal, int mode) {
this.goal = goal;
this.uit = this.getUnifiableIterator(goal.goalList, goal.literalIndex);
this.mode = mode;
if ((mode != APPLY_TO_GOALLIST) && (mode != APPLY_TO_GOAL_AND_GOALLIST) && (mode != APPLY_TO_GOAL)) {
throw new EngineException("SubGoalListIterator mode not valid");
}
}
/**
*
* @param gl GoalList
* @param term int
* @return Iterator
*/
private Iterator getUnifiableIterator(GoalList gl, int term) {
Term t = gl.getAtom(term);
Integer sym = t.getSymbol();
if (builtins.containsKey(sym)) {
// logger.debug("Using builtin for " +
// SymbolTable.symbol(sym.intValue()));
TDBuiltin b = (TDBuiltin) builtins.get(sym);
Vector v = new Vector();
DefiniteClause dc = b.buildResult(gl, term);
if (dc != null) {
v.add(dc);
}
return v.iterator();
}
if (!t.subTerms[0].isExpr() && t.subTerms[0].getSymbol() >= 0) {
// logger.debug("Retrieving by oid");
Integer oid = t.subTerms[0].getSymbol();
if (oids.containsKey(oid)) {
// logger.debug("Found oid: " + oid);
Vector v = (Vector) oids.get(oid);
Vector v2 = (Vector) oids.get(-1);
Vector v3 = new Vector();
v3.addAll(v);
v3.addAll(v2);
return v3.iterator();
} else {
// logger.debug("Did not find oid: " + oid);
Vector v = (Vector) oids.get(-1);
return v.iterator();
}
} else {
// logger.debug("Finding by symbol");
if (clauses.containsKey(sym)) {
Vector v = (Vector) clauses.get(sym);
return v.iterator();
} else {
Vector v = new Vector();
return v.iterator();
}
}
}
/**
*
* @return boolean
*/
public boolean hasNextOld() {
if (foundNext) {
return true;
} else if (!uit.hasNext()) {
return false;
} else {
nextGoalList = new GoalList((DefiniteClause) uit.next());
nextGoalList.init();
Unifier u = new Unifier(goal, nextGoalList, Unifier.DCTREE_MODE, true);
try {
if (u.unified) {
if ((mode == APPLY_TO_GOALLIST) || (mode == APPLY_TO_GOAL_AND_GOALLIST)) {
u.applyToGoalList();
// nextGoalList.setSymbolIndex();
}
if ((mode == APPLY_TO_GOAL_AND_GOALLIST) || (mode == APPLY_TO_GOAL)) {
goal.goalList.createBackup();
u.applyToGoal();
goal.setSymbolIndex();
}
foundNext = true;
return true;
} else {
count++;
System.out.println(count);
return hasNextOld();
}
} catch (Exception e) {
// logger.error(e.getMessage());
System.out.println("stack results: ");
e.printStackTrace();
return false;
}
}
}
//has next 2
public boolean hasNext() {
if (foundNext)
return true;
if (!uit.hasNext())
return false;
while (uit.hasNext()) {
nextGoalList = new GoalList((DefiniteClause) uit.next());
nextGoalList.init();
Unifier u = new Unifier(goal, nextGoalList, Unifier.DCTREE_MODE, true);
try {
if (u.unified) {
if ((mode == APPLY_TO_GOALLIST) || (mode == APPLY_TO_GOAL_AND_GOALLIST)) {
u.applyToGoalList();
}
if ((mode == APPLY_TO_GOAL_AND_GOALLIST) || (mode == APPLY_TO_GOAL)) {
goal.goalList.createBackup();
u.applyToGoal();
goal.setSymbolIndex();
}
foundNext = true;
return true;
}
} catch (Exception e) {
return false;
}
}
return false;
}
/**
*
* @return Object
*/
public Object next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
foundNext = false;
//logger.debug("SubGoalListIterator returns next as " + nextGoalList);
return nextGoalList;
}
/**
*
* @return int
*/
public int nextGoalListSize() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return nextGoalList.atomCount;
}
/**
* @throws UnsupportedOperationException
*/
public void remove() throws UnsupportedOperationException {
throw new UnsupportedOperationException("SubGoalListIterator does not allow remove");
}
}
} //BackwardReasoner