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

com.clarkparsia.modularity.AbstractModuleExtractor Maven / Gradle / Ivy

There is a newer version: 2.3.6-ansell
Show newest version
// Copyright (c) 2006 - 2008, Clark & Parsia, LLC. 
// 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: [email protected]

package com.clarkparsia.modularity;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.mindswap.pellet.PelletOptions;
import org.mindswap.pellet.KnowledgeBase.ChangeType;
import org.mindswap.pellet.taxonomy.Taxonomy;
import org.mindswap.pellet.taxonomy.TaxonomyNode;
import org.mindswap.pellet.utils.MultiValueMap;
import org.mindswap.pellet.utils.Timer;
import org.mindswap.pellet.utils.Timers;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLEntity;
import org.semanticweb.owlapi.model.OWLException;
import org.semanticweb.owlapi.model.OWLOntology;

import com.clarkparsia.modularity.io.ModuleExtractorPersistence;
import com.clarkparsia.modularity.io.UncloseableOutputStream;
import com.clarkparsia.owlapi.modularity.locality.LocalityClass;
import com.clarkparsia.owlapi.modularity.locality.LocalityEvaluator;
import com.clarkparsia.owlapi.modularity.locality.SyntacticLocalityEvaluator;
import com.clarkparsia.owlapiv3.OWL;
import com.clarkparsia.owlapiv3.OntologyUtils;
import com.clarkparsia.pellet.expressivity.Expressivity;

/**
 * 

* Title: *

*

* Description: *

*

* Copyright: Copyright (c) 2007 *

*

* Company: Clark & Parsia, LLC. *

* * @author Evren Sirin */ public abstract class AbstractModuleExtractor implements ModuleExtractor { public static final Logger log = Logger .getLogger( AbstractModuleExtractor.class .getName() ); private Set additions = new HashSet(); private Set allClasses = new HashSet(); private OWLOntology axiomOntology = null; /** * Map to find entities referenced in an axiom */ private MultiValueMap axiomEntities = new MultiValueMap(); /** * Set of axioms that will be deleted */ private Set deletions = new HashSet(); /** * The types of changes that are pending in additions and deletions */ protected EnumSet changes = EnumSet.noneOf(ChangeType.class); /** * Map to find axioms that references an axiom */ protected MultiValueMap entityAxioms = new MultiValueMap(); private LocalityEvaluator localityEvaluator = null; protected MultiValueMap modules = null; /** * Flag to check if a non-local axiom has been updated */ private boolean nonLocalAxioms = false; private Timers timers = new Timers(); public AbstractModuleExtractor() { this( new SyntacticLocalityEvaluator( LocalityClass.BOTTOM_BOTTOM ) ); } public AbstractModuleExtractor(LocalityEvaluator localityEvaluator) { this.localityEvaluator = localityEvaluator; } public void addAxiom(OWLAxiom axiom) { checkNonLocalAxiom( axiom ); if( axiomEntities.containsKey( axiom ) ) return; if( log.isLoggable( Level.FINE ) ) log.fine( "Adding " + axiom ); deletions.remove( axiom ); additions.add( axiom ); categorizeAddedAxiom( axiom ); } /** * Returns if the extracted modules can be updated. We can update the * modules if we have computed modules and no non-local axiom has been added * or deleted. * * @return */ public boolean canUpdate() { return modules != null && !nonLocalAxioms; } /** * Checks if the given axiom is non-local w.r.t. empty signature and updates * the {@link #nonLocalAxioms} field. * * @param axiom - * Axiom to be checked */ private void checkNonLocalAxiom(OWLAxiom axiom) { if( !axiom.isLogicalAxiom() ) return; // no need to check for non-locals if we already know that we cannot // update modules if( canUpdate() ) { if( !isLocal( axiom, Collections.emptySet() ) ) { log.warning( "*** Non-local axiom: " + axiom ); nonLocalAxioms = true; } } } public void deleteAxiom(OWLAxiom axiom) { checkNonLocalAxiom( axiom ); if( !axiomEntities.containsKey( axiom ) ) { if( additions.remove( axiom ) ) { if( log.isLoggable( Level.FINE ) ) log.fine( "Deleted axiom from add queue before processing " + axiom ); } return; } if( log.isLoggable( Level.FINE ) ) log.fine( "Deleting " + axiom ); additions.remove( axiom ); deletions.add( axiom ); categorizeRemovedAxiom( axiom ); } public MultiValueMap getModules() { return modules; } /** * Extract modules from scratch * * @return */ public MultiValueMap extractModules() { Timer timer = timers.startTimer( "extractModules" ); // cache the axiom signatures processAdditions(); additions.clear(); // no need to consider deletions for initial module extraction deletions.clear(); changes.clear(); nonLocalAxioms = false; modules = new MultiValueMap(); extractModuleSignatures( allClasses ); timer.stop(); return modules; } /** * This is a main method to extract the signature for a set of classes Note * that this method updates the modules for the classes which are maintained * (either newly created or already existing) in a module partial order * * @param entities - * the set of entities whose modules should be extracted */ protected abstract void extractModuleSignatures(Set entities); /** * Given an axiom, this function locates all root nodes in the partial order * that are affected by the update * * @param axiom - * the update * @param add - * Flag for additions/deletions */ private Set getAffectedRoots(OWLAxiom axiom, Taxonomy taxonomy, boolean add) { Set roots = new HashSet(); Set> visited = new HashSet>(); visited.add( taxonomy.getBottom() ); getAffectedRoots( axiom, taxonomy.getTop(), roots, add, visited ); /* * Special case when the only node affected by a deletion is * unsatisfiable */ if( !add && roots.isEmpty() ) { for( OWLClass unsat : taxonomy.getEquivalents( OWL.Nothing ) ) { Set signature = modules.get( unsat ); if( (signature != null) && signature.containsAll( getSignature( axiom ) ) ) roots.add( unsat ); } } return roots; } /** * Given an axiom, this function locates all root nodes in the partial order * that are affected by the update * * @param axiom - * the update * @param node - * the next node * @param effects - * the actual set of affected nodes collected * @param add - * Flag for additions/deletions * @param visited - * nodes visited so far */ private void getAffectedRoots(OWLAxiom axiom, TaxonomyNode node, Set effects, boolean add, Set> visited) { // only proceed if not seen this node if( visited.contains( node ) ) return; else visited.add( node ); OWLEntity entity = node.getName(); // get the sig for this module Set signature = modules.get( entity ); boolean outdated = false; // check if the entity has been removed due to a deletion if( signature == null ) { if( log.isLoggable( Level.FINE ) ) log.fine( "Removed entity " + entity ); } else if( add ) { // only affected if axiom is non-local w.r.t. the sig of the // module outdated = !isLocal( axiom, signature ); } else { // only affected if sig of axiom is contained in sig of module outdated = signature.containsAll( getSignature( axiom ) ); } // if outdated add to effected set if( outdated ) { effects.addAll( node.getEquivalents() ); } else { // recursive call to children for( TaxonomyNode next : node.getSubs() ) { getAffectedRoots( axiom, next, effects, add, visited ); } } } /** * Return the axioms which references this entity * * @param entity * @return */ public Set getAxioms(OWLEntity entity) { Set axioms = entityAxioms.get( entity ); if( axioms == null ) axioms = Collections.emptySet(); return axioms; } public OWLOntology getModule(OWLEntity entity) { return getModuleFromSignature( modules.get( entity ) ); } protected Set getModuleAxioms(Set signature) { Set referenced = new HashSet(); Set augmentedSig = new HashSet( signature ); augmentedSig.add( OWL.Thing ); Set axioms = new HashSet(); Set candidates = new HashSet(); for( OWLEntity e : signature ) { candidates.addAll( getAxioms( e ) ); } for( OWLAxiom axiom : candidates ) { Set sigAxiom = axiomEntities.get( axiom ); /* * An axiom is in the module of the signature if the augmented * signature contains all the entities referenced in the axiom. * However, there are cases this necessary condition is not * sufficient. For example, an axiom may be a tautology, e.g. * PropertyDomain(p owl:Thing), and if this axiom is included the * module would not be minimal anymore. The locality check we * perform for the axiom w.r.t. the given signature filters these * axioms. */ if( augmentedSig.containsAll( sigAxiom ) && !isLocal( axiom, signature ) ) { axioms.add( axiom ); referenced.addAll( sigAxiom ); } } /* * Special handling is required if an entity is in the input signature, * and is referenced by some axioms, but all those axioms are local to * the signature and contain entities not in the signature. A * declaration axiom is used to keep the entity in the module. */ Set notReferenced = new HashSet( signature ); notReferenced.removeAll( referenced ); for( OWLEntity e : notReferenced ) { if( entityAxioms.get( e ) != null ) axioms.add( OWL.declaration( e ) ); } return axioms; } /** * Returns a new ontology that contains the axioms that are in the module * for given set of entities * * @param signature * @return * @throws OWLException */ public OWLOntology getModuleFromSignature(Set signature) { Set moduleAxioms = getModuleAxioms( signature ); return OWL.Ontology( moduleAxioms ); } /** * Get the entities referenced in this axiom * * @param axiom * @return */ protected Set getSignature(OWLAxiom axiom) { return axiomEntities.get( axiom ); } /** * Checks if axioms have been added/removed and modules need to be updated * * @return true if axioms have been added/removed */ public boolean isChanged() { return !additions.isEmpty() || !deletions.isEmpty() || nonLocalAxioms; } protected boolean isLocal(OWLAxiom axiom, Set signature) { return localityEvaluator.isLocal( axiom, signature ); } public void addOntologies(Set ontologies) { for( OWLOntology ontology : ontologies ) addOntology( ontology ); } public void addOntology(OWLOntology ontology) { for( OWLAxiom axiom : ontology.getAxioms() ) addAxiom( axiom ); } private void processAdditions() { for( OWLAxiom axiom : additions ) { Set signature = OntologyUtils.getSignature( axiom ); axiomEntities.put( axiom, signature ); for( OWLEntity entity : signature ) { entityAxioms.add( entity, axiom ); if( entity instanceof OWLClass ) { OWLClass cls = (OWLClass) entity; allClasses.add( cls ); } } } } private void processDeletions() { for( OWLAxiom axiom : deletions ) { Set entities = axiomEntities.remove( axiom ); for( OWLEntity entity : entities ) { entityAxioms.remove( entity, axiom ); if( !entityAxioms.containsKey( entity ) ) { if( log.isLoggable( Level.FINE ) ) log.fine( "Remove " + entity + " which is not mentioned anymore" ); allClasses.remove( entity ); modules.remove( entity ); } } } } /** * Method to 1) find affected modules and 2) update them - that is extract * their new signatures * * @param Map * axiomMap Index from the new/deleted axioms to their signature * @param Segmentation * seg handle on segmentation for this ontology * @param boolean * add Flag for additions/deletions */ private Set updateEffectedModules(Set axioms, Taxonomy taxonomy, boolean add) { // affected root nodes in order Set affectedRoots = new HashSet(); // Set of all nodes affected Set affected = new HashSet(); // Set of all entities in the module of affected entities Set effects = new HashSet(); if( log.isLoggable( Level.FINE ) ) log.fine( "Update modules for " + (add ? "additions" : "deletions") ); // any new classes are affected affectedRoots.addAll( allClasses ); affectedRoots.removeAll( modules.keySet() ); // iterate over all axioms and get find the set of root nodes // affected by the update for( OWLAxiom axiom : axioms ) { // find affected roots - recursive function affectedRoots.addAll( getAffectedRoots( axiom, taxonomy, add ) ); } if( log.isLoggable( Level.FINE ) ) log.fine( "Affected roots " + affectedRoots ); // given root, get all affected objects for( OWLEntity nextRoot : affectedRoots ) { // add root to affected affected.add( nextRoot ); if( nextRoot instanceof OWLClass ) { // collect all the descendants of this class if( taxonomy.contains( (OWLClass) nextRoot ) ) affected.addAll( taxonomy.getFlattenedSubs( (OWLClass) nextRoot, false ) ); } } if( log.isLoggable( Level.FINE ) ) log.fine( "Affected entities " + affected ); for( OWLEntity entity : affected ) modules.remove( entity ); // Next update mods of all affected nodes extractModuleSignatures( affected ); for( OWLEntity entity : affected ) { Set module = modules.get( entity ); if( module == null ) { String msg = "No module for " + entity; log.log( Level.SEVERE, msg, new RuntimeException( msg ) ); } effects.addAll( module ); } return effects; } /** * Update the modules with the changes that have been put into the queue so * far. * * @param add * If true apply only addition changes, otherwise * apply deletions * @return The set of entities whose modules are affected by the changes * @throws UnsupportedOperationException * if modules cannot be updated as reported by * {@link #canUpdate()} function */ public Set updateModules(Taxonomy taxonomy, boolean add) throws UnsupportedOperationException { Timer timer = timers.startTimer( "updateModules" ); if( !canUpdate() ) throw new UnsupportedOperationException( "Modules cannot be updated!" ); Set effects = null; if( add ) { // cash the signatures for axioms as they are used in the next step processAdditions(); // compute effects effects = updateEffectedModules( additions, taxonomy, add ); // clear processed axioms additions.clear(); // update the pending change types -- remove the changes // that are additions changes.remove(ChangeType.ABOX_ADD); changes.remove(ChangeType.RBOX_ADD); changes.remove(ChangeType.TBOX_ADD); } else { // compute effects effects = updateEffectedModules( deletions, taxonomy, add ); // remove signatures for deleted axioms now that they are not needed processDeletions(); // clear processed axioms deletions.clear(); // update the pending change types -- remove the changes // that are additions changes.remove(ChangeType.ABOX_DEL); changes.remove(ChangeType.RBOX_DEL); changes.remove(ChangeType.TBOX_DEL); } timer.stop(); return effects; } public Timers getTimers() { return timers; } public Set getAxioms() { return Collections.unmodifiableSet( axiomEntities.keySet() ); } public OWLOntology getAxiomOntology() { if ( null == axiomOntology ) { axiomOntology = OWL.Ontology( axiomEntities.keySet() ); } return axiomOntology; } public Set getEntities() { return Collections.unmodifiableSet( entityAxioms.keySet() ); } public void resetModules() { // cache the axiom signatures processAdditions(); additions.clear(); // no need to consider deletions for initial module extraction deletions.clear(); changes.clear(); nonLocalAxioms = false; modules = new MultiValueMap(); } /** * @inheritDoc */ public boolean isClassificationNeeded(Expressivity expressivity) { return isTBoxChanged() // RBox did not change since classification || isRBoxChanged() // there are no nominals || (expressivity.hasNominal() && !PelletOptions.USE_PSEUDO_NOMINALS); } /** * Checks whether there are unapplied changes to the TBox * * @return true if there are unapplied changes to TBox */ public boolean isTBoxChanged() { return changes.contains( ChangeType.TBOX_ADD ) || changes.contains( ChangeType.TBOX_DEL ); } /** * Checks whether there are unapplied changes to the RBox * * @return true if there are unapplied changes to RBox */ public boolean isRBoxChanged() { return changes.contains( ChangeType.RBOX_ADD ) || changes.contains( ChangeType.RBOX_DEL ); } /** * Checks whether there are unapplied changes to the ABox * * @return true if there are unapplied changes to ABox */ public boolean isABoxChanged() { return changes.contains( ChangeType.ABOX_ADD ) || changes.contains( ChangeType.ABOX_DEL ); } /** * Checks the category of the change the addition of the axiom introduces * (i.e., change to a TBox, RBox, or ABox), and updates the changes set appropriately. * * @param axiom the axiom being added */ private void categorizeAddedAxiom(OWLAxiom axiom) { if (ChangeTypeDetector.isTBoxAxiom(axiom)) { changes.add(ChangeType.TBOX_ADD); } else if (ChangeTypeDetector.isRBoxAxiom(axiom)) { changes.add(ChangeType.RBOX_ADD); } else if (ChangeTypeDetector.isABoxAxiom(axiom)) { changes.add(ChangeType.ABOX_ADD); } } /** * Checks the category of the change the deletion of the axiom introduces * (i.e., change to a TBox, RBox, or ABox), and updates the changes set appropriately. * * @param axiom the axiom being removed */ private void categorizeRemovedAxiom(OWLAxiom axiom) { if (ChangeTypeDetector.isTBoxAxiom(axiom)) { changes.add(ChangeType.TBOX_DEL); } else if (ChangeTypeDetector.isRBoxAxiom(axiom)) { changes.add(ChangeType.RBOX_DEL); } else if (ChangeTypeDetector.isABoxAxiom(axiom)) { changes.add(ChangeType.ABOX_DEL); } } // I/O code to persist the state of the AbstractModuleExtractor /** * The name of the entry in the zip file that stores axioms */ private static final String MODULE_EXTRACTOR_AXIOMS_FILE_NAME = "ModuleExtractorAxioms"; /** * The name of the entry in the zip file that stores the module information */ private static final String MODULE_EXTRACTOR_MODULES_FILE_NAME = "ModuleExtractorModules"; /** * @inheritDoc */ public void save(ZipOutputStream outputStream) throws IOException, IllegalStateException { if ( !additions.isEmpty() || !deletions.isEmpty() ) { throw new IllegalStateException( "The module extractor contains unapplied changes to the modules, and therefore cannot be saved." ); } // first save the axioms ZipEntry axiomsEntry = new ZipEntry( MODULE_EXTRACTOR_AXIOMS_FILE_NAME ); outputStream.putNextEntry( axiomsEntry ); ModuleExtractorPersistence.saveAxioms( axiomEntities.keySet(), new UncloseableOutputStream( outputStream ) ); // next save the modules ZipEntry modulesEntry = new ZipEntry( MODULE_EXTRACTOR_MODULES_FILE_NAME ); outputStream.putNextEntry( modulesEntry ); ModuleExtractorPersistence.saveModules( modules, new UncloseableOutputStream( outputStream ) ); outputStream.flush(); } /** * @inheritDoc */ public void load(ZipInputStream inputStream) throws IOException, IllegalArgumentException { resetModules(); ZipEntry zipEntry = inputStream.getNextEntry(); if( !( MODULE_EXTRACTOR_AXIOMS_FILE_NAME.equals( zipEntry.getName() ) ) ) { throw new IllegalArgumentException( String.format( "Unexpected entry (%s) in ZipInputStream. Expected %s", zipEntry.getName(), MODULE_EXTRACTOR_AXIOMS_FILE_NAME ) ); } axiomOntology = ModuleExtractorPersistence.loadAxiomOntology( inputStream ); Collection axioms = axiomOntology.getAxioms(); // I am not sure that this is the right way to recompute this ... additions.addAll( axioms ); processAdditions(); additions.clear(); zipEntry = inputStream.getNextEntry(); if( !( MODULE_EXTRACTOR_MODULES_FILE_NAME.equals( zipEntry.getName() ) ) ) { throw new IllegalArgumentException( String.format( "Unexpected entry (%s) in ZipInputStream. Expected %s", zipEntry.getName(), MODULE_EXTRACTOR_MODULES_FILE_NAME ) ); } modules = ModuleExtractorPersistence.loadModules( inputStream ); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy