Skip to content

Commit 045f306

Browse files
committed
Include sub-properties in the inference
Ensure that sub-properties are included when matching terms via additional restrictions. Not all ontologies declare relevant sub-properties, so we enumerate the most common one from RO.
1 parent a08c608 commit 045f306

10 files changed

Lines changed: 110 additions & 38 deletions

File tree

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

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
package ubic.basecode.ontology.jena;
2121

2222
import com.hp.hpl.jena.ontology.*;
23-
import com.hp.hpl.jena.rdf.model.*;
23+
import com.hp.hpl.jena.rdf.model.ModelFactory;
24+
import com.hp.hpl.jena.rdf.model.NodeIterator;
25+
import com.hp.hpl.jena.rdf.model.Property;
26+
import com.hp.hpl.jena.rdf.model.Resource;
2427
import com.hp.hpl.jena.rdfxml.xmlinput.ARPErrorNumbers;
2528
import com.hp.hpl.jena.rdfxml.xmlinput.ParseException;
2629
import com.hp.hpl.jena.reasoner.ReasonerFactory;
@@ -67,12 +70,31 @@ public abstract class AbstractOntologyService implements OntologyService {
6770
/**
6871
* Properties through which propagation is allowed for {@link #getParents(Collection, boolean, boolean)}}
6972
*/
70-
private static final Set<String> DEFAULT_ADDITIONAL_PROPERTIES;
73+
private static final Set<Property> DEFAULT_ADDITIONAL_PROPERTIES;
7174

7275
static {
7376
DEFAULT_ADDITIONAL_PROPERTIES = new HashSet<>();
74-
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.partOf.getURI() );
75-
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.properPartOf.getURI() );
77+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.partOf );
78+
// all those are sub-properties of partOf, but some ontologies might not have them
79+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.activeIngredientIn );
80+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.boundingLayerOf );
81+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.branchingPartOf );
82+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.determinedBy );
83+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.ends );
84+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.isSubsequenceOf );
85+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.isEndSequenceOf );
86+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.isStartSequenceOf );
87+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.lumenOf );
88+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.luminalSpaceOf );
89+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.mainStemOf );
90+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.memberOf );
91+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.occurrentPartOf );
92+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.skeletonOf );
93+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.starts );
94+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.subclusterOf );
95+
// used by some older ontologies
96+
//noinspection deprecation
97+
DEFAULT_ADDITIONAL_PROPERTIES.add( RO.properPartOf );
7698
}
7799

