Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor unsat object-checking to leverage object hierarchy, for reasoners that support that #1100

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed
- Speed up unsatisfiable object-property check on certain reasoners including HermiT [#1100]

## [1.9.3] - 2023-02-16

### Added
Expand Down
103 changes: 72 additions & 31 deletions robot-core/src/main/java/org/obolibrary/robot/ReasonerHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.obolibrary.robot.reason.InferredSubObjectPropertyAxiomGeneratorIncludingIndirect;
import org.semanticweb.owlapi.model.*;
import org.semanticweb.owlapi.model.parameters.Imports;
import org.semanticweb.owlapi.reasoner.InferenceType;
import org.semanticweb.owlapi.reasoner.OWLReasoner;
import org.semanticweb.owlapi.util.*;
import org.slf4j.Logger;
Expand All @@ -36,6 +37,72 @@ public class ReasonerHelper {
/** Logger. */
private static final Logger logger = LoggerFactory.getLogger(ReasonerHelper.class);

/**
* Check an ontology for unsatisfiable object properties.
*
* @param reasoner OWLReasoner being used.
* @return A set of unsatisfiable OWLObjectProperty objects (will be empty if none are found).
*/
public static Set<OWLObjectProperty> getUnsatisfiableObjectProperties(OWLReasoner reasoner) {
Set<OWLObjectProperty> unsatObjectProps = new HashSet<>();

if (reasoner
.getPrecomputableInferenceTypes()
.contains(InferenceType.OBJECT_PROPERTY_HIERARCHY)) {
// Fast object-unsat check
logger.info(
"Object-property precomputation is supported; using that to find unsatisfiable object properties...");
reasoner.precomputeInferences(InferenceType.OBJECT_PROPERTY_HIERARCHY);
Set<OWLObjectPropertyExpression> unsatObjPropExps =
reasoner.getBottomObjectPropertyNode().getEntitiesMinusBottom();
for (OWLObjectPropertyExpression uOPE : unsatObjPropExps) {
if (uOPE.isNamed()) {
unsatObjectProps.add(uOPE.asOWLObjectProperty());
}
}
} else {
// Have to do this the slow way
OWLOntology ont = reasoner.getRootOntology();
OWLOntologyManager manager = ont.getOWLOntologyManager();
OWLDataFactory dataFactory = manager.getOWLDataFactory();
OWLClass thing = dataFactory.getOWLThing();
Set<OWLAxiom> tempAxioms = new HashSet<>();
Map<OWLClass, OWLObjectProperty> probeFor = new HashMap<>();

for (OWLObjectProperty p : ont.getObjectPropertiesInSignature(Imports.INCLUDED)) {
UUID uuid = UUID.randomUUID();
IRI probeIRI = IRI.create(p.getIRI().toString() + "-" + uuid.toString());
OWLClass probe = dataFactory.getOWLClass(probeIRI);
probeFor.put(probe, p);
tempAxioms.add(dataFactory.getOWLDeclarationAxiom(probe));
tempAxioms.add(
dataFactory.getOWLSubClassOfAxiom(
probe, dataFactory.getOWLObjectSomeValuesFrom(p, thing)));
}
manager.addAxioms(ont, tempAxioms);
reasoner.flush();

Set<OWLClass> unsatisfiableProbeClasses =
reasoner.getUnsatisfiableClasses().getEntitiesMinusBottom();

// leave no trace
manager.removeAxioms(ont, tempAxioms);
reasoner.flush();

if (unsatisfiableProbeClasses.size() > 0) {
logger.error(
"There are {} unsatisfiable properties in the ontology.",
unsatisfiableProbeClasses.size());
for (OWLClass cls : unsatisfiableProbeClasses) {
OWLObjectProperty unsatP = probeFor.get(cls);
unsatObjectProps.add(unsatP);
logger.error(" unsatisfiable property: " + unsatP.getIRI());
}
}
}
return unsatObjectProps;
}

/**
* Validates ontology.
*
Expand Down Expand Up @@ -118,40 +185,14 @@ public static void validate(
throw new IncoherentTBoxException(unsatisfiableClasses);
}

// TODO: can this be done by checking for equivalence to bottomObjectProperty?
logger.info("Checking for unsatisfiable object properties...");

Set<OWLAxiom> tempAxioms = new HashSet<>();
Map<OWLClass, OWLObjectProperty> probeFor = new HashMap<>();
for (OWLObjectProperty p : ont.getObjectPropertiesInSignature(Imports.INCLUDED)) {
UUID uuid = UUID.randomUUID();
IRI probeIRI = IRI.create(p.getIRI().toString() + "-" + uuid.toString());
OWLClass probe = dataFactory.getOWLClass(probeIRI);
probeFor.put(probe, p);
tempAxioms.add(dataFactory.getOWLDeclarationAxiom(probe));
tempAxioms.add(
dataFactory.getOWLSubClassOfAxiom(
probe, dataFactory.getOWLObjectSomeValuesFrom(p, thing)));
}
manager.addAxioms(ont, tempAxioms);
reasoner.flush();

Set<OWLClass> unsatisfiableProbeClasses =
reasoner.getUnsatisfiableClasses().getEntitiesMinus(nothing);

// leave no trace
manager.removeAxioms(ont, tempAxioms);
reasoner.flush();
Set<OWLObjectProperty> unsatPs = getUnsatisfiableObjectProperties(reasoner);

if (unsatisfiableProbeClasses.size() > 0) {
logger.error(
"There are {} unsatisfiable properties in the ontology.",
unsatisfiableProbeClasses.size());
Set<OWLObjectProperty> unsatPs = new HashSet<>();
for (OWLClass cls : unsatisfiableProbeClasses) {
OWLObjectProperty unsatP = probeFor.get(cls);
unsatPs.add(unsatP);
logger.error(" unsatisfiable property: " + unsatP.getIRI());
if (unsatPs.size() > 0) {
logger.error("There are {} unsatisfiable properties in the ontology.", unsatPs.size());
for (OWLObjectProperty p : unsatPs) {
logger.error(" unsatisfiable property: " + p.getIRI());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isnt this log output redundant with the one in the end of getUnsatisfiableObjectProperties?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @matentzn - yes indeed. Fixed in a new commit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice :)

}
throw new IncoherentRBoxException(unsatPs);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import static org.junit.Assert.*;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import org.junit.Test;
import org.obolibrary.robot.exceptions.IncoherentRBoxException;
Expand Down Expand Up @@ -34,16 +36,21 @@ public class ReasonerHelperTest extends CoreTest {
public void testIncoherentRBox()
throws IOException, IncoherentTBoxException, InconsistentOntologyException {
OWLOntology ontology = loadOntology("/incoherent-rbox.owl");
OWLReasonerFactory reasonerFactory = new org.semanticweb.elk.owlapi.ElkReasonerFactory();
OWLReasoner reasoner = reasonerFactory.createReasoner(ontology);
boolean isCaughtException = false;
try {
ReasonerHelper.validate(reasoner);
List<OWLReasonerFactory> factories =
Arrays.asList(
new org.semanticweb.elk.owlapi.ElkReasonerFactory(),
new org.semanticweb.HermiT.ReasonerFactory());
for (OWLReasonerFactory reasonerFactory : factories) {
OWLReasoner reasoner = reasonerFactory.createReasoner(ontology);
boolean isCaughtException = false;
try {
ReasonerHelper.validate(reasoner);

} catch (IncoherentRBoxException e) {
isCaughtException = true;
} catch (IncoherentRBoxException e) {
isCaughtException = true;
}
assertTrue(isCaughtException);
}
assertTrue(isCaughtException);
}

/**
Expand All @@ -57,16 +64,21 @@ public void testIncoherentRBox()
public void testIncoherentTBox()
throws IOException, InconsistentOntologyException, IncoherentRBoxException {
OWLOntology ontology = loadOntology("/incoherent-tbox.owl");
OWLReasonerFactory reasonerFactory = new org.semanticweb.elk.owlapi.ElkReasonerFactory();
OWLReasoner reasoner = reasonerFactory.createReasoner(ontology);
boolean isCaughtException = false;
try {
ReasonerHelper.validate(reasoner);
List<OWLReasonerFactory> factories =
Arrays.asList(
new org.semanticweb.elk.owlapi.ElkReasonerFactory(),
new org.semanticweb.HermiT.ReasonerFactory());
for (OWLReasonerFactory reasonerFactory : factories) {
OWLReasoner reasoner = reasonerFactory.createReasoner(ontology);
boolean isCaughtException = false;
try {
ReasonerHelper.validate(reasoner);

} catch (IncoherentTBoxException e) {
isCaughtException = true;
} catch (IncoherentTBoxException e) {
isCaughtException = true;
}
assertTrue(isCaughtException);
}
assertTrue(isCaughtException);
}

/**
Expand Down