Package org.semanticweb.owlapi.debugging

Source Code of org.semanticweb.owlapi.debugging.BlackBoxOWLDebugger

/*
* This file is part of the OWL API.
*
* The contents of this file are subject to the LGPL License, Version 3.0.
*
* Copyright (C) 2011, The University of Manchester
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is 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 http://www.gnu.org/licenses/.
*
*
* Alternatively, the contents of this file may be used under the terms of the Apache License, Version 2.0
* in which case, the provisions of the Apache License Version 2.0 are applicable instead of those above.
*
* Copyright 2011, The University of Manchester
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.semanticweb.owlapi.debugging;

import java.util.ArrayList;
import java.util.Arrays;
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.Level;
import java.util.logging.Logger;

import org.semanticweb.owlapi.model.AddAxiom;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAnnotationAxiom;
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.OWLDataProperty;
import org.semanticweb.owlapi.model.OWLEntity;
import org.semanticweb.owlapi.model.OWLException;
import org.semanticweb.owlapi.model.OWLIndividual;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyChange;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.RemoveAxiom;
import org.semanticweb.owlapi.reasoner.OWLReasoner;
import org.semanticweb.owlapi.reasoner.OWLReasonerFactory;
import org.semanticweb.owlapi.util.SimpleIRIMapper;

/**
* Author: Matthew Horridge<br>
* The University Of Manchester<br>
* Bio-Health Informatics Group<br>
* Date: 24-Nov-2006<br><br>
* <p/>
* This is an implementation of a blackbox debugger.  The implementation
* is based on the description of a black box debugger as described in
* Aditya Kalyanpur's PhD Thesis : "Debugging and Repair of OWL Ontologies".
*/
public class BlackBoxOWLDebugger extends AbstractOWLDebugger {


    private static final Logger logger = Logger.getLogger(BlackBoxOWLDebugger.class.getName());


    private OWLOntologyManager owlOntologyManager;

//    private OWLOntology ontology;

    private OWLClass currentClass;

    private OWLOntology debuggingOntology;

    private Set<OWLAxiom> debuggingAxioms;

    private Set<OWLEntity> objectsExpandedWithDefiningAxioms;

    private Set<OWLEntity> objectsExpandedWithReferencingAxioms;

    private Set<OWLAxiom> expandedWithDefiningAxioms;

    private Set<OWLAxiom> expandedWithReferencingAxioms;

    private OWLReasonerFactory reasonerFactory;

//    private OWLReasoner reasoner;

    private Set<OWLAxiom> temporaryAxioms;

    private Map<OWLAxiom, OWLAxiom> expandedAxiomMap;

    public static final int DEFAULT_INITIAL_EXPANSION_LIMIT = 50;

    private int initialExpansionLimit = DEFAULT_INITIAL_EXPANSION_LIMIT;

    private int expansionLimit = initialExpansionLimit;

    private double expansionFactor = 1.25;

    private static final int DEFAULT_FAST_PRUNING_WINDOW_SIZE = 10;

    private int fastPruningWindowSize = 0;

    private boolean performRepeatedFastPruning = false;


    public BlackBoxOWLDebugger(OWLOntologyManager owlOntologyManager, OWLOntology ontology, OWLReasonerFactory reasonerFactory) {
        super(owlOntologyManager, ontology);
        this.reasonerFactory = reasonerFactory;
        this.owlOntologyManager = owlOntologyManager;
//        this.reasoner = reasoner;
        debuggingAxioms = new LinkedHashSet<OWLAxiom>();
        objectsExpandedWithDefiningAxioms = new HashSet<OWLEntity>();
        objectsExpandedWithReferencingAxioms = new HashSet<OWLEntity>();
        expandedWithDefiningAxioms = new HashSet<OWLAxiom>();
        expandedWithReferencingAxioms = new HashSet<OWLAxiom>();
        temporaryAxioms = new HashSet<OWLAxiom>();
        expandedAxiomMap = new HashMap<OWLAxiom, OWLAxiom>();
        logger.setLevel(Level.INFO);
    }


    public void dispose() {
        reset();
//        reasoner.dispose();
    }

