Skip to content

Commit d40cac5

Browse files
authored
Merge pull request #64 from balhoff/faster-query
Avoid slowdown for existential fillers with large numbers of subclasses.
2 parents 088af7d + 644ec6a commit d40cac5

File tree

2 files changed

+39
-14
lines changed

2 files changed

+39
-14
lines changed

src/main/scala/org/renci/relationgraph/Main.scala

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import zio._
1717
import zio.blocking._
1818
import zio.stream._
1919

20-
import java.io.{File, FileOutputStream, OutputStream}
20+
import java.io.{File, FileOutputStream}
2121
import java.lang.{Runtime => JRuntime}
2222
import java.nio.charset.StandardCharsets
2323
import java.security.MessageDigest
@@ -46,12 +46,13 @@ object Main extends ZCaseApp[Config] {
4646
whelkOntology = Bridge.ontologyToAxioms(ontology)
4747
_ <- ZIO.succeed(scribe.info("Running reasoner"))
4848
whelk = Reasoner.assert(whelkOntology)
49+
indexedWhelk = IndexedReasonerState(whelk)
4950
_ <- ZIO.succeed(scribe.info("Done running reasoner"))
5051
_ <- ZIO.fail(new Exception("Ontology is incoherent; please correct unsatisfiable classes.")).when(isIncoherent(whelk))
5152
_ <- effectBlockingIO(rdfWriter.triple(Triple.create(NodeFactory.createBlankNode("redundant"), RDFType, OWLOntology)))
5253
.when(config.mode == OWLMode)
5354
start <- ZIO.succeed(System.currentTimeMillis())
54-
triplesStream = computeRelations(ontology, whelk, specifiedProperties, config.outputSubclasses.bool, config.reflexiveSubclasses.bool, config.equivalenceAsSubclass.bool, config.mode)
55+
triplesStream = computeRelations(ontology, indexedWhelk, specifiedProperties, config.outputSubclasses.bool, config.reflexiveSubclasses.bool, config.equivalenceAsSubclass.bool, config.mode)
5556
_ <- triplesStream.foreach {
5657
case TriplesGroup(triples) => ZIO.effect(triples.foreach(rdfWriter.triple))
5758
}
@@ -62,11 +63,11 @@ object Main extends ZCaseApp[Config] {
6263
program.exitCode
6364
}
6465

65-
def computeRelations(ontology: OWLOntology, whelk: ReasonerState, specifiedProperties: Set[AtomicConcept], outputSubclasses: Boolean, reflexiveSubclasses: Boolean, equivalenceAsSubclass: Boolean, mode: Config.OutputMode): UStream[TriplesGroup] = {
66-
val classes = classHierarchy(whelk)
66+
def computeRelations(ontology: OWLOntology, whelk: IndexedReasonerState, specifiedProperties: Set[AtomicConcept], outputSubclasses: Boolean, reflexiveSubclasses: Boolean, equivalenceAsSubclass: Boolean, mode: Config.OutputMode): UStream[TriplesGroup] = {
67+
val classes = classHierarchy(whelk.state)
6768
val properties = propertyHierarchy(ontology)
6869
val classesTasks = if (outputSubclasses) {
69-
allClasses(ontology).map(c => ZIO.succeed(processSuperclasses(c, whelk, reflexiveSubclasses, equivalenceAsSubclass)))
70+
allClasses(ontology).map(c => ZIO.succeed(processSuperclasses(c, whelk.state, reflexiveSubclasses, equivalenceAsSubclass)))
7071
} else Stream.empty
7172
val streamZ = for {
7273
queue <- Queue.unbounded[Restriction]
@@ -124,7 +125,7 @@ object Main extends ZCaseApp[Config] {
124125
} yield ()
125126
}
126127

127-
def processRestrictionAndExtendQueue(restriction: Restriction, properties: Hierarchy, classes: Hierarchy, whelk: ReasonerState, mode: Config.OutputMode, descendProperties: Boolean, queue: Queue[Restriction], activeRestrictions: Ref[Int], seenRef: Ref[Map[AtomicConcept, Set[AtomicConcept]]]): UIO[TriplesGroup] = {
128+
def processRestrictionAndExtendQueue(restriction: Restriction, properties: Hierarchy, classes: Hierarchy, whelk: IndexedReasonerState, mode: Config.OutputMode, descendProperties: Boolean, queue: Queue[Restriction], activeRestrictions: Ref[Int], seenRef: Ref[Map[AtomicConcept, Set[AtomicConcept]]]): UIO[TriplesGroup] = {
128129
val triples = processRestriction(restriction, whelk, mode)
129130
for {
130131
directFillerSubclassesRestrictions <- if (triples.redundant.nonEmpty) seenRef.modify { seen =>
@@ -153,7 +154,7 @@ object Main extends ZCaseApp[Config] {
153154
} yield triples
154155
}
155156

156-
def processRestriction(restriction: Restriction, whelk: ReasonerState, mode: Config.OutputMode): TriplesGroup = {
157+
def processRestriction(restriction: Restriction, whelk: IndexedReasonerState, mode: Config.OutputMode): TriplesGroup = {
157158
val subclasses = queryExistentialSubclasses(restriction, whelk)
158159
if (subclasses.nonEmpty) {
159160
val predicate = NodeFactory.createURI(restriction.property.id)
@@ -166,12 +167,17 @@ object Main extends ZCaseApp[Config] {
166167
} else TriplesGroup.empty
167168
}
168169

169-
def queryExistentialSubclasses(restriction: Restriction, whelk: ReasonerState): Set[AtomicConcept] = {
170-
val queryConcept = AtomicConcept(s"${restriction.property.id}${restriction.filler.id}")
170+
// This is a faster way to compute these than the standard Whelk algorithm
171+
def queryExistentialSubclasses(restriction: Restriction, whelk: IndexedReasonerState): Set[AtomicConcept] = {
171172
val er = ExistentialRestriction(restriction.property, restriction.filler)
172-
val axioms = Set(ConceptInclusion(queryConcept, er), ConceptInclusion(er, queryConcept))
173-
val updatedWhelk = Reasoner.assert(axioms, whelk)
174-
updatedWhelk.closureSubsBySuperclass(queryConcept).collect { case x: AtomicConcept => x } - queryConcept - Bottom
173+
val rs = whelk.reverseRoleHierarchy.getOrElse(er.role, Set.empty)
174+
val cs = whelk.state.closureSubsBySuperclass.getOrElse(er.concept, Set.empty)
175+
val validTargets = cs.intersect(whelk.allTargets)
176+
(for {
177+
target <- validTargets
178+
(r, es) <- whelk.linksByTargetList.getOrElse(target, Map.empty)
179+
if rs(r)
180+
} yield es.iterator).flatten.to(Set).collect { case x: AtomicConcept => x } - Bottom
175181
}
176182

177183
def classHierarchy(reasoner: ReasonerState): Hierarchy = {
@@ -253,4 +259,22 @@ object Main extends ZCaseApp[Config] {
253259

254260
}
255261

262+
final case class IndexedReasonerState(state: ReasonerState) {
263+
264+
val negativeExistentials: Set[ExistentialRestriction] = state.negExistsMapByConcept.flatMap(_._2).to(Set)
265+
266+
val reverseRoleHierarchy: Map[Role, Set[Role]] = (for {
267+
(r, ss) <- state.hier.toList
268+
s <- ss
269+
} yield {
270+
s -> r
271+
}).groupMapReduce(_._1)(e => Set(e._2))(_ ++ _)
272+
273+
val allTargets: Set[Concept] = state.linksByTarget.keySet
274+
275+
val linksByTargetList: Map[Concept, List[(Role, List[Concept])]] =
276+
state.linksByTarget.view.mapValues(_.to(List)).toMap
277+
278+
}
279+
256280
}

src/test/scala/org/renci/relationgraph/TestRelationGraph.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package org.renci.relationgraph
22

33
import org.apache.jena.graph.{Node, NodeFactory, Triple}
44
import org.geneontology.whelk.{Bridge, Reasoner}
5-
import org.renci.relationgraph.Main.TriplesGroup
5+
import org.renci.relationgraph.Main.{IndexedReasonerState, TriplesGroup}
66
import org.semanticweb.owlapi.apibinding.OWLManager
77
import zio._
88
import zio.test.Assertion._
@@ -23,7 +23,8 @@ object TestRelationGraph extends DefaultRunnableSpec {
2323
ontology <- ZIO.effect(manager.loadOntologyFromOntologyDocument(this.getClass.getResourceAsStream("materialize_test.ofn")))
2424
whelkOntology = Bridge.ontologyToAxioms(ontology)
2525
whelk = Reasoner.assert(whelkOntology)
26-
resultsStream = Main.computeRelations(ontology, whelk, Set.empty, true, false, false, Config.RDFMode)
26+
indexedWhelk = IndexedReasonerState(whelk)
27+
resultsStream = Main.computeRelations(ontology, indexedWhelk, Set.empty, true, false, false, Config.RDFMode)
2728
results <- resultsStream.runCollect
2829
triples <- ZIO.fromOption(results.reduceOption((left, right) => TriplesGroup(left.redundant ++ right.redundant)))
2930
TriplesGroup(redundant) = triples

0 commit comments

Comments
 (0)