org.semanticweb.HermiT.EntailmentChecker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.semanticweb.hermit Show documentation
Show all versions of org.semanticweb.hermit Show documentation
HermiT is reasoner for ontologies written using the Web Ontology Language (OWL). Given an OWL file, HermiT can determine whether or not the ontology is consistent, identify subsumption relationships between classes, and much more.
This is the maven build of HermiT and is designed for people who wish to use HermiT from within the OWL API. It is now versioned in the main HermiT version repository, although not officially supported by the HermiT developers.
The version number of this package is a composite of the HermiT version and a value representing the OWLAPI release it is compatible with. Note that the group id for the upstream HermiT is com.hermit-reasoner, while this fork is released under net.sourceforge.owlapi.
This fork exists to allow HermiT users to use newer OWLAPI versions than the ones supported by the original HermiT codebase.
This package includes the Jautomata library (http://jautomata.sourceforge.net/), and builds with it directly. This library appears to be no longer under active development, and so a "fork" seems appropriate. No development is intended or anticipated on this code base.
The newest version!
/* Copyright 2009 by the Oxford University Computing Laboratory
This file is part of HermiT.
HermiT is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
HermiT 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with HermiT. If not, see .
*/
package org.semanticweb.HermiT;
import static org.semanticweb.owlapi.util.OWLAPIStreamUtils.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import org.semanticweb.HermiT.model.AtomicRole;
import org.semanticweb.HermiT.model.InternalDatatype;
import org.semanticweb.HermiT.tableau.ReasoningTaskDescription;
import org.semanticweb.HermiT.tableau.Tableau;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.IsAnonymous;
import org.semanticweb.owlapi.model.OWLAnnotationAssertionAxiom;
import org.semanticweb.owlapi.model.OWLAnnotationPropertyDomainAxiom;
import org.semanticweb.owlapi.model.OWLAnnotationPropertyRangeAxiom;
import org.semanticweb.owlapi.model.OWLAnonymousIndividual;
import org.semanticweb.owlapi.model.OWLAsymmetricObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLAxiomVisitor;
import org.semanticweb.owlapi.model.OWLAxiomVisitorEx;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassAssertionAxiom;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLDataIntersectionOf;
import org.semanticweb.owlapi.model.OWLDataMaxCardinality;
import org.semanticweb.owlapi.model.OWLDataProperty;
import org.semanticweb.owlapi.model.OWLDataPropertyAssertionAxiom;
import org.semanticweb.owlapi.model.OWLDataPropertyDomainAxiom;
import org.semanticweb.owlapi.model.OWLDataPropertyExpression;
import org.semanticweb.owlapi.model.OWLDataPropertyRangeAxiom;
import org.semanticweb.owlapi.model.OWLDataRange;
import org.semanticweb.owlapi.model.OWLDataSomeValuesFrom;
import org.semanticweb.owlapi.model.OWLDataUnionOf;
import org.semanticweb.owlapi.model.OWLDatatype;
import org.semanticweb.owlapi.model.OWLDatatypeDefinitionAxiom;
import org.semanticweb.owlapi.model.OWLDeclarationAxiom;
import org.semanticweb.owlapi.model.OWLDifferentIndividualsAxiom;
import org.semanticweb.owlapi.model.OWLDisjointClassesAxiom;
import org.semanticweb.owlapi.model.OWLDisjointDataPropertiesAxiom;
import org.semanticweb.owlapi.model.OWLDisjointObjectPropertiesAxiom;
import org.semanticweb.owlapi.model.OWLDisjointUnionAxiom;
import org.semanticweb.owlapi.model.OWLEquivalentClassesAxiom;
import org.semanticweb.owlapi.model.OWLEquivalentDataPropertiesAxiom;
import org.semanticweb.owlapi.model.OWLEquivalentObjectPropertiesAxiom;
import org.semanticweb.owlapi.model.OWLFunctionalDataPropertyAxiom;
import org.semanticweb.owlapi.model.OWLFunctionalObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLHasKeyAxiom;
import org.semanticweb.owlapi.model.OWLIndividual;
import org.semanticweb.owlapi.model.OWLInverseFunctionalObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLInverseObjectPropertiesAxiom;
import org.semanticweb.owlapi.model.OWLIrreflexiveObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLNamedIndividual;
import org.semanticweb.owlapi.model.OWLNegativeDataPropertyAssertionAxiom;
import org.semanticweb.owlapi.model.OWLNegativeObjectPropertyAssertionAxiom;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import org.semanticweb.owlapi.model.OWLObjectPropertyAssertionAxiom;
import org.semanticweb.owlapi.model.OWLObjectPropertyDomainAxiom;
import org.semanticweb.owlapi.model.OWLObjectPropertyExpression;
import org.semanticweb.owlapi.model.OWLObjectPropertyRangeAxiom;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.OWLReflexiveObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLSameIndividualAxiom;
import org.semanticweb.owlapi.model.OWLSubAnnotationPropertyOfAxiom;
import org.semanticweb.owlapi.model.OWLSubClassOfAxiom;
import org.semanticweb.owlapi.model.OWLSubDataPropertyOfAxiom;
import org.semanticweb.owlapi.model.OWLSubObjectPropertyOfAxiom;
import org.semanticweb.owlapi.model.OWLSubPropertyChainOfAxiom;
import org.semanticweb.owlapi.model.OWLSymmetricObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLTransitiveObjectPropertyAxiom;
import org.semanticweb.owlapi.model.SWRLRule;
/**Entailment checker.*/
public class EntailmentChecker implements OWLAxiomVisitorEx {
protected final OWLDataFactory factory;
private final Reasoner reasoner;
protected final Set anonymousIndividualAxioms=new HashSet<>();
/**
* @param reasoner reasoner
* @param factory factory
*/
public EntailmentChecker(Reasoner reasoner,OWLDataFactory factory) {
this.reasoner=reasoner;
this.factory=factory;
}
/**
* Checks entailment of a set of axioms (an ontology) against the loaded ontology.
*
* @param axioms
* the axioms that should be checked for enailment
* @return true if all axioms follow from the loaded ontology and false otherwise.
*/
public boolean entails(Set extends OWLAxiom> axioms) {
anonymousIndividualAxioms.clear();
for (OWLAxiom axiom : axioms) {
if (axiom.isLogicalAxiom())
if (!axiom.accept(this).booleanValue())
return false;
}
return checkAnonymousIndividuals();
}
/**
* Use this method only if you really want to check just one axiom or
* if the axioms you want to check do not contain blind nodes/anonymous
* individuals. Otherwise use {@code entails(Set axioms)}
* because only then concepts for the anonymous individuals can be
* obtained by rolling-up as required.
*
* @param axiom
* an axiom for which entailment is to be checked
* @return true if the loaded ontology entails the axiom and false otherwise
*/
public boolean entails(OWLAxiom axiom) {
if (!axiom.accept(this).booleanValue())
return false;
return checkAnonymousIndividuals();
}
/**
* @return true if there are no individual axioms or if all rolled-up concepts for the anonymous individuals are entailed and false otherwise
*/
protected boolean checkAnonymousIndividuals() {
if (anonymousIndividualAxioms.isEmpty())
return true;
// go through the axioms and build the rolling-up concepts for them
AnonymousIndividualForestBuilder anonIndChecker=new AnonymousIndividualForestBuilder();
anonIndChecker.constructConceptsForAnonymousIndividuals(factory,anonymousIndividualAxioms);
for (OWLAxiom ax : anonIndChecker.getAnonIndAxioms())
if (!ax.accept(this).booleanValue())
return false;
for (OWLAxiom ax : anonIndChecker.getAnonNoNamedIndAxioms()) {
Tableau t=reasoner.getTableau(ax);
if (t.isSatisfiable(true,true,null,null,null,null,null,new ReasoningTaskDescription(false,"Anonymous individual check: "+ax.toString())))
return false;
}
return true;
}
// ************ non-logical axioms ****************************
@Override
public Boolean visit(OWLAnnotationAssertionAxiom axiom) {
return Boolean.TRUE;
}
@Override
public Boolean visit(OWLSubAnnotationPropertyOfAxiom axiom) {
return Boolean.TRUE;
}
@Override
public Boolean visit(OWLAnnotationPropertyDomainAxiom axiom) {
return Boolean.TRUE;
}
@Override
public Boolean visit(OWLAnnotationPropertyRangeAxiom axiom) {
return Boolean.TRUE;
}
@Override
public Boolean visit(OWLDeclarationAxiom axiom) {
return Boolean.TRUE;
}
// ************ assertions ****************************
@Override
public Boolean visit(OWLDifferentIndividualsAxiom axiom) {
// see OWL 2 Syntax, Sec 11.2
// No axiom in Ax of the following form contains anonymous individuals:
// SameIndividual, DifferentIndividuals, NegativeObjectPropertyAssertion, and NegativeDataPropertyAssertion.
List list=asList(axiom.individuals());
for (OWLIndividual i : list) {
if (i.isAnonymous()) {
throw new IllegalArgumentException("OWLDifferentIndividualsAxiom axioms are not allowed to be used "+"with anonymous individuals (see OWL 2 Syntax Sec 11.2) but the axiom "+axiom+" cotains an anonymous individual. ");
}
}
for (int i=0;i i=axiom.individuals().iterator();
if (i.hasNext()) {
OWLNamedIndividual first=i.next().asOWLNamedIndividual();
while (i.hasNext()) {
OWLNamedIndividual next=i.next().asOWLNamedIndividual();
if (!reasoner.isSameIndividual(first, next))
return Boolean.FALSE;
}
}
return Boolean.TRUE;
}
@Override
public Boolean visit(OWLClassAssertionAxiom axiom) {
OWLIndividual ind=axiom.getIndividual();
if (ind.isAnonymous()) {
anonymousIndividualAxioms.add(axiom);
return Boolean.TRUE; // will be checked afterwards by rolling-up
}
OWLClassExpression c=axiom.getClassExpression();
return Boolean.valueOf( reasoner.hasType(ind.asOWLNamedIndividual(),c,false));
}
@Override
public Boolean visit(OWLObjectPropertyAssertionAxiom axiom) {
OWLIndividual sub=axiom.getSubject();
OWLIndividual obj=axiom.getObject();
if (sub.isAnonymous()||obj.isAnonymous()) {
anonymousIndividualAxioms.add(axiom);
return Boolean.TRUE; // will be checked afterwards by rolling-up
}
return Boolean.valueOf(reasoner.hasObjectPropertyRelationship(sub.asOWLNamedIndividual(),axiom.getProperty(),obj.asOWLNamedIndividual()));
}
@Override
public Boolean visit(OWLNegativeObjectPropertyAssertionAxiom axiom) {
// see OWL 2 Syntax, Sec 11.2
// No axiom in Ax of the following form contains anonymous individuals:
// SameIndividual, DifferentIndividuals, NegativeObjectPropertyAssertion, and NegativeDataPropertyAssertion.
if (axiom.getSubject().isAnonymous()||axiom.getObject().isAnonymous()) {
throw new IllegalArgumentException("NegativeObjectPropertyAssertion axioms are not allowed to be used "+"with anonymous individuals (see OWL 2 Syntax Sec 11.2) but the axiom "+axiom+" cotains an anonymous subject or object. ");
}
OWLClassExpression hasValue=factory.getOWLObjectHasValue(axiom.getProperty(),axiom.getObject());
OWLClassExpression doesNotHaveValue=factory.getOWLObjectComplementOf(hasValue);
return Boolean.valueOf(reasoner.hasType(axiom.getSubject().asOWLNamedIndividual(),doesNotHaveValue,false));
}
@Override
public Boolean visit(OWLDataPropertyAssertionAxiom axiom) {
OWLIndividual sub=axiom.getSubject();
if (sub.isAnonymous()) {
anonymousIndividualAxioms.add(axiom);
return Boolean.TRUE; // will be checked afterwards by rolling-up
}
OWLClassExpression hasValue=factory.getOWLDataHasValue(axiom.getProperty(),axiom.getObject());
return Boolean.valueOf(reasoner.hasType(axiom.getSubject().asOWLNamedIndividual(),hasValue,false));
}
@Override
public Boolean visit(OWLNegativeDataPropertyAssertionAxiom axiom) {
// see OWL 2 Syntax, Sec 11.2
// No axiom in Ax of the following form contains anonymous individuals:
// SameIndividual, DifferentIndividuals, NegativeObjectPropertyAssertion, and NegativeDataPropertyAssertion.
if (axiom.getSubject().isAnonymous()) {
throw new IllegalArgumentException("NegativeDataPropertyAssertion axioms are not allowed to be used "+"with anonymous individuals (see OWL 2 Syntax Sec 11.2) and the subject "+axiom.getSubject()+" of the axiom "+axiom+" is anonymous. ");
}
OWLClassExpression hasValue=factory.getOWLDataHasValue(axiom.getProperty(),axiom.getObject());
OWLClassExpression doesNotHaveValue=factory.getOWLObjectComplementOf(hasValue);
return Boolean.valueOf(reasoner.hasType(axiom.getSubject().asOWLNamedIndividual(),doesNotHaveValue,false));
}
// ************ object properties ****************************
@Override
public Boolean visit(OWLObjectPropertyDomainAxiom axiom) {
return Boolean.valueOf(reasoner.isSubClassOf(factory.getOWLObjectSomeValuesFrom(axiom.getProperty(),factory.getOWLThing()),axiom.getDomain()));
}
@Override
public Boolean visit(OWLObjectPropertyRangeAxiom axiom) {
return Boolean.valueOf(reasoner.isSubClassOf(factory.getOWLThing(),factory.getOWLObjectAllValuesFrom(axiom.getProperty(),axiom.getRange())));
}
@Override
public Boolean visit(OWLInverseObjectPropertiesAxiom axiom) {
OWLObjectPropertyExpression objectPropertyExpression1=axiom.getFirstProperty().getInverseProperty();
OWLObjectPropertyExpression objectPropertyExpression2=axiom.getSecondProperty();
return Boolean.valueOf(reasoner.isSubObjectPropertyExpressionOf(objectPropertyExpression1,objectPropertyExpression2) && reasoner.isSubObjectPropertyExpressionOf(objectPropertyExpression2,objectPropertyExpression1));
}
@Override
public Boolean visit(OWLSymmetricObjectPropertyAxiom axiom) {
return Boolean.valueOf(reasoner.isSymmetric(axiom.getProperty()));
}
@Override
public Boolean visit(OWLTransitiveObjectPropertyAxiom axiom) {
return Boolean.valueOf(reasoner.isTransitive(axiom.getProperty()));
}
@Override
public Boolean visit(OWLReflexiveObjectPropertyAxiom axiom) {
return Boolean.valueOf(reasoner.isReflexive(axiom.getProperty()));
}
@Override
public Boolean visit(OWLIrreflexiveObjectPropertyAxiom axiom) {
return Boolean.valueOf(reasoner.isIrreflexive(axiom.getProperty()));
}
@Override
public Boolean visit(OWLAsymmetricObjectPropertyAxiom axiom) {
return Boolean.valueOf(reasoner.isAsymmetric(axiom.getProperty()));
}
@Override
public Boolean visit(OWLEquivalentObjectPropertiesAxiom axiom) {
Iterator it=axiom.properties().iterator();
if (it.hasNext()) {
OWLObjectPropertyExpression objectPropertyExpression1=it.next();
while (it.hasNext()) {
OWLObjectPropertyExpression objectPropertyExpression2=it.next();
if (!reasoner.isSubObjectPropertyExpressionOf(objectPropertyExpression1,objectPropertyExpression2) || !reasoner.isSubObjectPropertyExpressionOf(objectPropertyExpression2,objectPropertyExpression1))
return Boolean.FALSE;
}
}
return Boolean.TRUE;
}
@Override
public Boolean visit(OWLSubObjectPropertyOfAxiom axiom) {
return Boolean.valueOf(reasoner.isSubObjectPropertyExpressionOf(axiom.getSubProperty(),axiom.getSuperProperty()));
}
@Override
public Boolean visit(OWLSubPropertyChainOfAxiom axiom) {
return Boolean.valueOf(reasoner.isSubObjectPropertyExpressionOf(axiom.getPropertyChain(),axiom.getSuperProperty()));
}
@Override
public Boolean visit(OWLDisjointObjectPropertiesAxiom axiom) {
List props=asList(axiom.properties());
for (int i=0;i it=axiom.properties().iterator();
if (it.hasNext()) {
OWLDataProperty prop1=it.next().asOWLDataProperty();
while (it.hasNext()) {
OWLDataProperty dataProperty1=prop1.asOWLDataProperty();
OWLDataProperty dataProperty2=it.next().asOWLDataProperty();
if (!reasoner.isSubDataPropertyOf(dataProperty1,dataProperty2) || !reasoner.isSubDataPropertyOf(dataProperty2,dataProperty1))
return Boolean.FALSE;
}
}
return Boolean.TRUE;
}
@Override
public Boolean visit(OWLSubDataPropertyOfAxiom axiom) {
return Boolean.valueOf(reasoner.isSubDataPropertyOf(axiom.getSubProperty().asOWLDataProperty(),axiom.getSuperProperty().asOWLDataProperty()));
}
@Override
public Boolean visit(OWLDisjointDataPropertiesAxiom axiom) {
List props=asList(axiom.properties());
for (int i=0;i i=axiom.classExpressions().iterator();
if (i.hasNext()) {
OWLClassExpression first=i.next();
while (i.hasNext()&&isEntailed) {
OWLClassExpression next=i.next();
isEntailed=reasoner.isSubClassOf(first,next) && reasoner.isSubClassOf(next,first);
}
}
return Boolean.valueOf(isEntailed);
}
@Override
public Boolean visit(OWLDisjointClassesAxiom axiom) {
List classExpressions = asList(axiom.classExpressions());
for (int i=0;i bottom
OWLClass c=axiom.getOWLClass();
OWLClassExpression incl1=factory.getOWLObjectUnionOf(Stream.concat( axiom.classExpressions(), Stream.of(factory.getOWLObjectComplementOf(c))));
OWLClassExpression incl2=factory.getOWLObjectUnionOf(factory.getOWLObjectComplementOf(factory.getOWLObjectUnionOf(axiom.classExpressions())),c);
// incl1: not C or C1 or ... or Cn
// incl2: not(C1 or ... or Cn) or C
Set conjuncts=new HashSet<>();
conjuncts.add(incl1);
conjuncts.add(incl2);
List extends OWLClassExpression> list = asList(axiom.classExpressions());
for (int i=0;i axioms=new HashSet<>();
axioms.add(df.getOWLClassAssertionAxiom(axiom.getClassExpression(),individualA));
axioms.add(df.getOWLClassAssertionAxiom(axiom.getClassExpression(),individualB));
int i=0;
for (OWLObjectPropertyExpression p : asList(axiom.objectPropertyExpressions())) {
OWLIndividual tmp=df.getOWLNamedIndividual(IRI.create("internal:named-fresh-individual-"+i));
axioms.add(df.getOWLObjectPropertyAssertionAxiom(p,individualA,tmp));
axioms.add(df.getOWLObjectPropertyAssertionAxiom(p,individualB,tmp));
i++;
}
OWLDatatype anonymousConstantsDatatype=df.getOWLDatatype(IRI.create("internal:anonymous-constants"));
for (OWLDataPropertyExpression p : asList(axiom.dataPropertyExpressions())) {
OWLLiteral constant=df.getOWLLiteral("internal:constant-"+i,anonymousConstantsDatatype);
axioms.add(df.getOWLDataPropertyAssertionAxiom(p,individualA,constant));
axioms.add(df.getOWLDataPropertyAssertionAxiom(p,individualB,constant));
i++;
}
axioms.add(df.getOWLDifferentIndividualsAxiom(individualA,individualB));
Tableau tableau=reasoner.getTableau(axioms.toArray(new OWLAxiom[axioms.size()]));
return Boolean.valueOf(!tableau.isSatisfiable(true,true,null,null,null,null,null,ReasoningTaskDescription.isAxiomEntailed(axiom)));
}
protected class AnonymousIndividualForestBuilder implements OWLAxiomVisitor {
// No axiom in Ax of the following form contains anonymous individuals:
// - SameIndividual, DifferentIndividuals, NegativeObjectPropertyAssertion, and NegativeDataPropertyAssertion.
// A forest F over the anonymous individuals in Ax exists such that the following conditions are satisfied,
// for OPE an object property expression, _:x and _:y anonymous individuals, and a a named individual:
// - for each assertion in Ax of the form ObjectPropertyAssertion( OPE _:x _:y ), either _:x is a child of _:y
// or _:y is a child of _:x in F;
// - for each pair of anonymous individuals _:x and _:y such that _:y is a child of _:x in F, the set Ax contains
// at most one assertion of the form ObjectPropertyAssertion( OPE _:x _:y ) or
// ObjectPropertyAssertion( OPE _:y _:x ); and
// - for each anonymous individual _:x that is a root in F, the set Ax contains at most one assertion of the
// form ObjectPropertyAssertion( OPE _:x a ) or ObjectPropertyAssertion( OPE a _:x ).
protected final Set namedNodes=new HashSet<>();
protected final Set nodes=new HashSet<>();
// that is the forest we are trying to construct
protected final Map> edges=new HashMap<>();
// the following map determines which anonymous individual nodes are reachable from the named individuals
protected final Map>> specialOPEdges=new HashMap<>();
// node labels for the anonymous individual forest
protected final Map> nodelLabels=new HashMap<>();
// edge labels for the anonymous individual forest
protected final Map edgeOPLabels=new HashMap<>();
protected final Set anonIndAxioms=new HashSet<>();
protected final Set anonNoNamedIndAxioms=new HashSet<>();
/**
* @param df
* The data factory to be used when creating new concepts in the elimination of anonymous individuals.
* @param axioms
* The axioms from the conclusion ontology. After executing this method, the axioms that contain anonymous individuals can be discarded. Instead the methods getAssertions() and getConcepts() can be used to check entailment of the conclusion ontology.
*/
public void constructConceptsForAnonymousIndividuals(OWLDataFactory df,Set axioms) {
// The anonymous individuals together with the object property assertions
// induce a labelled forest with anonymous individuals as nodes and edges labelled
// with an object property and nodes labelled with a set of concepts.
// While visiting all axioms, we construct the induced forest. Data property assertions
// are rolled-up during the traversal and result in node labels, e.g.,
// DataPropertyAssertion(:dp _:x ) results in the concept
// DataHasValue(:dp ) being added to the label of concepts of the node
// for _:x.
// In case the result is not a forest, an error is thrown.
for (OWLAxiom ax : axioms) {
ax.accept(this);
}
// We now find the components among the anonymous individuals. It must be possible to
// arrange each component into a tree such that the root satisfies the above mentioned
// conditions on root. If that is not possible, an error is thrown.
Set> components=getComponents();
Map,OWLAnonymousIndividual> componentsToRoots=findSuitableRoots(components);
// It seems the forest is valid, so we can read off the concepts.
componentsToRoots.forEach((component,root)->{
if (!specialOPEdges.containsKey(root)) {
// It was not possible to find a root that has exactly one relationship with a named individual,
// otherwise findSuitableRoots() had given preference to that root.
// We have to roll-up into a concept.
OWLClassExpression c=getClassExpressionFor(df,root,null);
anonNoNamedIndAxioms.add(df.getOWLSubClassOfAxiom(df.getOWLThing(),df.getOWLObjectComplementOf(c)));
}
else {
// We can roll-up into a class assertion.
Map> ind2OP=specialOPEdges.get(root);
// We now that the set of object properties is a singleton set since otherwise this would
// not be a valid root and the map should have one single entry, but lets double-check.
if (ind2OP.size()!=1) {
throw new RuntimeException("Internal error: HermiT decided that the anonymous individuals form a valid forest, but actually they do not. ");
}
OWLNamedIndividual subject=ind2OP.keySet().iterator().next();
Set ops=ind2OP.get(subject);
if (ops.size()!=1) {
throw new RuntimeException("Internal error: HermiT decided that the anonymous individuals form a valid forest, but actually they do not. ");
}
OWLObjectPropertyExpression op=ops.iterator().next().getInverseProperty();
OWLClassExpression c=getClassExpressionFor(df,root,null);
anonIndAxioms.add(df.getOWLClassAssertionAxiom(df.getOWLObjectSomeValuesFrom(op,c),subject));
}
});
}
/**
* After calling constructConceptsForAnonymousIndividuals(), the method return a set of assertions that have to be entailed in order to satisfy the axioms that contain anonymous individuals. E.g., if the conclusion ontology contains axioms ObjectPropertyAssertion(:r :fred _:x), ObjectPropertyAssertion(:r _:x _:y), we get the assertion ClassAssertion(:fred ObjectSomeValuesFrom(:r ObjectSomeValuesFrom(:r owl:Thing))).
*
* @return a set of assertions without anonymous individuals that have to be entailed by the premise ontology
*/
public Set getAnonIndAxioms() {
return anonIndAxioms;
}
/**
* After calling constructConceptsForAnonymousIndividuals(), the method return a set of subclass axioms that, when added to the premise ontology should result in an inconsistency for the entailment to hold. E.g., if the conclusion ontology contains axiom ObjectPropertyAssertion(:r _:x _:y), we get the axiom SubClassOf(owl:Thing ObjectAllValuesFrom(:r owl:Nothing)) and if the premise ontology plus this axioms is inconsistent, then the entailment holds since each model of the premise contains some element that is an r-successor of something.
*
* @return a set of axioms without anonymous individuals that have make the premise ontology inconsistent for the entailment to hold
*/
public Set getAnonNoNamedIndAxioms() {
return anonNoNamedIndAxioms;
}
// /**
// * After calling constructConceptsForAnonymousIndividuals(), the method return a set of concepts that
// * have to be entailed in order to satisfy the axioms that contain anonymous individuals. E.g., if the conclusion
// * ontology contains axioms ObjectPropertyAssertion(:r _:x _:y), ClassAssertion(:C _:y) and there is no named
// * individual that is related to _:x, then we get the concept ObjectSomeValuesFrom(:r :C) and the entailment holds
// * if the premise ontology plus the axiom SubClassOf(owl:Thing ObjectcomplementOf(ObjectSomeValuesFrom(:r :C))) is
// * inconsistent.
// * @return a set of concepts that have to be non-empty in each model of the premise ontlogy in order for
// * the entailment to hold
// */
// public Set getConcepts() {
// return concepts;
// }
protected OWLClassExpression getClassExpressionFor(OWLDataFactory df,OWLAnonymousIndividual node,OWLAnonymousIndividual predecessor) {
Set successors=edges.get(node);
if (successors==null||(successors.size()==1&&successors.iterator().next()==predecessor)) {
// the tree consists of a single node
if (!nodelLabels.containsKey(node)) {
return df.getOWLThing();
}
else if (nodelLabels.get(node).size()==1) {
return nodelLabels.get(node).iterator().next();
}
else {
return df.getOWLObjectIntersectionOf(nodelLabels.get(node));
}
}
Set concepts=new HashSet<>();
for (OWLAnonymousIndividual successor : successors) {
OWLObjectProperty op;
Edge pair=new Edge(node,successor);
if (edgeOPLabels.containsKey(pair)) {
op=edgeOPLabels.get(pair);
}
else {
pair=new Edge(successor,node);
if (!edgeOPLabels.containsKey(pair)) {
throw new RuntimeException("Internal error: some edge in the forest of anonymous individuals has no edge label although it should. ");
}
else {
op=edgeOPLabels.get(pair);
}
}
concepts.add(df.getOWLObjectSomeValuesFrom(op,getClassExpressionFor(df,successor,node)));
}
return concepts.size()==1 ? concepts.iterator().next() : df.getOWLObjectIntersectionOf(concepts);
}
protected Map,OWLAnonymousIndividual> findSuitableRoots(Set> components) {
Map,OWLAnonymousIndividual> componentsToRoots=new HashMap<>();
for (Set component : components) {
// We have to find a node with at most one relation to the named individuals
// if there is one with exactly one relation that is a bit nicer for the rolling-up
// so we try to find that
OWLAnonymousIndividual root=null;
OWLAnonymousIndividual rootWithOneNamedRelation=null;
for (OWLAnonymousIndividual ind : component) {
if (specialOPEdges.containsKey(ind)) {
if (specialOPEdges.get(ind).size()<2) {
rootWithOneNamedRelation=ind;
}
}
else {
root=ind;
}
}
if (root==null&&rootWithOneNamedRelation==null) {
throw new IllegalArgumentException("Invalid input ontology: One of the trees in the forst of anomnymous individuals has no root that satisfies the criteria on roots (cf. OWL 2 Structural Specification and Functional-Style Syntax, Sec. 11.2).");
}
else if (rootWithOneNamedRelation!=null) {
componentsToRoots.put(component,rootWithOneNamedRelation);
}
else {
componentsToRoots.put(component,root);
}
}
return componentsToRoots;
}
protected Set> getComponents() {
Set> components=new HashSet<>();
if (nodes.isEmpty())
return components;
Set toProcess=nodes;
Set currentComponent;
List workQueue=new ArrayList<>();
Edge nodePlusPredecessor;
while (!toProcess.isEmpty()) {
currentComponent=new HashSet<>();
nodePlusPredecessor=new Edge(toProcess.iterator().next(),null);
workQueue.add(nodePlusPredecessor);
while (!workQueue.isEmpty()) {
nodePlusPredecessor=workQueue.remove(0);
currentComponent.add(nodePlusPredecessor.first);
// see whether there are any successors that we have to check
if (edges.containsKey(nodePlusPredecessor.first)) {
// add successors to the workQueue
for (OWLAnonymousIndividual ind : edges.get(nodePlusPredecessor.first)) {
if (nodePlusPredecessor.second==null||!(ind.getID().equals(nodePlusPredecessor.second.getID()))) {
// check for cycle
for (Edge pair : workQueue) {
if (pair.first==ind) {
throw new IllegalArgumentException("Invalid input ontology: The anonymous individuals cannot be arranged into a forest as required (cf. OWL 2 Structural Specification and Functional-Style Syntax, Sec. 11.2) because there is a cycle. ");
}
}
workQueue.add(new Edge(ind,nodePlusPredecessor.first));
}
}
}
}
components.add(currentComponent);
toProcess.removeAll(currentComponent);
}
return components;
}
@Override
public void visit(OWLClassAssertionAxiom axiom) {
if (axiom.getClassExpression().isOWLThing())
return;
OWLIndividual node=axiom.getIndividual();
if (!node.isAnonymous()) {
namedNodes.add(node.asOWLNamedIndividual());
}
else {
OWLAnonymousIndividual anon = node.asOWLAnonymousIndividual();
nodes.add(anon);
nodelLabels.computeIfAbsent(anon, x->new HashSet<>()).add(axiom.getClassExpression());
}
}
@Override
public void visit(OWLObjectPropertyAssertionAxiom axiom) {
OWLIndividual sub=axiom.getSubject();
OWLIndividual obj=axiom.getObject();
OWLObjectPropertyExpression ope=axiom.getProperty();
if (!sub.isAnonymous()&&!obj.isAnonymous()) {
return; // not interesting for the forest
}
else if ((!sub.isAnonymous()&&obj.isAnonymous())||(sub.isAnonymous()&&!obj.isAnonymous())) {
if (!sub.isAnonymous()&&obj.isAnonymous()) {
OWLIndividual tmp=sub;
sub=obj;
obj=tmp;
ope=ope.getInverseProperty();
}
OWLNamedIndividual named=obj.asOWLNamedIndividual();
OWLAnonymousIndividual unnamed=sub.asOWLAnonymousIndividual();
namedNodes.add(named);
nodes.add(unnamed);
if (specialOPEdges.containsKey(unnamed)) {
Map> specialEdges=specialOPEdges.get(unnamed);
if (specialEdges.containsKey(named)) {
specialEdges.get(named).add(ope);
}
else {
specialEdges=new HashMap<>();
Set label=new HashSet<>();
label.add(ope);
specialEdges.put(named,label);
specialOPEdges.put(unnamed,specialEdges);
}
}
else {
Map> specialEdge=new HashMap<>();
Set label=new HashSet<>();
label.add(ope);
specialEdge.put(named,label);
specialOPEdges.put(unnamed,specialEdge);
}
}
else {
// both sub and obj anonymous
OWLObjectProperty op;
if (ope.isAnonymous()) {
// inverse role
op=ope.getNamedProperty();
OWLIndividual tmp=sub;
sub=obj;
obj=tmp;
}
else {
op=ope.asOWLObjectProperty();
}
OWLAnonymousIndividual subAnon=sub.asOWLAnonymousIndividual();
OWLAnonymousIndividual objAnon=obj.asOWLAnonymousIndividual();
nodes.add(subAnon);
nodes.add(objAnon);
if ((edges.containsKey(subAnon)&&edges.get(subAnon).contains(objAnon))||(edges.containsKey(objAnon)&&edges.get(objAnon).contains(subAnon))) {
throw new IllegalArgumentException("Invalid input ontology: There are two object property assertions for the same anonymous individuals, "+"which is not allowed (see OWL 2 Syntax Sec 11.2). ");
}
if (edges.containsKey(subAnon)) {
edges.get(subAnon).add(objAnon);
}
else {
Set successors=new HashSet<>();
successors.add(objAnon);
edges.put(subAnon,successors);
}
if (edges.containsKey(objAnon)) {
edges.get(objAnon).add(subAnon);
}
else {
Set successors=new HashSet<>();
successors.add(subAnon);
edges.put(objAnon,successors);
}
edgeOPLabels.put(new Edge(subAnon,objAnon),op);
}
}
@Override
public void visit(OWLDataPropertyAssertionAxiom axiom) {
if (!axiom.getSubject().isAnonymous()) {
return; // not interesting for the anonymous individual forest
}
OWLAnonymousIndividual sub=axiom.getSubject().asOWLAnonymousIndividual();
nodes.add(sub);
OWLClassExpression c=factory.getOWLDataHasValue(axiom.getProperty(),axiom.getObject());
if (nodelLabels.containsKey(sub)) {
nodelLabels.get(sub).add(c);
}
else {
Set labels=new HashSet<>();
labels.add(c);
nodelLabels.put(sub,labels);
}
}
}
protected class Edge {
public final OWLAnonymousIndividual first;
public final OWLAnonymousIndividual second;
public Edge(OWLAnonymousIndividual first,OWLAnonymousIndividual second) {
this.first=first;
this.second=second;
}
@Override
public int hashCode() {
return 13+(3*(first!=null ? first.hashCode() : 0))+(7*(second!=null ? second.hashCode() : 0));
}
@Override
public boolean equals(Object o) {
if (o==this)
return true;
if (o==null||getClass()!=o.getClass())
return false;
Edge other=(Edge)o;
return this.first.equals(other.first)&&this.second.equals(other.second);
}
@Override
public String toString() {
return "("+first+", "+second+")";
}
}
}