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

pellet.PelletExplain Maven / Gradle / Ivy

The 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 pellet;

import static pellet.PelletCmdOptionArg.NONE;
import static pellet.PelletCmdOptionArg.REQUIRED;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.coode.owlapi.manchesterowlsyntax.ManchesterOWLSyntaxEditorParser;
import org.mindswap.pellet.utils.Timer;
import org.mindswap.pellet.utils.progress.ConsoleProgressMonitor;
import org.mindswap.pellet.utils.progress.ProgressMonitor;
import org.semanticweb.owlapi.expression.ParserException;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassExpression;
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.OWLLiteral;
import org.semanticweb.owlapi.model.OWLNamedIndividual;
import org.semanticweb.owlapi.model.OWLObject;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import org.semanticweb.owlapi.model.OWLProperty;
import org.semanticweb.owlapi.model.OWLSubClassOfAxiom;
import org.semanticweb.owlapi.reasoner.Node;
import org.semanticweb.owlapi.reasoner.NodeSet;

import com.clarkparsia.owlapi.explanation.BlackBoxExplanation;
import com.clarkparsia.owlapi.explanation.GlassBoxExplanation;
import com.clarkparsia.owlapi.explanation.HSTExplanationGenerator;
import com.clarkparsia.owlapi.explanation.MultipleExplanationGenerator;
import com.clarkparsia.owlapi.explanation.SatisfiabilityConverter;
import com.clarkparsia.owlapi.explanation.TransactionAwareSingleExpGen;
import com.clarkparsia.owlapi.explanation.io.ExplanationRenderer;
import com.clarkparsia.owlapi.explanation.io.manchester.ManchesterSyntaxExplanationRenderer;
import com.clarkparsia.owlapi.explanation.util.ExplanationProgressMonitor;
import com.clarkparsia.owlapiv3.OWL;
import com.clarkparsia.owlapiv3.OntologyUtils;
import com.clarkparsia.pellet.owlapiv3.OWLAPILoader;
import com.clarkparsia.pellet.owlapiv3.PelletReasoner;
import com.clarkparsia.pellet.owlapiv3.PelletReasonerFactory;

