Package org.mindswap.pellet.tbox.impl

Source Code of org.mindswap.pellet.tbox.impl.TBoxExpImpl

// Copyright (c) 2006 - 2008, Clark & Parsia, LLC. <http://www.clarkparsia.com>
// This source code is 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.tbox.impl;

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

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
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.KnowledgeBase;
import org.mindswap.pellet.PelletOptions;
import org.mindswap.pellet.tbox.TBox;
import org.mindswap.pellet.utils.ATermUtils;
import org.mindswap.pellet.utils.iterator.MultiIterator;

import aterm.AFun;
import aterm.ATermAppl;
import aterm.ATermList;

import com.clarkparsia.pellet.utils.CollectionUtils;
import com.clarkparsia.pellet.utils.MultiMapUtils;

/**
* <p>
* Title: Implementation of TBox interface to generate explanations efficiently
* and correctly.
* </p>
* <p>
* Description:
* </p>
* <p>
* Copyright: Copyright (c) 2007
* </p>
* <p>
* Company: Clark & Parsia, LLC. <http://www.clarkparsia.com>
* </p>
*
* @author Evren Sirin
*/
public class TBoxExpImpl implements TBox {
  public static Logger            log          = Logger.getLogger( TBox.class
                                      .getName() );

  private static final Set<Set<ATermAppl>>  SINGLE_EMPTY_SET  = Collections
                                      .singleton( Collections
                                          .<ATermAppl> emptySet() );

  protected KnowledgeBase            kb;

  protected Set<ATermAppl>          classes        = CollectionUtils
                                      .makeIdentitySet();
  private Set<ATermAppl>            allClasses;

  /**
   * MultiValueMap where key is an axiom and the values are the explanations
   * of the key
   */
  private Map<ATermAppl, Set<Set<ATermAppl>>>  tboxAxioms      = CollectionUtils
                                      .makeIdentityMap();
  /**
   * MultiValueMap where key is an axiom and the values are axioms for which
   * the key is a part of an explanation
   */
  private Map<ATermAppl, Set<ATermAppl>>    reverseExplain    = CollectionUtils
                                      .makeIdentityMap();

  private Set<ATermAppl>            tboxAssertedAxioms  = CollectionUtils
                                      .makeIdentitySet();
  
  /**
   * Set of axioms that have been absorbed into ABox or RBox
   */
  private Set<ATermAppl>            absorbedAxioms    = CollectionUtils
                                      .makeIdentitySet();

  public TuBox                Tu          = null;
  public TgBox                Tg          = null;

  /*
   * Constructors
   */

  public TBoxExpImpl(KnowledgeBase kb) {
    this.kb = kb;

    Tu = new TuBox( this );
    Tg = new TgBox( this );

    this.kb = kb;
  }

  public KnowledgeBase getKB() {
    return kb;
  }
 
  public Set<ATermAppl> getAllClasses() {
    if( allClasses == null ) {
      allClasses = new HashSet<ATermAppl>( classes );
      allClasses.add( ATermUtils.TOP );
      allClasses.add( ATermUtils.BOTTOM );
    }
    return allClasses;
  }

  public Set<Set<ATermAppl>> getAxiomExplanations(ATermAppl axiom) {
    return tboxAxioms.get( axiom );
  }

  public Set<ATermAppl> getAxiomExplanation(ATermAppl axiom) {
    Set<Set<ATermAppl>> explains = tboxAxioms.get( axiom );

    if( explains == null || explains.isEmpty() ) {
      log.warning( "No explanation for " + axiom );
      return Collections.emptySet();
    }

    // we won't be generating multiple explanations using axiom
    // tracing so we just pick one explanation. the other option
    // would be to return the union of all explanations which
    // would cause Pellet to return non-minimal explanations sets
    for ( Set<ATermAppl> explain : explains ) {
      return explain;
    }
    return Collections.emptySet();
  }

  /**
   * Add a new explanation for the given axiom. If a previous explanation
   * exists this will be stored as another explanation.
   *
   * @param axiom
   * @param explain
   * @return
   */
  protected boolean addAxiomExplanation(ATermAppl axiom, Set<ATermAppl> explain) {
    if( log.isLoggable( Level.FINE ) )
      log.fine( "Axiom: " + ATermUtils.toString( axiom ) + " Explanation: " + explain );

    boolean added = false;
    if( !PelletOptions.USE_TRACING ) {
      added = tboxAxioms.put( axiom, SINGLE_EMPTY_SET ) == null;
    }
    else {
      added = MultiMapUtils.add( tboxAxioms, axiom, explain );
    }

    if( added ) {
      for( ATermAppl explainAxiom : explain ) {
        if( !axiom.equals( explainAxiom ) )
          MultiMapUtils.add( reverseExplain, explainAxiom, axiom );
      }
    }

    return added;
  }
 
  private static void addDisjointAxiom(ATermAppl c1, ATermAppl c2, List<ATermAppl> axioms) {
    ATermAppl notC2 = ATermUtils.makeNot( c2 );
    axioms.add( ATermUtils.makeSub( c1, notC2 ) );

    if( ATermUtils.isPrimitive( c2 ) ) {
      ATermAppl notC1 = ATermUtils.makeNot( c1 );   
      axioms.add( ATermUtils.makeSub( c2, notC1 ) );
    }
  }

  public boolean addAxiom(ATermAppl axiom) {
    tboxAssertedAxioms.add( axiom );
   
    List<ATermAppl> axioms = null;

    Set<ATermAppl> explain = PelletOptions.USE_TRACING
      ? Collections.singleton( axiom )
      : Collections.<ATermAppl>emptySet();
   
    if( axiom.getAFun().equals( ATermUtils.EQCLASSFUN ) ) {
      axioms = Collections.singletonList( axiom );
    }
    else if( axiom.getAFun().equals( ATermUtils.SUBFUN ) ) {
      axioms = Collections.singletonList( axiom );
    }
    else if( axiom.getAFun().equals( ATermUtils.DISJOINTFUN ) ) {
      axioms = CollectionUtils.makeList();     

      ATermAppl c1 = (ATermAppl) axiom.getArgument( 0 );
      ATermAppl c2 = (ATermAppl) axiom.getArgument( 1 );     
      addDisjointAxiom( c1, c2, axioms );
    }
    else if( axiom.getAFun().equals( ATermUtils.DISJOINTSFUN ) ) {
      axioms = CollectionUtils.makeList();     
     
      ATermList concepts = (ATermList) axiom.getArgument( 0 );     
      for( ATermList l1 = concepts; !l1.isEmpty(); l1 = l1.getNext() ) {
        ATermAppl c1 = (ATermAppl) l1.getFirst();
        for( ATermList l2 = l1.getNext(); !l2.isEmpty(); l2 = l2.getNext() ) {
          ATermAppl c2 = (ATermAppl) l2.getFirst();
          addDisjointAxiom( c1, c2, axioms );
        }
      }
    }
    else {
      log.warning( "Not a valid TBox axiom: " + axiom );
      return false;
    }

    boolean added = false;
    for( ATermAppl a : axioms ) {
      if( absorbNominals( a, explain ) )
        added = true;
      else
        added |= addAxiom( a, explain, false );
    }
   
    return added;
  }

  protected boolean absorbNominals(ATermAppl axiom, Set<ATermAppl> explain) {
    // absorb nominals on the fly because sometimes they might end up in the
    // Tu directly without going into Tg which is still less effective than
    // absorbing
    if( PelletOptions.USE_NOMINAL_ABSORPTION || PelletOptions.USE_PSEUDO_NOMINALS ) {
      if( axiom.getAFun().equals( ATermUtils.EQCLASSFUN ) ) {
        ATermAppl c1 = (ATermAppl) axiom.getArgument( 0 );
        ATermAppl c2 = (ATermAppl) axiom.getArgument( 1 );

        // the first concept is oneOF
        if( ATermUtils.isOneOf( c1 ) ) {
          // absorb SubClassOf(c1,c2)
          Tg.absorbOneOf( c1, c2, explain );
          // the second concept is oneOf
          if( ATermUtils.isOneOf( c2 ) ) {
            // absorb SubClassOf(c2,c1)
            Tg.absorbOneOf( c2, c1, explain );
            // axioms completely absorbed so return
            return true;
          }
          else {
            // SubClassOf(c2,c1) is not absorbed so continue with
            // addAxiom function
            axiom = ATermUtils.makeSub( c2, c1 );
          }
        }
        else if( ATermUtils.isOneOf( c2 ) ) {
          // absorb SubClassOf(c2,c1)
          Tg.absorbOneOf( c2, c1, explain );
         
          // TODO: axiom not referenced again - make sure this is correct.
          // SubClassOf(c1,c2) is not absorbed so continue with
          // addAxiom function
          // axiom = ATermUtils.makeSub( c1, c2 );
        }
      }
      else if( axiom.getAFun().equals( ATermUtils.SUBFUN ) ) {
        ATermAppl sub = (ATermAppl) axiom.getArgument( 0 );

        if( ATermUtils.isOneOf( sub ) ) {
          ATermAppl sup = (ATermAppl) axiom.getArgument( 1 );
          Tg.absorbOneOf( sub, sup, explain );
          return true;
        }
      }
    }

    return false;
  }

  protected boolean addAxiom(ATermAppl axiom, Set<ATermAppl> explain, boolean forceAddition) {
    boolean added = addAxiomExplanation( axiom, explain );

    if( added || forceAddition ) {
      if( !Tu.addIfUnfoldable( axiom ) ) {
        if( axiom.getAFun().equals( ATermUtils.EQCLASSFUN ) ) {
          // Try reversing the term if it is a 'same' construct
          ATermAppl name = (ATermAppl) axiom.getArgument( 0 );
          ATermAppl desc = (ATermAppl) axiom.getArgument( 1 );
          ATermAppl reversedAxiom = ATermUtils.makeEqClasses( desc, name );

          if( !Tu.addIfUnfoldable( reversedAxiom ) )
            Tg.addDef( axiom );
          else
            addAxiomExplanation( reversedAxiom, explain );
        }
        else {
          Tg.addDef( axiom );
        }
      }
    }

    return added;
  }

  public boolean removeAxiom(ATermAppl axiom) {
    return removeAxiom( axiom, axiom );
  }

  public boolean removeAxiom(ATermAppl dependantAxiom, ATermAppl explanationAxiom) {

    if( !PelletOptions.USE_TRACING ) {
      if( log.isLoggable( Level.FINE ) )
        log.fine( "Cannot remove axioms when PelletOptions.USE_TRACING is false" );
      return false;
    }

    if( absorbedAxioms.contains( dependantAxiom ) ) {
      if( log.isLoggable( Level.FINE ) )
        log.fine( "Cannot remove axioms that have been absorbed outside TBox" );
      return false;
    }

    tboxAssertedAxioms.remove( dependantAxiom );

    Set<ATermAppl> sideEffects = new HashSet<ATermAppl>();
    boolean removed = removeExplanation( dependantAxiom, explanationAxiom, sideEffects );

    // an axiom might be effectively removed as a side-effect of another
    // removal. For example see TBoxTests.removedByAbsorbReaddedOnChange
    for( ATermAppl readdAxiom : sideEffects ) {
      Set<Set<ATermAppl>> explanations = tboxAxioms.get( readdAxiom );
      // if the axiom is really removed (and not just side-effected)
      // then there wouldn't be any explanation and we shouldn't readd
      if( explanations != null ) {
        Iterator<Set<ATermAppl>> i = explanations.iterator();
        addAxiom( readdAxiom, i.next(), true );
        while( i.hasNext() )
          addAxiomExplanation( readdAxiom, i.next() );
      }
    }

    return removed;
  }

  private boolean removeExplanation(ATermAppl dependantAxiom, ATermAppl explanationAxiom,
      Set<ATermAppl> sideEffects) {
    boolean success = false;

    if( !PelletOptions.USE_TRACING ) {
      if( log.isLoggable( Level.FINE ) )
        log.fine( "Cannot remove axioms when PelletOptions.USE_TRACING is false" );
      return false;
    }

    if( log.isLoggable( Level.FINE ) )
      log.fine( "Removing " + ATermUtils.toString(explanationAxiom) );

    // this axiom is being removed so it cannot support any other axiom
    MultiMapUtils.remove( reverseExplain, explanationAxiom, dependantAxiom );

    Set<Set<ATermAppl>> explains = tboxAxioms.get( dependantAxiom );
    Set<Set<ATermAppl>> newExplains = new HashSet<Set<ATermAppl>>();

    if( explains != null ) {
      for( Set<ATermAppl> explain : explains ) {
        if( !explain.contains( explanationAxiom ) )
          newExplains.add( explain );
        else {
          sideEffects.addAll( explain );
          sideEffects.remove( explanationAxiom );
        }
      }
    }

    if( !newExplains.isEmpty() ) {
      // there are still other axioms supporting this axiom so it won't be
      // removed but we still need to update the explanations
      tboxAxioms.put( dependantAxiom, newExplains );

      // also make sure the concept on the left hand side is normalized
      Tu.updateDef( dependantAxiom );

      // this axiom is not removed but the operation is successful (so far)
      success = true;
    }
    else {
      // there is no other explanation for this dependant axiom so
      // we can safely remove it
      success |= (tboxAxioms.remove( dependantAxiom ) != null);
 
      AFun fun = dependantAxiom.getAFun();
      if( fun.equals( ATermUtils.SUBFUN ) || fun.equals( ATermUtils.EQCLASSFUN ) ) {
        // remove the axiom fom Tu and Tg
        success |= Tu.removeDef( dependantAxiom );
        success |= Tg.removeDef( dependantAxiom );
      }
    }

    // find if this axiom supports any other axiom
    // note that it is possible dependantAxiom itself is not removed but an axiom that dependantAxiom supports
    // will be removed. this situation occurs typically when there is redundancy in the TBox.
    Set<ATermAppl> otherDependants = reverseExplain.remove( dependantAxiom );
    if( otherDependants != null ) {
      for( ATermAppl otherDependant : otherDependants ) {
        // remove this axiom from any explanation it contributes to

        if( otherDependant.equals( dependantAxiom ) )
          continue;

        success |= removeExplanation( otherDependant, dependantAxiom, sideEffects );
      }
    }

    return success;
  }

  public Collection<ATermAppl> getAxioms() {
    return tboxAxioms.keySet();
  }

  public Collection<ATermAppl> getAssertedAxioms() {
    return tboxAssertedAxioms;
  }
 
  public Collection<ATermAppl> getAbsorbedAxioms() {
    return absorbedAxioms;
  }

  public boolean containsAxiom(ATermAppl axiom) {
    return tboxAxioms.containsKey( axiom );
  }

  public void absorb() {
    Tg.absorb();
  }

  public void print() {
    print( System.out );
  }

  public String toString() {
    StringBuilder sb = new StringBuilder();
    print( sb );
    return sb.toString();
  }

  public void print(Appendable str) {
    try {
      Tu.print( str );
      Tg.print( str );
      str.append( "Explain: [\n" );
      for( ATermAppl axiom : tboxAxioms.keySet() ) {
        str.append( ATermUtils.toString( axiom ) );
        str.append( " -> " );       
        str.append( "[" );
        boolean first = true;
        for( Set<ATermAppl> axioms : tboxAxioms.get( axiom ) ) {
          if( first )
            first = false;
          else
            str.append( ", " );           
          str.append( ATermUtils.toString( axioms ) );
        }
        str.append( "]" );
        str.append( "\n" );
      }
      str.append( "]\nReverseExplain: [\n" );
      for( ATermAppl axiom : reverseExplain.keySet() ) {
        str.append( ATermUtils.toString( axiom ) );
        str.append( " -> " );
        str.append( ATermUtils.toString( reverseExplain.get( axiom ) ) );
        str.append( "\n" );
      }
      str.append( "]\n" );
    } catch( IOException e ) {
      e.printStackTrace();
    }
  }

  public boolean addClass(ATermAppl term) {
    boolean added = classes.add( term );

    if( added )
      allClasses = null;

    return added;
  }

  public Set<ATermAppl> getClasses() {
    return classes;
  }

  public Collection<ATermAppl> getAxioms(ATermAppl term) {
    List<ATermAppl> axioms = new ArrayList<ATermAppl>();
    TermDefinition def = Tg.getTD( term );
    if( def != null ) {
      axioms.addAll( def.getSubClassAxioms() );
      axioms.addAll( def.getEqClassAxioms() );
    }
    def = Tu.getTD( term );
    if( def != null ) {
      axioms.addAll( def.getSubClassAxioms() );
      axioms.addAll( def.getEqClassAxioms() );
    }

    return axioms;
  }

  public void prepare() {
    Tg.absorb();
    Tg.internalize();
    Tu.normalize();
  }

  public Iterator<Unfolding> unfold(ATermAppl c) {
    MultiIterator<Unfolding> result = new MultiIterator<Unfolding>( Tu.unfold( c ).iterator() );
    if( c.equals( TOP ) && !Tg.getUC().isEmpty() )
      result.append( Tg.getUC().iterator() );
    return result;
  }

  public boolean isPrimitive(ATermAppl c) {
    TermDefinition td = Tu.getTD( c );   
    return ATermUtils.isPrimitive( c ) && (td == null || td.isPrimitive());
  }
}
TOP

Related Classes of org.mindswap.pellet.tbox.impl.TBoxExpImpl

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.