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

it.unife.ml.probowlapi.explanation.BundleHSTExplanationGenerator2 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 com.clarkparsia.owlapi.explanation.MultipleExplanationGenerator;
import com.clarkparsia.owlapi.explanation.TransactionAwareSingleExpGen;
import com.clarkparsia.owlapi.explanation.util.ExplanationProgressMonitor;
import com.clarkparsia.owlapi.explanation.util.OntologyUtils;
import com.clarkparsia.owlapi.explanation.util.SilentExplanationProgressMonitor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
//import java.util.logging.Logger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import org.semanticweb.owlapi.model.AxiomType;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLDeclarationAxiom;
import org.semanticweb.owlapi.model.OWLEntity;
import org.semanticweb.owlapi.model.OWLEquivalentClassesAxiom;
import org.semanticweb.owlapi.model.OWLException;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.OWLRuntimeException;
import org.semanticweb.owlapi.model.RemoveAxiom;
import static org.semanticweb.owlapi.model.parameters.Imports.INCLUDED;
import org.semanticweb.owlapi.reasoner.OWLReasoner;
import org.semanticweb.owlapi.reasoner.OWLReasonerFactory;
import org.semanticweb.owlapi.util.CollectionFactory;
import org.semanticweb.owlapi.util.OWLAPIPreconditions;
import org.semanticweb.owlapi.util.OWLEntityCollector;
import static org.semanticweb.owlapi.util.OWLAPIPreconditions.checkNotNull;

/**
 *
 * @author Riccardo Zese , Giuseppe Cota
 * 
 */
public class BundleHSTExplanationGenerator2 implements MultipleExplanationGenerator {

    /**
     * The Constant log.
     */
//    private static final Logger log = Logger
//            .getLogger(BundleHSTExplanationGenerator2.class.getName());
    private static final Logger LOGGER = LoggerFactory
            .getLogger(BundleHSTExplanationGenerator2.class.getName());
    /**
     * The single explanation generator.
     */
    private final TransactionAwareSingleExpGen singleExplanationGenerator;
    /**
     * The progress monitor.
     */
    private ExplanationProgressMonitor progressMonitor = new SilentExplanationProgressMonitor();

    Map> annType;

    public BundleHSTExplanationGenerator2(TransactionAwareSingleExpGen singleExplanationGenerator, Map> probType) {
        this.singleExplanationGenerator = singleExplanationGenerator;
        annType = probType;
    }

    public BundleHSTExplanationGenerator2(TransactionAwareSingleExpGen singleExplanationGenerator) {
        this.singleExplanationGenerator = singleExplanationGenerator;
        annType = new HashMap>();
    }

    @Override
    public void setProgressMonitor(ExplanationProgressMonitor progressMonitor) {
        this.progressMonitor = checkNotNull(progressMonitor,
                "progressMonitor cannot be null");
    }

    @Override
    public OWLOntologyManager getOntologyManager() {
        return singleExplanationGenerator.getOntologyManager();
    }

    @Override
    public OWLOntology getOntology() {
        return singleExplanationGenerator.getOntology();
    }

    @Override
    public OWLReasoner getReasoner() {
        return singleExplanationGenerator.getReasoner();
    }

    @Override
    public OWLReasonerFactory getReasonerFactory() {
        return singleExplanationGenerator.getReasonerFactory();
    }

    /**
     * Gets the single explanation generator.
     *
     * @return the explanation generator
     */
    @Nonnull
    public TransactionAwareSingleExpGen getSingleExplanationGenerator() {
        return singleExplanationGenerator;
    }

    @Override
    public Set getExplanation(OWLClassExpression unsatClass) {
        return singleExplanationGenerator.getExplanation(unsatClass);
    }

    @Override
    public Set> getExplanations(OWLClassExpression unsatClass) {
        return getExplanations(unsatClass, 0);
    }

    @Override
    public void dispose() {
        singleExplanationGenerator.dispose();
    }

