// 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.BottomUp; import java.util.Hashtable; import java.util.Vector; import org.ruleml.oojdrew.util.DefiniteClause; import org.ruleml.oojdrew.util.EngineException; import org.ruleml.oojdrew.util.SymbolTable; import org.ruleml.oojdrew.util.Term; import org.ruleml.oojdrew.util.Types; /** * <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 Unifier { /** * This variable stores references to the two clauses (facts) that are * being considered; the fact stored at index 0 is the one to be made * ground and checked for subsumption; the fact that will be stored at * index 1 is the one to check against. */ private DefiniteClause[] clauses; /** * This variable stores a copy of the atoms of the two clauses; the same * indexing convention is used as for DefiniteClause[] clauses. */ private Term[][] atoms; /** * This variable is used to temporarly store variable bindings; the same * indexing convention is used as for DefiniteClause[] clauses. */ private Term[][] vars; /** * This is use as a index for the clauses, atoms, vars variables. */ private static final int FACT = 0; /** * This is use as a index for the clauses, atoms, vars variables. */ private static final int RULE = 1; /** * This variable is set to true if the 2 clauses unified or not. */ private boolean unified = false; /** * This hashtable contains the variablesUsed used when creating variable bindings. */ private Hashtable variableUsed; /** * This vector contains the variable names used when creating variable binding. */ private Vector variableNames; /** * This method is used to create a unifier that is * used to test whether or not 2 terms are equal. */ public Unifier(){ vars = new Term[2][]; vars[FACT] = new Term[100]; vars[RULE] = new Term[100]; } /** * This method is used to create a unifier that is * used to test whether a fact unifies with a rule. * * @param fact DefiniteClause fact - this is the fact that will be used * to try and unify with the rule. * * @param rule DefiniteClause - this is the rule that will be used to * try and unify with the fact. */ public Unifier(DefiniteClause fact, DefiniteClause rule) { super(); if (!fact.isFact()) { throw new EngineException( "DefiniteClause fact must be a fact clause."); } if (rule.isFact()) { throw new EngineException( "DefiniteClause rule must be a rule clause."); } this.clauses = new DefiniteClause[2]; this.clauses[FACT] = fact; this.clauses[RULE] = rule; this.atoms = new Term[2][]; this.atoms[FACT] = new Term[1]; this.atoms[RULE] = new Term[clauses[RULE].atoms.length]; this.atoms[FACT][0] = fact.atoms[0].deepCopy(FACT); for (int i = 0; i < this.atoms[RULE].length; i++) { atoms[RULE][i] = rule.atoms[i].deepCopy(RULE); } vars = new Term[2][]; vars[FACT] = new Term[fact.variableNames.length]; vars[RULE] = new Term[rule.variableNames.length]; unified = unify(atoms[FACT][0], atoms[RULE][1]); } /** * This method will produce the resolvent of unifing a fact with a rule. * * @return DefiniteClause the resolvent that was produced by unifying a * fact and rule. */ public DefiniteClause resolvent() { this.variableNames = new Vector(); this.variableUsed = new Hashtable(); Vector newatoms = new Vector(); newatoms.add(apply(atoms[RULE][0])); for (int i = 2; i < atoms[RULE].length; i++) { newatoms.add(apply(atoms[RULE][i])); } DefiniteClause dc = new DefiniteClause(newatoms, variableNames); return dc; } /** * This method returns the value of the unified variable. * * @return boolean */ public boolean unifies() { return unified; } /** * This method is used to dereference a variable; i.e. to find any variable * bindings that have already been made. * * @param term Term The term to dereference. * * @return Term The dereferenced term; if the initial term is not a variable * then this is that initial term; if the inital term is a variable, then * this will either be the original variable; or the term that that * variable was bound to if it has been bound. */ private Term deref(Term term) { if (term.getSymbol() > 0) { return term; } else { int side = term.getSide(); int sym = -(term.getSymbol() + 1); Term termd = vars[side][sym]; if (termd == null) { return term; } else { return deref(termd); } } } /** * This method is used to check if two terms unify with each other; and to * perform any variable bindings that are necessary to make the terms unfiy. * * For more details see the inline comments in the source of this method * and/or look at the description of the unify(Term, Term) method of the * jdrew.oo.bu.Unifier class. * * @param term1 Term One of the terms to attempt to unify. * * @param term2 Term The second term to attempt to unify. * * @return boolean Returns true if the two terms unify; false otherwise. */ public boolean unify(Term term1, Term term2) { Term t1 = deref(term1); Term t2 = deref(term2); if (t1.isExpr() && t2.isExpr()) { if (t1.getSymbol() == t2.getSymbol() && Types.isSuperClass(t2.getType(), t1.getType())) { Vector t1restterms = new Vector(); Vector t2restterms = new Vector(); Vector t1prestterms = new Vector(); Vector t2prestterms = new Vector(); boolean t1rest = (t1.rest >= 0); boolean t2rest = (t2.rest >= 0); boolean t1prest = (t1.prest >= 0); boolean t2prest = (t2.prest >= 0); int i = 0; int j = 0; while (i < t1.subTerms.length && j < t2.subTerms.length) { if (t1.subTerms[i].role == SymbolTable.IREST || t1.subTerms[i].role == SymbolTable.IPREST) { // This is a rest term in t1 - skip for now - this is handeled at the end of unification i++; continue; } if (t2.subTerms[j].role == SymbolTable.IREST || t2.subTerms[j].role == SymbolTable.IPREST) { // This is a rest term in t2 - skip for now - this is handeled at the end of unification j++; continue; } if (t1.subTerms[i].role < t2.subTerms[j].role) { // role(t1[i]) is before role(t2[j]) - go to next i or fail if (t1.subTerms[i].role == SymbolTable.INOROLE && t2prest) { // add to positional rest term list for t2 t2prestterms.add(t1.subTerms[i]); i++; } else if (t1.subTerms[i].role > SymbolTable.INOROLE && t2rest) { // add to slotted rest term list for t2 t2restterms.add(t1.subTerms[i]); i++; } else { return false; // no appropriate rest term in t2 - unification fails } } else if (t1.subTerms[i].role == t2.subTerms[j].role) { // role(t1[i]) is same as role(t2[j]) - unify t1[i] and t2[j] if (!unify(t1.subTerms[i], t2.subTerms[j])) { return false; } i++; j++; } else if (t1.subTerms[i].role > t2.subTerms[j].role) { // role(t1[i]) is after role(t2[j]) - go to next j if (t2.subTerms[j].role == SymbolTable.INOROLE && t1prest) { // add to positional rest term list for t1 t1prestterms.add(t2.subTerms[j]); j++; } else if (t2.subTerms[j].role > SymbolTable.INOROLE && t1rest) { // add to slotted rest term list for t1 t1restterms.add(t2.subTerms[j]); j++; } else { return false; // no appropriate rest term in t1 - unification fails } } } while (i < t1.subTerms.length) { if (t1.subTerms[i].role == SymbolTable.IREST || t1.subTerms[i].role == SymbolTable.IPREST) { // This is a rest term in t1 - skip for now - this is handeled at the end of unification i++; } else if (t1.subTerms[i].role == SymbolTable.INOROLE && t2prest) { t2prestterms.add(t1.subTerms[i]); i++; } else if (t1.subTerms[i].role > SymbolTable.INOROLE && t2rest) { t2restterms.add(t1.subTerms[i]); i++; } else { return false; // no appropriate rest term in t2 - unification fails } } while (j < t2.subTerms.length) { if (t2.subTerms[j].role == SymbolTable.IREST || t2.subTerms[j].role == SymbolTable.IPREST) { // This is a rest term in t1 - skip for now - this is handeled at the end of unification j++; } else if (t2.subTerms[j].role == SymbolTable.INOROLE && t1prest) { t1prestterms.add(t2.subTerms[j]); j++; } else if (t2.subTerms[j].role > SymbolTable.INOROLE && t1rest) { t1restterms.add(t2.subTerms[j]); j++; } else { return false; // no appropriate rest term in t2 - unification fails } } // Now do unification of rest term with rest term list that was created Term t1prestterm = new Term(SymbolTable.IPLEX, SymbolTable.IPREST, Types.IOBJECT, t1prestterms); Term t1restterm = new Term(SymbolTable.IPLEX, SymbolTable.IREST, Types.IOBJECT, t1restterms); Term t2prestterm = new Term(SymbolTable.IPLEX, SymbolTable.IPREST, Types.IOBJECT, t2prestterms); Term t2restterm = new Term(SymbolTable.IPLEX, SymbolTable.IREST, Types.IOBJECT, t2restterms); if (t1prest) { if (!unify(t1.subTerms[t1.prest], t1prestterm)) { return false; } } else { if (t1prestterms.size() > 0) { return false; // t1 has no positional rest term, but one is required for successful unification } } if (t1rest) { if (!unify(t1.subTerms[t1.rest], t1restterm)) { return false; } } else { if (t1restterms.size() > 0) { return false; // t1 has no slotted rest term, but one is required for successful unification } } if (t2prest) { if (!unify(t2.subTerms[t2.prest], t2prestterm)) { return false; } } else { if (t2prestterms.size() > 0) { return false; // t2 has no positional rest term, but one is required for successful unification } } if (t2rest) { if (!unify(t2.subTerms[t2.rest], t2restterm)) { return false; } } else { if (t2restterms.size() > 0) { return false; // t2 has no slotted rest term, but one is required for successful unification } } return true; // All subterms unified correctly, symbols and types are compatible, therefore t1 and t2 unify } else { return false; // Symbols were different or types were not compatible (! (type(t2) >= type(t1))) } } else if (t1.isExpr() && !t2.isExpr()) { if (t2.getSymbol() < 0 && Types.isSuperClass(t2.getType(), t1.getType())) { // t2 is a variable, t1 is a complex term (Cterm, Plex, Atom) int side = t2.getSide(); int sym = -(t2.getSymbol() + 1); this.vars[side][sym] = t1; return true; } else { // t2 is an individual constant (Ind) and t2 is a complex term (Cterm, Plex, Atom) return false; } } else if (!t1.isExpr() && t2.isExpr()) { if (t1.getSymbol() < 0 && Types.isSuperClass(t1.getType(), t2.getType())) { // t1 is a variable, t2 is a complex term (Cterm, Plex, Atom) int side = t1.getSide(); int sym = -(t1.getSymbol() + 1); this.vars[side][sym] = t2; return true; } else { // t1 is an individual constant (Ind) and t2 is a complex term (Cterm, Plex, Atom) return false; } } else if (!t1.isExpr() && !t2.isExpr()) { if (t1.getSymbol() >= 0 && t2.getSymbol() >= 0) { // Both t1 and t2 are individual constants (Ind) //edit here //exact area where to make ind and data not bind together //but need to get this data thing working if (t1.getSymbol() == t2.getSymbol() && Types.isSuperClass(t2.getType(), t1.getType()) && (t1.getData() == t2.getData())) { //Both symbols are the same, and the types are compatible (type(t2) >= type(t1)) return true; } else { return false; } } else if (t1.getSymbol() < 0 && t2.getSymbol() >= 0) { // t1 is a variable (Var) and t2 is an individual constant (Ind) if (Types.isSuperClass(t1.getType(), t2.getType())) { int sym = -(t1.getSymbol() + 1); int side = t1.getSide(); this.vars[side][sym] = t2; return true; } else { return false; // Types are not compatible (! (type(t1) >= type(t2)) ) } } else if (t1.getSymbol() >= 0 && t2.getSymbol() < 0) { // t1 is an individual constant (Ind) and t2 is a variable (Var) if (Types.isSuperClass(t2.getType(), t1.getType())) { int sym = -(t2.getSymbol() + 1); int side = t2.getSide(); this.vars[side][sym] = t1; return true; } else { return false; // Types are not compatible (! (type(t2) >= type(t2)) ) } } else if (t1.getSymbol() < 0 && t2.getSymbol() < 0) { if (t1.getSide() == t2.getSide() && t1.getSymbol() == t2.getSymbol()) { return true; // Same variable - do nothing. prevents an infinite dereferencing loop. } // Both t1 and t2 are variables (Var) int type = Types.greatestLowerBound(t1.getType(), t2.getType()); int side = t2.getSide(); int sym = -(t2.getSymbol() + 1); Term t1dc = t1.deepCopy(); t1dc.setType(type); this.vars[side][sym] = t1dc; return true; } else { throw new EngineException("Terms are not valid."); } // This should never happen - one of hte previous cases will always occur } else { throw new EngineException("Terms are not valid."); } // This should never happen - one of the previous cases will always occur } /** * This method applies the varibale bindings to a term. * * @param t Term the term to apply variable bindings too. * * @return Term the term with variable bindings applied to it. */ private Term apply(Term t) { int role = t.getRole(); if (!t.isExpr()) { Term dt = deref(t); // dereference Term Term n = dt.deepCopy(); // make working copy n.setRole(role); if (dt == t) { // Have we reached the final referenced value (has it been dereferenced further by the call to deref) if (n.symbol < 0) { // If dereferenced term is a variable - calculate the new variable symbol id String var = n.getSide() + ":" + n.getSymbol(); int idx; if (this.variableUsed.containsKey(var)) { idx = ((Integer) (variableUsed.get(var))).intValue(); } else { idx = this.variableNames.size(); this.variableNames.add(clauses[n.getSide()]. variableNames[ -(n.getSymbol() + 1)]); this.variableUsed.put(var, idx); } n.setSymbol( -(idx + 1)); // set new variable id } return n; // return updated term } else { return apply(n); // apply changes to dereferenced value } } else { Term[] subs = t.subTerms; Vector newsubs = new Vector(); for (int i = 0; i < subs.length; i++) { // for each subterm Term sub = deref(subs[i]); // get deferenced subterm if ((sub.getRole() == SymbolTable.IREST || sub.getRole() == SymbolTable.IPREST) && sub.isExpr() && sub.getSymbol() == SymbolTable.IPLEX) { // If this is a rest term, and is bound to a PLEX of terms // merge in rest term list into main body Term[] restterms = sub.subTerms; for (int j = 0; j < restterms.length; j++) { newsubs.add(apply(restterms[j])); // apply variable binds to sub-term and add to sub-term list } } else { newsubs.add(apply(subs[i])); // apply variable bindings to sub-term and add to sub-term list } } Term t2 = new Term(t.getSymbol(), t.getRole(), t.getType(), newsubs); // create term for new clause t2.setAtom(t.isAtom()); return t2; } } }