From b0b532bb8666bef7ae91bfab9a81524a453cb8a3 Mon Sep 17 00:00:00 2001 From: ignazio Date: Tue, 25 Sep 2018 10:00:24 +0100 Subject: [PATCH] Fix Inverses cause false property equivalences #3 --- .../HermiT/structural/OWLAxioms.java | 5 +- .../HermiT/structural/OWLNormalization.java | 2 + .../ObjectPropertyInclusionManager.java | 74 +++++++++++-------- .../HermiT/reasoner/InverseAnonymousTest.java | 58 +++++++++++++++ 4 files changed, 109 insertions(+), 30 deletions(-) create mode 100644 src/test/java/org/semanticweb/HermiT/reasoner/InverseAnonymousTest.java diff --git a/src/main/java/org/semanticweb/HermiT/structural/OWLAxioms.java b/src/main/java/org/semanticweb/HermiT/structural/OWLAxioms.java index 72d45aad..bf817fe1 100644 --- a/src/main/java/org/semanticweb/HermiT/structural/OWLAxioms.java +++ b/src/main/java/org/semanticweb/HermiT/structural/OWLAxioms.java @@ -19,8 +19,10 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import org.semanticweb.owlapi.model.OWLClass; @@ -51,7 +53,8 @@ public class OWLAxioms { final Set m_irreflexiveObjectProperties =new HashSet<>(); final Set m_asymmetricObjectProperties =new HashSet<>(); final Collection> m_dataPropertyInclusions =new ArrayList<>(); - final Collection> m_disjointDataProperties =new ArrayList<>(); + final Collection> m_disjointDataProperties =new ArrayList<>(); + final Map> m_explicitInverses = new HashMap<>(); final Collection m_facts =new HashSet<>(); final Set m_hasKeys =new HashSet<>(); /** contains custom datatypes from DatatypeDefinition axioms*/ diff --git a/src/main/java/org/semanticweb/HermiT/structural/OWLNormalization.java b/src/main/java/org/semanticweb/HermiT/structural/OWLNormalization.java index e2d4497d..1724f797 100644 --- a/src/main/java/org/semanticweb/HermiT/structural/OWLNormalization.java +++ b/src/main/java/org/semanticweb/HermiT/structural/OWLNormalization.java @@ -537,6 +537,8 @@ public void visit(OWLDisjointObjectPropertiesAxiom axiom) { public void visit(OWLInverseObjectPropertiesAxiom axiom) { OWLObjectPropertyExpression first=axiom.getFirstProperty(); OWLObjectPropertyExpression second=axiom.getSecondProperty(); + m_axioms.m_explicitInverses.computeIfAbsent(first, x->new HashSet<>()).add(second); + m_axioms.m_explicitInverses.computeIfAbsent(second, x->new HashSet<>()).add(first); addInclusion(first,second.getInverseProperty()); addInclusion(second,first.getInverseProperty()); m_axioms.m_objectPropertiesOccurringInOWLAxioms.add(first.getNamedProperty()); diff --git a/src/main/java/org/semanticweb/HermiT/structural/ObjectPropertyInclusionManager.java b/src/main/java/org/semanticweb/HermiT/structural/ObjectPropertyInclusionManager.java index 3e1d0a11..9180721e 100644 --- a/src/main/java/org/semanticweb/HermiT/structural/ObjectPropertyInclusionManager.java +++ b/src/main/java/org/semanticweb/HermiT/structural/ObjectPropertyInclusionManager.java @@ -50,7 +50,7 @@ public class ObjectPropertyInclusionManager { */ public ObjectPropertyInclusionManager(OWLAxioms axioms) { m_automataByProperty=new HashMap<>(); - createAutomata(m_automataByProperty,axioms.m_complexObjectPropertyExpressions,axioms.m_simpleObjectPropertyInclusions,axioms.m_complexObjectPropertyInclusions); + createAutomata(m_automataByProperty,axioms.m_complexObjectPropertyExpressions,axioms.m_simpleObjectPropertyInclusions,axioms.m_complexObjectPropertyInclusions, axioms.m_explicitInverses); } /** * @param factory factory @@ -184,10 +184,11 @@ else if (classExpression instanceof OWLObjectHasSelf) { } } } - protected void createAutomata(Map automataByProperty,Set complexObjectPropertyExpressions,Collection> simpleObjectPropertyInclusions,Collection complexObjectPropertyInclusions) { + protected void createAutomata(Map automataByProperty,Set complexObjectPropertyExpressions,Collection> simpleObjectPropertyInclusions,Collection complexObjectPropertyInclusions, + Map> explicitInverses) { Map> equivalentPropertiesMap=findEquivalentProperties(simpleObjectPropertyInclusions); Set symmetricObjectProperties=findSymmetricProperties(simpleObjectPropertyInclusions); - Map> inversePropertiesMap=buildInversePropertiesMap(simpleObjectPropertyInclusions); + Map> inversePropertiesMap=buildInversePropertiesMap(simpleObjectPropertyInclusions, explicitInverses); Graph propertyDependencyGraph=buildPropertyOrdering(simpleObjectPropertyInclusions,complexObjectPropertyInclusions,equivalentPropertiesMap); checkForRegularity(propertyDependencyGraph,equivalentPropertiesMap); @@ -242,38 +243,53 @@ private static Set findSymmetricProperties(Collecti } return symmetricProperties; } - protected Map> buildInversePropertiesMap(Collection> simpleObjectPropertyInclusions) { - Map> inversePropertiesMap=new HashMap<>(); - for (List inclusion : simpleObjectPropertyInclusions) + protected Map> buildInversePropertiesMap(Collection> simpleObjectPropertyInclusions, Map> explicitInverses) { + Map> inversePropertiesMap=new HashMap<>(explicitInverses); + // Both inclusions must be found for an inverse relation to be built: + // a subproperty of inverse(b) + // b subproperty of inverse(a) + // implies + // a inverseOf b + + // inverse(b) subproperty of a + // inverse(a) subproperty of b + // implies + // a inverseOf b (commutative, the order is unimportant) + List> inclusionCandidates=new ArrayList<>(); + for (List inclusion : simpleObjectPropertyInclusions) { + if(isInverseOf( inclusion.get(0)) ^ isInverseOf(inclusion.get(1))) { + inclusionCandidates.add(inclusion); + } + } + for (List inclusion : inclusionCandidates) { + OWLObjectPropertyExpression inverse0 = inclusion.get(0).getInverseProperty(); + OWLObjectPropertyExpression inverse1 = inclusion.get(1).getInverseProperty(); if (inclusion.get(1) instanceof OWLObjectInverseOf) { - Set inverseProperties=inversePropertiesMap.get(inclusion.get(0)); - if (inverseProperties==null) - inverseProperties=new HashSet<>(); - inverseProperties.add(inclusion.get(1).getInverseProperty()); - inversePropertiesMap.put(inclusion.get(0),inverseProperties); - - inverseProperties=inversePropertiesMap.get(inclusion.get(1).getInverseProperty()); - if (inverseProperties==null) - inverseProperties=new HashSet<>(); - inverseProperties.add(inclusion.get(0)); - inversePropertiesMap.put(inclusion.get(1).getInverseProperty(),inverseProperties); - + if(contains(inclusionCandidates, inverse1, inverse0)) { + inversePropertiesMap.computeIfAbsent(inclusion.get(0), x->new HashSet<>()).add(inverse1); + inversePropertiesMap.computeIfAbsent(inverse1, x->new HashSet<>()).add(inclusion.get(0)); + } } else if (inclusion.get(0) instanceof OWLObjectInverseOf) { - Set inverseProperties=inversePropertiesMap.get(inclusion.get(1)); - if (inverseProperties==null) - inverseProperties=new HashSet<>(); - inverseProperties.add(inclusion.get(0).getInverseProperty()); - inversePropertiesMap.put(inclusion.get(1),inverseProperties); - - inverseProperties=inversePropertiesMap.get(inclusion.get(0).getInverseProperty()); - if (inverseProperties==null) - inverseProperties=new HashSet<>(); - inverseProperties.add(inclusion.get(1)); - inversePropertiesMap.put(inclusion.get(0).getInverseProperty(),inverseProperties); + if(contains(inclusionCandidates, inverse0, inverse1)) { + inversePropertiesMap.computeIfAbsent(inclusion.get(1), x->new HashSet<>()).add(inverse0); + inversePropertiesMap.computeIfAbsent(inverse0, x->new HashSet<>()).add(inclusion.get(1)); + } } + } return inversePropertiesMap; } + private static boolean isInverseOf(OWLObjectPropertyExpression e) { + return e instanceof OWLObjectInverseOf; + } + private static boolean contains(List> list, OWLObjectPropertyExpression o1, OWLObjectPropertyExpression o2 ) { + for (List l : list) { + if(l.get(0).equals(o1) && l.get(1).equals(o2)) { + return true; + } + } + return false; + } protected Map> findEquivalentProperties(Collection> simpleObjectPropertyInclusions) { Graph propertyDependencyGraph=new Graph<>(); Map> equivalentObjectPropertiesMapping=new HashMap<>(); diff --git a/src/test/java/org/semanticweb/HermiT/reasoner/InverseAnonymousTest.java b/src/test/java/org/semanticweb/HermiT/reasoner/InverseAnonymousTest.java new file mode 100644 index 00000000..a843fa5f --- /dev/null +++ b/src/test/java/org/semanticweb/HermiT/reasoner/InverseAnonymousTest.java @@ -0,0 +1,58 @@ +package org.semanticweb.HermiT.reasoner; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.junit.Test; +import org.semanticweb.HermiT.ReasonerFactory; +import org.semanticweb.owlapi.apibinding.OWLManager; +import org.semanticweb.owlapi.io.StringDocumentSource; +import org.semanticweb.owlapi.model.*; +import org.semanticweb.owlapi.reasoner.OWLReasoner; +import org.semanticweb.owlapi.reasoner.OWLReasonerFactory; + +public class InverseAnonymousTest { + String input = "Prefix: : \n" + " Ontology: \n" + + " ObjectProperty: hasBigPart\n" + " SubPropertyOf: hasPart, inverse (partOf)\n" + + " ObjectProperty: hasPart\n" + " InverseOf: partOf\n" + + " ObjectProperty: partOf\n" + " InverseOf: hasPart"; + OWLOntologyManager man = OWLManager.createOWLOntologyManager(); + OWLDataFactory df = man.getOWLDataFactory(); + OWLObjectProperty bigPart = df.getOWLObjectProperty(IRI.create("http://example.org/hasBigPart")); + OWLObjectProperty partOf = df.getOWLObjectProperty(IRI.create("http://example.org/partOf")); + OWLObjectProperty hasPart = df.getOWLObjectProperty(IRI.create("http://example.org/hasPart")); + OWLObjectPropertyExpression invPartOf = df.getOWLObjectInverseOf(partOf); + OWLObjectPropertyExpression invHasPart = df.getOWLObjectInverseOf(hasPart); + Set expectedHasPart = new HashSet<>(Arrays.asList(invPartOf, hasPart)); + Set expectedPartOf = new HashSet<>(Arrays.asList(invHasPart, partOf)); + Set expectedBigPart = new HashSet<>(Arrays.asList(bigPart)); + + protected void assertExpectedEquivalencies(OWLReasoner reasoner) { + assertEquals(expectedBigPart, reasoner.getEquivalentObjectProperties(bigPart).getEntities()); + assertEquals(expectedHasPart, reasoner.getEquivalentObjectProperties(hasPart).getEntities()); + assertEquals(expectedPartOf, reasoner.getEquivalentObjectProperties(partOf).getEntities()); + } + + protected OWLReasoner reason(OWLOntology ont) { + OWLReasoner reasoner = new ReasonerFactory().createReasoner(ont); + return reasoner; + } + + @Test + public void shouldFindBigPartSingletonWithoutTransitiveCharacteristic() throws OWLOntologyCreationException { + OWLOntology ont = man.loadOntologyFromOntologyDocument(new StringDocumentSource(input)); + OWLReasoner reasoner = reason(ont); + assertExpectedEquivalencies(reasoner); + } + + @Test + public void shouldFindBigPartSingletonWithTransitiveCharacteristic() throws OWLOntologyCreationException { + String in = input + "\n Characteristics: Transitive"; + OWLOntology ont = man.loadOntologyFromOntologyDocument(new StringDocumentSource(in)); + OWLReasoner reasoner = new ReasonerFactory().createReasoner(ont); + assertExpectedEquivalencies(reasoner); + } +}