78100
/**
@@ -87,7 +109,7 @@ public abstract class AbstractOntologyService implements OntologyService {
87109
private boolean processImports = true;
88110
private boolean searchEnabled = true;
89111
private Set<String> excludedWordsFromStemming = Collections.emptySet();
90-
private Set<String> additionalPropertyUris = DEFAULT_ADDITIONAL_PROPERTIES;
112+
private Set<String> additionalPropertyUris = DEFAULT_ADDITIONAL_PROPERTIES.stream().map( Property::getURI ).collect( Collectors.toSet() );
91113

92114
@Override
93115
public String getName() {
@@ -183,8 +205,6 @@ private synchronized void initialize( @Nullable InputStream stream, boolean forc
183205
String ontologyUrl = getOntologyUrl();
184206
String ontologyName = getOntologyName();
185207
String cacheName = getCacheName();
186-
Set<Property> additionalProperties = this.additionalPropertyUris.stream()
187-
.map( ResourceFactory::createProperty ).collect( Collectors.toSet() );
188208
LanguageLevel languageLevel = this.languageLevel;
189209
InferenceMode inferenceMode = this.inferenceMode;
190210
boolean processImports = this.processImports;
@@ -242,9 +262,9 @@ private synchronized void initialize( @Nullable InputStream stream, boolean forc
242262
return;
243263

244264
// compute additional restrictions
245-
Set<Restriction> additionalRestrictions = model.listRestrictions()
246-
.filterKeep( new RestrictionWithOnPropertyFilter( additionalProperties ) )
247-
.toSet();
265+
Set<Property> additionalProperties = additionalPropertyUris.stream().map( model::getProperty ).collect( Collectors.toSet() );
266+
Set<Restriction> additionalRestrictions = JenaUtils.listRestrictionsOnProperties( model, additionalProperties, true ).toSet();
267+
248268

249269
// indexing is lengthy, don't bother if we're interrupted
250270
if ( Thread.currentThread().isInterrupted() )
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package ubic.basecode.ontology.jena;
2+
3+
import com.hp.hpl.jena.rdf.model.Property;
4+
import com.hp.hpl.jena.rdf.model.ResourceFactory;
5+
6+
public class IAO {
7+
8+
public static final Property alternativeLabel = ResourceFactory.createProperty( "http://purl.obolibrary.org/obo/IAO_0000118" );
9+
}

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

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package ubic.basecode.ontology.jena;
22

3-
import com.hp.hpl.jena.ontology.ConversionException;
4-
import com.hp.hpl.jena.ontology.OntClass;
5-
import com.hp.hpl.jena.ontology.OntModel;
6-
import com.hp.hpl.jena.ontology.Restriction;
3+
import com.hp.hpl.jena.ontology.*;
74
import com.hp.hpl.jena.rdf.model.*;
85
import com.hp.hpl.jena.util.iterator.ExtendedIterator;
96
import com.hp.hpl.jena.util.iterator.Filter;
@@ -193,7 +190,6 @@ public static Resource getRestrictionValue( Restriction r ) {
193190
}
194191
}
195192

196-
197193
/**
198194
* Use to pretty-print a RDFNode
199195
*/
@@ -232,4 +228,28 @@ public static <T extends RDFNode> Optional<T> as( RDFNode resource, Class<T> cla
232228
return Optional.empty();
233229
}
234230
}
231+
232+
/**
233+
* List all restrictions in the given model on any of the given properties.
234+
*/
235+
public static ExtendedIterator<Restriction> listRestrictionsOnProperties( OntModel model, Set<? extends Property> props, boolean includeSubProperties ) {
236+
if ( includeSubProperties ) {
237+
Set<Property> allProps = new HashSet<>( props );
238+
for ( Property p : props ) {
239+
Property property = p.inModel( model );
240+
// include sub-properties for inference
241+
if ( property.canAs( OntProperty.class ) ) {
242+
OntProperty op = property.as( OntProperty.class );
243+
ExtendedIterator<? extends OntProperty> it = op.listSubProperties( false );
244+
while ( it.hasNext() ) {
245+
OntProperty sp = it.next();
246+
allProps.add( sp );
247+
log.info( "Inferred {} from {}", sp, property );
248+
}
249+
}
250+
}
251+
props = allProps;
252+
}
253+
return model.listRestrictions().filterKeep( new RestrictionWithOnPropertyFilter( props ) );
254+
}
235255
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ private static Property property( String name ) {
1919
public static final Property hasBroadSynonym = property( "hasBroadSynonm" );
2020
public static final Property hasNarrowSynonym = property( "hasNarrowSynonym" );
2121
public static final Property hasRelatedSynonym = property( "hasRelatedSynonym" );
22-
public static final Property alternativeLabel = ResourceFactory.createProperty( "http://purl.obolibrary.org/obo/IAO_0000118" );
2322
public static final Resource ObsoleteClass = ResourceFactory.createResource( "http://www.geneontology.org/formats/oboInOwl#ObsoleteClass" );
2423
public static final Property ObsoleteProperty = property( "ObsoleteProperty" );
2524
}

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class OntologyIndexer {
6969
private static final Logger log = LoggerFactory.getLogger( OntologyIndexer.class );
7070

7171
/**
72-
* THose are build-in fields that are always indexed.
72+
* Those are build-in fields that are always indexed.
7373
*/
7474
private static final String
7575
ID_FIELD = "_ID",
@@ -107,7 +107,7 @@ public boolean isAnalyzed() {
107107
DEFAULT_INDEXABLE_PROPERTIES.add( new IndexableProperty( OBO.hasBroadSynonym, true ) );
108108
DEFAULT_INDEXABLE_PROPERTIES.add( new IndexableProperty( OBO.hasNarrowSynonym, true ) );
109109
DEFAULT_INDEXABLE_PROPERTIES.add( new IndexableProperty( OBO.hasRelatedSynonym, true ) );
110-
DEFAULT_INDEXABLE_PROPERTIES.add( new IndexableProperty( OBO.alternativeLabel, true ) );
110+
DEFAULT_INDEXABLE_PROPERTIES.add( new IndexableProperty( IAO.alternativeLabel, true ) );
111111
}
112112

113113
/**
@@ -125,7 +125,7 @@ public static SearchIndex getSubjectIndex( String name, Set<String> excludedFrom
125125
*/
126126
@Nullable
127127
public static SearchIndex getSubjectIndex( String name, Collection<IndexableProperty> indexableProperties, Set<String> excludedFromStemming ) {
128-
log.debug( "Loading index: {}", name );
128+
log.debug( "Loading index for {}...", name );
129129
try {
130130
// we do not put this in the try-with-open because we want these to *stay* open
131131
FSDirectory directory = FSDirectory.open( getIndexPath( name ).toFile() );
@@ -136,10 +136,9 @@ public static SearchIndex getSubjectIndex( String name, Collection<IndexableProp
136136
if ( !IndexReader.indexExists( directoryStd ) ) {
137137
return null;
138138
}
139-
log.info( "Loading index at {} and {}", directory, directoryStd );
140139
return openIndex( directory, directoryStd, indexableProperties, excludedFromStemming );
141140
} catch ( IOException e ) {
142-
log.warn( "Index for {} could not be read: {}", name, e.getMessage(), e );
141+
log.warn( "Index for {} could not be opened.", name, e );
143142
return null;
144143
}
145144
}

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

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,33 @@
55

66
class RO {
77

8+
private static final String NS = "http://purl.obolibrary.org/obo/";
9+
10+
private static Property property( String localName ) {
11+
return ResourceFactory.createProperty( NS + localName );
12+
}
13+
14+
public static final Property partOf = property( "BFO_0000050" );
15+
public static final Property activeIngredientIn = property( "RO_0002249" );
16+
public static final Property boundingLayerOf = property( "RO_0002007" );
17+
public static final Property branchingPartOf = property( "RO_0002380" );
18+
public static final Property determinedBy = property( "RO_0002507" );
19+
public static final Property ends = property( "RO_0002229" );
20+
public static final Property isSubsequenceOf = property( "RO_0002525" );
21+
public static final Property isEndSequenceOf = property( "RO_0002519" );
22+
public static final Property isStartSequenceOf = property( "RO_0002517" );
23+
public static final Property lumenOf = property( "RO_0002571" );
24+
public static final Property luminalSpaceOf = property( "RO_0002572" );
25+
public static final Property mainStemOf = property( "RO_0002381" );
26+
public static final Property memberOf = property( "RO_0002350" );
27+
public static final Property occurrentPartOf = property( "RO_0002012" );
28+
public static final Property skeletonOf = property( "RO_0002576" );
29+
public static final Property starts = property( "RO_0002223" );
30+
public static final Property subclusterOf = property( "RO_0015003" );
31+
832
/**
9-
* This is actually part of RO, see
33+
* This term is still used in older ontologies.
1034
*/
11-
public static final Property partOf = ResourceFactory.createProperty( "http://purl.obolibrary.org/obo/BFO_0000050" );
35+
@Deprecated
1236
public static final Property properPartOf = ResourceFactory.createProperty( "http://www.obofoundry.org/ro/ro.owl#proper_part_of" );
1337
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
* Filter that retain only the restrictions on any of the given properties.
1111
*/
1212
class RestrictionWithOnPropertyFilter extends Filter<Restriction> {
13-
private final Set<Property> properties;
13+
private final Set<? extends Property> properties;
1414

15-
public RestrictionWithOnPropertyFilter( Set<Property> properties ) {
15+
public RestrictionWithOnPropertyFilter( Set<? extends Property> properties ) {
1616
this.properties = properties;
1717
}
1818

src/ubic/basecode/ontology/jena/package-info.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
*
2+
* Implementation of {@link ubic.basecode.ontology.providers.OntologyService} using Apache Jena.
33
*/
44
@ParametersAreNonnullByDefault
55
package ubic.basecode.ontology.jena;

test/ubic/basecode/ontology/providers/GenericOntologyServiceTest.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
package ubic.basecode.ontology.providers;
2121

22+
import org.assertj.core.api.Assertions;
2223
import org.junit.Test;
2324
import ubic.basecode.ontology.AbstractOntologyTest;
2425
import ubic.basecode.ontology.model.OntologyTerm;
@@ -80,10 +81,10 @@ public void testWithoutOntologyCacheDir() {
8081
new GenericOntologyService( "foo", resource.toString(), false, false )
8182
.initialize( true, true );
8283
} );
83-
assertTrue( e.getMessage().matches( "No cache directory is set for foo \\[file:.+], cannot force indexing." ) );
84+
Assertions.assertThat( e )
85+
.hasMessageMatching( "No cache directory is set for foo.+, cannot force indexing\\." );
8486
} finally {
85-
Configuration.setString( "ontology.cache.dir", prevCacheDir );
86-
Configuration.setString( "ontology.index.dir", prevIndexDir );
87+
Configuration.reset();
8788
}
8889
}
8990
}

test/ubic/basecode/ontology/providers/UberonOntologyServiceTest.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public void testGetParentsFromMultipleTerms() {
5757
OntologyTerm brain = uberon.getTerm( "http://purl.obolibrary.org/obo/UBERON_0000955" );
5858
OntologyTerm liver = uberon.getTerm( "http://purl.obolibrary.org/obo/UBERON_0002107" );
5959
Collection<OntologyTerm> children = uberon.getParents( Arrays.asList( brain, liver ), false, true );
60-
assertEquals( 30, children.size() );
60+
assertEquals( 41, children.size() );
6161
assertFalse( children.contains( uberon.getTerm( OWL2.Nothing.getURI() ) ) );
6262
}
6363

@@ -66,7 +66,7 @@ public void testGetParentsHasPart() {
6666
OntologyTerm t = uberon.getTerm( "http://purl.obolibrary.org/obo/UBERON_0000955" );
6767
assertNotNull( t );
6868
Collection<OntologyTerm> parents = t.getParents( true );
69-
assertEquals( 3, parents.size() );
69+
assertEquals( 4, parents.size() );
7070
// does not contain itself
7171
assertFalse( parents.contains( t ) );
7272
// via subclass
@@ -81,9 +81,9 @@ public void testGetParentsHasPart() {
8181
public void testGetChildrenHasPart() {
8282
OntologyTerm t = uberon.getTerm( "http://purl.obolibrary.org/obo/UBERON_0000955" );
8383
assertNotNull( t );
84-
assertEquals( 76, t.getChildren( true ).size() );
84+
assertEquals( 81, t.getChildren( true ).size() );
8585
Collection<OntologyTerm> children = t.getChildren( false );
86-
assertEquals( 1496, children.size() );
86+
assertEquals( 1995, children.size() );
8787
// via subclass of, insect adult brain
8888
assertTrue( children.contains( uberon.getTerm( "http://purl.obolibrary.org/obo/UBERON_6003624" ) ) );
8989
// via part of, nucleus of brain
@@ -97,22 +97,22 @@ public void testGetChildrenFromMultipleTerms() {
9797
OntologyTerm brain = uberon.getTerm( "http://purl.obolibrary.org/obo/UBERON_0000955" );
9898
OntologyTerm liver = uberon.getTerm( "http://purl.obolibrary.org/obo/UBERON_0002107" );
9999
Collection<OntologyTerm> children = uberon.getChildren( Arrays.asList( brain, liver ), false, true );
100-
assertEquals( 1562, children.size() );
100+
assertEquals( 2077, children.size() );
101101
}
102102

103103
@Test
104104
public void testGetChildrenFromMultipleTermsWithSearch() throws OntologySearchException {
105105
Collection<OntologySearchResult<OntologyTerm>> terms = uberon.findTerm( "brain", 500 );
106106
Collection<OntologyTerm> matches = uberon.getChildren( terms.stream().map( OntologySearchResult::getResult ).collect( Collectors.toSet() ), false, true );
107-
assertEquals( 1870, matches.size() );
107+
assertEquals( 2684, matches.size() );
108108
}
109109

110110
@Test
111111
public void testFindTerm() throws OntologySearchException {
112-
assertEquals( 123, uberon.findTerm( "brain", 500 ).size() );
113-
assertEquals( 128, uberon.findTerm( "brain", 500, true ).size() );
112+
assertEquals( 98, uberon.findTerm( "brain", 500 ).size() );
113+
assertEquals( 103, uberon.findTerm( "brain", 500, true ).size() );
114114
OntologySearchResult<OntologyTerm> firstResult = uberon.findTerm( "brain", 500 ).iterator().next();
115115
assertNotNull( firstResult );
116-
assertEquals( 2.8577, firstResult.getScore(), 0.0001 );
116+
assertEquals( 1.5367, firstResult.getScore(), 0.0001 );
117117
}
118118
}

0 commit comments

Comments
 (0)