Package org.mindswap.pellet.tableau.completion

Source Code of org.mindswap.pellet.tableau.completion.CompletionStrategy

// Portions Copyright (c) 2006 - 2008, Clark & Parsia, LLC. <http://www.clarkparsia.com>
// Clark & Parsia, LLC parts of this source code are available under the terms of the Affero General Public License v3.
//
// Please see LICENSE.txt for full license terms, including the availability of proprietary exceptions.
// Questions, comments, or requests for clarification: licensing@clarkparsia.com

package org.mindswap.pellet.tableau.completion;

import static com.clarkparsia.pellet.utils.TermFactory.TOP_OBJECT_PROPERTY;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.mindswap.pellet.ABox;
import org.mindswap.pellet.Clash;
import org.mindswap.pellet.DependencySet;
import org.mindswap.pellet.Edge;
import org.mindswap.pellet.EdgeList;
import org.mindswap.pellet.Individual;
import org.mindswap.pellet.IndividualIterator;
import org.mindswap.pellet.Literal;
import org.mindswap.pellet.Node;
import org.mindswap.pellet.NodeMerge;
import org.mindswap.pellet.PelletOptions;
import org.mindswap.pellet.Role;
import org.mindswap.pellet.exceptions.InternalReasonerException;
import org.mindswap.pellet.tableau.blocking.Blocking;
import org.mindswap.pellet.tableau.blocking.BlockingFactory;
import org.mindswap.pellet.tableau.branch.Branch;
import org.mindswap.pellet.tableau.branch.GuessBranch;
import org.mindswap.pellet.tableau.completion.queue.NodeSelector;
import org.mindswap.pellet.tableau.completion.queue.QueueElement;
import org.mindswap.pellet.tableau.completion.rule.AllValuesRule;
import org.mindswap.pellet.tableau.completion.rule.ChooseRule;
import org.mindswap.pellet.tableau.completion.rule.DataCardinalityRule;
import org.mindswap.pellet.tableau.completion.rule.DataSatisfiabilityRule;
import org.mindswap.pellet.tableau.completion.rule.DisjunctionRule;
import org.mindswap.pellet.tableau.completion.rule.GuessRule;
import org.mindswap.pellet.tableau.completion.rule.MaxRule;
import org.mindswap.pellet.tableau.completion.rule.MinRule;
import org.mindswap.pellet.tableau.completion.rule.NominalRule;
import org.mindswap.pellet.tableau.completion.rule.SelfRule;
import org.mindswap.pellet.tableau.completion.rule.SimpleAllValuesRule;
import org.mindswap.pellet.tableau.completion.rule.SomeValuesRule;
import org.mindswap.pellet.tableau.completion.rule.TableauRule;
import org.mindswap.pellet.tableau.completion.rule.UnfoldingRule;
import org.mindswap.pellet.tbox.TBox;
import org.mindswap.pellet.utils.ATermUtils;
import org.mindswap.pellet.utils.Timer;
import org.mindswap.pellet.utils.Timers;

import aterm.ATerm;
import aterm.ATermAppl;
import aterm.ATermList;

import com.clarkparsia.pellet.expressivity.Expressivity;
import com.clarkparsia.pellet.rules.model.DifferentIndividualsAtom;
import com.clarkparsia.pellet.rules.model.Rule;
import com.clarkparsia.pellet.rules.model.RuleAtom;
import com.clarkparsia.pellet.rules.model.SameIndividualAtom;

/**
* A completion strategy specifies how the tableau rules will be applied to an ABox. Depending on the expressivity of
* the KB, e.g. SHIN, SHON, etc., different (more efficient) strategies may be used. This class is the base for all
* different implementations and contains strategy independent functions.
*
* @author Evren Sirin
*/
public abstract class CompletionStrategy {
  public final static Logger log = Logger.getLogger(CompletionStrategy.class.getName());

  /**
   * ABox being completed
   */
  protected ABox abox;

  /**
   * TBox associated with the abox
   */
  protected TBox tbox;

  /**
   * Blocking method specific to this completion strategy
   */
  protected Blocking blocking;

  /**
   * Timers of the associated KB
   */
  protected Timers timers;

  /**
   * Timer to be used by the complete function. KB's consistency timer depends on this one and this dependency is set
   * in the constructor. Any concrete class that extends CompletionStrategy should check this timer to respect the
   * timeouts defined in the KB.
   */
  protected Timer completionTimer;

  /**
   * Flag to indicate that a merge operation is going on
   */
  private boolean merging = false;

  /**
   * Flat to indicate that we are merging all nodes in the queue
   */
  private boolean mergingAll = false;

  /**
   * The queue of node pairs that are waiting to be merged
   */
  protected List<NodeMerge> mergeList;

  protected TableauRule unfoldingRule = new UnfoldingRule(this);
  protected TableauRule disjunctionRule = new DisjunctionRule(this);
  protected AllValuesRule allValuesRule = new AllValuesRule(this);
  protected TableauRule someValuesRule = new SomeValuesRule(this);
  protected TableauRule chooseRule = new ChooseRule(this);
  protected TableauRule minRule = new MinRule(this);
  protected MaxRule maxRule = new MaxRule(this);
  protected TableauRule selfRule = new SelfRule(this);
  protected TableauRule nominalRule = new NominalRule(this);
  protected TableauRule guessRule = new GuessRule(this);
  protected TableauRule dataSatRule = new DataSatisfiabilityRule(this);
  protected TableauRule dataCardRule = new DataCardinalityRule(this);

  protected List<TableauRule> tableauRules;

  /**
     *
     */
  public CompletionStrategy(ABox abox) {
    this.abox = abox;
    this.tbox = abox.getTBox();
    this.timers = abox.getKB().timers;

    completionTimer = timers.getTimer("complete");
  }

  public ABox getABox() {
    return abox;
  }

  public TBox getTBox() {
    return tbox;
  }

  public Blocking getBlocking() {
    return blocking;
  }

  public void checkTimer() {
    completionTimer.check();
  }

  /**
   * Return individuals to which we need to apply the initialization rules
   *
   * @return
   */
  public Iterator<Individual> getInitializeIterator() {
    return new IndividualIterator(abox);
  }

  protected void configureTableauRules(Expressivity expr) {
    if (!PelletOptions.USE_COMPLETION_STRATEGY) {
      addAllRules();
      return;
    }

    boolean fullDatatypeReasoning = PelletOptions.USE_FULL_DATATYPE_REASONING
                                    && (expr.hasUserDefinedDatatype() || expr.hasCardinalityD() || expr.hasKeys());

    tableauRules = new ArrayList<TableauRule>();

    if ((!PelletOptions.USE_PSEUDO_NOMINALS && expr.hasNominal()) || implicitNominals()) {
      tableauRules.add(nominalRule);

      if (expr.hasCardinalityQ()) {
        tableauRules.add(guessRule);
      }
    }

    if (expr.hasCardinalityQ() || expr.hasCardinalityD()) {
      tableauRules.add(chooseRule);
    }

    tableauRules.add(maxRule);

    if (fullDatatypeReasoning) {
      tableauRules.add(dataCardRule);
    }

    tableauRules.add(dataSatRule);

    tableauRules.add(unfoldingRule);

    tableauRules.add(disjunctionRule);

    tableauRules.add(someValuesRule);

    tableauRules.add(minRule);

    // no need to add allValuesRule to the list since it is applied on-the-fly
    if (expr.hasComplexSubRoles()) {
      allValuesRule = new AllValuesRule(this);
    }
    else {
      allValuesRule = new SimpleAllValuesRule(this);
    }

  }

  protected void addAllRules() {
    tableauRules = new ArrayList<TableauRule>();

    tableauRules.add(nominalRule);
    tableauRules.add(guessRule);
    tableauRules.add(chooseRule);
    tableauRules.add(maxRule);
    tableauRules.add(dataCardRule);
    tableauRules.add(dataSatRule);
    tableauRules.add(unfoldingRule);
    tableauRules.add(disjunctionRule);
    tableauRules.add(someValuesRule);
    tableauRules.add(minRule);

    allValuesRule = new AllValuesRule(this);
  }

  protected boolean implicitNominals() {
    Collection<Rule> rules = abox.getKB().getNormalizedRules().values();
    for (Rule rule : rules) {
      if (rule == null) {
        continue;
      }
     
            for (RuleAtom atom : rule.getBody()) {
                if (atom instanceof DifferentIndividualsAtom) {
                  return true;
                }
            }
           
            for (RuleAtom atom : rule.getHead()) {
                if (atom instanceof SameIndividualAtom) {
                  return true;
                }
            }
        }
   
    return false;
  }

  public void initialize(Expressivity expressivity) {
    mergeList = new ArrayList<NodeMerge>();

    blocking = BlockingFactory.createBlocking(expressivity);

    configureTableauRules(expressivity);

    for (Branch branch : abox.getBranches()) {
      branch.setStrategy(this);
    }

    if (abox.isInitialized()) {

      Iterator<Individual> i = getInitializeIterator();
      while (i.hasNext()) {
        Individual n = i.next();

        if (n.isMerged()) {
          continue;
        }

        if (n.isConceptRoot()) {
          applyUniversalRestrictions(n);
        }

        allValuesRule.apply(n);
        if (n.isMerged()) {
          continue;
        }
        nominalRule.apply(n);
        if (n.isMerged()) {
          continue;
        }
        selfRule.apply(n);

        // CHW-added for inc. queue must see if this is bad
        EdgeList allEdges = n.getOutEdges();
        for (int e = 0; e < allEdges.size(); e++) {
          Edge edge = allEdges.edgeAt(e);
          if (edge.getTo().isPruned()) {
            continue;
          }

          applyPropertyRestrictions(edge);
          if (n.isMerged()) {
            break;
          }
        }

      }

      return;
    }

    if (log.isLoggable(Level.FINE)) {
      log.fine("Initialize started");
    }

    abox.setBranch(0);

    mergeList.addAll(abox.getToBeMerged());

    if (!mergeList.isEmpty()) {
      mergeAll();
    }

    Role topRole = abox.getRole(TOP_OBJECT_PROPERTY);
    Iterator<Individual> i = getInitializeIterator();
    while (i.hasNext()) {
      Individual n = i.next();

      if (n.isMerged()) {
        continue;
      }

      applyUniversalRestrictions(n);
      if (n.isMerged()) {
        continue;
      }

      selfRule.apply(n);
      if (n.isMerged()) {
        continue;
      }

      EdgeList allEdges = n.getOutEdges();
      for (int e = 0; e < allEdges.size(); e++) {
        Edge edge = allEdges.edgeAt(e);

        if (edge.getTo().isPruned()) {
          continue;
        }

        applyPropertyRestrictions(edge);

        if (n.isMerged()) {
          break;
        }
      }

      if (n.isMerged()) {
        continue;
      }

      // The top object role isn't in the edge list, so pretend it exists
      applyPropertyRestrictions(n, topRole, n, DependencySet.INDEPENDENT);
    }

    if (log.isLoggable(Level.FINE)) {
      log.fine("Merging: " + mergeList);
    }

    if (!mergeList.isEmpty()) {
      mergeAll();
    }

    if (log.isLoggable(Level.FINE)) {
      log.fine("Initialize finished");
    }

    abox.setBranch(abox.getBranches().size() + 1);
    abox.stats.treeDepth = 1;
    abox.setChanged(true);
    abox.setComplete(false);
    abox.setInitialized(true);
  }

  /**
   * apply all the tableau rules to the designated ABox
   *
   */
  public abstract void complete(Expressivity expr);

  public Individual createFreshIndividual(Individual parent, DependencySet ds) {
    Individual ind = abox.addFreshIndividual(parent, ds);

    applyUniversalRestrictions(ind);

    return ind;
  }

  void applyUniversalRestrictions(Individual node) {
    addType(node, ATermUtils.TOP, DependencySet.INDEPENDENT);

    Set<Role> reflexives = abox.getKB().getRBox().getReflexiveRoles();
    for (Iterator<Role> i = reflexives.iterator(); i.hasNext();) {
      Role r = i.next();
      if (log.isLoggable(Level.FINE) && !node.hasRNeighbor(r, node)) {
        log.fine("REF : " + node + " " + r);
      }
      addEdge(node, r, node, r.getExplainReflexive());
      if (node.isMerged()) {
        return;
      }
    }

    Role topObjProp = abox.getKB().getRole(ATermUtils.TOP_OBJECT_PROPERTY);
    for (ATermAppl domain : topObjProp.getDomains()) {
      addType(node, domain, topObjProp.getExplainDomain(domain));
      if (node.isMerged()) {
        continue;
      }
    }
    for (ATermAppl range : topObjProp.getRanges()) {
      addType(node, range, topObjProp.getExplainRange(range));
      if (node.isMerged()) {
        continue;
      }
    }

  }

  public void addType(Node node, ATermAppl c, DependencySet ds) {
    if (abox.isClosed()) {
      return;
    }

    node.addType(c, ds);
    if (node.isLiteral()) {
      final Literal l = (Literal) node;
      final NodeMerge mtc = l.getMergeToConstant();
      if (mtc != null) {
        l.clearMergeToConstant();
        Literal mergeTo = abox.getLiteral(mtc.getTarget());
        mergeTo(l, mergeTo, mtc.getDepends());
        node = mergeTo;
      }
    }

    // update dependency index for this node
    if (PelletOptions.USE_INCREMENTAL_DELETION) {
      abox.getKB().getDependencyIndex().addTypeDependency(node.getName(), c, ds);
    }

    if (log.isLoggable(Level.FINER)) {
      log.finer("ADD: " + node + " " + c + " - " + ds + " " + ds.getExplain());
    }

    if (c.getAFun().equals(ATermUtils.ANDFUN)) {
      for (ATermList cs = (ATermList) c.getArgument(0); !cs.isEmpty(); cs = cs.getNext()) {
        ATermAppl conj = (ATermAppl) cs.getFirst();

        addType(node, conj, ds);

        node = node.getSame();
      }
    }
    else if (c.getAFun().equals(ATermUtils.ALLFUN)) {
      allValuesRule.applyAllValues((Individual) node, c, ds);
    }
    else if (c.getAFun().equals(ATermUtils.SELFFUN)) {
      ATermAppl pred = (ATermAppl) c.getArgument(0);
      Role role = abox.getRole(pred);
      if (log.isLoggable(Level.FINE) && !((Individual) node).hasRSuccessor(role, node)) {
        log.fine("SELF: " + node + " " + role + " " + node.getDepends(c));
      }
      addEdge((Individual) node, role, node, ds);
    }
    // else if( c.getAFun().equals( ATermUtils.VALUE ) ) {
    // applyNominalRule( (Individual) node, c, ds);
    // }
  }

  /**
   * This method updates the queue in the event that there is an edge added between two nodes. The individual must be
   * added back onto the MAXLIST
   */
  protected void updateQueueAddEdge(Individual subj, Role pred, Node obj) {
    // for each min and max card restrictions for the subject, a new
    // queueElement must be generated and added
    List<ATermAppl> types = subj.getTypes(Node.MAX);
    int size = types.size();
    for (int j = 0; j < size; j++) {
      ATermAppl c = types.get(j);
      ATermAppl max = (ATermAppl) c.getArgument(0);
      Role r = abox.getRole(max.getArgument(0));
      if (pred.isSubRoleOf(r)) {
        QueueElement newElement = new QueueElement(subj, c);
        abox.getCompletionQueue().add(newElement, NodeSelector.MAX_NUMBER);
        abox.getCompletionQueue().add(newElement, NodeSelector.CHOOSE);
      }
    }

    // if the predicate has an inverse or is inversefunctional and the obj
    // is an individual, then add the object to the list.
    if (obj instanceof Individual) {
      types = ((Individual) obj).getTypes(Node.MAX);
      size = types.size();
      for (int j = 0; j < size; j++) {
        ATermAppl c = types.get(j);
        ATermAppl max = (ATermAppl) c.getArgument(0);
        Role r = abox.getRole(max.getArgument(0));

        Role invR = pred.getInverse();

        if (invR != null) {
          if (invR.isSubRoleOf(r)) {
            QueueElement newElement = new QueueElement(obj, c);
            abox.getCompletionQueue().add(newElement, NodeSelector.MAX_NUMBER);
            abox.getCompletionQueue().add(newElement, NodeSelector.CHOOSE);
          }
        }
      }
    }
  }

  public Edge addEdge(Individual subj, Role pred, Node obj, DependencySet ds) {
    Edge edge = subj.addEdge(pred, obj, ds);

    // add to the kb dependencies
    if (PelletOptions.USE_INCREMENTAL_DELETION) {
      abox.getKB().getDependencyIndex().addEdgeDependency(edge, ds);
    }

    if (PelletOptions.TRACK_BRANCH_EFFECTS) {
      abox.getBranchEffectTracker().add(abox.getBranch(), subj.getName());
      abox.getBranchEffectTracker().add(abox.getBranch(), obj.getName());
    }

    if (PelletOptions.USE_COMPLETION_QUEUE) {
      // update the queue as we are adding an edge - we must add
      // elements to the MAXLIST
      updateQueueAddEdge(subj, pred, obj);
    }

    if (edge != null) {
      // note that we do not need to enforce the guess rule for
      // datatype properties because we may only have inverse
      // functional datatype properties which will be handled
      // inside applyPropertyRestrictions
      if (subj.isBlockable() && obj.isNominal() && !obj.isLiteral() && pred.isInverseFunctional()) {
        Individual o = (Individual) obj;
        int max = 1;
        if (!o.hasDistinctRNeighborsForMin(pred.getInverse(), max, ATermUtils.TOP, true)) {
          int guessMin = o.getMinCard(pred.getInverse(), ATermUtils.TOP);
          if (guessMin == 0) {
            guessMin = 1;
          }

          if (guessMin > max) {
            return edge;
          }

          GuessBranch newBranch = new GuessBranch(abox, this, o, pred.getInverse(), guessMin, max,
                          ATermUtils.TOP, ds);
          addBranch(newBranch);

          // try a merge that does not trivially fail
          if (newBranch.tryNext() == false) {
            return edge;
          }

          if (abox.isClosed()) {
            return edge;
          }

          if (subj.isPruned()) {
            return edge;
          }
        }
      }

      applyPropertyRestrictions(subj, pred, obj, ds);
    }
   
    return edge;
  }

  void applyPropertyRestrictions(Edge edge) {
    applyPropertyRestrictions(edge.getFrom(), edge.getRole(), edge.getTo(), edge.getDepends());
  }

  void applyPropertyRestrictions(Individual subj, Role pred, Node obj, DependencySet ds) {
    applyDomainRange(subj, pred, obj, ds);
    if (subj.isPruned() || obj.isPruned()) {
      return;
    }
    applyFunctionality(subj, pred, obj);
    if (subj.isPruned() || obj.isPruned()) {
      return;
    }
    applyDisjointness(subj, pred, obj, ds);
    allValuesRule.applyAllValues(subj, pred, obj, ds);
    if (subj.isPruned() || obj.isPruned()) {
      return;
    }
    if (pred.isObjectRole()) {
      Individual o = (Individual) obj;
      allValuesRule.applyAllValues(o, pred.getInverse(), subj, ds);
      checkReflexivitySymmetry(subj, pred, o, ds);
      checkReflexivitySymmetry(o, pred.getInverse(), subj, ds);
      applyDisjointness(o, pred.getInverse(), subj, ds);
    }
  }

  void applyDomainRange(Individual subj, Role pred, Node obj, DependencySet ds) {
    Set<ATermAppl> domains = pred.getDomains();
    Set<ATermAppl> ranges = pred.getRanges();

    for (ATermAppl domain : domains) {
      if (log.isLoggable(Level.FINE) && !subj.hasType(domain)) {
        log.fine("DOM : " + obj + " <- " + pred + " <- " + subj + " : " + ATermUtils.toString(domain));
      }
      addType(subj, domain, ds.union(pred.getExplainDomain(domain), abox.doExplanation()));
      if (subj.isPruned() || obj.isPruned()) {
        return;
      }
    }
    for (ATermAppl range : ranges) {
      if (log.isLoggable(Level.FINE) && !obj.hasType(range)) {
        log.fine("RAN : " + subj + " -> " + pred + " -> " + obj + " : " + ATermUtils.toString(range));
      }
      addType(obj, range, ds.union(pred.getExplainRange(range), abox.doExplanation()));
      if (subj.isPruned() || obj.isPruned()) {
        return;
      }
    }
  }

