Skip to content

Commit eb2bd72

Browse files
authored
Output triples with individuals as subjects. (#111)
1 parent 16a8911 commit eb2bd72

3 files changed

Lines changed: 60 additions & 41 deletions

File tree

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ final case class Config(ontologyFile: String,
1212
outputSubclasses: BoolValue = FalseValue,
1313
reflexiveSubclasses: BoolValue = TrueValue,
1414
equivalenceAsSubclass: BoolValue = TrueValue,
15+
outputClasses: BoolValue = TrueValue,
16+
outputIndividuals: BoolValue = FalseValue,
1517
disableOwlNothing: BoolValue = FalseValue,
1618
verbose: Boolean = false)
1719

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

Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ object Main extends ZCaseApp[Config] {
6262
_ <- effectBlockingIO(rdfWriter.triple(Triple.create(NodeFactory.createBlankNode("redundant"), RDFType, OWLOntology)))
6363
.when(config.mode == OWLMode)
6464
start <- ZIO.succeed(System.currentTimeMillis())
65-
triplesStream = computeRelations(ontology, indexedWhelk, specifiedProperties, config.outputSubclasses.bool, config.reflexiveSubclasses.bool, config.equivalenceAsSubclass.bool, config.mode)
65+
triplesStream = computeRelations(ontology, indexedWhelk, specifiedProperties, config.outputSubclasses.bool, config.reflexiveSubclasses.bool, config.equivalenceAsSubclass.bool, config.outputClasses.bool, config.outputIndividuals.bool, config.mode)
6666
_ <- triplesStream.foreach {
6767
case TriplesGroup(triples) => ZIO.effect(triples.foreach(rdfWriter.triple))
6868
}
@@ -78,18 +78,18 @@ object Main extends ZCaseApp[Config] {
7878
}
7979
}
8080

81-
def computeRelations(ontology: OWLOntology, whelk: IndexedReasonerState, specifiedProperties: Set[AtomicConcept], outputSubclasses: Boolean, reflexiveSubclasses: Boolean, equivalenceAsSubclass: Boolean, mode: Config.OutputMode): UStream[TriplesGroup] = {
81+
def computeRelations(ontology: OWLOntology, whelk: IndexedReasonerState, specifiedProperties: Set[AtomicConcept], outputSubclasses: Boolean, reflexiveSubclasses: Boolean, equivalenceAsSubclass: Boolean, outputClasses: Boolean, outputIndividuals: Boolean, mode: Config.OutputMode): UStream[TriplesGroup] = {
8282
val classes = classHierarchy(whelk.state)
8383
val properties = propertyHierarchy(ontology)
8484
val classesTasks = if (outputSubclasses) {
85-
allClasses(ontology).map(c => ZIO.succeed(processSuperclasses(c, whelk.state, reflexiveSubclasses, equivalenceAsSubclass)))
85+
allClasses(ontology).map(c => ZIO.succeed(processSubclasses(c, whelk.state, reflexiveSubclasses, equivalenceAsSubclass, outputClasses, outputIndividuals)))
8686
} else Stream.empty
8787
val streamZ = for {
8888
queue <- Queue.unbounded[Restriction]
8989
activeRestrictions <- Ref.make(0)
9090
seenRef <- Ref.make(Map.empty[AtomicConcept, Set[AtomicConcept]])
9191
_ <- traverse(specifiedProperties, properties, classes, queue, activeRestrictions, seenRef)
92-
restrictionsStream = Stream.fromQueue(queue).map(r => processRestrictionAndExtendQueue(r, properties, classes, whelk, mode, specifiedProperties.isEmpty, queue, activeRestrictions, seenRef))
92+
restrictionsStream = Stream.fromQueue(queue).map(r => processRestrictionAndExtendQueue(r, properties, classes, whelk, mode, specifiedProperties.isEmpty, outputClasses, outputIndividuals, queue, activeRestrictions, seenRef))
9393
allTasks = classesTasks ++ restrictionsStream
9494
} yield allTasks.mapMParUnordered(JRuntime.getRuntime.availableProcessors)(identity)
9595
Stream.unwrap(streamZ)
@@ -141,8 +141,8 @@ object Main extends ZCaseApp[Config] {
141141
} yield ()
142142
}
143143

144-
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] = {
145-
val triples = processRestriction(restriction, whelk, mode)
144+
def processRestrictionAndExtendQueue(restriction: Restriction, properties: Hierarchy, classes: Hierarchy, whelk: IndexedReasonerState, mode: Config.OutputMode, descendProperties: Boolean, outputClasses: Boolean, outputIndividuals: Boolean, queue: Queue[Restriction], activeRestrictions: Ref[Int], seenRef: Ref[Map[AtomicConcept, Set[AtomicConcept]]]): UIO[TriplesGroup] = {
145+
val triples = processRestriction(restriction, whelk, mode, outputClasses, outputIndividuals)
146146
for {
147147
directFillerSubclassesRestrictions <- if (triples.redundant.nonEmpty) seenRef.modify { seen =>
148148
val propertyConcept = AtomicConcept(restriction.property.id)
@@ -170,21 +170,30 @@ object Main extends ZCaseApp[Config] {
170170
} yield triples
171171
}
172172

173-
def processRestriction(restriction: Restriction, whelk: IndexedReasonerState, mode: Config.OutputMode): TriplesGroup = {
174-
val subclasses = queryExistentialSubclasses(restriction, whelk)
175-
if (subclasses.nonEmpty) {
176-
val predicate = NodeFactory.createURI(restriction.property.id)
177-
val target = NodeFactory.createURI(restriction.filler.id)
178-
val outputTriples = mode match {
179-
case RDFMode => subclasses.map(sc => Triple.create(NodeFactory.createURI(sc.id), predicate, target))
180-
case OWLMode => subclasses.flatMap(sc => owlTriples(NodeFactory.createURI(sc.id), predicate, target))
173+
def processRestriction(restriction: Restriction, whelk: IndexedReasonerState, mode: Config.OutputMode, outputClasses: Boolean, outputIndividuals: Boolean): TriplesGroup = {
174+
val subConcepts = queryExistentialSubclasses(restriction, whelk)
175+
val subclasses = if (outputClasses) (subConcepts - Bottom).collect { case AtomicConcept(id) => id } else Set.empty[String]
176+
val instances = if (outputIndividuals) subConcepts.collect { case Nominal(Individual(id)) => id } else Set.empty[String]
177+
val predicate = NodeFactory.createURI(restriction.property.id)
178+
val target = NodeFactory.createURI(restriction.filler.id)
179+
val subclassTriples = subclasses.flatMap { id =>
180+
mode match {
181+
case RDFMode => Set(Triple.create(NodeFactory.createURI(id), predicate, target))
182+
case OWLMode => owlTriples(NodeFactory.createURI(id), predicate, target, RDFSSubClassOf)
181183
}
182-
TriplesGroup(outputTriples)
183-
} else TriplesGroup.empty
184+
}
185+
val instanceTriples = instances.flatMap { id =>
186+
mode match {
187+
case RDFMode => Set(Triple.create(NodeFactory.createURI(id), predicate, target))
188+
case OWLMode => owlTriples(NodeFactory.createURI(id), predicate, target, RDFType)
189+
}
190+
}
191+
val outputTriples = subclassTriples ++ instanceTriples
192+
TriplesGroup(outputTriples)
184193
}
185194

186195
// This is a faster way to compute these than the standard Whelk algorithm
187-
def queryExistentialSubclasses(restriction: Restriction, whelk: IndexedReasonerState): Set[AtomicConcept] = {
196+
def queryExistentialSubclasses(restriction: Restriction, whelk: IndexedReasonerState): Set[Concept] = {
188197
val er = ExistentialRestriction(restriction.property, restriction.filler)
189198
val rs = whelk.reverseRoleHierarchy.getOrElse(er.role, Set.empty)
190199
val cs = whelk.state.closureSubsBySuperclass.getOrElse(er.concept, Set.empty)
@@ -193,7 +202,7 @@ object Main extends ZCaseApp[Config] {
193202
target <- validTargets
194203
(r, es) <- whelk.linksByTargetList.getOrElse(target, Map.empty)
195204
if rs(r)
196-
} yield es.iterator).flatten.to(Set).collect { case x: AtomicConcept => x } - Bottom
205+
} yield es.iterator).flatten.to(Set)
197206
}
198207

199208
def classHierarchy(reasoner: ReasonerState): Hierarchy = {
@@ -223,37 +232,45 @@ object Main extends ZCaseApp[Config] {
223232
classHierarchy(whelk)
224233
}
225234

226-
def processSuperclasses(cls: OWLClass, whelk: ReasonerState, reflexiveSubclasses: Boolean, equivalenceAsSubclass: Boolean): TriplesGroup = {
227-
val subject = NodeFactory.createURI(cls.getIRI.toString)
235+
def processSubclasses(cls: OWLClass, whelk: ReasonerState, reflexiveSubclasses: Boolean, equivalenceAsSubclass: Boolean, outputClasses: Boolean, outputIndividuals: Boolean): TriplesGroup = {
236+
val obj = NodeFactory.createURI(cls.getIRI.toString)
228237
val concept = AtomicConcept(cls.getIRI.toString)
229-
val allSuperclasses = (whelk.closureSubsBySubclass.getOrElse(concept, Set.empty) - BuiltIn.Top)
230-
.collect { case ac @ AtomicConcept(_) => ac }
231-
if (allSuperclasses(BuiltIn.Bottom)) TriplesGroup.empty //unsatisfiable
232-
else {
238+
val subConcepts = (whelk.closureSubsBySuperclass.getOrElse(concept, Set.empty) - BuiltIn.Bottom)
239+
val individualsTriples = if (outputIndividuals) {
240+
subConcepts.collect { case Nominal(Individual(id)) =>
241+
Triple.create(NodeFactory.createURI(id), RDFType, obj)
242+
}
243+
} else Set.empty[Triple]
244+
val classesTriples = if (outputClasses) {
245+
val allSubclasses = subConcepts.collect { case ac @ AtomicConcept(_) => ac }
233246
val (equivs, _) = whelk.directlySubsumedBy(concept)
234-
val adjustedEquivs = if (reflexiveSubclasses) equivs + concept else equivs - concept
235-
val equivalentClassTriples = if (equivalenceAsSubclass)
236-
adjustedEquivs.map(c => Triple.create(subject, RDFSSubClassOf, NodeFactory.createURI(c.id)))
237-
else
238-
adjustedEquivs.map(c => Triple.create(subject, OWLEquivalentClass, NodeFactory.createURI(c.id)))
239-
val adjustedSuperclasses = if (reflexiveSubclasses) allSuperclasses + concept else allSuperclasses - concept
240-
val outputTriples = if (equivalenceAsSubclass)
241-
adjustedSuperclasses.map(c => Triple.create(subject, RDFSSubClassOf, NodeFactory.createURI(c.id)))
247+
val unsatisfiable = equivs(BuiltIn.Bottom)
248+
if (unsatisfiable) Set.empty[Triple]
242249
else {
243-
val superclassesMinusEquiv = adjustedSuperclasses -- adjustedEquivs
244-
superclassesMinusEquiv.map(c => Triple.create(subject, RDFSSubClassOf, NodeFactory.createURI(c.id))) ++
245-
equivalentClassTriples
250+
val adjustedEquivs = if (reflexiveSubclasses) equivs + concept else equivs - concept
251+
val equivalentClassTriples = if (equivalenceAsSubclass)
252+
adjustedEquivs.map(c => Triple.create(NodeFactory.createURI(c.id), RDFSSubClassOf, obj))
253+
else
254+
adjustedEquivs.map(c => Triple.create(NodeFactory.createURI(c.id), OWLEquivalentClass, obj))
255+
val adjustedSubclasses = if (reflexiveSubclasses) allSubclasses + concept else allSubclasses - concept
256+
if (equivalenceAsSubclass)
257+
adjustedSubclasses.map(c => Triple.create(NodeFactory.createURI(c.id), RDFSSubClassOf, obj))
258+
else {
259+
val subclassesMinusEquiv = adjustedSubclasses -- adjustedEquivs
260+
subclassesMinusEquiv.map(c => Triple.create(NodeFactory.createURI(c.id), RDFSSubClassOf, obj)) ++ equivalentClassTriples
261+
}
246262
}
247-
TriplesGroup(outputTriples)
248-
}
263+
} else Set.empty[Triple]
264+
val outputTriples = classesTriples ++ individualsTriples
265+
TriplesGroup(outputTriples)
249266
}
250267

251-
def owlTriples(subj: Node, pred: Node, obj: Node): Set[Triple] = {
268+
def owlTriples(subj: Node, pred: Node, obj: Node, relation: Node): Set[Triple] = {
252269
val hash = MessageDigest.getInstance("SHA-256").digest(s"$subj$pred$obj".getBytes(StandardCharsets.UTF_8))
253270
val id = Base64.getEncoder.encodeToString(hash)
254271
val restrictionNode = NodeFactory.createBlankNode(id)
255272
Set(
256-
Triple.create(subj, RDFSSubClassOf, restrictionNode),
273+
Triple.create(subj, relation, restrictionNode),
257274
Triple.create(restrictionNode, RDFType, OWLRestriction),
258275
Triple.create(restrictionNode, OWLOnProperty, pred),
259276
Triple.create(restrictionNode, OWLSomeValuesFrom, obj)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ object TestRelationGraph extends DefaultRunnableSpec {
2626
whelkOntology = Bridge.ontologyToAxioms(ontology)
2727
whelk = Reasoner.assert(whelkOntology)
2828
indexedWhelk = IndexedReasonerState(whelk)
29-
resultsStream = Main.computeRelations(ontology, indexedWhelk, Set.empty, true, false, false, Config.RDFMode)
29+
resultsStream = Main.computeRelations(ontology, indexedWhelk, Set.empty, true, false, false, true, false, Config.RDFMode)
3030
results <- resultsStream.runCollect
3131
triples <- ZIO.fromOption(results.reduceOption((left, right) => TriplesGroup(left.redundant ++ right.redundant)))
3232
TriplesGroup(redundant) = triples
@@ -45,7 +45,7 @@ object TestRelationGraph extends DefaultRunnableSpec {
4545
whelkOntology = Bridge.ontologyToAxioms(ontology)
4646
whelk = Reasoner.assert(whelkOntology)
4747
indexedWhelk = IndexedReasonerState(whelk)
48-
resultsStream = Main.computeRelations(ontology, indexedWhelk, Set.empty, true, false, false, Config.RDFMode)
48+
resultsStream = Main.computeRelations(ontology, indexedWhelk, Set.empty, true, false, false, true, false, Config.RDFMode)
4949
results <- resultsStream.runCollect
5050
triples <- ZIO.fromOption(results.reduceOption((left, right) => TriplesGroup(left.redundant ++ right.redundant)))
5151
TriplesGroup(redundant) = triples

0 commit comments

Comments
 (0)