Skip to content

Commit 09d7b20

Browse files
committed
Revisit parents/children of terms to make sure none were missed
When using a limited reasoner (i.e. transitive, OWL micro/mini), some of the ancestors or descendants of a term may be missed. To compensate for that, do a few additional direct checks.
1 parent 6f62477 commit 09d7b20

File tree

3 files changed

+119
-8
lines changed

3 files changed

+119
-8
lines changed

src/ubic/basecode/ontology/jena/JenaUtils.java

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import com.hp.hpl.jena.util.iterator.Filter;
99
import com.hp.hpl.jena.util.iterator.UniqueExtendedIterator;
1010
import org.apache.commons.lang3.time.StopWatch;
11+
import org.slf4j.Logger;
12+
import org.slf4j.LoggerFactory;
1113

1214
import javax.annotation.Nullable;
1315
import java.util.*;
@@ -18,7 +20,25 @@
1820

1921
class JenaUtils {
2022

23+
protected static Logger log = LoggerFactory.getLogger( JenaUtils.class );
24+
2125
public static Collection<OntClass> getParents( OntModel model, Collection<OntClass> ontClasses, boolean direct, @Nullable Set<Restriction> additionalRestrictions ) {
26+
Collection<OntClass> parents = getParentsInternal( model, ontClasses, direct, additionalRestrictions );
27+
if ( shouldRevisit( parents, direct, model, additionalRestrictions ) ) {
28+
// if there are some missing direct parents, revisit the hierarchy
29+
Set<OntClass> parentsToRevisit = new HashSet<>( parents );
30+
while ( !parentsToRevisit.isEmpty() ) {
31+
log.debug( "Revisiting the direct parents of {} terms...", parentsToRevisit.size() );
32+
parentsToRevisit = new HashSet<>( getParentsInternal( model, parentsToRevisit, true, additionalRestrictions ) );
33+
parentsToRevisit.removeAll( parents );
34+
log.debug( "Found {} missed parents.", parentsToRevisit.size() );
35+
parents.addAll( parentsToRevisit );
36+
}
37+
}
38+
return parents;
39+
}
40+
41+
private static Collection<OntClass> getParentsInternal( OntModel model, Collection<OntClass> ontClasses, boolean direct, @Nullable Set<Restriction> additionalRestrictions ) {
2242
ontClasses = ontClasses.stream()
2343
.map( t -> t.inModel( model ) )
2444
.filter( t -> t.canAs( OntClass.class ) )
@@ -65,6 +85,22 @@ public static Collection<OntClass> getParents( OntModel model, Collection<OntCla
6585
}
6686

6787
public static Collection<OntClass> getChildren( OntModel model, Collection<OntClass> terms, boolean direct, @Nullable Set<Restriction> additionalRestrictions ) {
88+
Collection<OntClass> children = getChildrenInternal( model, terms, direct, additionalRestrictions );
89+
if ( shouldRevisit( children, direct, model, additionalRestrictions ) ) {
90+
// if there are some missing direct children, revisit the hierarchy
91+
Set<OntClass> childrenToRevisit = new HashSet<>( children );
92+
while ( !childrenToRevisit.isEmpty() ) {
93+
log.debug( "Revisiting the direct parents of {} terms...", childrenToRevisit.size() );
94+
childrenToRevisit = new HashSet<>( JenaUtils.getChildrenInternal( model, childrenToRevisit, true, additionalRestrictions ) );
95+
childrenToRevisit.removeAll( children );
96+
log.debug( "Found {} missed children.", childrenToRevisit.size() );
97+
children.addAll( childrenToRevisit );
98+
}
99+
}
100+
return children;
101+
}
102+
103+
public static Collection<OntClass> getChildrenInternal( OntModel model, Collection<OntClass> terms, boolean direct, @Nullable Set<Restriction> additionalRestrictions ) {
68104
terms = terms.stream()
69105
.map( t -> t.inModel( model ) )
70106
.filter( t -> t.canAs( OntClass.class ) )
@@ -103,6 +139,42 @@ public static Collection<OntClass> getChildren( OntModel model, Collection<OntCl
103139
return result;
104140
}
105141

142+
/**
143+
* Check if a set of terms should be revisited to find missing parents or children.
144+
* <p>
145+
* To be considered, the model must have a reasoner that lacks one of {@code rdfs:subClassOf}, {@code owl:subValuesFrom}
146+
* or {@code owl:allValuesFrom} inference capabilities. If a model has no reasoner, revisiting is not desirable and
147+
* thus this will return false.
148+
* <p>
149+
* If direct is false or terms is empty, it's not worth revisiting.
150+
*/
151+
private static boolean shouldRevisit( Collection<OntClass> terms, boolean direct, OntModel model, @Nullable Set<Restriction> additionalRestrictions ) {
152+
return !direct
153+
&& !terms.isEmpty()
154+
&& additionalRestrictions != null
155+
&& model.getReasoner() != null
156+
&& ( !supportsSubClassInference( model ) || !supportsAdditionalRestrictionsInference( model ) );
157+
}
158+
159+
/**
160+
* Check if an ontology model supports inference of {@code rdfs:subClassOf}.
161+
*/
162+
public static boolean supportsSubClassInference( OntModel model ) {
163+
return model.getReasoner() != null
164+
&& model.getReasoner().supportsProperty( model.getProfile().SUB_CLASS_OF() );
165+
}
166+
167+
/**
168+
* Checks if an ontology model supports inference of with additional restrictions.
169+
* <p>
170+
* This covers {@code owl:subValuesFrom} and {@code owl:allValuesFrom} restrictions.
171+
*/
172+
public static boolean supportsAdditionalRestrictionsInference( OntModel model ) {
173+
return model.getReasoner() != null
174+
&& model.getReasoner().supportsProperty( model.getProfile().SOME_VALUES_FROM() )
175+
&& model.getReasoner().supportsProperty( model.getProfile().ALL_VALUES_FROM() );
176+
}
177+
106178
public static Resource getRestrictionValue( Restriction r ) {
107179
if ( r.isSomeValuesFromRestriction() ) {
108180
return r.asSomeValuesFromRestriction().getSomeValuesFrom();

test/ubic/basecode/ontology/OntologyTermTest.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,16 @@
1414
*/
1515
package ubic.basecode.ontology;
1616

17-
import com.hp.hpl.jena.vocabulary.OWL2;
1817
import org.apache.commons.lang3.StringUtils;
19-
import org.junit.BeforeClass;
2018
import org.junit.Test;
2119
import org.slf4j.Logger;
2220
import org.slf4j.LoggerFactory;
2321
import ubic.basecode.ontology.model.OntologyTerm;
24-
import ubic.basecode.ontology.providers.*;
25-
import ubic.basecode.ontology.search.OntologySearchException;
22+
import ubic.basecode.ontology.providers.CellLineOntologyService;
23+
import ubic.basecode.ontology.providers.DiseaseOntologyService;
24+
import ubic.basecode.ontology.providers.NIFSTDOntologyService;
2625

27-
import java.io.IOException;
2826
import java.io.InputStream;
29-
import java.util.Arrays;
3027
import java.util.Collection;
3128
import java.util.zip.GZIPInputStream;
3229

@@ -111,7 +108,7 @@ public void testGetChildrenHasProperPart() throws Exception {
111108
assertTrue( c.contains( t1 ) );
112109

113110
Collection<OntologyTerm> c2 = t.getChildren( false );
114-
assertEquals( 6, c2.size() );
111+
assertEquals( 7, c2.size() );
115112
OntologyTerm t2 = s.getTerm( "http://ontology.neuinfo.org/NIF/BiomaterialEntities/NIF-GrossAnatomy.owl#birnlex_4037" );
116113
assertNotNull( t2 );
117114
assertTrue( c.contains( t2 ) );
@@ -199,7 +196,7 @@ public void testGetParentsHasProperPart() throws Exception {
199196
// Diencephalon [birnlex_1503] x
200197

201198
Collection<OntologyTerm> parents2 = t.getParents( false );
202-
assertEquals( 7, parents2.size() );
199+
assertEquals( 14, parents2.size() );
203200

204201
// does not includes 'continuant' and 'independent continuant' or parents of those terms.
205202
assertFalse( parents2.contains( s.getTerm( "http://ontology.neuinfo.org/NIF/BiomaterialEntities/NIF-GrossAnatomy.owl#birnlex_1503" ) ) );
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package ubic.basecode.ontology.jena;
2+
3+
import com.hp.hpl.jena.ontology.OntModel;
4+
import com.hp.hpl.jena.ontology.OntModelSpec;
5+
import com.hp.hpl.jena.rdf.model.ModelFactory;
6+
import org.junit.Test;
7+
8+
import static org.junit.Assert.*;
9+
import static ubic.basecode.ontology.jena.JenaUtils.supportsAdditionalRestrictionsInference;
10+
import static ubic.basecode.ontology.jena.JenaUtils.supportsSubClassInference;
11+
12+
public class JenaUtilsTest {
13+
@Test
14+
public void testInferenceCapabilities() {
15+
OntModel model;
16+
17+
model = ModelFactory.createOntologyModel( OntModelSpec.OWL_MEM );
18+
assertNull( model.getReasoner() );
19+
assertFalse( supportsSubClassInference( model ) );
20+
assertFalse( supportsAdditionalRestrictionsInference( model ) );
21+
22+
model = ModelFactory.createOntologyModel( OntModelSpec.OWL_MEM_TRANS_INF );
23+
assertNotNull( model.getReasoner() );
24+
assertTrue( supportsSubClassInference( model ) );
25+
assertFalse( supportsAdditionalRestrictionsInference( model ) );
26+
27+
model = ModelFactory.createOntologyModel( OntModelSpec.OWL_MEM_MICRO_RULE_INF );
28+
assertNotNull( model.getReasoner() );
29+
assertTrue( supportsSubClassInference( model ) );
30+
assertFalse( supportsAdditionalRestrictionsInference( model ) );
31+
32+
model = ModelFactory.createOntologyModel( OntModelSpec.OWL_MEM_MINI_RULE_INF );
33+
assertNotNull( model.getReasoner() );
34+
assertTrue( supportsSubClassInference( model ) );
35+
assertFalse( supportsAdditionalRestrictionsInference( model ) );
36+
37+
model = ModelFactory.createOntologyModel( OntModelSpec.OWL_MEM_RULE_INF );
38+
assertNotNull( model.getReasoner() );
39+
assertTrue( supportsSubClassInference( model ) );
40+
assertTrue( supportsAdditionalRestrictionsInference( model ) );
41+
}
42+
}

0 commit comments

Comments
 (0)