    @Override
    public Set> getExplanations(OWLClassExpression unsatClass,
            @Nonnegative int maxExplanations) {
        OWLAPIPreconditions.checkNotNegative(maxExplanations,
                "max explanations cannot be negative");
        Object max = maxExplanations == 0 ? "all" : maxExplanations;
//        log.fine("Get " + max + " explanation(s) for: " + unsatClass);
        LOGGER.info("Get {} explanation(s) for: {}", max, unsatClass);
        try {
            Set firstMups = getExplanation(unsatClass);
            if (firstMups.isEmpty()) {
                return CollectionFactory.emptySet();
            }
            Set> allMups = new LinkedHashSet<>();
            progressMonitor.foundExplanation(firstMups);
            allMups.add(firstMups);
            Set> satPaths = new HashSet<>();
            Set currentPathContents = new HashSet<>();
            singleExplanationGenerator.beginTransaction();
            try {
                constructHittingSetTree(unsatClass, firstMups, allMups,
                        satPaths, currentPathContents, maxExplanations);
            } finally {
                singleExplanationGenerator.endTransaction();
            }
            progressMonitor.foundAllExplanations();
            return allMups;
        } catch (OWLException e) {
            throw new OWLRuntimeException(e);
        }
    }

    // Hitting Set Stuff
    /**
     * Orders the axioms in a single MUPS by the frequency of which they appear
     * in all MUPS.
     *
     * @param mups The MUPS containing the axioms to be ordered
     * @param allMups The set of all MUPS which is used to calculate the
     * ordering
     * @return the ordered mups
     */
    @Nonnull
    private static List getOrderedMUPS(@Nonnull List mups,
            @Nonnull final Set> allMups) {
        Comparator mupsComparator = new Comparator() {

            @Override
            public int compare(OWLAxiom o1, OWLAxiom o2) {
                // The axiom that appears in most MUPS has the lowest index
                // in the list
                assert o1 != null;
                assert o2 != null;
                int occ1 = getOccurrences(o1, allMups);
                int occ2 = getOccurrences(o2, allMups);
                return -occ1 + occ2;
            }
        };
        Collections.sort(mups, mupsComparator);
        return mups;
    }

    /**
     * Given an axiom and a set of axioms this method determines how many sets
     * contain the axiom.
     *
     * @param ax The axiom that will be counted.
     * @param axiomSets The sets to count from
     * @return the occurrences
     */
    protected static int getOccurrences(@Nonnull OWLAxiom ax,
            @Nonnull Set> axiomSets) {
        int count = 0;
        for (Set axioms : axiomSets) {
            if (axioms.contains(ax)) {
                count++;
            }
        }
        return count;
    }

    /**
     * Returns the entities referenced in an axiom.
     *
     * @param axiom axiom whose signature is being computed
     * @return the entities referenced in the axiom
     */
    @Nonnull
    private static Set getSignature(@Nonnull OWLAxiom axiom) {
        Set toReturn = new HashSet<>();
        OWLEntityCollector collector = new OWLEntityCollector(toReturn);
        axiom.accept(collector);
        return toReturn;
    }

    /**
     * This is a recursive method that builds a hitting set tree to obtain all
     * justifications for an unsatisfiable class.
     *
     * @param unsatClass the unsat class
     * @param mups The current justification for the current class. This
     * corresponds to a node in the hitting set tree.
     * @param allMups All of the MUPS that have been found - this set gets
     * populated over the course of the tree building process. Initially this
     * should just contain the first justification
     * @param satPaths Paths that have been completed.
     * @param currentPathContents The contents of the current path. Initially
     * this should be an empty set.
     * @param maxExplanations the max explanations
     * @throws OWLException the oWL exception
     */
    private void constructHittingSetTree(
            @Nonnull OWLClassExpression unsatClass,
            @Nonnull Set mups, @Nonnull Set> allMups,
            @Nonnull Set> satPaths,
            @Nonnull Set currentPathContents, int maxExplanations)
            throws OWLException {
//        LOGGER.fine("MUPS " + allMups.size() + ": " + mups);
        LOGGER.info("MUPS {}: {}", allMups.size(), mups);
        if (progressMonitor.isCancelled()) {
            return;
        }
        // We go through the current mups, axiom by axiom, and extend the tree
        // with edges for each axiom
        List orderedMups = getOrderedMUPS(new ArrayList<>(mups),
                allMups);
        while (!orderedMups.isEmpty()) {
            if (progressMonitor.isCancelled()) {
                return;
            }
            OWLAxiom axiom = orderedMups.get(0);
            assert axiom != null;
            orderedMups.remove(0);
            // get annotated axiom (the annotation contains the probability)
            OWLAxiom axiomToRestore = axiom;

            Set rootOntologyAxioms = getReasoner().getRootOntology().getAxiomsIgnoreAnnotations(axiom);
            for (OWLAxiom annotatedAxiom : rootOntologyAxioms) {
                if (annotatedAxiom.equalsIgnoreAnnotations(axiom)) {
                    axiomToRestore = annotatedAxiom;
                }
            }

            if (allMups.size() == maxExplanations) {
                LOGGER.info("Computed {} explanations", maxExplanations);
                return;
            }
            LOGGER.info("Removing axiom: {} {} more removed: {}", axiom,
                    currentPathContents.size(), currentPathContents);
            // Removal may have dereferenced some entities, if so declarations
            // are added
            List temporaryDeclarations = new ArrayList<>();
            Set ontologies = removeAxiomAndAddDeclarations(axiom,
                    temporaryDeclarations);
//            getReasoner().flush();
            currentPathContents.add(axiom);
            boolean earlyTermination = checkEarlyTermination(satPaths,
                    currentPathContents);
            if (!earlyTermination) {
                orderedMups = recurse(unsatClass, allMups, satPaths,
                        currentPathContents, maxExplanations, orderedMups,
                        axiom);
            }
            backtrack(currentPathContents, axiomToRestore, temporaryDeclarations,
                    ontologies);
        }
    }

    /**
     * Check early termination.
     *
     * @param satPaths the sat paths
     * @param currentPathContents the current path contents
     * @return true, if successful
     */
    private static boolean checkEarlyTermination(
            @Nonnull Set> satPaths,
            @Nonnull Set currentPathContents) {
        boolean earlyTermination = false;
        // Early path termination. If our path contents are the superset of
        // the contents of a path then we can terminate here.
        for (Set satPath : satPaths) {
            if (currentPathContents.containsAll(satPath)) {
                earlyTermination = true;
//                LOGGER.fine("Stop - satisfiable (early termination)");
                LOGGER.info("Stop - satisfiable (early termination)");
                break;
            }
        }
        return earlyTermination;
    }

    /**
     * Recurse.
     *
     * @param unsatClass the unsat class
     * @param allMups the all mups
     * @param satPaths the sat paths
     * @param currentPathContents the current path contents
     * @param maxExplanations the max explanations
     * @param orderedMups the ordered mups
     * @param axiom the axiom
     * @return the list
     * @throws OWLException the oWL exception
     */
    @Nonnull
    private List recurse(@Nonnull OWLClassExpression unsatClass,
            @Nonnull Set> allMups,
            @Nonnull Set> satPaths,
            @Nonnull Set currentPathContents, int maxExplanations,
            @Nonnull List orderedMups, @Nonnull OWLAxiom axiom)
            throws OWLException {
        Set newMUPS = getNewMUPS(unsatClass, allMups,
                currentPathContents);
        // Generate a new node - i.e. a new justification set
        if (newMUPS.contains(axiom)) {
            // How can this be the case???
            throw new OWLRuntimeException(
                    "Explanation contains removed axiom: " + axiom);
        }
        if (!newMUPS.isEmpty()) {
            // Note that getting a previous justification does not mean
            // we can stop. stopping here causes some justifications to
            // be missed
            allMups.add(newMUPS);
            progressMonitor.foundExplanation(newMUPS);
            // Recompute priority here?
            constructHittingSetTree(unsatClass, newMUPS, allMups, satPaths,
                    currentPathContents, maxExplanations);
            // We have found a new MUPS, so recalculate the ordering
            // axioms in the MUPS at the current level
            return getOrderedMUPS(orderedMups, allMups);
        } else {
//            LOGGER.fine("Stop - satisfiable");
            LOGGER.info("Stop - satisfiable");
            // End of current path - add it to the list of paths
            satPaths.add(new HashSet<>(currentPathContents));
        }
        return orderedMups;
    }

    private void backtrack(@Nonnull Set currentPathContents,
            @Nonnull OWLAxiom axiom,
            @Nonnull List temporaryDeclarations,
            @Nonnull Set ontologies) {
        // Back track - go one level up the tree and run for the next axiom
        currentPathContents.remove(axiom);
//        LOGGER.fine("Restoring axiom: " + axiom);
        LOGGER.info("Restoring axiom: {}", axiom);
        // Remove any temporary declarations
        for (OWLDeclarationAxiom decl : temporaryDeclarations) {
            assert decl != null;
            OntologyUtils.removeAxiom(decl, getReasoner().getRootOntology()
                    .getImportsClosure(), getOntologyManager());
        }
        // Done with the axiom that was removed. Add it back in
        OntologyUtils.addAxiom(axiom, ontologies, getOntologyManager());
//        getReasoner().flush();
    }

    /**
     * Gets the new mups.
     *
     * @param unsatClass the unsat class
     * @param allMups the all mups
     * @param currentPathContents the current path contents
     * @return the new mups
     */
    @Nonnull
    private Set getNewMUPS(@Nonnull OWLClassExpression unsatClass,
            @Nonnull Set> allMups,
            @Nonnull Set currentPathContents) {
        Set newMUPS = null;
        for (Set foundMUPS : allMups) {
            Set foundMUPSCopy = new HashSet<>(foundMUPS);
            foundMUPSCopy.retainAll(currentPathContents);
            if (foundMUPSCopy.isEmpty()) {
                newMUPS = foundMUPS;
                break;
            }
        }
        if (newMUPS == null) {
            newMUPS = getExplanation(unsatClass);
        }
        return newMUPS;
    }

    /**
     * Removes the axiom and add declarations.
     *
     * @param axiom the axiom
     * @param temporaryDeclarations the temporary declarations
     * @return the sets the
     */
    @Nonnull
    private Set removeAxiomAndAddDeclarations(
            @Nonnull OWLAxiom axiom,
            @Nonnull List temporaryDeclarations) {
        // Remove the current axiom from all the ontologies it is included
        // in
        Set ontologies = removeAxiom(axiom,
                getReasoner().getRootOntology().getImportsClosure(),
                getOntologyManager());
        collectTemporaryDeclarations(axiom, temporaryDeclarations);
        for (OWLDeclarationAxiom decl : temporaryDeclarations) {
            assert decl != null;
            OntologyUtils.addAxiom(decl, getReasoner().getRootOntology()
                    .getImportsClosure(), getOntologyManager());
        }
        return ontologies;
    }

    private Set removeAxiom(OWLAxiom axiom,
            Set ontologies, OWLOntologyManager manager) {
        Set modifiedOnts = new HashSet<>();
        for (OWLOntology ont : ontologies) {
            Set axioms = ont.getAxioms();
            for (OWLAxiom axiomit : axioms) {
                boolean cond = axiomit.equalsIgnoreAnnotations(axiom);
//                if (axiom.isOfType(AxiomType.EQUIVALENT_CLASSES)
//                        && axiomit.isOfType(AxiomType.EQUIVALENT_CLASSES)) {
//                    Set classExpAxiom = ((OWLEquivalentClassesAxiom) axiom).getClassExpressions();
//                    Set classExpAxiomIt = ((OWLEquivalentClassesAxiom) axiomit).getClassExpressions();
//                    cond = cond || classExpAxiom.equals(classExpAxiomIt);
//                }
                if (cond) {
                    modifiedOnts.add(ont);
                    manager.applyChange(new RemoveAxiom(ont, axiomit));
                    break;
                }
            }
        }
        return modifiedOnts;
    }

    private void collectTemporaryDeclarations(@Nonnull OWLAxiom axiom,
            @Nonnull List temporaryDeclarations) {
        for (OWLEntity e : getSignature(axiom)) {
            assert e != null;
            boolean referenced = getReasoner().getRootOntology().isDeclared(e,
                    INCLUDED);
            if (!referenced) {
                temporaryDeclarations.add(getDeclaration(e));
            }
        }
    }

    /**
     * Gets the declaration.
     *
     * @param e the e
     * @return the declaration
     */
    @Nonnull
    private OWLDeclarationAxiom getDeclaration(@Nonnull OWLEntity e) {
        return getOntologyManager().getOWLDataFactory().getOWLDeclarationAxiom(
                e);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy