All Downloads are FREE. Search and download functionalities are using the official Maven repository.

aima.core.logic.fol.Unifier Maven / Gradle / Ivy

Go to download

AIMA-Java Core Algorithms from the book Artificial Intelligence a Modern Approach 3rd Ed.

The newest version!
package aima.core.logic.fol;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import aima.core.logic.fol.parsing.ast.FOLNode;
import aima.core.logic.fol.parsing.ast.Function;
import aima.core.logic.fol.parsing.ast.Term;
import aima.core.logic.fol.parsing.ast.Variable;

/**
 * Artificial Intelligence A Modern Approach (3rd Edition): Figure 9.1, page
 * 328.
*
* *
 * function UNIFY(x, y, theta) returns a substitution to make x and y identical
 *   inputs: x, a variable, constant, list, or compound
 *           y, a variable, constant, list, or compound
 *           theta, the substitution built up so far (optional, defaults to empty)
 *           
 *   if theta = failure then return failure
 *   else if x = y the return theta
 *   else if VARIABLE?(x) then return UNIVY-VAR(x, y, theta)
 *   else if VARIABLE?(y) then return UNIFY-VAR(y, x, theta)
 *   else if COMPOUND?(x) and COMPOUND?(y) then
 *       return UNIFY(x.ARGS, y.ARGS, UNIFY(x.OP, y.OP, theta))
 *   else if LIST?(x) and LIST?(y) then
 *       return UNIFY(x.REST, y.REST, UNIFY(x.FIRST, y.FIRST, theta))
 *   else return failure
 *   
 * ---------------------------------------------------------------------------------------------------
 * 
 * function UNIFY-VAR(var, x, theta) returns a substitution
 *            
 *   if {var/val} E theta then return UNIFY(val, x, theta)
 *   else if {x/val} E theta then return UNIFY(var, val, theta)
 *   else if OCCUR-CHECK?(var, x) then return failure
 *   else return add {var/x} to theta
 * 
* * Figure 9.1 The unification algorithm. The algorithm works by comparing the * structures of the inputs, elements by element. The substitution theta that is * the argument to UNIFY is built up along the way and is used to make sure that * later comparisons are consistent with bindings that were established earlier. * In a compound expression, such as F(A, B), the OP field picks out the * function symbol F and the ARGS field picks out the argument list (A, B). * * @author Ciaran O'Reilly * @author Ravi Mohan * @author Mike Stampone * */ public class Unifier { // private static SubstVisitor _substVisitor = new SubstVisitor(); public Unifier() { } /** * Returns a Map representing the substitution (i.e. a set * of variable/term pairs) or null which is used to indicate a failure to * unify. * * @param x * a variable, constant, list, or compound * @param y * a variable, constant, list, or compound * * @return a Map representing the substitution (i.e. a set * of variable/term pairs) or null which is used to indicate a * failure to unify. */ public Map unify(FOLNode x, FOLNode y) { return unify(x, y, new LinkedHashMap()); } /** * Returns a Map representing the substitution (i.e. a set * of variable/term pairs) or null which is used to indicate a failure to * unify. * * @param x * a variable, constant, list, or compound * @param y * a variable, constant, list, or compound * @param theta * the substitution built up so far * * @return a Map representing the substitution (i.e. a set * of variable/term pairs) or null which is used to indicate a * failure to unify. */ public Map unify(FOLNode x, FOLNode y, Map theta) { // if theta = failure then return failure if (theta == null) { return null; } else if (x.equals(y)) { // else if x = y then return theta return theta; } else if (x instanceof Variable) { // else if VARIABLE?(x) then return UNIVY-VAR(x, y, theta) return unifyVar((Variable) x, y, theta); } else if (y instanceof Variable) { // else if VARIABLE?(y) then return UNIFY-VAR(y, x, theta) return unifyVar((Variable) y, x, theta); } else if (isCompound(x) && isCompound(y)) { // else if COMPOUND?(x) and COMPOUND?(y) then // return UNIFY(x.ARGS, y.ARGS, UNIFY(x.OP, y.OP, theta)) return unify(args(x), args(y), unifyOps(op(x), op(y), theta)); } else { // else return failure return null; } } /** * Returns a Map representing the substitution (i.e. a set * of variable/term pairs) or null which is used to indicate a failure to * unify. * * @param x * a variable, constant, list, or compound * @param y * a variable, constant, list, or compound * @param theta * the substitution built up so far * * @return a Map representing the substitution (i.e. a set * of variable/term pairs) or null which is used to indicate a * failure to unify. */ // else if LIST?(x) and LIST?(y) then // return UNIFY(x.REST, y.REST, UNIFY(x.FIRST, y.FIRST, theta)) public Map unify(List x, List y, Map theta) { if (theta == null) { return null; } else if (x.size() != y.size()) { return null; } else if (x.size() == 0 && y.size() == 0) { return theta; } else if (x.size() == 1 && y.size() == 1) { return unify(x.get(0), y.get(0), theta); } else { return unify(x.subList(1, x.size()), y.subList(1, y.size()), unify(x.get(0), y.get(0), theta)); } } // // PROTECTED METHODS // // Note: You can subclass and override this method in order // to re-implement the OCCUR-CHECK?() to always // return false if you want that to be the default // behavior, as is the case with Prolog. // Note: Implementation is based on unify-bug.pdf document by Peter Norvig: // http://norvig.com/unify-bug.pdf protected boolean occurCheck(Map theta, Variable var, FOLNode x) { // ((equal var x) t) if (var.equals(x)) { return true; // ((bound? x subst) } else if (theta.containsKey(x)) { // (occurs-in? var (lookup x subst) subst)) return occurCheck(theta, var, theta.get(x)); // ((consp x) (or (occurs-in? var (first x) subst) (occurs-in? var // (rest x) subst))) } else if (x instanceof Function) { // (or (occurs-in? var (first x) subst) (occurs-in? var (rest x) // subst))) Function fx = (Function) x; for (Term fxt : fx.getArgs()) { if (occurCheck(theta, var, fxt)) { return true; } } } return false; } // // PRIVATE METHODS // /** * * function UNIFY-VAR(var, x, theta) returns a substitution * inputs: var, a variable * x, any expression * theta, the substitution built up so far * */ private Map unifyVar(Variable var, FOLNode x, Map theta) { if (!Term.class.isInstance(x)) { return null; } else if (theta.keySet().contains(var)) { // if {var/val} E theta then return UNIFY(val, x, theta) return unify(theta.get(var), x, theta); } else if (theta.keySet().contains(x)) { // else if {x/val} E theta then return UNIFY(var, val, theta) return unify(var, theta.get(x), theta); } else if (occurCheck(theta, var, x)) { // else if OCCUR-CHECK?(var, x) then return failure return null; } else { // else return add {var/x} to theta cascadeSubstitution(theta, var, (Term) x); return theta; } } private Map unifyOps(String x, String y, Map theta) { if (theta == null) { return null; } else if (x.equals(y)) { return theta; } else { return null; } } private List args(FOLNode x) { return x.getArgs(); } private String op(FOLNode x) { return x.getSymbolicName(); } private boolean isCompound(FOLNode x) { return x.isCompound(); } // See: // http://logic.stanford.edu/classes/cs157/2008/miscellaneous/faq.html#jump165 // for need for this. private Map cascadeSubstitution(Map theta, Variable var, Term x) { theta.put(var, x); for (Variable v : theta.keySet()) { theta.put(v, _substVisitor.subst(theta, theta.get(v))); } // Ensure Function Terms are correctly updates by passing over them // again. Fix for testBadCascadeSubstitution_LCL418_1() for (Variable v : theta.keySet()) { Term t = theta.get(v); if (t instanceof Function) { theta.put(v, _substVisitor.subst(theta, t)); } } return theta; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy