11package org .renci .relationgraph
22
3- import java .io .{File , FileOutputStream , OutputStream }
4- import java .lang .{Runtime => JRuntime }
5- import java .nio .charset .StandardCharsets
6- import java .security .MessageDigest
7- import java .util .Base64
83import caseapp ._
94import org .apache .jena .graph .{Node , NodeFactory , Triple }
105import org .apache .jena .riot .RDFFormat
@@ -21,6 +16,11 @@ import zio._
2116import zio .blocking ._
2217import zio .stream ._
2318
19+ import java .io .{File , FileOutputStream , OutputStream }
20+ import java .lang .{Runtime => JRuntime }
21+ import java .nio .charset .StandardCharsets
22+ import java .security .MessageDigest
23+ import java .util .Base64
2424import scala .io .Source
2525import scala .jdk .CollectionConverters ._
2626
@@ -33,7 +33,6 @@ object Main extends ZCaseApp[Config] {
3333 private val OWLOnProperty = OWL2 .onProperty.asNode
3434 private val OWLSomeValuesFrom = OWL2 .someValuesFrom.asNode
3535 private val OWLOntology = OWL2 .Ontology .asNode
36- private val df = OWLManager .getOWLDataFactory
3736
3837 override def run (config : Config , arg : RemainingArgs ): ZIO [ZEnv , Nothing , ExitCode ] = {
3938 val ontologyFile = new File (config.ontologyFile)
@@ -48,8 +47,8 @@ object Main extends ZCaseApp[Config] {
4847 val program = streamsManaged.use {
4948 case (nonredundantRDFWriter, redundantRDFWriter) =>
5049 for {
51- fileProperties <- config.propertiesFile.map(readPropertiesFile).getOrElse(ZIO .succeed(Set .empty[OWLObjectProperty ]))
52- specifiedProperties = fileProperties ++ config.property.map(prop => df.getOWLObjectProperty( IRI .create( prop) )).to(Set )
50+ fileProperties <- config.propertiesFile.map(readPropertiesFile).getOrElse(ZIO .succeed(Set .empty[AtomicConcept ]))
51+ specifiedProperties = fileProperties ++ config.property.map(prop => AtomicConcept ( prop)).to(Set )
5352 manager <- ZIO .effect(OWLManager .createOWLOntologyManager())
5453 ontology <- ZIO .effect(manager.loadOntologyFromOntologyDocument(ontologyFile))
5554 whelkOntology = Bridge .ontologyToAxioms(ontology)
@@ -68,8 +67,8 @@ object Main extends ZCaseApp[Config] {
6867 queue <- Queue .unbounded[Restriction ]
6968 activeRestrictions <- Ref .make(0 )
7069 seenRef <- Ref .make(Map .empty[AtomicConcept , Set [AtomicConcept ]])
71- _ <- traverse(properties, classes, whelk, config.mode , queue, activeRestrictions, seenRef)
72- restrictionsStream = Stream .fromQueue(queue).map(r => processRestrictionAndExtendQueue(r, classes, whelk, config.mode, queue, activeRestrictions, seenRef))
70+ _ <- traverse(specifiedProperties, properties, classes , queue, activeRestrictions, seenRef)
71+ restrictionsStream = Stream .fromQueue(queue).map(r => processRestrictionAndExtendQueue(r, properties, classes, whelk, config.mode, specifiedProperties.isEmpty , queue, activeRestrictions, seenRef))
7372 allTasks = classesTasks ++ restrictionsStream
7473 processed = allTasks.mapMParUnordered(JRuntime .getRuntime.availableProcessors)(identity)
7574 _ <- processed.foreach {
@@ -86,9 +85,9 @@ object Main extends ZCaseApp[Config] {
8685 program.exitCode
8786 }
8887
89- def readPropertiesFile (file : String ): ZIO [Blocking , Throwable , Set [OWLObjectProperty ]] =
88+ def readPropertiesFile (file : String ): ZIO [Blocking , Throwable , Set [AtomicConcept ]] =
9089 effectBlocking(Source .fromFile(file, " utf-8" )).bracketAuto { source =>
91- effectBlocking(source.getLines().map(_.trim).filter(_.nonEmpty).map(line => df.getOWLObjectProperty( IRI .create( line) )).to(Set ))
90+ effectBlocking(source.getLines().map(_.trim).filter(_.nonEmpty).map(line => AtomicConcept ( line)).to(Set ))
9291 }
9392
9493 def createStreamRDF (output : OutputStream ): Managed [Throwable , StreamRDF ] =
@@ -102,27 +101,20 @@ object Main extends ZCaseApp[Config] {
102101
103102 def allClasses (ont : OWLOntology ): ZStream [Any , Nothing , OWLClass ] = Stream .fromIterable(ont.getClassesInSignature(Imports .INCLUDED ).asScala.to(Set ) - OWLThing - OWLNothing )
104103
105- def traverse (properties : Hierarchy , classes : Hierarchy , reasoner : ReasonerState , mode : Config .OutputMode , queue : Queue [Restriction ], activeRestrictions : Ref [Int ], seenRef : Ref [Map [AtomicConcept , Set [AtomicConcept ]]]): UIO [Unit ] = {
106- ZIO .foreachPar_(properties.subclasses.getOrElse(Top , Set .empty) - Bottom ) { subprop =>
107- traverseProperty(subprop, properties, classes, reasoner, mode, queue, activeRestrictions, seenRef)
104+ def traverse (specifiedProperties : Set [AtomicConcept ], properties : Hierarchy , classes : Hierarchy , queue : Queue [Restriction ], activeRestrictions : Ref [Int ], seenRef : Ref [Map [AtomicConcept , Set [AtomicConcept ]]]): UIO [Unit ] = {
105+ val descendProperties = specifiedProperties.isEmpty
106+ val queryProperties = if (descendProperties) properties.subclasses.getOrElse(Top , Set .empty) - Bottom else specifiedProperties
107+ ZIO .foreachPar_(queryProperties) { subprop =>
108+ traverseProperty(subprop, classes, queue, activeRestrictions, seenRef)
108109 }
109110 }
110111
111- def traverseProperty (property : AtomicConcept , properties : Hierarchy , classes : Hierarchy , whelk : ReasonerState , mode : Config .OutputMode , queue : Queue [Restriction ], activeRestrictions : Ref [Int ], seenRef : Ref [Map [AtomicConcept , Set [AtomicConcept ]]]): UIO [Unit ] = {
112- val propertyHasResults = hasSubClasses(property, whelk)
113- val restrictions = if (propertyHasResults) {
114- (classes.subclasses.getOrElse(Top , Set .empty) - Bottom ).map(filler => Restriction (Role (property.id), filler))
115- } else Set .empty[Restriction ]
112+ def traverseProperty (property : AtomicConcept , classes : Hierarchy , queue : Queue [Restriction ], activeRestrictions : Ref [Int ], seenRef : Ref [Map [AtomicConcept , Set [AtomicConcept ]]]): UIO [Unit ] = {
113+ val restrictions = (classes.subclasses.getOrElse(Top , Set .empty) - Bottom ).map(filler => Restriction (Role (property.id), filler))
116114 for {
117- subProperties <- seenRef.modify { seen =>
118- val subProperties = properties.subclasses.getOrElse(property, Set .empty) - Bottom
115+ _ <- seenRef.update { seen =>
119116 val seenForThisProperty = seen.getOrElse(property, Set .empty) ++ restrictions.map(_.filler)
120- val updatedSeen = seen.updated(property, seenForThisProperty)
121- val unseenProperties = subProperties.filterNot(updatedSeen.keySet)
122- (unseenProperties, updatedSeen ++ unseenProperties.map(p => p -> Set .empty[AtomicConcept ]).toMap)
123- }
124- _ <- ZIO .foreachPar_(subProperties) { subProp =>
125- traverseProperty(subProp, properties, classes, whelk, mode, queue, activeRestrictions, seenRef)
117+ seen.updated(property, seenForThisProperty)
126118 }
127119 _ <- activeRestrictions.update(current => current + restrictions.size)
128120 _ <- queue.offerAll(restrictions).unit
@@ -131,15 +123,7 @@ object Main extends ZCaseApp[Config] {
131123 } yield ()
132124 }
133125
134- def hasSubClasses (property : AtomicConcept , whelk : ReasonerState ): Boolean = {
135- val queryConcept = AtomicConcept (s " ${property.id}${Top .id}" )
136- val restriction = ExistentialRestriction (Role (property.id), Top )
137- val axioms = Set (ConceptInclusion (queryConcept, restriction), ConceptInclusion (restriction, queryConcept))
138- val updatedWhelk = Reasoner .assert(axioms, whelk)
139- (updatedWhelk.closureSubsBySuperclass.getOrElse(queryConcept, Set .empty) - Bottom ).nonEmpty
140- }
141-
142- def processRestrictionAndExtendQueue (restriction : Restriction , classes : Hierarchy , whelk : ReasonerState , mode : Config .OutputMode , queue : Queue [Restriction ], activeRestrictions : Ref [Int ], seenRef : Ref [Map [AtomicConcept , Set [AtomicConcept ]]]): UIO [TriplesGroup ] = {
126+ 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 ] =
143127 for {
144128 triples <- ZIO .effectTotal(processRestriction(restriction, whelk, mode))
145129 directFillerSubclassesRestrictions <- if (triples.redundant.nonEmpty) seenRef.modify { seen =>
@@ -149,14 +133,23 @@ object Main extends ZCaseApp[Config] {
149133 val unseenSubClasses = subClasses -- seenForThisProperty
150134 val updatedSeen = seen.updated(propertyConcept, seenForThisProperty ++ subClasses)
151135 val newRestrictions = unseenSubClasses.map(c => Restriction (restriction.property, c))
152- (newRestrictions, updatedSeen)
136+ if (descendProperties) {
137+ val subProperties = properties.subclasses.getOrElse(propertyConcept, Set .empty) - Bottom
138+ subProperties.foldLeft((newRestrictions, updatedSeen)) { case ((accRestrictions, accSeen), subProperty) =>
139+ val seenClassesForSubProperty = accSeen.getOrElse(subProperty, Set .empty)
140+ val updatedRestrictions = if (! seenClassesForSubProperty(restriction.filler))
141+ accRestrictions + Restriction (Role (subProperty.id), restriction.filler)
142+ else accRestrictions
143+ val updatedAccSeen = accSeen.updated(subProperty, seenClassesForSubProperty + restriction.filler)
144+ (updatedRestrictions, updatedAccSeen)
145+ }
146+ } else (newRestrictions, updatedSeen)
153147 } else ZIO .succeed(Set .empty[Restriction ])
154148 _ <- activeRestrictions.update(current => current - 1 + directFillerSubclassesRestrictions.size)
155149 _ <- queue.offerAll(directFillerSubclassesRestrictions)
156150 active <- activeRestrictions.get
157151 _ <- queue.shutdown.when(active < 1 )
158152 } yield triples
159- }
160153
161154 def processRestriction (restriction : Restriction , whelk : ReasonerState , mode : Config .OutputMode ): TriplesGroup = {
162155 val queryConcept = AtomicConcept (s " ${restriction.property.id}${restriction.filler.id}" )
@@ -201,7 +194,7 @@ object Main extends ZCaseApp[Config] {
201194 AtomicConcept (ax.getSubProperty.asOWLObjectProperty.getIRI.toString),
202195 AtomicConcept (ax.getSuperProperty.asOWLObjectProperty.getIRI.toString))
203196 }
204- val allProps = ont.getObjectPropertiesInSignature(( Imports .INCLUDED ) ).asScala.to(Set )
197+ val allProps = ont.getObjectPropertiesInSignature(Imports .INCLUDED ).asScala.to(Set )
205198 .filterNot(_.isOWLTopObjectProperty)
206199 .map(prop =>
207200 ConceptInclusion (AtomicConcept (prop.getIRI.toString), AtomicConcept (prop.getIRI.toString)))
0 commit comments