  void applyFunctionality(Individual subj, Role pred, Node obj) {
    DependencySet maxCardDS = pred.isFunctional() ? pred.getExplainFunctional() : subj.hasMax1(pred);

    if (maxCardDS != null) {
      maxRule.applyFunctionalMaxRule(subj, pred, ATermUtils.getTop(pred), maxCardDS);
    }

    if (pred.isDatatypeRole() && pred.isInverseFunctional()) {
      applyFunctionalMaxRule((Literal) obj, pred, DependencySet.INDEPENDENT);
    }
    else if (pred.isObjectRole()) {
      Individual val = (Individual) obj;
      Role invR = pred.getInverse();

      maxCardDS = invR.isFunctional() ? invR.getExplainFunctional() : val.hasMax1(invR);

      if (maxCardDS != null) {
        maxRule.applyFunctionalMaxRule(val, invR, ATermUtils.TOP, maxCardDS);
      }
    }

  }

  void applyDisjointness(Individual subj, Role pred, Node obj, DependencySet ds) {
    // TODO what about inv edges?
    // TODO improve this check
    Set<Role> disjoints = pred.getDisjointRoles();
    if (disjoints.isEmpty()) {
      return;
    }
    EdgeList edges = subj.getEdgesTo(obj);
    for (int i = 0, n = edges.size(); i < n; i++) {
      Edge otherEdge = edges.edgeAt(i);

      if (disjoints.contains(otherEdge.getRole())) {
        ds = ds.union(otherEdge.getDepends(), abox.doExplanation());
        ds = ds.union(pred.getExplainDisjointRole(otherEdge.getRole()), abox.doExplanation());
        abox.setClash(Clash.disjointProps(subj, ds, pred.getName(), otherEdge.getRole().getName()));
        return;
      }
    }

  }

  void checkReflexivitySymmetry(Individual subj, Role pred, Individual obj, DependencySet ds) {
    if (pred.isAsymmetric() && obj.hasRSuccessor(pred, subj)) {
      EdgeList edges = obj.getEdgesTo(subj, pred);
      ds = ds.union(edges.edgeAt(0).getDepends(), abox.doExplanation());
      if (PelletOptions.USE_TRACING) {
        ds = ds.union(pred.getExplainAsymmetric(), abox.doExplanation());
      }
      abox.setClash(Clash.unexplained(subj, ds, "Antisymmetric property " + pred));
    }
    else if (subj.equals(obj)) {
      if (pred.isIrreflexive()) {
        abox.setClash(Clash.unexplained(subj, ds.union(pred.getExplainIrreflexive(), abox.doExplanation()),
                        "Irreflexive property " + pred));
      }
      else {
        ATerm notSelfP = ATermUtils.makeNot(ATermUtils.makeSelf(pred.getName()));
        if (subj.hasType(notSelfP)) {
          abox.setClash(Clash.unexplained(subj, ds.union(subj.getDepends(notSelfP), abox.doExplanation()),
                          "Local irreflexive property " + pred));
        }
      }
    }
  }

  protected void applyFunctionalMaxRule(Literal x, Role r, DependencySet ds) {
    EdgeList edges = x.getInEdges().getEdges(r);

    // if there is not more than one edge then func max rule won't be triggered
    if (edges.size() <= 1) {
      return;// continue;
    }

    // find all distinct R-neighbors of x
    Set<Node> neighbors = edges.getNeighbors(x);

    // if there is not more than one neighbor then func max rule won't be triggered
    if (neighbors.size() <= 1) {
      return;// continue;
    }

    Individual head = null;
    DependencySet headDS = null;
    // find a nominal node to use as the head
    for (int edgeIndex = 0; edgeIndex < edges.size(); edgeIndex++) {
      Edge edge = edges.edgeAt(edgeIndex);
      Individual ind = edge.getFrom();

      if (ind.isNominal() && (head == null || ind.getNominalLevel() < head.getNominalLevel())) {
        head = ind;
        headDS = edge.getDepends();
      }
    }

    // if there is no nominal in the merge list we need to create one
    if (head == null) {
      head = abox.addFreshIndividual(null, ds);
    }
    else {
      ds = ds.union(headDS, abox.doExplanation());
    }

    for (int i = 0; i < edges.size(); i++) {
      Edge edge = edges.edgeAt(i);
      Individual next = edge.getFrom();

      if (next.isPruned()) {
        continue;
      }

      // it is possible that there are multiple edges to the same
      // node, e.g. property p and its super property, so check if
      // we already merged this one
      if (head.isSame(next)) {
        continue;
      }

      ds = ds.union(edge.getDepends(), abox.doExplanation());

      if (next.isDifferent(head)) {
        ds = ds.union(next.getDifferenceDependency(head), abox.doExplanation());
        if (r.isFunctional()) {
          abox.setClash(Clash.functionalCardinality(x, ds, r.getName()));
        }
        else {
          abox.setClash(Clash.maxCardinality(x, ds, r.getName(), 1));
        }

        break;
      }

      if (log.isLoggable(Level.FINE)) {
        log.fine("FUNC: " + x + " for prop " + r + " merge " + next + " -> " + head + " " + ds);
      }

      mergeTo(next, head, ds);

      if (abox.isClosed()) {
        return;
      }

      if (head.isPruned()) {
        ds = ds.union(head.getMergeDependency(true), abox.doExplanation());
        head = head.getSame();
      }
    }
  }

  private void mergeLater(Node y, Node z, DependencySet ds) {
    mergeList.add(new NodeMerge(y, z, ds));
  }

  /**
   * Merge all node pairs in the queue.
   */
  public void mergeAll() {
    if (mergingAll) {
      return;
    }

    mergingAll = true;
    while (!merging && !mergeList.isEmpty() && !abox.isClosed()) {
      NodeMerge merge = mergeList.remove(0);

      Node y = abox.getNode(merge.getSource());
      Node z = abox.getNode(merge.getTarget());
      DependencySet ds = merge.getDepends();

      if (y.isMerged()) {
        ds = ds.union(y.getMergeDependency(true), abox.doExplanation());
        y = y.getSame();
      }

      if (z.isMerged()) {
        ds = ds.union(z.getMergeDependency(true), abox.doExplanation());
        z = z.getSame();
      }

      if (y.isPruned() || z.isPruned()) {
        continue;
      }

      mergeTo(y, z, ds);
    }
    mergingAll = false;
  }

  /**
   * Merge node y into z. Node y and all its descendants will be pruned from the completion graph.
   *
   * @param y
   *            Node being pruned
   * @param z
   *            Node that is being merged into
   * @param ds
   *            Dependency of this merge operation
   */
  public void mergeTo(Node y, Node z, DependencySet ds) {

    // add to effected list
    if (abox.getBranch() >= 0 && PelletOptions.TRACK_BRANCH_EFFECTS) {
      abox.getBranchEffectTracker().add(abox.getBranch(), y.getName());
      abox.getBranchEffectTracker().add(abox.getBranch(), z.getName());
    }

    // add to merge dependency to dependency index
    if (PelletOptions.USE_INCREMENTAL_DELETION) {
      abox.getKB().getDependencyIndex().addMergeDependency(y.getName(), z.getName(), ds);
    }

    if (y.isDifferent(z)) {
      abox.setClash(Clash.nominal(y, y.getDifferenceDependency(z).union(ds, abox.doExplanation())));
      return;
    }
    else if (!y.isSame(z)) {
      abox.setChanged(true);

      if (merging) {
        mergeLater(y, z, ds);
        return;
      }

      merging = true;

      if (log.isLoggable(Level.FINE)) {
        log.fine("MERG: " + y + " -> " + z + " " + ds);
      }

      ds = ds.copy(abox.getBranch());

      if (y instanceof Literal && z instanceof Literal) {
        mergeLiterals((Literal) y, (Literal) z, ds);
      }
      else if (y instanceof Individual && z instanceof Individual) {
        mergeIndividuals((Individual) y, (Individual) z, ds);
      }
      else {
        throw new InternalReasonerException("Invalid merge operation!");
      }
    }

    merging = false;
    mergeAll();
  }

  /**
   * Merge individual y into x. Individual y and all its descendants will be pruned from the completion graph.
   *
   * @param y
   *            Individual being pruned
   * @param x
   *            Individual that is being merged into
   * @param ds
   *            Dependency of this merge operation
   */
  protected boolean mergeIndividuals(Individual y, Individual x, DependencySet ds) {
    boolean merged = y.setSame(x, ds);
    if (!merged) {
      return false;
    }

    // if both x and y are blockable x still remains blockable (nominal level
    // is still set to BLOCKABLE), if one or both are nominals then x becomes
    // a nominal with the minimum level
    x.setNominalLevel(Math.min(x.getNominalLevel(), y.getNominalLevel()));

    // copy the types
    Map<ATermAppl, DependencySet> types = y.getDepends();
    for (Map.Entry<ATermAppl, DependencySet> entry : types.entrySet()) {
      ATermAppl yType = entry.getKey();
      DependencySet finalDS = ds.union(entry.getValue(), abox.doExplanation());
      addType(x, yType, finalDS);
    }

    // for all edges (z, r, y) add an edge (z, r, x)
    EdgeList inEdges = y.getInEdges();
    for (int e = 0; e < inEdges.size(); e++) {
      Edge edge = inEdges.edgeAt(e);

      Individual z = edge.getFrom();
      Role r = edge.getRole();
      DependencySet finalDS = ds.union(edge.getDepends(), abox.doExplanation());

      // if y has a self edge then x should have the same self edge
      if (y.equals(z)) {
        addEdge(x, r, x, finalDS);
      }
      // if z is already a successor of x add the reverse edge
      else if (x.hasSuccessor(z)) {
        // FIXME what if there were no inverses in this expressitivity
        addEdge(x, r.getInverse(), z, finalDS);
      }
      else {
        addEdge(z, r, x, finalDS);
      }

      // only remove the edge from z and keep a copy in y for a
      // possible restore operation in the future
      z.removeEdge(edge);

      // add to effected list of queue
      // if( abox.getBranch() >= 0 && PelletOptions.USE_COMPLETION_QUEUE ) {
      // abox.getCompletionQueue().addEffected( abox.getBranch(), z.getName() );
      // }
      if (abox.getBranch() >= 0 && PelletOptions.TRACK_BRANCH_EFFECTS) {
        abox.getBranchEffectTracker().add(abox.getBranch(), z.getName());
      }

    }

    // for all z such that y != z set x != z
    x.inheritDifferents(y, ds);

    // we want to prune y early due to an implementation issue about literals
    // if y has an outgoing edge to a literal with concrete value
    y.prune(ds);

    // for all edges (y, r, z) where z is a nominal add an edge (x, r, z)
    EdgeList outEdges = y.getOutEdges();
    for (int e = 0; e < outEdges.size(); e++) {
      Edge edge = outEdges.edgeAt(e);
      Node z = edge.getTo();

      if (z.isNominal() && !y.equals(z)) {
        Role r = edge.getRole();
        DependencySet finalDS = ds.union(edge.getDepends(), abox.doExplanation());

        addEdge(x, r, z, finalDS);

        // add to effected list
        if (abox.getBranch() >= 0 && PelletOptions.TRACK_BRANCH_EFFECTS) {
          abox.getBranchEffectTracker().add(abox.getBranch(), z.getName());
        }

        // do not remove edge here because prune will take care of that
      }
    }
   
    return true;
  }

  /**
   * Merge literal y into x. Literal y will be pruned from* the completion graph.
   *
   * @param y
   *            Literal being pruned
   * @param x
   *            Literal that is being merged into
   * @param ds
   *            Dependency of this merge operation
   */
  protected void mergeLiterals(Literal y, Literal x, DependencySet ds) {
    y.setSame(x, ds);

    x.addAllTypes(y.getDepends(), ds);

    // for all edges (z, r, y) add an edge (z, r, x)
    EdgeList inEdges = y.getInEdges();
    for (int e = 0; e < inEdges.size(); e++) {
      Edge edge = inEdges.edgeAt(e);

      Individual z = edge.getFrom();
      Role r = edge.getRole();
      DependencySet finalDS = ds.union(edge.getDepends(), abox.doExplanation());

      addEdge(z, r, x, finalDS);

      // only remove the edge from z and keep a copy in y for a
      // possible restore operation in the future
      z.removeEdge(edge);

      // add to effected list
      if (abox.getBranch() >= 0 && PelletOptions.TRACK_BRANCH_EFFECTS) {
        abox.getBranchEffectTracker().add(abox.getBranch(), z.getName());
      }
    }

    x.inheritDifferents(y, ds);

    y.prune(ds);

    if (x.getNodeDepends() == null || y.getNodeDepends() == null) {
      throw new NullPointerException();
    }
  }

  public boolean setDifferent(Node y, Node z, DependencySet ds) {
    return y.setDifferent(z, ds);
  }
 
  public void restoreLocal(Individual ind, Branch br) {
    abox.stats.localRestores++;
    abox.setClash(null);
    abox.setBranch(br.getBranch());

    Map<Node, Boolean> visited = new HashMap<Node, Boolean>();

    restoreLocal(ind, br.getBranch(), visited);

    for (Map.Entry<Node, Boolean> entry : visited.entrySet()) {
      boolean restored = entry.getValue();
      if (restored) {
        allValuesRule.apply((Individual) entry.getKey());
      }
    }
  }

  private void restoreLocal(Individual ind, int branch, Map<Node, Boolean> visited) {
    boolean restored = ind.restore(branch);
    visited.put(ind, restored);

    if (restored) {
      for (Edge edge : ind.getOutEdges()) {
        Node succ = edge.getTo();
        if (visited.containsKey(succ)) {
          continue;
        }

        if (succ.isLiteral()) {
          visited.put(succ, Boolean.FALSE);
          succ.restore(branch);
        }
        else {
          restoreLocal(((Individual) succ), branch, visited);
        }
      }

      for (Edge edge : ind.getInEdges()) {
        Individual pred = edge.getFrom();
        if (visited.containsKey(pred)) {
          continue;
        }
        restoreLocal(pred, branch, visited);
      }
    }
  }

  public void restore(Branch br) {
    // Timers timers = abox.getKB().timers;
    // Timer timer = timers.startTimer("restore");
    abox.setBranch(br.getBranch());
    abox.setClash(null);
    // Setting the anonCount to the value at the time of branch creation is incorrect
    // when SMART_RESTORE option is turned on. If we create an anon node after branch
    // creation but node depends on an earlier branch restore operation will not remove
    // the node. But setting anonCount to a smaller number may mean the anonCount will
    // be incremented to that value and creating a fresh anon node will actually reuse
    // the not-removed node. The only advantage of setting anonCount to a smaller value
    // is to keep the name of anon nodes smaller to make debugging easier. For this reason,
    // the above line is not removed and under special circumstances may be uncommented
    // to help debugging only with the intent that it will be commented again after
    // debugging is complete
    // abox.setAnonCount( br.getAnonCount() );
    abox.rulesNotApplied = true;
    mergeList.clear();

    List<ATermAppl> nodeList = abox.getNodeNames();

    if (log.isLoggable(Level.FINE)) {
      log.fine("RESTORE: Branch " + br.getBranch());
    }

    if (PelletOptions.USE_COMPLETION_QUEUE) {
      // clear the all values list as they must have already fired and blocking never prevents the all values rule
      // from firing
      abox.getCompletionQueue().clearQueue(NodeSelector.UNIVERSAL);

      // reset the queues
      abox.getCompletionQueue().restore(br.getBranch());
    }

    // the restore may cause changes which require using the allValuesRule -
    // incremental change tracker will track those
    if (PelletOptions.USE_INCREMENTAL_CONSISTENCY) {
      abox.getIncrementalChangeTracker().clear();
    }

    // for each node we either need to restore the node to the status it
    // had at the time branch was created or remove the node completely if
    // it was created after the branch. To optimize removing elements from
    // the ArrayList we compute the block to be deleted and then remove all
    // at once to utilize the underlying System.arraycopy operation.

    // number of nodes in the nodeList
    int nodeCount = nodeList.size();
    // number of nodes
    int deleteBlock = 0;
    for (int i = 0; i < nodeCount; i++) {
      // get the node name
      ATermAppl a = nodeList.get(i);
      // and the corresponding node
      Node node = abox.getNode(a);

      // node dependency tells us if the node was created after the branch
      // and if that is the case we remove it completely
      // NOTE: for literals, node.getNodeDepends() may be null when a literal value branch is
      // restored, in that case we can remove the literal since there is no other reference
      // left for that literal
      if (node.getNodeDepends() == null || node.getNodeDepends().getBranch() > br.getBranch()) {
        // remove the node from the node map
        abox.removeNode(a);
        // if the node is merged to another one we should remove it from
        // the other node's merged list
        if (node.isMerged()) {
          node.undoSetSame();
        }
        // increment the size of block that will be deleted
        deleteBlock++;
      }
      else {
        // this node will be restored to previous state not removed

        // first if there are any nodes collected earlier delete them
        if (deleteBlock > 0) {
          // create the sub list for nodes to be removed
          List<ATermAppl> subList = nodeList.subList(i - deleteBlock, i);
          if (log.isLoggable(Level.FINE)) {
            log.fine("Remove nodes " + subList);
          }
          // clear the sublist causing all elements to removed from nodeList
          subList.clear();
          // update counters
          nodeCount -= deleteBlock;
          i -= deleteBlock;
          deleteBlock = 0;
        }

        // restore only if not tracking branch effects
        if (!PelletOptions.TRACK_BRANCH_EFFECTS) {
          node.restore(br.getBranch());
        }
      }
    }

    // if there were nodes to be removed at the end of the list do it now
    if (deleteBlock > 0) {
      nodeList.subList(nodeCount - deleteBlock, nodeCount).clear();
    }

    if (PelletOptions.TRACK_BRANCH_EFFECTS) {
      // when tracking branch effects only restore nodes explicitly stored in the effected list
      Set<ATermAppl> effected = abox.getBranchEffectTracker().removeAll(br.getBranch() + 1);
      for (ATermAppl a : effected) {
        Node n = abox.getNode(a);
        if (n != null) {
          n.restore(br.getBranch());
        }
      }
    }

    restoreAllValues();

    if (log.isLoggable(Level.FINE)) {
      abox.printTree();
    }

    if (!abox.isClosed()) {
      abox.validate();
    }

    // timer.stop();
  }

  public void addBranch(Branch newBranch) {
    abox.getBranches().add(newBranch);

    if (newBranch.getBranch() != abox.getBranches().size()) {
      throw new RuntimeException("Invalid branch created: "
                                 + newBranch.getBranch()
                                 + " != "
                                 + abox.getBranches().size());
    }

    completionTimer.check();

    // CHW - added for incremental deletion support
    if (PelletOptions.USE_INCREMENTAL_DELETION) {
      abox.getKB().getDependencyIndex().addBranchAddDependency(newBranch);
    }
  }

  void printBlocked() {
    int blockedCount = 0;
    StringBuffer blockedNodes = new StringBuffer();
    Iterator<Individual> n = abox.getIndIterator();
    while (n.hasNext()) {
      Individual node = n.next();
      ATermAppl x = node.getName();

      if (blocking.isBlocked(node)) {
        blockedCount++;
        blockedNodes.append(x).append(" ");
      }
    }

    log.fine("Blocked nodes " + blockedCount + " [" + blockedNodes + "]");
  }

  @Override
  public String toString() {
    String name = getClass().getName();
    int lastIndex = name.lastIndexOf('.');
    return name.substring(lastIndex + 1);
  }

  protected void restoreAllValues() {
    for (Iterator<Individual> i = new IndividualIterator(abox); i.hasNext();) {
      Individual ind = i.next();
      allValuesRule.apply(ind);
    }
  }
}
TOP

Related Classes of org.mindswap.pellet.tableau.completion.CompletionStrategy

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.