Skip to content

Commit 6a8d05c

Browse files
committed
Stop computing nonredundant. Clean up code.
1 parent 8326315 commit 6a8d05c

4 files changed

Lines changed: 75 additions & 85 deletions

File tree

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ organization := "org.renci"
44

55
name := "relation-graph"
66

7-
version := "1.2.1"
7+
version := "2.0-SNAPSHOT"
88

99
licenses := Seq("MIT license" -> url("https://opensource.org/licenses/MIT"))
1010

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import caseapp.core.argparser.{ArgParser, SimpleArgParser}
55
import org.renci.relationgraph.Config.{BoolValue, FalseValue, TrueValue}
66

77
final case class Config(ontologyFile: String,
8-
nonRedundantOutputFile: String,
9-
redundantOutputFile: String,
8+
outputFile: String,
109
mode: Config.OutputMode = Config.RDFMode,
1110
property: List[String] = Nil,
1211
propertiesFile: Option[String],

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

Lines changed: 64 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import caseapp._
44
import org.apache.jena.graph.{Node, NodeFactory, Triple}
55
import org.apache.jena.riot.RDFFormat
66
import org.apache.jena.riot.system.{StreamRDF, StreamRDFWriter}
7+
import org.apache.jena.sys.JenaSystem
78
import org.apache.jena.vocabulary.{OWL2, RDF, RDFS}
89
import org.geneontology.whelk.BuiltIn.{Bottom, Top}
910
import org.geneontology.whelk._
@@ -26,6 +27,8 @@ import scala.jdk.CollectionConverters._
2627

2728
object 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,69 @@ 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 streamManaged = for {
42+
outputStream <- Managed.fromAutoCloseable(ZIO.effect(new FileOutputStream(new File(config.outputFile))))
43+
rdfWriter <- createStreamRDF(outputStream)
44+
} yield rdfWriter
45+
val program = streamManaged.use { rdfWriter =>
46+
for {
47+
fileProperties <- config.propertiesFile.map(readPropertiesFile).getOrElse(ZIO.succeed(Set.empty[AtomicConcept]))
48+
specifiedProperties = fileProperties ++ config.property.map(prop => AtomicConcept(prop)).to(Set)
49+
ontology <- loadOntology(config.ontologyFile)
50+
whelkOntology = Bridge.ontologyToAxioms(ontology)
51+
_ <- ZIO.succeed(scribe.info("Running reasoner"))
52+
whelk = Reasoner.assert(whelkOntology)
53+
_ <- ZIO.succeed(scribe.info("Done running reasoner"))
54+
_ <- ZIO.fail(new Exception("Ontology is incoherent; please correct unsatisfiable classes.")).when(isIncoherent(whelk))
55+
_ <- effectBlockingIO(rdfWriter.triple(Triple.create(NodeFactory.createBlankNode("redundant"), RDFType, OWLOntology)))
56+
.when(config.mode == OWLMode)
57+
start <- ZIO.succeed(System.currentTimeMillis())
58+
triplesStream = computeRelations(ontology, whelk, specifiedProperties, config.outputSubclasses.bool, config.reflexiveSubclasses.bool, config.equivalenceAsSubclass.bool, config.mode)
59+
_ <- triplesStream.foreach {
60+
case TriplesGroup(triples) => ZIO.effect(triples.foreach(rdfWriter.triple))
61+
}
62+
stop <- ZIO.succeed(System.currentTimeMillis())
63+
_ <- ZIO.succeed(scribe.info(s"Computed relations in ${(stop - start) / 1000.0}s"))
64+
} yield ()
7465
}
7566
program.exitCode
7667
}
7768

78-
def computeRelations(ontology: OWLOntology, whelk: ReasonerState, specifiedProperties: Set[AtomicConcept], reflexiveSubclasses: Boolean, equivalenceAsSubclass: Boolean, mode: Config.OutputMode): UIO[UStream[TriplesGroup]] = {
69+
def computeRelations(ontology: OWLOntology, whelk: ReasonerState, specifiedProperties: Set[AtomicConcept], outputSubclasses: Boolean, reflexiveSubclasses: Boolean, equivalenceAsSubclass: Boolean, mode: Config.OutputMode): UStream[TriplesGroup] = {
7970
val classes = classHierarchy(whelk)
8071
val properties = propertyHierarchy(ontology)
81-
val classesStream = allClasses(ontology)
82-
val classesTasks = classesStream.map(c => ZIO.effectTotal(processSuperclasses(c, whelk, reflexiveSubclasses, equivalenceAsSubclass)))
83-
for {
72+
val classesTasks = if (outputSubclasses) {
73+
allClasses(ontology).map(c => ZIO.succeed(processSuperclasses(c, whelk, reflexiveSubclasses, equivalenceAsSubclass)))
74+
} else Stream.empty
75+
val streamZ = for {
8476
queue <- Queue.unbounded[Restriction]
8577
activeRestrictions <- Ref.make(0)
8678
seenRef <- Ref.make(Map.empty[AtomicConcept, Set[AtomicConcept]])
8779
_ <- traverse(specifiedProperties, properties, classes, queue, activeRestrictions, seenRef)
8880
restrictionsStream = Stream.fromQueue(queue).map(r => processRestrictionAndExtendQueue(r, properties, classes, whelk, mode, specifiedProperties.isEmpty, queue, activeRestrictions, seenRef))
8981
allTasks = classesTasks ++ restrictionsStream
9082
} yield allTasks.mapMParUnordered(JRuntime.getRuntime.availableProcessors)(identity)
83+
Stream.unwrap(streamZ)
9184
}
9285

9386
def readPropertiesFile(file: String): ZIO[Blocking, Throwable, Set[AtomicConcept]] =
9487
effectBlocking(Source.fromFile(file, "utf-8")).bracketAuto { source =>
9588
effectBlocking(source.getLines().map(_.trim).filter(_.nonEmpty).map(line => AtomicConcept(line)).to(Set))
9689
}
9790

91+
def loadOntology(path: String): Task[OWLOntology] = for {
92+
manager <- ZIO.effect(OWLManager.createOWLOntologyManager())
93+
ontology <- ZIO.effect(manager.loadOntologyFromOntologyDocument(new File(path)))
94+
} yield ontology
95+
9896
def createStreamRDF(output: OutputStream): Managed[Throwable, StreamRDF] =
9997
Managed.make {
10098
ZIO.effect {
10199
val stream = StreamRDFWriter.getWriterStream(output, RDFFormat.TURTLE_FLAT, null)
102100
stream.start()
103101
stream
104102
}
105-
}(stream => ZIO.effectTotal(stream.finish()))
103+
}(stream => ZIO.succeed(stream.finish()))
106104

107105
def allClasses(ont: OWLOntology): ZStream[Any, Nothing, OWLClass] = Stream.fromIterable(ont.getClassesInSignature(Imports.INCLUDED).asScala.to(Set) - OWLThing - OWLNothing)
108106

@@ -128,9 +126,9 @@ object Main extends ZCaseApp[Config] {
128126
} yield ()
129127
}
130128

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] =
129+
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] = {
130+
val triples = processRestriction(restriction, whelk, mode)
132131
for {
133-
triples <- ZIO.effectTotal(processRestriction(restriction, whelk, mode))
134132
directFillerSubclassesRestrictions <- if (triples.redundant.nonEmpty) seenRef.modify { seen =>
135133
val propertyConcept = AtomicConcept(restriction.property.id)
136134
val seenForThisProperty = seen.getOrElse(propertyConcept, Set.empty)
@@ -155,32 +153,29 @@ object Main extends ZCaseApp[Config] {
155153
active <- activeRestrictions.get
156154
_ <- queue.shutdown.when(active < 1)
157155
} yield triples
156+
}
158157

159158
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 {
159+
val subclasses = queryExistentialSubclasses(restriction, whelk)
160+
if (subclasses.nonEmpty) {
161+
val predicate = NodeFactory.createURI(restriction.property.id)
162+
val target = NodeFactory.createURI(restriction.filler.id)
163+
val outputTriples = mode match {
177164
case RDFMode => subclasses.map(sc => Triple.create(NodeFactory.createURI(sc.id), predicate, target))
178165
case OWLMode => subclasses.flatMap(sc => owlTriples(NodeFactory.createURI(sc.id), predicate, target))
179166
}
180-
TriplesGroup(nonredundantTriples, redundantTriples)
167+
TriplesGroup(outputTriples)
181168
} else TriplesGroup.empty
182169
}
183170

171+
def queryExistentialSubclasses(restriction: Restriction, whelk: ReasonerState): Set[AtomicConcept] = {
172+
val queryConcept = AtomicConcept(s"${restriction.property.id}${restriction.filler.id}")
173+
val er = ExistentialRestriction(restriction.property, restriction.filler)
174+
val axioms = Set(ConceptInclusion(queryConcept, er), ConceptInclusion(er, queryConcept))
175+
val updatedWhelk = Reasoner.assert(axioms, whelk)
176+
updatedWhelk.closureSubsBySuperclass(queryConcept).collect { case x: AtomicConcept => x } - queryConcept - Bottom
177+
}
178+
184179
def classHierarchy(reasoner: ReasonerState): Hierarchy = {
185180
val taxonomy = reasoner.computeTaxonomy
186181
val subclassTaxonomy = taxonomy.foldLeft(Map.empty[AtomicConcept, Set[AtomicConcept]]) { case (accum, (concept, (_, superclasses))) =>
@@ -215,23 +210,21 @@ object Main extends ZCaseApp[Config] {
215210
.collect { case ac @ AtomicConcept(_) => ac }
216211
if (allSuperclasses(BuiltIn.Bottom)) TriplesGroup.empty //unsatisfiable
217212
else {
218-
val (equivs, directSuperclasses) = whelk.directlySubsumedBy(concept)
213+
val (equivs, _) = whelk.directlySubsumedBy(concept)
219214
val adjustedEquivs = if (reflexiveSubclasses) equivs + concept else equivs - concept
220-
val directSuperclassTriples = directSuperclasses.map(c => Triple.create(subject, RDFSSubClassOf, NodeFactory.createURI(c.id)))
221215
val equivalentClassTriples = if (equivalenceAsSubclass)
222216
adjustedEquivs.map(c => Triple.create(subject, RDFSSubClassOf, NodeFactory.createURI(c.id)))
223217
else
224218
adjustedEquivs.map(c => Triple.create(subject, OWLEquivalentClass, NodeFactory.createURI(c.id)))
225-
val nonredundantTriples = directSuperclassTriples ++ equivalentClassTriples
226219
val adjustedSuperclasses = if (reflexiveSubclasses) allSuperclasses + concept else allSuperclasses - concept
227-
val redundantTriples = if (equivalenceAsSubclass)
220+
val outputTriples = if (equivalenceAsSubclass)
228221
adjustedSuperclasses.map(c => Triple.create(subject, RDFSSubClassOf, NodeFactory.createURI(c.id)))
229222
else {
230223
val superclassesMinusEquiv = adjustedSuperclasses -- adjustedEquivs
231224
superclassesMinusEquiv.map(c => Triple.create(subject, RDFSSubClassOf, NodeFactory.createURI(c.id))) ++
232225
equivalentClassTriples
233226
}
234-
TriplesGroup(nonredundantTriples, redundantTriples)
227+
TriplesGroup(outputTriples)
235228
}
236229
}
237230

@@ -247,15 +240,18 @@ object Main extends ZCaseApp[Config] {
247240
)
248241
}
249242

243+
private def isIncoherent(state: ReasonerState): Boolean =
244+
state.closureSubsBySuperclass(Bottom).exists(t => !t.isAnonymous && t != Bottom)
245+
250246
final case class Hierarchy(equivs: Map[AtomicConcept, Set[AtomicConcept]], subclasses: Map[AtomicConcept, Set[AtomicConcept]])
251247

252248
final case class Restriction(property: Role, filler: AtomicConcept)
253249

254-
final case class TriplesGroup(nonredundant: Set[Triple], redundant: Set[Triple])
250+
final case class TriplesGroup(redundant: Set[Triple])
255251

256252
object TriplesGroup {
257253

258-
val empty: TriplesGroup = TriplesGroup(Set.empty, Set.empty)
254+
val empty: TriplesGroup = TriplesGroup(Set.empty)
259255

260256
}
261257

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

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,17 @@ 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, false, false, Config.RDFMode)
26+
resultsStream = Main.computeRelations(ontology, whelk, Set.empty, true, false, false, Config.RDFMode)
2727
results <- resultsStream.runCollect
28-
triples <- ZIO.fromOption(results.reduceOption((left, right) => TriplesGroup(left.nonredundant ++ right.nonredundant, left.redundant ++ right.redundant)))
29-
TriplesGroup(nonredundant, redundant) = triples
30-
} yield assert(nonredundant)(contains(Triple.create(n(s"$Prefix#A"), P, n(s"$Prefix#D")))) &&
28+
triples <- ZIO.fromOption(results.reduceOption((left, right) => TriplesGroup(left.redundant ++ right.redundant)))
29+
TriplesGroup(redundant) = triples
30+
} yield
3131
assert(redundant)(contains(Triple.create(n(s"$Prefix#A"), P, n(s"$Prefix#D")))) &&
32-
assert(nonredundant)(not(contains(Triple.create(n(s"$Prefix#C"), P, n(s"$Prefix#D"))))) &&
33-
assert(redundant)(contains(Triple.create(n(s"$Prefix#C"), P, n(s"$Prefix#D")))) &&
34-
assert(nonredundant)(contains(Triple.create(n(s"$Prefix#F"), P, n(s"$Prefix#B")))) &&
35-
assert(redundant)(contains(Triple.create(n(s"$Prefix#F"), P, n(s"$Prefix#B")))) &&
36-
assert(nonredundant)(not(contains(Triple.create(n(s"$Prefix#F"), P, n(s"$Prefix#C"))))) &&
37-
assert(redundant)(not(contains(Triple.create(n(s"$Prefix#F"), P, n(s"$Prefix#C"))))) &&
38-
assert(nonredundant)(contains(Triple.create(n(s"$Prefix#E"), P, n(s"$Prefix#C")))) &&
39-
assert(redundant)(contains(Triple.create(n(s"$Prefix#E"), P, n(s"$Prefix#C")))) &&
40-
assert(nonredundant)(not(contains(Triple.create(n(s"$Prefix#E"), P, n(s"$Prefix#A"))))) &&
41-
assert(redundant)(contains(Triple.create(n(s"$Prefix#E"), P, n(s"$Prefix#A"))))
32+
assert(redundant)(contains(Triple.create(n(s"$Prefix#C"), P, n(s"$Prefix#D")))) &&
33+
assert(redundant)(contains(Triple.create(n(s"$Prefix#F"), P, n(s"$Prefix#B")))) &&
34+
assert(redundant)(not(contains(Triple.create(n(s"$Prefix#F"), P, n(s"$Prefix#C"))))) &&
35+
assert(redundant)(contains(Triple.create(n(s"$Prefix#E"), P, n(s"$Prefix#C")))) &&
36+
assert(redundant)(contains(Triple.create(n(s"$Prefix#E"), P, n(s"$Prefix#A"))))
4237
}
4338
}
4439

0 commit comments

Comments
 (0)