/**
 * 

* Title: *

*

* Description: *

*

* Copyright: Copyright (c) 2008 *

*

* Company: Clark & Parsia, LLC. *

* * @author Evren Sirin * @author Markus Stocker */ public class PelletExplain extends PelletCmdApp { private SatisfiabilityConverter converter; /** * inferences for which there was an error while generating the explanation */ private int errorExpCount = 0; private OWLAPILoader loader; private int maxExplanations = 1; private boolean useBlackBox = false; private ProgressMonitor monitor; /** * inferences whose explanation contains more than on axiom */ private int multiAxiomExpCount = 0; /** * inferences with multiple explanations */ private int multipleExpCount = 0; private PelletReasoner reasoner; private OWLEntity name1; private OWLEntity name2; private OWLObject name3; public PelletExplain() { GlassBoxExplanation.setup(); } @Override public String getAppId() { return "PelletExplain: Explains one or more inferences in a given ontology including ontology inconsistency"; } @Override public String getAppCmd() { return "pellet explain " + getMandatoryOptions() + "[options] ...\n\n" + "The options --unsat, --all-unsat, --inconsistent, --subclass, \n" + "--hierarchy, and --instance are mutually exclusive. By default \n " + "--inconsistent option is assumed. In the following descriptions \n" + "C, D, and i can be URIs or local names."; } @Override public PelletCmdOptions getOptions() { PelletCmdOptions options = getGlobalOptions(); options.add( getIgnoreImportsOption() ); PelletCmdOption option = new PelletCmdOption( "unsat" ); option.setType( "C" ); option.setDescription( "Explain why the given class is unsatisfiable" ); option.setIsMandatory( false ); option.setArg( REQUIRED ); options.add( option ); option = new PelletCmdOption( "all-unsat" ); option.setDescription( "Explain all unsatisfiable classes" ); option.setDefaultValue( false ); option.setIsMandatory( false ); option.setArg( NONE ); options.add( option ); option = new PelletCmdOption( "inconsistent" ); option.setDescription( "Explain why the ontology is inconsistent" ); option.setDefaultValue( false ); option.setIsMandatory( false ); option.setArg( NONE ); options.add( option ); option = new PelletCmdOption( "hierarchy" ); option.setDescription( "Print all explanations for the class hierarchy" ); option.setDefaultValue( false ); option.setIsMandatory( false ); option.setArg( NONE ); options.add( option ); option = new PelletCmdOption( "subclass" ); option.setDescription( "Explain why C is a subclass of D" ); option.setType( "C,D" ); option.setIsMandatory( false ); option.setArg( REQUIRED ); options.add( option ); option = new PelletCmdOption( "instance" ); option.setDescription( "Explain why i is an instance of C" ); option.setType( "i,C" ); option.setIsMandatory( false ); option.setArg( REQUIRED ); options.add( option ); option = new PelletCmdOption( "property-value" ); option.setDescription( "Explain why s has value o for property p" ); option.setType( "s,p,o" ); option.setIsMandatory( false ); option.setArg( REQUIRED ); options.add( option ); option = new PelletCmdOption( "method" ); option.setShortOption( "m" ); option.setType( "glass | black" ); option.setDescription( "Method that will be used to generate explanations" ); option.setDefaultValue( "glass" ); option.setIsMandatory( false ); option.setArg( REQUIRED ); options.add( option ); option = new PelletCmdOption( "max" ); option.setShortOption( "x" ); option.setType( "positive integer" ); option.setDescription( "Maximum number of generated explanations for each inference" ); option.setDefaultValue( 1 ); option.setIsMandatory( false ); option.setArg( REQUIRED ); options.add( option ); option = options.getOption( "verbose" ); option.setDescription( "Print detailed exceptions and messages about the progress" ); return options; } @Override public void parseArgs(String[] args) { super.parseArgs( args ); maxExplanations = options.getOption( "max" ).getValueAsNonNegativeInteger(); loader = (OWLAPILoader) getLoader( "OWLAPIv3" ); getKB(); converter = new SatisfiabilityConverter( loader.getManager().getOWLDataFactory() ); reasoner = loader.getReasoner(); loadMethod(); loadNames(); } @Override public void run() { try { if( name1 == null ) { // Option --hierarchy verbose( "Explain all the subclass relations in the ontology" ); explainClassHierarchy(); } else if( name2 == null ) { if( ((OWLClassExpression) name1).isOWLNothing() ) { // Option --all-unsat verbose( "Explain all the unsatisfiable classes" ); explainUnsatisfiableClasses(); } else { // Option --inconsistent && --unsat C verbose( "Explain unsatisfiability of " + name1 ); explainUnsatisfiableClass( (OWLClass) name1 ); } } else if( name3 != null ) { // Option --property-value s,p,o verbose( "Explain property assertion " + name1 + " and " + name2 + " and " + name3 ); explainPropertyValue( (OWLIndividual) name1, (OWLProperty) name2, name3 ); } else if( name1.isOWLClass() && name2.isOWLClass() ) { // Option --subclass C,D verbose( "Explain subclass relation between " + name1 + " and " + name2 ); explainSubClass( (OWLClass) name1, (OWLClass) name2 ); } else if( name1.isOWLNamedIndividual() && name2.isOWLClass() ) { // Option --instance i,C verbose( "Explain instance relation between " + name1 + " and " + name2 ); explainInstance( (OWLIndividual) name1, (OWLClass) name2 ); } printStatistics(); } catch( OWLException e ) { throw new RuntimeException( e ); } } private void explainAxiom(OWLAxiom axiom) throws OWLException { MultipleExplanationGenerator expGen = new HSTExplanationGenerator(getSingleExplanationGenerator()); RendererExplanationProgressMonitor rendererMonitor = new RendererExplanationProgressMonitor(axiom); expGen.setProgressMonitor(rendererMonitor); OWLClassExpression unsatClass = converter.convert( axiom ); Timer timer = timers.startTimer("explain"); Set> explanations = expGen.getExplanations( unsatClass, maxExplanations ); timer.stop(); if (explanations.isEmpty()) { rendererMonitor.foundNoExplanations(); } if( timer.getCount() % 10 == 0 ) { // printStatistics(); } int expSize = explanations.size(); if( expSize == 0 ) { errorExpCount++; } else if( expSize == 1 ) { if( explanations.iterator().next().size() > 1 ) { multiAxiomExpCount++; // else // return; } } else { multipleExpCount++; } } public void explainClassHierarchy() throws OWLException { Set visited = new HashSet(); reasoner.flush(); startTask( "Classification" ); reasoner.getKB().classify(); finishTask( "Classification" ); startTask( "Realization" ); reasoner.getKB().realize(); finishTask( "Realization" ); monitor = new ConsoleProgressMonitor(); monitor.setProgressTitle( "Explaining" ); monitor.setProgressLength( reasoner.getRootOntology().getClassesInSignature().size() ); monitor.taskStarted(); Node bottoms = reasoner.getEquivalentClasses( OWL.Nothing ); explainClassHierarchy( OWL.Nothing, bottoms, visited ); Node tops = reasoner.getEquivalentClasses( OWL.Thing ); explainClassHierarchy( OWL.Thing, tops, visited ); monitor.taskFinished(); } public void explainEquivalentClass(OWLClass c1, OWLClass c2) throws OWLException { if( c1.equals( c2 ) ) { return; } OWLAxiom axiom = OWL.equivalentClasses( c1, c2 ); explainAxiom( axiom ); } public void explainInstance(OWLIndividual ind, OWLClass c) throws OWLException { if( c.isOWLThing() ) { return; } OWLAxiom axiom = OWL.classAssertion( ind, c ); explainAxiom( axiom ); } // In the following method(s) we intentionally do not use OWLPropertyExpression // because of a bug in some Sun's implementation of javac // http://bugs.sun.com/view_bug.do?bug_id=6548436 // Since lack of generic type generates a warning, we suppress it @SuppressWarnings("unchecked") public void explainPropertyValue(OWLIndividual s, OWLProperty p, OWLObject o) throws OWLException { if( p.isOWLObjectProperty() ) { explainAxiom( OWL.propertyAssertion( s, (OWLObjectProperty) p, (OWLIndividual) o ) ); } else { explainAxiom( OWL.propertyAssertion( s, (OWLDataProperty) p, (OWLLiteral) o ) ); } } public void explainSubClass(OWLClass sub, OWLClass sup) throws OWLException { if( sub.equals( sup ) ) { return; } if( sub.isOWLNothing() ) { return; } if( sup.isOWLThing() ) { return; } OWLSubClassOfAxiom axiom = OWL.subClassOf( sub, sup ); explainAxiom( axiom ); } public void explainUnsatisfiableClasses() throws OWLException { for( OWLClass cls : reasoner.getEquivalentClasses( OWL.Nothing ) ) { if( cls.isOWLNothing() ) { continue; } explainUnsatisfiableClass( cls ); } } public void explainUnsatisfiableClass(OWLClass cls) throws OWLException { explainSubClass( cls, OWL.Nothing ); } private void explainClassHierarchy(OWLClass cls, Node eqClasses, Set visited) throws OWLException { if( visited.contains( cls ) ) { return; } visited.add( cls ); visited.addAll( eqClasses.getEntities() ); for( OWLClass eqClass : eqClasses ) { monitor.incrementProgress(); explainEquivalentClass( cls, eqClass ); } for( OWLNamedIndividual ind : reasoner.getInstances( cls, true ).getFlattened() ) { explainInstance( ind, cls ); } NodeSet subClasses = reasoner.getSubClasses( cls, true ); Map> subClassEqs = new HashMap>(); for( Node equivalenceSet : subClasses ) { if( equivalenceSet.isBottomNode() ) { continue; } OWLClass subClass = equivalenceSet.getRepresentativeElement(); subClassEqs.put( subClass, equivalenceSet ); explainSubClass( subClass, cls ); } for( Map.Entry> entry : subClassEqs.entrySet() ) { explainClassHierarchy( entry.getKey(), entry.getValue(), visited ); } } private TransactionAwareSingleExpGen getSingleExplanationGenerator() { if( useBlackBox ) { if ( options.getOption( "inconsistent" ) != null ) { if( !options.getOption( "inconsistent" ).getValueAsBoolean() ) { return new BlackBoxExplanation( reasoner.getRootOntology(), PelletReasonerFactory.getInstance(), reasoner ); } else { output( "WARNING: black method cannot be used to explain inconsistency. Switching to glass." ); return new GlassBoxExplanation( reasoner ); } } else { return new BlackBoxExplanation( reasoner.getRootOntology(), PelletReasonerFactory.getInstance(), reasoner ); } } else { return new GlassBoxExplanation( reasoner ); } } private void loadMethod() { String method = options.getOption( "method" ).getValueAsString(); if( method.equalsIgnoreCase( "black" ) ) { useBlackBox = true; } else if( method.equalsIgnoreCase( "glass" ) ) { useBlackBox = false; } else { throw new PelletCmdException( "Unrecognized method: " + method ); } } private void loadNames() { PelletCmdOption option; name1 = name2 = null; name3 = null; if( (option = options.getOption( "hierarchy" )) != null ) { if( option.getValueAsBoolean() ) { return; } } if( (option = options.getOption( "all-unsat" )) != null ) { if( option.getValueAsBoolean() ) { name1 = OWL.Nothing; return; } } if( (option = options.getOption( "inconsistent" )) != null ) { if( option.getValueAsBoolean() ) { if( useBlackBox ) { throw new PelletCmdException("Black box method cannot be used to explain ontology inconsistency"); } name1 = OWL.Thing; return; } } if( (option = options.getOption( "unsat" )) != null ) { String unsatisfiable = option.getValueAsString(); if( unsatisfiable != null ) { name1 = OntologyUtils.findEntity( unsatisfiable, loader.getAllOntologies() ); if( name1 == null ) { throw new PelletCmdException( "Undefined entity: " + unsatisfiable ); } else if( !name1.isOWLClass() ) { throw new PelletCmdException( "Not a defined class: " + unsatisfiable ); } else if( name1.isTopEntity() && useBlackBox) { throw new PelletCmdException("Black box method cannot be used to explain unsatisfiability of owl:Thing"); } return; } } if( (option = options.getOption( "subclass" )) != null ) { String subclass = option.getValueAsString(); if( subclass != null ) { String[] names = subclass.split( "," ); if( names.length != 2 ) { throw new PelletCmdException( "Invalid format for subclass option: " + subclass ); } name1 = OntologyUtils.findEntity( names[0], loader.getAllOntologies() ); name2 = OntologyUtils.findEntity( names[1], loader.getAllOntologies() ); if( name1 == null ) { throw new PelletCmdException( "Undefined entity: " + names[0] ); } else if( !name1.isOWLClass() ) { throw new PelletCmdException( "Not a defined class: " + names[0] ); } if( name2 == null ) { throw new PelletCmdException( "Undefined entity: " + names[1] ); } else if( !name2.isOWLClass() ) { throw new PelletCmdException( "Not a defined class: " + names[1] ); } return; } } if( (option = options.getOption( "instance" )) != null ) { String instance = option.getValueAsString(); if( instance != null ) { String[] names = instance.split( "," ); if( names.length != 2 ) { throw new PelletCmdException( "Invalid format for instance option: " + instance ); } name1 = OntologyUtils.findEntity( names[0], loader.getAllOntologies() ); name2 = OntologyUtils.findEntity( names[1], loader.getAllOntologies() ); if( name1 == null ) { throw new PelletCmdException( "Undefined entity: " + names[0] ); } else if( !name1.isOWLNamedIndividual() ) { throw new PelletCmdException( "Not a defined individual: " + names[0] ); } if( name2 == null ) { throw new PelletCmdException( "Undefined entity: " + names[1] ); } else if( !name2.isOWLClass() ) { throw new PelletCmdException( "Not a defined class: " + names[1] ); } return; } } if( (option = options.getOption( "property-value" )) != null ) { String optionValue = option.getValueAsString(); if( optionValue != null ) { String[] names = optionValue.split( "," ); if( names.length != 3 ) { throw new PelletCmdException( "Invalid format for property-value option: " + optionValue ); } name1 = OntologyUtils.findEntity( names[0], loader.getAllOntologies() ); name2 = OntologyUtils.findEntity( names[1], loader.getAllOntologies() ); if( name1 == null ) { throw new PelletCmdException( "Undefined entity: " + names[0] ); } else if( !name1.isOWLNamedIndividual() ) { throw new PelletCmdException( "Not an individual: " + names[0] ); } if( name2 == null ) { throw new PelletCmdException( "Undefined entity: " + names[1] ); } else if( !name2.isOWLObjectProperty() && !name2.isOWLDataProperty() ) { throw new PelletCmdException( "Not a defined property: " + names[1] ); } if( name2.isOWLObjectProperty() ) { name3 = OntologyUtils.findEntity( names[2], loader.getAllOntologies() ); if( name3 == null ) { throw new PelletCmdException( "Undefined entity: " + names[2] ); } else if( !(name3 instanceof OWLIndividual) ) { throw new PelletCmdException( "Not a defined individual: " + names[2] ); } } else { ManchesterOWLSyntaxEditorParser parser = new ManchesterOWLSyntaxEditorParser( loader.getManager().getOWLDataFactory(), names[2] ); try { name3 = parser.parseConstant(); } catch( ParserException e ) { throw new PelletCmdException( "Not a valid literal: " + names[2] ); } } return; } } // Per default we explain why the ontology is inconsistent name1 = OWL.Thing; if( useBlackBox ) { throw new PelletCmdException("Black box method cannot be used to explain ontology inconsistency"); } return; } private void printStatistics() throws OWLException { if(!verbose) { return; } Timer timer = timers.getTimer( "explain" ); if( timer != null ) { verbose( "Subclass relations : " + timer.getCount() ); verbose( "Multiple explanations: " + multipleExpCount ); verbose( "Single explanation " ); verbose( " with multiple axioms: " + multiAxiomExpCount ); verbose( "Error explaining : " + errorExpCount ); verbose( "Average time : " + timer.getAverage() + "ms" ); } } private class RendererExplanationProgressMonitor implements ExplanationProgressMonitor { private ExplanationRenderer rend = new ManchesterSyntaxExplanationRenderer(); private OWLAxiom axiom; private Set> setExplanations; private PrintWriter pw; private RendererExplanationProgressMonitor(OWLAxiom axiom) { this.axiom = axiom; this.pw = new PrintWriter(System.out); setExplanations = new HashSet>(); try { rend.startRendering(pw); } catch (OWLException e) { System.err.println("Error rendering explanation: " + e); } catch (IOException e) { System.err.println("Error rendering explanation: " + e); } } public void foundExplanation(Set axioms) { if (!setExplanations.contains(axioms)) { setExplanations.add(axioms); pw.flush(); try { rend.render(axiom, Collections.singleton(axioms)); } catch (IOException e) { System.err.println("Error rendering explanation: " + e); } catch (OWLException e) { System.err.println("Error rendering explanation: " + e); } } } public boolean isCancelled() { return false; } public void foundAllExplanations() { try { rend.endRendering(); } catch (OWLException e) { System.err.println("Error rendering explanation: " + e); } catch (IOException e) { System.err.println("Error rendering explanation: " + e); } } public void foundNoExplanations() { try { rend.render(axiom, Collections.>emptySet()); rend.endRendering(); } catch (OWLException e) { System.err.println("Error rendering explanation: " + e); } catch (IOException e) { System.err.println("Error rendering explanation: " + e); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy