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

it.unife.ml.probowlapi.explanation.BundleGlassBoxExplanation 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;

/**
 *
 * @author riccardo
 */
//@Deprecated
public class BundleGlassBoxExplanation 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(BundleGlassBoxExplanation.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 BundleGlassBoxExplanation(OWLOntology ontology, PelletReasonerFactory factory) {
        this(factory, factory.createReasoner(ontology));
    }

    public BundleGlassBoxExplanation(PelletReasoner reasoner) {
        this(new PelletReasonerFactory(), reasoner);
    }

    public BundleGlassBoxExplanation(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());
//                altReasoner = getReasonerFactory().createReasoner(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;
    }

    @Override
    public Set getExplanation(OWLClassExpression unsatClass) {
        Set result = null;

        boolean firstExplanation = isFirstExplanation();

        if (log.isLoggable(Level.FINE)) {
            log.fine("Explain: " + unsatClass + " " + "First: " + firstExplanation);
        }

        if (firstExplanation) {
            if (altReasoner != null) {
                altReasoner.dispose();
                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();

        try {
            pellet.getKB().prepare();
        } catch (NullPointerException ex) {
            log.warning("Prepare KB problem: " + ex.getLocalizedMessage());
            pellet.dispose();
            if (altReasonerEnabled) {
                altReasoner = getReasonerFactory().createNonBufferingReasoner(getOntology());
                pellet = altReasoner;
                pellet.getKB().prepare();
            } else {
                throw ex;
            }
        }

        // 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);
            }

            // it seems there are some problems with the incremental version
            Set prunedExplanation = pruneExplanation(unsatClass, explanation, false);

            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); if (!defTracker.isDefined(unsatClass)) { log.warning("Some of the entities in " + unsatClass + " are not defined in the explanation " + explanation); // reasoner.dispose(); return Collections.emptySet(); } // 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); // PelletReasoner reasoner = getReasonerFactory().createReasoner(debuggingOntology); if (isSatisfiable(reasoner, unsatClass, false)) { log.warning("Explanation incomplete: Concept " + unsatClass + " is satisfiable in the explanation " + explanation); reasoner.dispose(); return Collections.emptySet(); } // 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); // reasoner.flush(); } } 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(); } @Override public void dispose() { getOntologyManager().removeOntologyChangeListener(getDefinitionTracker()); if (altReasoner != null) { altReasoner.dispose(); } } public String toString() { return "BundleGlassBox"; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy