@@ -4,6 +4,7 @@ import caseapp._
44import org .apache .jena .graph .{Node , NodeFactory , Triple }
55import org .apache .jena .riot .RDFFormat
66import org .apache .jena .riot .system .{StreamRDF , StreamRDFWriter }
7+ import org .apache .jena .sys .JenaSystem
78import org .apache .jena .vocabulary .{OWL2 , RDF , RDFS }
89import org .geneontology .whelk .BuiltIn .{Bottom , Top }
910import org .geneontology .whelk ._
@@ -26,6 +27,8 @@ import scala.jdk.CollectionConverters._
2627
2728object Main extends ZCaseApp [Config ] {
2829
30+ JenaSystem .init()
31+
2932 private val RDFType = RDF .`type`.asNode
3033 private val RDFSSubClassOf = RDFS .subClassOf.asNode
3134 private val OWLEquivalentClass = OWL2 .equivalentClass.asNode
@@ -35,74 +38,67 @@ object Main extends ZCaseApp[Config] {
3538 private val OWLOntology = OWL2 .Ontology .asNode
3639
3740 override def run (config : Config , arg : RemainingArgs ): ZIO [ZEnv , Nothing , ExitCode ] = {
38- val ontologyFile = new File (config.ontologyFile)
39- val nonRedundantOutputFile = new File (config.nonRedundantOutputFile)
40- val redundantOutputFile = new File (config.redundantOutputFile)
41- val streamsManaged = for {
42- nonredundantOutputStream <- Managed .fromAutoCloseable(ZIO .effect(new FileOutputStream (nonRedundantOutputFile)))
43- redundantOutputStream <- Managed .fromAutoCloseable(ZIO .effect(new FileOutputStream (redundantOutputFile)))
44- nonredundantRDFWriter <- createStreamRDF(nonredundantOutputStream)
45- redundantRDFWriter <- createStreamRDF(redundantOutputStream)
46- } yield (nonredundantRDFWriter, redundantRDFWriter)
47- val program = streamsManaged.use {
48- case (nonredundantRDFWriter, redundantRDFWriter) =>
49- for {
50- fileProperties <- config.propertiesFile.map(readPropertiesFile).getOrElse(ZIO .succeed(Set .empty[AtomicConcept ]))
51- specifiedProperties = fileProperties ++ config.property.map(prop => AtomicConcept (prop)).to(Set )
52- manager <- ZIO .effect(OWLManager .createOWLOntologyManager())
53- ontology <- ZIO .effect(manager.loadOntologyFromOntologyDocument(ontologyFile))
54- whelkOntology = Bridge .ontologyToAxioms(ontology)
55- _ <- ZIO .effectTotal(scribe.info(" Running reasoner" ))
56- whelk = Reasoner .assert(whelkOntology)
57- _ <- ZIO .effectTotal(scribe.info(" Done running reasoner" ))
58- _ <- (effectBlockingIO(
59- nonredundantRDFWriter.triple(Triple .create(NodeFactory .createBlankNode(" nonredundant" ), RDFType , OWLOntology ))) *>
60- effectBlockingIO(redundantRDFWriter.triple(Triple .create(NodeFactory .createBlankNode(" redundant" ), RDFType , OWLOntology ))))
61- .when(config.mode == OWLMode )
62- start <- ZIO .effectTotal(System .currentTimeMillis())
63- processed <- computeRelations(ontology, whelk, specifiedProperties, config.reflexiveSubclasses.bool, config.equivalenceAsSubclass.bool, config.mode)
64- _ <- processed.foreach {
65- case TriplesGroup (nonredundant, redundant) =>
66- ZIO .effect {
67- nonredundant.foreach(nonredundantRDFWriter.triple)
68- redundant.foreach(redundantRDFWriter.triple)
69- }
70- }
71- stop <- ZIO .effectTotal(System .currentTimeMillis())
72- _ <- ZIO .effectTotal(scribe.info(s " Computed relations in ${(stop - start) / 1000.0 }s " ))
73- } yield ()
41+ val program = createStreamRDF(config.outputFile).use { rdfWriter =>
42+ for {
43+ fileProperties <- config.propertiesFile.map(readPropertiesFile).getOrElse(ZIO .succeed(Set .empty[AtomicConcept ]))
44+ specifiedProperties = fileProperties ++ config.property.map(prop => AtomicConcept (prop)).to(Set )
45+ ontology <- loadOntology(config.ontologyFile)
46+ whelkOntology = Bridge .ontologyToAxioms(ontology)
47+ _ <- ZIO .succeed(scribe.info(" Running reasoner" ))
48+ whelk = Reasoner .assert(whelkOntology)
49+ _ <- ZIO .succeed(scribe.info(" Done running reasoner" ))
50+ _ <- ZIO .fail(new Exception (" Ontology is incoherent; please correct unsatisfiable classes." )).when(isIncoherent(whelk))
51+ _ <- effectBlockingIO(rdfWriter.triple(Triple .create(NodeFactory .createBlankNode(" redundant" ), RDFType , OWLOntology )))
52+ .when(config.mode == OWLMode )
53+ start <- ZIO .succeed(System .currentTimeMillis())
54+ triplesStream = computeRelations(ontology, whelk, specifiedProperties, config.outputSubclasses.bool, config.reflexiveSubclasses.bool, config.equivalenceAsSubclass.bool, config.mode)
55+ _ <- triplesStream.foreach {
56+ case TriplesGroup (triples) => ZIO .effect(triples.foreach(rdfWriter.triple))
57+ }
58+ stop <- ZIO .succeed(System .currentTimeMillis())
59+ _ <- ZIO .succeed(scribe.info(s " Computed relations in ${(stop - start) / 1000.0 }s " ))
60+ } yield ()
7461 }
7562 program.exitCode
7663 }
7764
78- def computeRelations (ontology : OWLOntology , whelk : ReasonerState , specifiedProperties : Set [AtomicConcept ], reflexiveSubclasses : Boolean , equivalenceAsSubclass : Boolean , mode : Config .OutputMode ): UIO [ UStream [TriplesGroup ] ] = {
65+ def computeRelations (ontology : OWLOntology , whelk : ReasonerState , specifiedProperties : Set [AtomicConcept ], outputSubclasses : Boolean , reflexiveSubclasses : Boolean , equivalenceAsSubclass : Boolean , mode : Config .OutputMode ): UStream [TriplesGroup ] = {
7966 val classes = classHierarchy(whelk)
8067 val properties = propertyHierarchy(ontology)
81- val classesStream = allClasses(ontology)
82- val classesTasks = classesStream.map(c => ZIO .effectTotal(processSuperclasses(c, whelk, reflexiveSubclasses, equivalenceAsSubclass)))
83- for {
68+ val classesTasks = if (outputSubclasses) {
69+ allClasses(ontology).map(c => ZIO .succeed(processSuperclasses(c, whelk, reflexiveSubclasses, equivalenceAsSubclass)))
70+ } else Stream .empty
71+ val streamZ = for {
8472 queue <- Queue .unbounded[Restriction ]
8573 activeRestrictions <- Ref .make(0 )
8674 seenRef <- Ref .make(Map .empty[AtomicConcept , Set [AtomicConcept ]])
8775 _ <- traverse(specifiedProperties, properties, classes, queue, activeRestrictions, seenRef)
8876 restrictionsStream = Stream .fromQueue(queue).map(r => processRestrictionAndExtendQueue(r, properties, classes, whelk, mode, specifiedProperties.isEmpty, queue, activeRestrictions, seenRef))
8977 allTasks = classesTasks ++ restrictionsStream
9078 } yield allTasks.mapMParUnordered(JRuntime .getRuntime.availableProcessors)(identity)
79+ Stream .unwrap(streamZ)
9180 }
9281
9382 def readPropertiesFile (file : String ): ZIO [Blocking , Throwable , Set [AtomicConcept ]] =
9483 effectBlocking(Source .fromFile(file, " utf-8" )).bracketAuto { source =>
9584 effectBlocking(source.getLines().map(_.trim).filter(_.nonEmpty).map(line => AtomicConcept (line)).to(Set ))
9685 }
9786
98- def createStreamRDF (output : OutputStream ): Managed [Throwable , StreamRDF ] =
99- Managed .make {
87+ def loadOntology (path : String ): Task [OWLOntology ] = for {
88+ manager <- ZIO .effect(OWLManager .createOWLOntologyManager())
89+ ontology <- ZIO .effect(manager.loadOntologyFromOntologyDocument(new File (path)))
90+ } yield ontology
91+
92+ def createStreamRDF (path : String ): TaskManaged [StreamRDF ] = for {
93+ outputStream <- Managed .fromAutoCloseable(ZIO .effect(new FileOutputStream (new File (path))))
94+ rdfWriter <- Managed .make {
10095 ZIO .effect {
101- val stream = StreamRDFWriter .getWriterStream(output , RDFFormat .TURTLE_FLAT , null )
96+ val stream = StreamRDFWriter .getWriterStream(outputStream , RDFFormat .TURTLE_FLAT , null )
10297 stream.start()
10398 stream
10499 }
105- }(stream => ZIO .effectTotal(stream.finish()))
100+ }(stream => ZIO .succeed(stream.finish()))
101+ } yield rdfWriter
106102
107103 def allClasses (ont : OWLOntology ): ZStream [Any , Nothing , OWLClass ] = Stream .fromIterable(ont.getClassesInSignature(Imports .INCLUDED ).asScala.to(Set ) - OWLThing - OWLNothing )
108104
@@ -128,9 +124,9 @@ object Main extends ZCaseApp[Config] {
128124 } yield ()
129125 }
130126
131- 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 ] =
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+ val triples = processRestriction(restriction, whelk, mode)
132129 for {
133- triples <- ZIO .effectTotal(processRestriction(restriction, whelk, mode))
134130 directFillerSubclassesRestrictions <- if (triples.redundant.nonEmpty) seenRef.modify { seen =>
135131 val propertyConcept = AtomicConcept (restriction.property.id)
136132 val seenForThisProperty = seen.getOrElse(propertyConcept, Set .empty)
@@ -155,32 +151,29 @@ object Main extends ZCaseApp[Config] {
155151 active <- activeRestrictions.get
156152 _ <- queue.shutdown.when(active < 1 )
157153 } yield triples
154+ }
158155
159156 def processRestriction (restriction : Restriction , whelk : ReasonerState , mode : Config .OutputMode ): TriplesGroup = {
160- val queryConcept = AtomicConcept (s " ${restriction.property.id}${restriction.filler.id}" )
161- val er = ExistentialRestriction (restriction.property, restriction.filler)
162- val axioms = Set (ConceptInclusion (queryConcept, er), ConceptInclusion (er, queryConcept))
163- val updatedWhelk = Reasoner .assert(axioms, whelk)
164- // FIXME don't create these if result is empty
165- val predicate = NodeFactory .createURI(restriction.property.id)
166- val target = NodeFactory .createURI(restriction.filler.id)
167- val (equivalents, directSubclasses) = updatedWhelk.directlySubsumes(queryConcept)
168- val subclasses =
169- updatedWhelk.closureSubsBySuperclass(queryConcept).collect { case x : AtomicConcept => x } - queryConcept - Bottom
170- if (! equivalents(BuiltIn .Bottom )) {
171- val nonredundantTerms = directSubclasses - BuiltIn .Bottom ++ equivalents
172- val nonredundantTriples = mode match {
173- case RDFMode => nonredundantTerms.map(sc => Triple .create(NodeFactory .createURI(sc.id), predicate, target))
174- case OWLMode => nonredundantTerms.flatMap(sc => owlTriples(NodeFactory .createURI(sc.id), predicate, target))
175- }
176- val redundantTriples = mode match {
157+ val subclasses = queryExistentialSubclasses(restriction, whelk)
158+ if (subclasses.nonEmpty) {
159+ val predicate = NodeFactory .createURI(restriction.property.id)
160+ val target = NodeFactory .createURI(restriction.filler.id)
161+ val outputTriples = mode match {
177162 case RDFMode => subclasses.map(sc => Triple .create(NodeFactory .createURI(sc.id), predicate, target))
178163 case OWLMode => subclasses.flatMap(sc => owlTriples(NodeFactory .createURI(sc.id), predicate, target))
179164 }
180- TriplesGroup (nonredundantTriples, redundantTriples )
165+ TriplesGroup (outputTriples )
181166 } else TriplesGroup .empty
182167 }
183168
169+ def queryExistentialSubclasses (restriction : Restriction , whelk : ReasonerState ): Set [AtomicConcept ] = {
170+ val queryConcept = AtomicConcept (s " ${restriction.property.id}${restriction.filler.id}" )
171+ 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
175+ }
176+
184177 def classHierarchy (reasoner : ReasonerState ): Hierarchy = {
185178 val taxonomy = reasoner.computeTaxonomy
186179 val subclassTaxonomy = taxonomy.foldLeft(Map .empty[AtomicConcept , Set [AtomicConcept ]]) { case (accum, (concept, (_, superclasses))) =>
@@ -215,23 +208,21 @@ object Main extends ZCaseApp[Config] {
215208 .collect { case ac @ AtomicConcept (_) => ac }
216209 if (allSuperclasses(BuiltIn .Bottom )) TriplesGroup .empty // unsatisfiable
217210 else {
218- val (equivs, directSuperclasses ) = whelk.directlySubsumedBy(concept)
211+ val (equivs, _ ) = whelk.directlySubsumedBy(concept)
219212 val adjustedEquivs = if (reflexiveSubclasses) equivs + concept else equivs - concept
220- val directSuperclassTriples = directSuperclasses.map(c => Triple .create(subject, RDFSSubClassOf , NodeFactory .createURI(c.id)))
221213 val equivalentClassTriples = if (equivalenceAsSubclass)
222214 adjustedEquivs.map(c => Triple .create(subject, RDFSSubClassOf , NodeFactory .createURI(c.id)))
223215 else
224216 adjustedEquivs.map(c => Triple .create(subject, OWLEquivalentClass , NodeFactory .createURI(c.id)))
225- val nonredundantTriples = directSuperclassTriples ++ equivalentClassTriples
226217 val adjustedSuperclasses = if (reflexiveSubclasses) allSuperclasses + concept else allSuperclasses - concept
227- val redundantTriples = if (equivalenceAsSubclass)
218+ val outputTriples = if (equivalenceAsSubclass)
228219 adjustedSuperclasses.map(c => Triple .create(subject, RDFSSubClassOf , NodeFactory .createURI(c.id)))
229220 else {
230221 val superclassesMinusEquiv = adjustedSuperclasses -- adjustedEquivs
231222 superclassesMinusEquiv.map(c => Triple .create(subject, RDFSSubClassOf , NodeFactory .createURI(c.id))) ++
232223 equivalentClassTriples
233224 }
234- TriplesGroup (nonredundantTriples, redundantTriples )
225+ TriplesGroup (outputTriples )
235226 }
236227 }
237228
@@ -247,15 +238,18 @@ object Main extends ZCaseApp[Config] {
247238 )
248239 }
249240
241+ private def isIncoherent (state : ReasonerState ): Boolean =
242+ state.closureSubsBySuperclass(Bottom ).exists(t => ! t.isAnonymous && t != Bottom )
243+
250244 final case class Hierarchy (equivs : Map [AtomicConcept , Set [AtomicConcept ]], subclasses : Map [AtomicConcept , Set [AtomicConcept ]])
251245
252246 final case class Restriction (property : Role , filler : AtomicConcept )
253247
254- final case class TriplesGroup (nonredundant : Set [ Triple ], redundant : Set [Triple ])
248+ final case class TriplesGroup (redundant : Set [Triple ])
255249
256250 object TriplesGroup {
257251
258- val empty : TriplesGroup = TriplesGroup (Set .empty, Set .empty )
252+ val empty : TriplesGroup = TriplesGroup (Set .empty)
259253
260254 }
261255
0 commit comments