/**
* PODD is an OWL ontology database used for scientific project management
*
* Copyright (C) 2009-2013 The University Of Queensland
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero 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
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
// Originally derived from PelletExplain.java which had the following copyright notice:
// Copyright (c) 2006 - 2008, Clark & Parsia, LLC. <http://www.clarkparsia.com>
// 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: licensing@clarkparsia.com
package com.github.podd.impl;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
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.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 org.semanticweb.owlapi.util.NullProgressMonitor;
import org.semanticweb.owlapi.util.ProgressMonitor;
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.util.ExplanationProgressMonitor;
import com.clarkparsia.owlapiv3.OWL;
import com.clarkparsia.pellet.owlapiv3.PelletReasoner;
import com.clarkparsia.pellet.owlapiv3.PelletReasonerFactory;
public class ExplanationUtils
{
private static class RendererExplanationProgressMonitor implements ExplanationProgressMonitor
{
private OWLAxiom axiom;
private ExplanationRenderer rend;
private Set<Set<OWLAxiom>> setExplanations = new HashSet<Set<OWLAxiom>>();
private RendererExplanationProgressMonitor(final ExplanationRenderer rend, final OWLAxiom axiom)
{
this.axiom = axiom;
this.rend = rend;
}
@Override
public void foundAllExplanations()
{
// Do nothing to support multiple uses of renderer
}
@Override
public void foundExplanation(final Set<OWLAxiom> axioms)
{
if(!this.setExplanations.contains(axioms))
{
this.setExplanations.add(axioms);
try
{
if(this.rend != null)
{
this.rend.render(this.axiom, Collections.singleton(axioms));
}
}
catch(final IOException e)
{
System.err.println("Error rendering explanation: " + e);
}
catch(final OWLException e)
{
System.err.println("Error rendering explanation: " + e);
}
}
}
public void foundNoExplanations()
{
try
{
if(this.rend != null)
{
this.rend.render(this.axiom, Collections.<Set<OWLAxiom>> emptySet());
}
}
catch(final OWLException e)
{
System.err.println("Error rendering explanation: " + e);
}
catch(final IOException e)
{
System.err.println("Error rendering explanation: " + e);
}
}
@Override
public boolean isCancelled()
{
return false;
}
}
static
{
GlassBoxExplanation.setup();
}
private boolean allowInconsistency = true;
private SatisfiabilityConverter converter;
private int errorExpCount = 0;
private ExplanationRenderer explanationRenderer;
private int maxExplanations = 1;
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 PelletReasonerFactory reasonerFactory;
// private ExplanationProgressMonitor explanationMonitor;
public ExplanationUtils(final PelletReasoner reasoner, final PelletReasonerFactory reasonerFactory,
final ExplanationRenderer explanationRenderer, final ProgressMonitor monitor, final int maxExplanations)
{
this.maxExplanations = maxExplanations;
this.reasoner = reasoner;
this.reasonerFactory = reasonerFactory;
if(monitor == null)
{
this.monitor = new NullProgressMonitor();
}
else
{
this.monitor = monitor;
}
// this.explanationMonitor = explanationMonitor;
this.explanationRenderer = explanationRenderer;
this.converter = new SatisfiabilityConverter(reasoner.getManager().getOWLDataFactory());
}
private Set<Set<OWLAxiom>> explainAxiom(final OWLAxiom axiom) throws OWLException
{
final MultipleExplanationGenerator expGen = new HSTExplanationGenerator(this.getSingleExplanationGenerator());
final RendererExplanationProgressMonitor rendererMonitor =
new RendererExplanationProgressMonitor(this.explanationRenderer, axiom);
expGen.setProgressMonitor(rendererMonitor);
final OWLClassExpression unsatClass = this.converter.convert(axiom);
final Set<Set<OWLAxiom>> explanations = expGen.getExplanations(unsatClass, this.maxExplanations);
if(explanations.isEmpty())
{
rendererMonitor.foundNoExplanations();
}
final int expSize = explanations.size();
if(expSize == 0)
{
this.errorExpCount++;
}
else if(expSize == 1)
{
if(explanations.iterator().next().size() > 1)
{
this.multiAxiomExpCount++;
}
}
else
{
this.multipleExpCount++;
}
return explanations;
}
public Set<Set<OWLAxiom>> explainClassHierarchy() throws OWLException
{
final Set<OWLClass> visited = new HashSet<OWLClass>();
this.reasoner.flush();
this.reasoner.getKB().classify();
this.reasoner.getKB().realize();
this.monitor.setMessage("Explaining");
this.monitor.setProgress(this.reasoner.getRootOntology().getClassesInSignature().size());
this.monitor.setStarted();
final Set<Set<OWLAxiom>> result = new HashSet<Set<OWLAxiom>>();
final Node<OWLClass> bottoms = this.reasoner.getEquivalentClasses(OWL.Nothing);
result.addAll(this.explainClassHierarchy(OWL.Nothing, bottoms, visited));
final Node<OWLClass> tops = this.reasoner.getEquivalentClasses(OWL.Thing);
result.addAll(this.explainClassHierarchy(OWL.Thing, tops, visited));
this.monitor.setFinished();
return result;
}
private Set<Set<OWLAxiom>> explainClassHierarchy(final OWLClass cls, final Node<OWLClass> eqClasses,
final Set<OWLClass> visited) throws OWLException
{
final Set<Set<OWLAxiom>> result = new HashSet<Set<OWLAxiom>>();
if(visited.contains(cls))
{
return result;
}
visited.add(cls);
visited.addAll(eqClasses.getEntities());
for(final OWLClass eqClass : eqClasses)
{
// TODO: Support this
// this.monitor.incrementProgress();
result.addAll(this.explainEquivalentClass(cls, eqClass));
}
for(final OWLNamedIndividual ind : this.reasoner.getInstances(cls, true).getFlattened())
{
result.addAll(this.explainInstance(ind, cls));
}
final NodeSet<OWLClass> subClasses = this.reasoner.getSubClasses(cls, true);
final Map<OWLClass, Node<OWLClass>> subClassEqs = new HashMap<OWLClass, Node<OWLClass>>();
for(final Node<OWLClass> equivalenceSet : subClasses)
{
if(!equivalenceSet.isBottomNode())
{
final OWLClass subClass = equivalenceSet.getRepresentativeElement();
subClassEqs.put(subClass, equivalenceSet);
result.addAll(this.explainSubClass(subClass, cls));
}
}
for(final Map.Entry<OWLClass, Node<OWLClass>> entry : subClassEqs.entrySet())
{
result.addAll(this.explainClassHierarchy(entry.getKey(), entry.getValue(), visited));
}
return result;
}
public Set<Set<OWLAxiom>> explainEquivalentClass(final OWLClass c1, final OWLClass c2) throws OWLException
{
final Set<Set<OWLAxiom>> result = new HashSet<Set<OWLAxiom>>();
if(c1.equals(c2))
{
return result;
}
final OWLAxiom axiom = OWL.equivalentClasses(c1, c2);
return this.explainAxiom(axiom);
}
public Set<Set<OWLAxiom>> explainInstance(final OWLIndividual ind, final OWLClass c) throws OWLException
{
final Set<Set<OWLAxiom>> result = new HashSet<Set<OWLAxiom>>();
if(c.isOWLThing())
{
return result;
}
final OWLAxiom axiom = OWL.classAssertion(ind, c);
return this.explainAxiom(axiom);
}
public Set<Set<OWLAxiom>> explainPropertyValue(final OWLIndividual s,
@SuppressWarnings("rawtypes") final OWLProperty p, final OWLObject o) throws OWLException
{
if(p.isOWLObjectProperty())
{
return this.explainAxiom(OWL.propertyAssertion(s, (OWLObjectProperty)p, (OWLIndividual)o));
}
else
{
return this.explainAxiom(OWL.propertyAssertion(s, (OWLDataProperty)p, (OWLLiteral)o));
}
}
public Set<Set<OWLAxiom>> explainSubClass(final OWLClass sub, final OWLClass sup) throws OWLException
{
final Set<Set<OWLAxiom>> result = new HashSet<Set<OWLAxiom>>();
if(sub.equals(sup))
{
return result;
}
else if(sub.isOWLNothing())
{
return result;
}
else if(sup.isOWLThing())
{
return result;
}
final OWLSubClassOfAxiom axiom = OWL.subClassOf(sub, sup);
return this.explainAxiom(axiom);
}
public Set<Set<OWLAxiom>> explainUnsatisfiableClass(final OWLClass cls) throws OWLException
{
return this.explainSubClass(cls, OWL.Nothing);
}
public Set<Set<OWLAxiom>> explainUnsatisfiableClasses() throws OWLException
{
final Set<Set<OWLAxiom>> result = new HashSet<Set<OWLAxiom>>();
for(final OWLClass cls : this.reasoner.getEquivalentClasses(OWL.Nothing))
{
if(!cls.isOWLNothing())
{
result.addAll(this.explainUnsatisfiableClass(cls));
}
}
return result;
}
private TransactionAwareSingleExpGen getSingleExplanationGenerator()
{
if(this.allowInconsistency)
{
return new GlassBoxExplanation(this.reasoner);
}
else
{
return new BlackBoxExplanation(this.reasoner.getRootOntology(), this.reasonerFactory, this.reasoner);
}
}
public boolean getAllowInconsistency()
{
return this.allowInconsistency;
}
public void setAllowInconsistency(final boolean allowInconsistency)
{
this.allowInconsistency = allowInconsistency;
}
}