    private void reset() {
        currentClass = null;
        debuggingOntology = null;
        debuggingAxioms.clear();
        objectsExpandedWithDefiningAxioms.clear();
        objectsExpandedWithReferencingAxioms.clear();
        expandedWithDefiningAxioms.clear();
        expandedWithReferencingAxioms.clear();
        temporaryAxioms.clear();
        expandedAxiomMap.clear();
        expansionLimit = initialExpansionLimit;
    }


    @Override
  protected OWLClassExpression getCurrentClass() throws OWLException {
        return currentClass;
    }


    private OWLClass setupDebuggingClass(OWLClassExpression cls) throws OWLException {
        if (!cls.isAnonymous()) {
            return (OWLClass) cls;
        }
        else {
            // The class is anonymous, so we need to assign it a name
            OWLClass curCls = owlOntologyManager.getOWLDataFactory().getOWLClass(createIRI());
            Set<OWLClassExpression> operands = new HashSet<OWLClassExpression>();
            operands.add(curCls);
            operands.add(cls);
            temporaryAxioms.add(owlOntologyManager.getOWLDataFactory().getOWLEquivalentClassesAxiom(operands));
            for (OWLAxiom ax : temporaryAxioms) {
                owlOntologyManager.applyChanges(Arrays.asList(new AddAxiom(getOWLOntology(), ax)));
            }
            return curCls;
        }
    }


    public Set<OWLAxiom> getSOSForIncosistentClass(OWLClassExpression cls) throws OWLException {
        reset();
        currentClass = setupDebuggingClass(cls);
        generateSOSAxioms();
        for (OWLAxiom ax : temporaryAxioms) {
            owlOntologyManager.applyChanges(Arrays.asList(new RemoveAxiom(getOWLOntology(), ax)));
        }
        debuggingAxioms.removeAll(temporaryAxioms);
        ontologyCounter = 0;
        return new HashSet<OWLAxiom>(debuggingAxioms);
    }


//    private static List<OWLAxiom> toList(Set<OWLAxiom> axioms) {
//        return new ArrayList<OWLAxiom>(axioms);
//    }

    ///////////////////////////////////////////////////////////////////////////////////////////
    //
    // Expansion
    //
    ///////////////////////////////////////////////////////////////////////////////////////////


    private int expandAxioms() throws OWLException {
        // We expand the axiom set using axioms that define entities that are already
        // referenced in the existing set of axioms.  If this fails to expand the axiom
        // set we expand using axioms that reference the entities in the axioms that have
        // already been expanded.

        // Keep track of the number of axioms that have been added
        int axiomsAdded = 0;

        int remainingSpace = expansionLimit;
//        System.out.println("Expansion limit: " + expansionLimit);
        for (OWLAxiom ax : new ArrayList<OWLAxiom>(debuggingAxioms)) {
            if (expandedWithDefiningAxioms.contains(ax)) {
                // Skip if already done
                continue;
            }
            // Collect the entities that have been used in the axiom

            for (OWLEntity curObj : ax.getSignature()) {
                if (!objectsExpandedWithDefiningAxioms.contains(curObj)) {
                    int added = expandWithDefiningAxioms(curObj, remainingSpace);
                    axiomsAdded += added;
                    remainingSpace = remainingSpace - added;
                    if (remainingSpace == 0) {
                        expansionLimit *= expansionFactor;
                        return axiomsAdded;
                    }
                    // Flag that we have completely expanded all defining axioms
                    // for this particular entity
                    objectsExpandedWithDefiningAxioms.add(curObj);
                }
            }

            // Flag that we've completely expanded this particular axiom
            expandedWithDefiningAxioms.add(ax);
        }

        if (axiomsAdded > 0) {
            return axiomsAdded;
        }

        // No axioms added at this point.  Start adding axioms that reference
        // entities contained in the current set of debugging axioms
        for (OWLAxiom ax : new ArrayList<OWLAxiom>(debuggingAxioms)) {
            if (expandedWithReferencingAxioms.contains(ax)) {
                // Skip - already done this one
                continue;
            }
            // Keep track of the number of axioms that have been added
            for (OWLEntity curObj : ax.getSignature()) {
                if (!objectsExpandedWithReferencingAxioms.contains(curObj)) {
                    int added = expandWithReferencingAxioms(curObj, expansionLimit);
                    axiomsAdded += added;
                    remainingSpace -= added;
                    if (remainingSpace == 0) {
                        expansionLimit *= expansionFactor;
                        return axiomsAdded;
                    }
                    objectsExpandedWithReferencingAxioms.add(curObj);
                }
            }
            expandedWithReferencingAxioms.add(ax);
        }
        return axiomsAdded;
    }


    /**
     * Creates a set of axioms to expands the debugging axiom set by adding the defining axioms for the
     * specified entity.
     */
    private int expandWithDefiningAxioms(OWLEntity obj, int limit) throws OWLException {
        Set<OWLAxiom> expansionAxioms = new HashSet<OWLAxiom>();
        for (OWLOntology ont : owlOntologyManager.getImportsClosure(getOWLOntology())) {
            if (obj instanceof OWLClass) {
                expansionAxioms.addAll(ont.getAxioms((OWLClass) obj));
            }
            else if (obj instanceof OWLObjectProperty) {
                expansionAxioms.addAll(ont.getAxioms((OWLObjectProperty) obj));
            }
            else if (obj instanceof OWLDataProperty) {
                expansionAxioms.addAll(ont.getAxioms((OWLDataProperty) obj));
            }
            else if (obj instanceof OWLIndividual) {
                expansionAxioms.addAll(ont.getAxioms((OWLIndividual) obj));
            }
        }
        expansionAxioms.removeAll(debuggingAxioms);
        return addMax(expansionAxioms, debuggingAxioms, limit);
    }


    /**
     * Expands the axiom set by adding the referencing axioms for the
     * specified entity.
     */
    private int expandWithReferencingAxioms(OWLEntity obj, int limit) throws OWLException {
        Set<OWLAxiom> expansionAxioms = new HashSet<OWLAxiom>();
        // First expand by getting the defining axioms - if this doesn't
        // return any axioms, then get the axioms that reference the entity
        for (OWLOntology ont : owlOntologyManager.getImportsClosure(getOWLOntology())) {
            expansionAxioms.addAll(ont.getReferencingAxioms(obj));
        }
        expansionAxioms.removeAll(debuggingAxioms);
        return addMax(expansionAxioms, debuggingAxioms, limit);
    }


    /**
     * A utility method.  Adds axioms from one set to another set upto a specified limit.
     * Annotation axioms are stripped out
     *
     * @param source The source set.  Objects from this set will be added to the
     * destination set
     * @param dest The destination set.  Objects will be added to this set
     * @param limit The maximum number of objects to be added.
     * @return The number of objects that were actuall added.
     */
    private static <N extends OWLAxiom> int addMax(Set<N> source, Set<N> dest, int limit) {
        int count = 0;
        for (N obj : source) {
            if (count == limit) {
                break;
            }
            if (!(obj instanceof OWLAnnotationAxiom)) {
                if (dest.add(obj)) {
                    count++;
                }
            }
        }
        return count;
    }

    ///////////////////////////////////////////////////////////////////////////////////////////
    //
    // Contraction/Pruning - Fast pruning is performed and then slow pruning is performed.
    //
    ///////////////////////////////////////////////////////////////////////////////////////////


    private void performFastPruning() throws OWLException {
        logger.setLevel(Level.INFO);
        Set<OWLAxiom> axiomWindow = new HashSet<OWLAxiom>();
        Object[] axioms = debuggingAxioms.toArray();
        if (logger.isLoggable(Level.INFO)) {
            logger.info("Fast pruning: ");
        }
//        if (performRepeatedFastPruning) {
//            int desiredWindowSize = debuggingAxioms.size() / 30;
//
//            if(desiredWindowSize == fastPruningWindowSize) {
//                fastPruningWindowSize = desiredWindowSize / 3;
//
//            }
//            else {
//                fastPruningWindowSize = desiredWindowSize;
//            }
//            if(fastPruningWindowSize < 1) {
//                fastPruningWindowSize = 1;
//            }
//        }
//        else {
//            fastPruningWindowSize = DEFAULT_FAST_PRUNING_WINDOW_SIZE;
//        }
        if (logger.isLoggable(Level.INFO)) {
            logger.info("     - Window size: " + fastPruningWindowSize);
        }
        int windowCount = debuggingAxioms.size() / fastPruningWindowSize;
        for (int currentWindow = 0; currentWindow < windowCount; currentWindow++) {
            axiomWindow.clear();
            int startIndex = currentWindow * fastPruningWindowSize;
            int endIndex = startIndex + fastPruningWindowSize;
            for (int axiomIndex = startIndex; axiomIndex < endIndex; axiomIndex++) {
                OWLAxiom currentAxiom = (OWLAxiom) axioms[axiomIndex];
                axiomWindow.add(currentAxiom);
                debuggingAxioms.remove(currentAxiom);
            }
            if (isSatisfiable()) {
                debuggingAxioms.addAll(axiomWindow);
            }
        }
        // Add any left over axioms
        axiomWindow.clear();
        int remainingAxiomsCount = debuggingAxioms.size() % fastPruningWindowSize;
        if (remainingAxiomsCount > 0) {
            int fragmentIndex = windowCount * fastPruningWindowSize;
            while (fragmentIndex < axioms.length) {
                OWLAxiom curAxiom = (OWLAxiom) axioms[fragmentIndex];
                axiomWindow.add(curAxiom);
                debuggingAxioms.remove(curAxiom);
                fragmentIndex++;
            }
            if (isSatisfiable()) {
                debuggingAxioms.addAll(axiomWindow);
            }
        }
        if (logger.isLoggable(Level.INFO)) {
            logger.info("    - End of fast pruning");
        }
    }


    private void performSlowPruning() throws OWLException {
        // Simply remove axioms one at a time.  If the class
        // being debugged turns satisfiable then we know we have
        // an SOS axoiom.
        List<OWLAxiom> axiomsCopy = new ArrayList<OWLAxiom>(debuggingAxioms);
        for (OWLAxiom ax : axiomsCopy) {
            debuggingAxioms.remove(ax);
            if (isSatisfiable()) {
                // Affects satisfiability, so add back in
                debuggingAxioms.add(ax);
            }
        }
    }

    ////////////////////////////////////////////////////////////////////////////////////////////
    //
    // Creation of debugging ontology and satisfiability testing
    //
    ///////////////////////////////////////////////////////////////////////////////////////////

    private int satTestCount = 0;


    /**
     * Tests the satisfiability of the test class.  The ontology
     * is recreated before the test is performed.
     */
    private boolean isSatisfiable() throws OWLException {
        createDebuggingOntology();
//        RDFXMLRenderer ren = new RDFXMLRenderer();
//        ren.setOWLOntologyManager(owlOntologyManager);
//        ren.render(debuggingOntology, IRI.create("file:/Users/matthewhorridge/Desktop/DebuggingOntology" + ontologyCounter + ".owlapi"));
        ontologyCounter++;
        OWLReasoner reasoner = reasonerFactory.createNonBufferingReasoner(debuggingOntology);
        satTestCount++;
        boolean sat = reasoner.isSatisfiable(currentClass);
        reasoner.dispose();
        return sat;
    }


    int ontologyCounter = 0;


    private void createDebuggingOntology() throws OWLException {
        //System.out.println("Debugging ontology: " + ontologyCounter);
        if (debuggingOntology != null) {
            owlOntologyManager.removeOntology(debuggingOntology);
        }
        IRI iri = createIRI();
        SimpleIRIMapper mapper = new SimpleIRIMapper(iri, iri);
        owlOntologyManager.addIRIMapper(mapper);
        debuggingOntology = owlOntologyManager.createOntology(iri);
        owlOntologyManager.removeIRIMapper(mapper);
        List<OWLOntologyChange> changes = new ArrayList<OWLOntologyChange>();
        for (OWLAxiom ax : debuggingAxioms) {
            changes.add(new AddAxiom(debuggingOntology, ax));
        }
        for (OWLAxiom ax : temporaryAxioms) {
            changes.add(new AddAxiom(debuggingOntology, ax));
        }

        // Ensure the ontology contains the signature of the class which is being debugged
        OWLDataFactory factory = owlOntologyManager.getOWLDataFactory();
        OWLAxiom ax = factory.getOWLSubClassOfAxiom(currentClass, factory.getOWLThing());
        changes.add(new AddAxiom(debuggingOntology, ax));
        owlOntologyManager.applyChanges(changes);
    }


    private void resetSatisfiabilityTestCounter() {
        satTestCount = 0;
    }


    private void generateSOSAxioms() throws OWLException {
        // Perform the initial expansion - this will cause
        // the debugging axioms set to be expanded to the
        // defining axioms for the class being debugged
        resetSatisfiabilityTestCounter();
        expandWithDefiningAxioms(currentClass, expansionLimit);

        if (logger.isLoggable(Level.INFO)) {
            logger.info("Initial axiom count: " + debuggingAxioms.size());
        }

        int totalAdded = 0;
        int expansionCount = 0;
        while (isSatisfiable()) {

            if (logger.isLoggable(Level.INFO)) {
                logger.info("Expanding axioms (expansion " + expansionCount + ")");
            }

            expansionCount++;
            int numberAdded = expandAxioms();
            totalAdded += numberAdded;

            if (logger.isLoggable(Level.INFO)) {
                logger.info("    ... expanded by " + numberAdded);
            }

            if (numberAdded == 0) {

                if (logger.isLoggable(Level.INFO)) {
                    logger.info("ERROR! Cannot find SOS axioms!");
                }

                debuggingAxioms.clear();
                return;
            }
        }

        if (logger.isLoggable(Level.INFO)) {
            logger.info("Total number of axioms added: " + totalAdded);
        }

        if (logger.isLoggable(Level.INFO)) {
            logger.info("FOUND CLASH! Prunning " + debuggingAxioms.size() + " axioms...");
        }

        resetSatisfiabilityTestCounter();
        if (logger.isLoggable(Level.INFO)) {
            logger.info("Fast pruning...");
        }

//        fastPruningWindowSize = 0;
        if (performRepeatedFastPruning) {
            // Base the initial fast pruning window size on the number of axioms
            fastPruningWindowSize = debuggingAxioms.size() / 10;
            if (fastPruningWindowSize < DEFAULT_FAST_PRUNING_WINDOW_SIZE) {
                fastPruningWindowSize = DEFAULT_FAST_PRUNING_WINDOW_SIZE;
            }
            if (logger.isLoggable(Level.INFO)) {
                logger.info("    Initial fast prunung window size: " + fastPruningWindowSize);
            }
            int fastPruningCounter = 0;
            while (fastPruningWindowSize != 1) {
                if (logger.isLoggable(Level.INFO)) {
                    logger.info("    Round: " + fastPruningCounter + " (axioms to prune: " + debuggingAxioms.size() + ")");
                }
                fastPruningCounter++;
                performFastPruning();
                fastPruningWindowSize = fastPruningWindowSize / 3;
                if (fastPruningWindowSize < 1) {
                    fastPruningWindowSize = 1;
                }
            }
            if (logger.isLoggable(Level.INFO)) {
                logger.info("... end of fast pruning. Axioms remaining: " + debuggingAxioms.size());
                logger.info("Performed " + satTestCount + " satisfiability tests during fast pruning");
            }
        }
        else {
            fastPruningWindowSize = DEFAULT_FAST_PRUNING_WINDOW_SIZE;
            performFastPruning();
            if (logger.isLoggable(Level.INFO)) {
                logger.info("... end of fast pruning. Axioms remaining: " + debuggingAxioms.size());
                logger.info("Performed " + satTestCount + " satisfiability tests during fast pruning");
            }
        }


        int totalSatTests = satTestCount;

        resetSatisfiabilityTestCounter();
        if (logger.isLoggable(Level.INFO)) {
            logger.info("Slow pruning...");
        }

        performSlowPruning();

        if (logger.isLoggable(Level.INFO)) {
            logger.info("... end of slow pruning");
            logger.info("Performed " + satTestCount + " satisfiability tests during slow pruning");
        }
        totalSatTests += satTestCount;
        if (logger.isLoggable(Level.INFO)) {
            logger.info("Total number of satisfiability tests performed: " + totalSatTests);
        }
    }


    private static IRI createIRI() {
        return IRI.create("http://debugging.blackbox#" + System.nanoTime());
    }
}
TOP

Related Classes of org.semanticweb.owlapi.debugging.BlackBoxOWLDebugger

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.