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

it.unife.ml.probowlapi.explanation.GlassBoxExplanation Maven / Gradle / Ivy

Go to download

This package encapsulates OWL API adding tools for managing DISPONTE probabilistic semantics. Used by the reasoner BUNDLE.

The newest version!
/**
 *  This file is part of BUNDLE.
 * 
 *  BUNDLE is a probabilistic reasoner for OWL 2 ontologies.
 * 
 *  BUNDLE can be used both as module and as standalone.
 * 
 *  LEAP was implemented as a plugin of DL-Learner http://dl-learner.org, 
 *  but some components can be used as stand-alone.
 * 
 *  BUNDLE and all its parts are distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see .
 * 
 */
package it.unife.ml.probowlapi.explanation;

import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.mindswap.pellet.PelletOptions;
import org.mindswap.pellet.utils.Pair;
import org.mindswap.pellet.utils.SetUtils;
import org.mindswap.pellet.utils.TaxonomyUtils;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLObjectComplementOf;
import org.semanticweb.owlapi.model.OWLObjectIntersectionOf;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyChangeException;
import org.semanticweb.owlapi.model.OWLRuntimeException;

import aterm.ATermAppl;
import com.clarkparsia.owlapi.explanation.SingleExplanationGeneratorImpl;

import com.clarkparsia.owlapi.explanation.util.DefinitionTracker;
import com.clarkparsia.owlapiv3.OWL;
import com.clarkparsia.owlapiv3.OntologyUtils;
import com.clarkparsia.pellet.owlapiv3.AxiomConverter;
import com.clarkparsia.pellet.owlapiv3.PelletReasoner;
import com.clarkparsia.pellet.owlapiv3.PelletReasonerFactory;

/**
 * 

* Title: GlassBoxExplanation *

*

* Description: Implementation of SingleExplanationGenerator interface using the * axiom tracing facilities of Pellet. *

*

* Copyright: Copyright (c) 2007 *

*

* Company: Clark & Parsia, LLC. *

* * @author Evren Sirin */ public class GlassBoxExplanation extends SingleExplanationGeneratorImpl { static { setup(); } /** * Very important initialization step that needs to be called once before a * reasoner is created. this function will be called automatically when * GlassBoxExplanation is loaded by the class loader. */ public static void setup() { // initialize PelletOptions to required values for explanations // to work before any Pellet reasoner instance is created PelletOptions.USE_TRACING = true; } public static final Logger log = Logger .getLogger(GlassBoxExplanation.class.getName()); /** * Alternative reasoner. We use a second reasoner because we do not want to * lose the state in the original reasoner. */ private PelletReasoner altReasoner = null; private boolean altReasonerEnabled = false; private AxiomConverter axiomConverter; public GlassBoxExplanation(OWLOntology ontology, PelletReasonerFactory factory) { this(factory, factory.createReasoner(ontology)); } public GlassBoxExplanation(PelletReasoner reasoner) { this(new PelletReasonerFactory(), reasoner); } public GlassBoxExplanation(PelletReasonerFactory factory, PelletReasoner reasoner) { super(reasoner.getRootOntology(), factory, reasoner); axiomConverter = new AxiomConverter(reasoner); } private void setAltReasonerEnabled(boolean enabled) { if (enabled) { if (altReasoner == null) { log.fine("Create alt reasoner"); altReasoner = getReasonerFactory().createNonBufferingReasoner(getOntology()); } } altReasonerEnabled = enabled; } private OWLClass getNegation(OWLClassExpression desc) { if (!(desc instanceof OWLObjectComplementOf)) { return null; } OWLClassExpression not = ((OWLObjectComplementOf) desc).getOperand(); if (not.isAnonymous()) { return null; } return (OWLClass) not; } private Pair getSubClassAxiom(OWLClassExpression desc) { if (!(desc instanceof OWLObjectIntersectionOf)) { return null; } OWLObjectIntersectionOf conj = (OWLObjectIntersectionOf) desc; if (conj.getOperands().size() != 2) { return null; } Iterator conjuncts = conj.getOperands().iterator(); OWLClassExpression c1 = conjuncts.next(); OWLClassExpression c2 = conjuncts.next(); OWLClass sub = null; OWLClass sup = null; if (!c1.isAnonymous()) { sub = (OWLClass) c1; sup = getNegation(c2); } else if (!c2.isAnonymous()) { sub = (OWLClass) c2; sup = getNegation(c2); } if (sup == null) { return null; } return new Pair(sub, sup); } private Set getCachedExplanation(OWLClassExpression unsatClass) { PelletReasoner pellet = getReasoner(); if (!pellet.getKB().isClassified()) { return null; } Pair pair = getSubClassAxiom(unsatClass); if (pair != null) { Set> exps = TaxonomyUtils.getSuperExplanations( pellet.getKB().getTaxonomy(), pellet.term(pair.first), pellet.term(pair.second)); if (exps != null) { Set result = convertExplanation(exps.iterator().next()); if (log.isLoggable(Level.FINE)) { log.fine("Cached explanation: " + result); } return result; } } return null; } public Set getExplanation(OWLClassExpression unsatClass) { Set result = null; boolean firstExplanation = isFirstExplanation(); if (log.isLoggable(Level.FINE)) { log.fine("Explain: " + unsatClass + " " + "First: " + firstExplanation); } if (firstExplanation) { altReasoner = null; result = getCachedExplanation(unsatClass); if (result == null) { result = getPelletExplanation(unsatClass); } } else { setAltReasonerEnabled(true); try { result = getPelletExplanation(unsatClass); } catch (RuntimeException e) { log.log(Level.SEVERE, "Unexpected error while trying to get explanation set from Pellet", e); throw new OWLRuntimeException(e); } finally { setAltReasonerEnabled(false); } } return result; } private Set getPelletExplanation(OWLClassExpression unsatClass) { PelletReasoner pellet = getReasoner(); pellet.getKB().prepare(); // satisfiable if there is an undefined entity boolean sat = !getDefinitionTracker().isDefined(unsatClass); if (!sat) { sat = isSatisfiable(pellet, unsatClass, true); } else if (log.isLoggable(Level.FINE)) { log.fine("Undefined entity in " + unsatClass); } if (sat) { return Collections.emptySet(); } else { Set explanation = convertExplanation(pellet.getKB().getExplanationSet()); if (log.isLoggable(Level.FINE)) { log.fine("Explanation " + explanation); } Set prunedExplanation = pruneExplanation(unsatClass, explanation, true); int prunedAxiomCount = explanation.size() - prunedExplanation.size(); if (log.isLoggable(Level.FINE) && prunedAxiomCount > 0) { log.fine("Pruned " + prunedAxiomCount + " axioms from the explanation: " + SetUtils.difference(explanation, prunedExplanation)); log.fine("New explanation " + prunedExplanation); } return prunedExplanation; } } private boolean isSatisfiable(PelletReasoner pellet, OWLClassExpression unsatClass, boolean doExplanation) { pellet.getKB().setDoExplanation(doExplanation); boolean sat = unsatClass.isOWLThing() ? pellet.isConsistent() : pellet.isSatisfiable(unsatClass); pellet.getKB().setDoExplanation(false); return sat; } private Set convertExplanation(Set explanation) { if (explanation == null || explanation.isEmpty()) { throw new OWLRuntimeException("No explanation computed"); } Set result = new HashSet(); for (ATermAppl term : explanation) { OWLAxiom axiom = axiomConverter.convert(term); if (axiom == null) { throw new OWLRuntimeException("Cannot convert: " + term); } result.add(axiom); } return result; } /** *

* Prunes the given explanation using slow pruning technique of BlackBox * explanation. The explanation returned from Pellet axiom tracing is not * guaranteed to be minimal so pruning is necessary to ensure minimality. * The idea is to create an ontology with only the axioms in the * explanation, remove an axiom, test satisfiability, and restore the axiom * if the class turns to be satisfiable after the removal. * *

* There are two different pruning techniques. Incremental pruning attaches * the reasoner as a listener and updates the reasoner with axiom * removals/restores. Non-incremental pruning clears the reasoner at each * iteration and reloads the axioms from scratch each time. Incremental * pruning is faster but may return incorrect answers since axiom updates * are less robust. */ private Set pruneExplanation(OWLClassExpression unsatClass, Set explanation, boolean incremental) { try { // initialize pruned explanation to be same as the given explanation Set prunedExplanation = new HashSet(explanation); // we can only prune if there is more than one axiom in the // explanation if (prunedExplanation.size() <= 1) { return prunedExplanation; } // create an ontology from the explanation axioms OWLOntology debuggingOntology = OWL.Ontology(explanation); DefinitionTracker defTracker = new DefinitionTracker(debuggingOntology); // since explanation size is generally small we can create and use a // completely new reasoner rather than destroying the state on already // existing reasoner PelletReasoner reasoner = getReasonerFactory().createNonBufferingReasoner(debuggingOntology); if (!defTracker.isDefined(unsatClass)) { log.warning("Some of the entities in " + unsatClass + " are not defined in the explanation " + explanation); } if (isSatisfiable(reasoner, unsatClass, true)) { log.warning("Explanation incomplete: Concept " + unsatClass + " is satisfiable in the explanation " + explanation); } // simply remove axioms one at a time. If the unsatClass turns // satisfiable then we know that axiom cannot be a part of minimal // explanation for (OWLAxiom axiom : explanation) { if (log.isLoggable(Level.FINER)) { log.finer("Try pruning " + axiom); } if (!incremental) { reasoner.dispose(); } OntologyUtils.removeAxioms(debuggingOntology, axiom); if (!incremental) { reasoner = getReasonerFactory().createNonBufferingReasoner(debuggingOntology); } reasoner.getKB().prepare(); if (defTracker.isDefined(unsatClass) && !isSatisfiable(reasoner, unsatClass, false)) { // does not affect satisfiability so remove from the results prunedExplanation.remove(axiom); if (log.isLoggable(Level.FINER)) { log.finer("Pruned " + axiom); } } else { // affects satisfiability so add back to the ontology OntologyUtils.addAxioms(debuggingOntology, axiom); } } if (incremental) { // remove the listener and the ontology to avoid memory leaks reasoner.dispose(); } OWL.manager.removeOntology(debuggingOntology); OWL.manager.removeOntologyChangeListener(defTracker); return prunedExplanation; } catch (OWLOntologyChangeException e) { throw new OWLRuntimeException(e); } } @Override public PelletReasoner getReasoner() { return altReasonerEnabled ? altReasoner : (PelletReasoner) super.getReasoner(); } @Override public PelletReasonerFactory getReasonerFactory() { return (PelletReasonerFactory) super.getReasonerFactory(); } public void dispose() { getOntologyManager().removeOntologyChangeListener(getDefinitionTracker()); if (altReasoner != null) { altReasoner.dispose(); } } public String toString() { return "GlassBox"; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy