Package aima.core.logic.fol.inference

Source Code of aima.core.logic.fol.inference.FOLOTTERLikeTheoremProver$OTTERAnswerHandler

package aima.core.logic.fol.inference;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import aima.core.logic.fol.Connectors;
import aima.core.logic.fol.SubsumptionElimination;
import aima.core.logic.fol.inference.otter.ClauseFilter;
import aima.core.logic.fol.inference.otter.ClauseSimplifier;
import aima.core.logic.fol.inference.otter.LightestClauseHeuristic;
import aima.core.logic.fol.inference.otter.defaultimpl.DefaultClauseFilter;
import aima.core.logic.fol.inference.otter.defaultimpl.DefaultClauseSimplifier;
import aima.core.logic.fol.inference.otter.defaultimpl.DefaultLightestClauseHeuristic;
import aima.core.logic.fol.inference.proof.Proof;
import aima.core.logic.fol.inference.proof.ProofFinal;
import aima.core.logic.fol.inference.proof.ProofStepGoal;
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.ConnectedSentence;
import aima.core.logic.fol.parsing.ast.NotSentence;
import aima.core.logic.fol.parsing.ast.Sentence;
import aima.core.logic.fol.parsing.ast.Term;
import aima.core.logic.fol.parsing.ast.TermEquality;
import aima.core.logic.fol.parsing.ast.Variable;

/**
* Artificial Intelligence A Modern Approach (2nd Edition): Figure 9.14, page
* 307.<br>
* <br>
*
* <pre>
* procedure OTTER(sos, usable)
*   inputs: sos, a set of support-clauses defining the problem (a global variable)
*   usable, background knowledge potentially relevant to the problem
*  
*   repeat
*      clause <- the lightest member of sos
*      move clause from sos to usable
*      PROCESS(INFER(clause, usable), sos)
*   until sos = [] or a refutation has been found
*
* --------------------------------------------------------------------------------
*
* function INFER(clause, usable) returns clauses
*  
*   resolve clause with each member of usable
*   return the resulting clauses after applying filter
*  
* --------------------------------------------------------------------------------
*
* procedure PROCESS(clauses, sos)
*
*   for each clause in clauses do
*       clause <- SIMPLIFY(clause)
*       merge identical literals
*       discard clause if it is a tautology
*       sos <- [clause | sos]
*       if clause has no literals then a refutation has been found
*       if clause has one literal then look for unit refutation
* </pre>
*
* Figure 9.14 Sketch of the OTTER theorem prover. Heuristic control is applied
* in the selection of the "lightest" clause and in the FILTER function that
* eliminates uninteresting clauses from consideration.<br>
* <br>
* <b>Note:</b> The original implementation of OTTER has been retired but its
* successor, <b>Prover9</b>, can be found at:<br>
* <a href="http://www.prover9.org/">http://www.prover9.org/</a><br>
* or<br>
* <a href="http://www.cs.unm.edu/~mccune/mace4/">http://www.cs.unm.edu/~mccune/
* mace4/</a><br>
* Should you wish to play with a mature implementation of a theorem prover :-)<br>
* <br>
* For lots of interesting problems to play with, see <b>The TPTP Problem
* Library for Automated Theorem Proving</b>:<br>
* <a href="http://www.cs.miami.edu/~tptp/">http://www.cs.miami.edu/~tptp/</a><br>
*
* @author Ciaran O'Reilly
*
*/
public class FOLOTTERLikeTheoremProver implements InferenceProcedure {
  //
  // Ten seconds is default maximum query time permitted
  private long maxQueryTime = 10 * 1000;
  private boolean useParamodulation = true;
  private LightestClauseHeuristic lightestClauseHeuristic = new DefaultLightestClauseHeuristic();
  private ClauseFilter clauseFilter = new DefaultClauseFilter();
  private ClauseSimplifier clauseSimplifier = new DefaultClauseSimplifier();
  //
  private Paramodulation paramodulation = new Paramodulation();

  public FOLOTTERLikeTheoremProver() {

  }

  public FOLOTTERLikeTheoremProver(long maxQueryTime) {
    setMaxQueryTime(maxQueryTime);
  }

  public FOLOTTERLikeTheoremProver(boolean useParamodulation) {
    setUseParamodulation(useParamodulation);
  }

  public FOLOTTERLikeTheoremProver(long maxQueryTime,
      boolean useParamodulation) {
    setMaxQueryTime(maxQueryTime);
    setUseParamodulation(useParamodulation);
  }

  public long getMaxQueryTime() {
    return maxQueryTime;
  }

  public void setMaxQueryTime(long maxQueryTime) {
    this.maxQueryTime = maxQueryTime;
  }

  public boolean isUseParamodulation() {
    return useParamodulation;
  }

  public void setUseParamodulation(boolean useParamodulation) {
    this.useParamodulation = useParamodulation;
  }

  public LightestClauseHeuristic getLightestClauseHeuristic() {
    return lightestClauseHeuristic;
  }

  public void setLightestClauseHeuristic(
      LightestClauseHeuristic lightestClauseHeuristic) {
    this.lightestClauseHeuristic = lightestClauseHeuristic;
  }

  public ClauseFilter getClauseFilter() {
    return clauseFilter;
  }

  public void setClauseFilter(ClauseFilter clauseFilter) {
    this.clauseFilter = clauseFilter;
  }

  public ClauseSimplifier getClauseSimplifier() {
    return clauseSimplifier;
  }

  public void setClauseSimplifier(ClauseSimplifier clauseSimplifier) {
    this.clauseSimplifier = clauseSimplifier;
  }

  //
  // START-InferenceProcedure
  public InferenceResult ask(FOLKnowledgeBase KB, Sentence alpha) {
    Set<Clause> sos = new HashSet<Clause>();
    Set<Clause> usable = new HashSet<Clause>();

    // Usable set will be the set of clauses in the KB,
    // are assuming this is satisfiable as using the
    // Set of Support strategy.
    for (Clause c : KB.getAllClauses()) {
      c = KB.standardizeApart(c);
      c.setStandardizedApartCheckNotRequired();
      usable.addAll(c.getFactors());
    }

    // Ensure reflexivity axiom is added to usable if using paramodulation.
    if (isUseParamodulation()) {
      // Reflexivity Axiom: x = x
      TermEquality reflexivityAxiom = new TermEquality(new Variable("x"),
          new Variable("x"));
      Clause reflexivityClause = new Clause();
      reflexivityClause.addLiteral(new Literal(reflexivityAxiom));
      reflexivityClause = KB.standardizeApart(reflexivityClause);
      reflexivityClause.setStandardizedApartCheckNotRequired();
      usable.add(reflexivityClause);
    }

    Sentence notAlpha = new NotSentence(alpha);
    // Want to use an answer literal to pull
    // query variables where necessary
    Literal answerLiteral = KB.createAnswerLiteral(notAlpha);
    Set<Variable> answerLiteralVariables = KB
        .collectAllVariables(answerLiteral.getAtomicSentence());
    Clause answerClause = new Clause();

    if (answerLiteralVariables.size() > 0) {
      Sentence notAlphaWithAnswer = new ConnectedSentence(Connectors.OR,
          notAlpha, answerLiteral.getAtomicSentence());
      for (Clause c : KB.convertToClauses(notAlphaWithAnswer)) {
        c = KB.standardizeApart(c);
        c.setProofStep(new ProofStepGoal(c));
        c.setStandardizedApartCheckNotRequired();
        sos.addAll(c.getFactors());
      }

      answerClause.addLiteral(answerLiteral);
    } else {
      for (Clause c : KB.convertToClauses(notAlpha)) {
        c = KB.standardizeApart(c);
        c.setProofStep(new ProofStepGoal(c));
        c.setStandardizedApartCheckNotRequired();
        sos.addAll(c.getFactors());
      }
    }

    // Ensure all subsumed clauses are removed
    usable.removeAll(SubsumptionElimination.findSubsumedClauses(usable));
    sos.removeAll(SubsumptionElimination.findSubsumedClauses(sos));

    OTTERAnswerHandler ansHandler = new OTTERAnswerHandler(answerLiteral,
        answerLiteralVariables, answerClause, maxQueryTime);

    IndexedClauses idxdClauses = new IndexedClauses(
        getLightestClauseHeuristic(), sos, usable);

    return otter(ansHandler, idxdClauses, sos, usable);
  }

  // END-InferenceProcedure
  //

  /**
   * <pre>
   * procedure OTTER(sos, usable)
   *   inputs: sos, a set of support-clauses defining the problem (a global variable)
   *   usable, background knowledge potentially relevant to the problem
   * </pre>
   */
  private InferenceResult otter(OTTERAnswerHandler ansHandler,
      IndexedClauses idxdClauses, Set<Clause> sos, Set<Clause> usable) {

    getLightestClauseHeuristic().initialSOS(sos);

    // * repeat
    do {
      // * clause <- the lightest member of sos
      Clause clause = getLightestClauseHeuristic().getLightestClause();
      if (null != clause) {
        // * move clause from sos to usable
        sos.remove(clause);
        getLightestClauseHeuristic().removedClauseFromSOS(clause);
        usable.add(clause);
        // * PROCESS(INFER(clause, usable), sos)
        process(ansHandler, idxdClauses, infer(clause, usable), sos,
            usable);
      }

      // * until sos = [] or a refutation has been found
    } while (sos.size() != 0 && !ansHandler.isComplete());

    return ansHandler;
  }

  /**
   * <pre>
   * function INFER(clause, usable) returns clauses
   */
  private Set<Clause> infer(Clause clause, Set<Clause> usable) {
    Set<Clause> resultingClauses = new LinkedHashSet<Clause>();

    // * resolve clause with each member of usable
    for (Clause c : usable) {
      Set<Clause> resolvents = clause.binaryResolvents(c);
      for (Clause rc : resolvents) {
        resultingClauses.add(rc);
      }

      // if using paramodulation to handle equality
      if (isUseParamodulation()) {
        Set<Clause> paras = paramodulation.apply(clause, c, true);
        for (Clause p : paras) {
          resultingClauses.add(p);
        }
      }
    }

    // * return the resulting clauses after applying filter
    return getClauseFilter().filter(resultingClauses);
  }

  // procedure PROCESS(clauses, sos)
  private void process(OTTERAnswerHandler ansHandler,
      IndexedClauses idxdClauses, Set<Clause> clauses, Set<Clause> sos,
      Set<Clause> usable) {

    // * for each clause in clauses do
    for (Clause clause : clauses) {
      // * clause <- SIMPLIFY(clause)
      clause = getClauseSimplifier().simplify(clause);

      // * merge identical literals
      // Note: Not required as handled by Clause Implementation
      // which keeps literals within a Set, so no duplicates
      // will exist.

      // * discard clause if it is a tautology
      if (clause.isTautology()) {
        continue;
      }

      // * if clause has no literals then a refutation has been found
      // or if it just contains the answer literal.
      if (!ansHandler.isAnswer(clause)) {
        // * sos <- [clause | sos]
        // This check ensure duplicate clauses are not
        // introduced which will cause the
        // LightestClauseHeuristic to loop continuously
        // on the same pair of objects.
        if (!sos.contains(clause) && !usable.contains(clause)) {
          for (Clause ac : clause.getFactors()) {
            if (!sos.contains(ac) && !usable.contains(ac)) {
              idxdClauses.addClause(ac, sos, usable);

              // * if clause has one literal then look for unit
              // refutation
              lookForUnitRefutation(ansHandler, idxdClauses, ac,
                  sos, usable);
            }
          }
        }
      }

      if (ansHandler.isComplete()) {
        break;
      }
    }
  }

  private void lookForUnitRefutation(OTTERAnswerHandler ansHandler,
      IndexedClauses idxdClauses, Clause clause, Set<Clause> sos,
      Set<Clause> usable) {

    Set<Clause> toCheck = new LinkedHashSet<Clause>();

    if (ansHandler.isCheckForUnitRefutation(clause)) {
      for (Clause s : sos) {
        if (s.isUnitClause()) {
          toCheck.add(s);
        }
      }
      for (Clause u : usable) {
        if (u.isUnitClause()) {
          toCheck.add(u);
        }
      }
    }

    if (toCheck.size() > 0) {
      toCheck = infer(clause, toCheck);
      for (Clause t : toCheck) {
        // * clause <- SIMPLIFY(clause)
        t = getClauseSimplifier().simplify(t);

        // * discard clause if it is a tautology
        if (t.isTautology()) {
          continue;
        }

        // * if clause has no literals then a refutation has been found
        // or if it just contains the answer literal.
        if (!ansHandler.isAnswer(t)) {
          // * sos <- [clause | sos]
          // This check ensure duplicate clauses are not
          // introduced which will cause the
          // LightestClauseHeuristic to loop continuously
          // on the same pair of objects.
          if (!sos.contains(t) && !usable.contains(t)) {
            idxdClauses.addClause(t, sos, usable);
          }
        }

        if (ansHandler.isComplete()) {
          break;
        }
      }
    }
  }

  // This is a simple indexing on the clauses to support
  // more efficient forward and backward subsumption testing.
  class IndexedClauses {
    private LightestClauseHeuristic lightestClauseHeuristic = null;
    // Group the clauses by their # of literals.
    private Map<Integer, Set<Clause>> clausesGroupedBySize = new HashMap<Integer, Set<Clause>>();
    // Keep track of the min and max # of literals.
    private int minNoLiterals = Integer.MAX_VALUE;
    private int maxNoLiterals = 0;

    public IndexedClauses(LightestClauseHeuristic lightestClauseHeuristic,
        Set<Clause> sos, Set<Clause> usable) {
      this.lightestClauseHeuristic = lightestClauseHeuristic;
      for (Clause c : sos) {
        indexClause(c);
      }
      for (Clause c : usable) {
        indexClause(c);
      }
    }

    public void addClause(Clause c, Set<Clause> sos, Set<Clause> usable) {
      // Perform forward subsumption elimination
      boolean addToSOS = true;
      for (int i = minNoLiterals; i < c.getNumberLiterals(); i++) {
        Set<Clause> fs = clausesGroupedBySize.get(i);
        if (null != fs) {
          for (Clause s : fs) {
            if (s.subsumes(c)) {
              addToSOS = false;
              break;
            }
          }
        }
        if (!addToSOS) {
          break;
        }
      }

      if (addToSOS) {
        sos.add(c);
        lightestClauseHeuristic.addedClauseToSOS(c);
        indexClause(c);
        // Have added clause, therefore
        // perform backward subsumption elimination
        Set<Clause> subsumed = new HashSet<Clause>();
        for (int i = c.getNumberLiterals() + 1; i <= maxNoLiterals; i++) {
          subsumed.clear();
          Set<Clause> bs = clausesGroupedBySize.get(i);
          if (null != bs) {
            for (Clause s : bs) {
              if (c.subsumes(s)) {
                subsumed.add(s);
                if (sos.contains(s)) {
                  sos.remove(s);
                  lightestClauseHeuristic
                      .removedClauseFromSOS(s);
                }
                usable.remove(s);
              }
            }
            bs.removeAll(subsumed);
          }
        }
      }
    }

    //
    // PRIVATE METHODS
    //
    private void indexClause(Clause c) {
      int size = c.getNumberLiterals();
      if (size < minNoLiterals) {
        minNoLiterals = size;
      }
      if (size > maxNoLiterals) {
        maxNoLiterals = size;
      }
      Set<Clause> cforsize = clausesGroupedBySize.get(size);
      if (null == cforsize) {
        cforsize = new HashSet<Clause>();
        clausesGroupedBySize.put(size, cforsize);
      }
      cforsize.add(c);
    }
  }

  class OTTERAnswerHandler implements InferenceResult {
    private Literal answerLiteral = null;
    private Set<Variable> answerLiteralVariables = null;
    private Clause answerClause = null;
    private long finishTime = 0L;
    private boolean complete = false;
    private List<Proof> proofs = new ArrayList<Proof>();
    private boolean timedOut = false;

    public OTTERAnswerHandler(Literal answerLiteral,
        Set<Variable> answerLiteralVariables, Clause answerClause,
        long maxQueryTime) {
      this.answerLiteral = answerLiteral;
      this.answerLiteralVariables = answerLiteralVariables;
      this.answerClause = answerClause;
      //
      this.finishTime = System.currentTimeMillis() + maxQueryTime;
    }

    //
    // START-InferenceResult
    public boolean isPossiblyFalse() {
      return !timedOut && proofs.size() == 0;
    }

    public boolean isTrue() {
      return proofs.size() > 0;
    }

    public boolean isUnknownDueToTimeout() {
      return timedOut && proofs.size() == 0;
    }

    public boolean isPartialResultDueToTimeout() {
      return timedOut && proofs.size() > 0;
    }

    public List<Proof> getProofs() {
      return proofs;
    }

    // END-InferenceResult
    //

    public boolean isComplete() {
      return complete;
    }

    public boolean isLookingForAnswerLiteral() {
      return !answerClause.isEmpty();
    }

    public boolean isCheckForUnitRefutation(Clause clause) {

      if (isLookingForAnswerLiteral()) {
        if (2 == clause.getNumberLiterals()) {
          for (Literal t : clause.getLiterals()) {
            if (t.getAtomicSentence()
                .getSymbolicName()
                .equals(answerLiteral.getAtomicSentence()
                    .getSymbolicName())) {
              return true;
            }
          }
        }
      } else {
        return clause.isUnitClause();
      }

      return false;
    }

    public boolean isAnswer(Clause clause) {
      boolean isAns = false;

      if (answerClause.isEmpty()) {
        if (clause.isEmpty()) {
          proofs.add(new ProofFinal(clause.getProofStep(),
              new HashMap<Variable, Term>()));
          complete = true;
          isAns = true;
        }
      } else {
        if (clause.isEmpty()) {
          // This should not happen
          // as added an answer literal to sos, which
          // implies the database (i.e. premises) are
          // unsatisfiable to begin with.
          throw new IllegalStateException(
              "Generated an empty clause while looking for an answer, implies original KB or usable is unsatisfiable");
        }

        if (clause.isUnitClause()
            && clause.isDefiniteClause()
            && clause
                .getPositiveLiterals()
                .get(0)
                .getAtomicSentence()
                .getSymbolicName()
                .equals(answerLiteral.getAtomicSentence()
                    .getSymbolicName())) {
          Map<Variable, Term> answerBindings = new HashMap<Variable, Term>();
          List<Term> answerTerms = clause.getPositiveLiterals()
              .get(0).getAtomicSentence().getArgs();
          int idx = 0;
          for (Variable v : answerLiteralVariables) {
            answerBindings.put(v, answerTerms.get(idx));
            idx++;
          }
          boolean addNewAnswer = true;
          for (Proof p : proofs) {
            if (p.getAnswerBindings().equals(answerBindings)) {
              addNewAnswer = false;
              break;
            }
          }
          if (addNewAnswer) {
            proofs.add(new ProofFinal(clause.getProofStep(),
                answerBindings));
          }
          isAns = true;
        }
      }

      if (System.currentTimeMillis() > finishTime) {
        complete = true;
        // Indicate that I have run out of query time
        timedOut = true;
      }

      return isAns;
    }

    @Override
    public String toString() {
      StringBuilder sb = new StringBuilder();
      sb.append("isComplete=" + complete);
      sb.append("\n");
      sb.append("result=" + proofs);
      return sb.toString();
    }
  }
}
TOP

Related Classes of aima.core.logic.fol.inference.FOLOTTERLikeTheoremProver$OTTERAnswerHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.