diff --git a/build.sbt b/build.sbt
index a2a244b..0b249a4 100644
--- a/build.sbt
+++ b/build.sbt
@@ -1,9 +1,8 @@
-
lazy val owlapiVersion = "4.5.17"
lazy val commonSettings = Seq(
organization := "org.geneontology",
- version := "1.0.3",
+ version := "1.0.4",
licenses := Seq("BSD-3-Clause" -> url("https://opensource.org/licenses/BSD-3-Clause")),
homepage := Some(url("https://github.com/balhoff/whelk")),
crossScalaVersions := Seq("2.12.11", "2.13.3"),
@@ -38,27 +37,28 @@ lazy val testSettings = Seq(
libraryDependencies ++= Seq("com.lihaoyi" %% "utest" % "0.7.5" % Test)
)
-lazy val parentProject = project.in(file("."))
+lazy val parentProject = project
+ .in(file("."))
.settings(commonSettings)
- .settings(
- name := "whelk-project",
- skip in publish := true)
+ .settings(name := "whelk-project", skip in publish := true)
.aggregate(
core,
owlapi,
protege
)
-lazy val core = project.in(file("modules/core"))
+lazy val core = project
+ .in(file("modules/core"))
.settings(commonSettings)
.settings(testSettings)
.settings(publishSettings)
.settings(
name := "whelk",
- description := "Whelk reasoner core",
+ description := "Whelk reasoner core"
)
-lazy val owlapi = project.in(file("modules/owlapi"))
+lazy val owlapi = project
+ .in(file("modules/owlapi"))
.dependsOn(core)
.enablePlugins(JavaAppPackaging)
.settings(commonSettings)
@@ -69,29 +69,31 @@ lazy val owlapi = project.in(file("modules/owlapi"))
description := "Whelk reasoner OWL API bindings",
mainClass in Compile := Some("org.geneontology.whelk.Main"),
libraryDependencies ++= Seq(
- "net.sourceforge.owlapi" % "owlapi-distribution" % owlapiVersion,
- "org.phenoscape" %% "scowl" % "1.3.4",
- "org.semanticweb.elk" % "elk-owlapi" % "0.4.3" % Test,
+ "net.sourceforge.owlapi" % "owlapi-distribution" % owlapiVersion,
+ "org.phenoscape" %% "scowl" % "1.3.4",
+ "org.semanticweb.elk" % "elk-owlapi" % "0.4.3" % Test,
"net.sourceforge.owlapi" % "org.semanticweb.hermit" % "1.4.0.432" % Test,
- "net.sourceforge.owlapi" % "jfact" % "4.0.4" % Test
+ "net.sourceforge.owlapi" % "jfact" % "4.0.4" % Test
)
)
def isJarToEmbed(file: java.io.File): Boolean = file.getName match {
- case name if name startsWith "scala" => true
- case _ => false
+ case name if (name startsWith "scala") || (name startsWith "scowl") => true
+ case _ => false
}
-lazy val protege = project.in(file("modules/protege"))
+lazy val protege = project
+ .in(file("modules/protege"))
.dependsOn(owlapi)
.enablePlugins(SbtOsgi)
.settings(commonSettings)
.settings(
skip in publish := true,
- name := "whelk-protege",
+ name := "Whelk reasoner Protege plugin",
description := "Whelk reasoner Protégé plugin",
// Bundle-Version is set to the version by default.
OsgiKeys.bundleSymbolicName := "org.geneontology.whelk;singleton:=true",
+ OsgiKeys.bundleActivator := Some("org.protege.editor.owl.ProtegeOWL"),
// Include the packages specified by privatePackage in the bundle.
OsgiKeys.privatePackage := Seq("org.geneontology.*"),
OsgiKeys.exportPackage := Seq("!*"),
@@ -105,7 +107,7 @@ lazy val protege = project.in(file("modules/protege"))
),
libraryDependencies ++= Seq(
"net.sourceforge.owlapi" % "owlapi-distribution" % owlapiVersion % Provided,
- "edu.stanford.protege" % "protege-editor-core" % "5.2.0" % Provided,
- "edu.stanford.protege" % "protege-editor-owl" % "5.2.0" % Provided
+ "edu.stanford.protege" % "protege-editor-core" % "5.2.0" % Provided,
+ "edu.stanford.protege" % "protege-editor-owl" % "5.2.0" % Provided
)
)
diff --git a/modules/core/src/main/scala/org/geneontology/whelk/Reasoner.scala b/modules/core/src/main/scala/org/geneontology/whelk/Reasoner.scala
index 036a8f6..08e54e4 100644
--- a/modules/core/src/main/scala/org/geneontology/whelk/Reasoner.scala
+++ b/modules/core/src/main/scala/org/geneontology/whelk/Reasoner.scala
@@ -60,7 +60,7 @@ final case class ReasonerState(
// Using the regular algorithm for Bottom takes too long
val directSuperClassesOfBottom = closureSubsBySuperclass.collect { case (superclass: AtomicConcept, subclasses) if (subclasses - Bottom - superclass).forall(_.isAnonymous) => superclass }.toSet
val equivalentsToBottom = closureSubsBySuperclass(Bottom).collect { case subclass: AtomicConcept => subclass }
- closureSubsBySubclass.collect {
+ (closureSubsBySubclass - Bottom).collect {
case (c: AtomicConcept, subsumers) => c -> directSubsumers(c, subsumers + Top)
} + (Bottom -> (equivalentsToBottom, directSuperClassesOfBottom))
}
@@ -179,16 +179,20 @@ object Reasoner {
private[this] def processConcept(concept: Concept, reasoner: ReasonerState): ReasonerState = {
if (reasoner.inits(concept)) reasoner
else {
- val newState = `R⊤`(concept, R0(concept, reasoner.copy(inits = reasoner.inits + concept)))
+ val superClassesOfBottom = reasoner.closureSubsBySubclass.getOrElse(Bottom, Set.empty)
+ val updatedClosureSubsBySubclass = reasoner.closureSubsBySubclass.updated(Bottom, superClassesOfBottom + concept)
+ val newState = `R⊤`(concept, R0(concept, reasoner.copy(inits = reasoner.inits + concept, closureSubsBySubclass = updatedClosureSubsBySubclass)))
newState.queueDelegates.keysIterator.foldLeft(newState) { (state, delegateKey) =>
state.queueDelegates(delegateKey).processConcept(concept, state)
}
}
}
+ private[this] val emptySubClassSet: Set[Concept] = Set(Bottom)
+
private[this] def processConceptInclusion(ci: ConceptInclusion, reasoner: ReasonerState): ReasonerState = {
val ConceptInclusion(subclass, superclass) = ci
- val subs = reasoner.closureSubsBySuperclass.getOrElse(superclass, Set.empty)
+ val subs = reasoner.closureSubsBySuperclass.getOrElse(superclass, emptySubClassSet)
if (subs(subclass)) reasoner else {
val closureSubsBySuperclass = reasoner.closureSubsBySuperclass.updated(superclass, subs + subclass)
val supers = reasoner.closureSubsBySubclass.getOrElse(subclass, Set.empty)
diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owl-reasoner-nothing-equiv.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owl-reasoner-nothing-equiv.ofn
new file mode 100644
index 0000000..61f855d
--- /dev/null
+++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owl-reasoner-nothing-equiv.ofn
@@ -0,0 +1,47 @@
+Prefix(:=)
+Prefix(owl:=)
+Prefix(rdf:=)
+Prefix(xml:=)
+Prefix(xsd:=)
+Prefix(rdfs:=)
+
+
+Ontology(
+
+Declaration(Class(:A))
+Declaration(Class(:B))
+Declaration(Class(:BY))
+Declaration(Class(:C))
+Declaration(Class(:X))
+Declaration(Class(:Y))
+Declaration(Class(:Z))
+############################
+# Classes
+############################
+
+# Class: :A (:A)
+
+DisjointClasses(:A :X)
+
+# Class: :B (:B)
+
+SubClassOf(:B :A)
+
+# Class: :BY (:BY)
+
+EquivalentClasses(:BY ObjectIntersectionOf(:B :Y))
+
+# Class: :C (:C)
+
+SubClassOf(:C :B)
+
+# Class: :Y (:Y)
+
+SubClassOf(:Y :X)
+
+# Class: :Z (:Z)
+
+SubClassOf(:Z :Y)
+
+
+)
\ No newline at end of file
diff --git a/modules/owlapi/src/test/scala/org/geneontology/whelk/TestOWLReasoner.scala b/modules/owlapi/src/test/scala/org/geneontology/whelk/TestOWLReasoner.scala
index adfdce7..3dd66e2 100644
--- a/modules/owlapi/src/test/scala/org/geneontology/whelk/TestOWLReasoner.scala
+++ b/modules/owlapi/src/test/scala/org/geneontology/whelk/TestOWLReasoner.scala
@@ -66,6 +66,18 @@ object TestOWLReasoner extends TestSuite {
reasoner.getInstances(Class("http://example.org/blah"), true)
}
+ "Unsatisfiable classes should be equivalent to owl:Nothing" - {
+ val manager = OWLManager.createOWLOntologyManager()
+ val ontology = manager.loadOntologyFromOntologyDocument(this.getClass.getResourceAsStream("owl-reasoner-nothing-equiv.ofn"))
+ val reasoner = new WhelkOWLReasonerFactory().createReasoner(ontology)
+ assert(reasoner.getSubClasses(Class("http://example.org/C") and Class("http://example.org/Z"), false).isEmpty)
+ assert(reasoner.getEquivalentClasses(Class("http://example.org/C") and Class("http://example.org/Z")).getEntities.asScala.toSet == Set(OWLNothing))
+ assert(reasoner.getSubClasses(Class("http://example.org/BY"), false).isEmpty)
+ assert(reasoner.getEquivalentClasses(Class("http://example.org/BY")).getEntities.asScala.toSet == Set(OWLNothing))
+ assert(reasoner.getSubClasses(OWLNothing, false).getFlattened.isEmpty)
+ assert(reasoner.getEquivalentClasses(OWLNothing).getEntities.asScala.toSet == Set(Class("http://example.org/BY")))
+ }
+
}
}
\ No newline at end of file
diff --git a/modules/protege/src/main/resource/plugin.xml b/modules/protege/src/main/resources/plugin.xml
similarity index 100%
rename from modules/protege/src/main/resource/plugin.xml
rename to modules/protege/src/main/resources/plugin.xml
diff --git a/modules/protege/update.properties b/modules/protege/update.properties
index c87363f..581f876 100644
--- a/modules/protege/update.properties
+++ b/modules/protege/update.properties
@@ -1,7 +1,6 @@
id=org.geneontology.whelk
-version=1.0
-#FIXME
-download=https://github.com/balhoff/whelk-protege/releases/download/v0.1.0.1/whelk-protege-0.1.0.1.jar
+version=1.0.4
+download=https://github.com/balhoff/whelk-protege/releases/download/v1.0.4/whelk-reasoner-protege-plugin-1.0.4.jar
name=Whelk plug-in for Protégé
readme=https://raw.githubusercontent.com/balhoff/whelk/master/README.md
license=https://opensource.org/licenses/BSD-3-Clause