diff --git a/modules/core/src/main/scala/org/geneontology/whelk/Model.scala b/modules/core/src/main/scala/org/geneontology/whelk/Model.scala index 4407718..8074d8e 100644 --- a/modules/core/src/main/scala/org/geneontology/whelk/Model.scala +++ b/modules/core/src/main/scala/org/geneontology/whelk/Model.scala @@ -74,6 +74,10 @@ object BuiltIn { final val Bottom = AtomicConcept(s"$owl#Nothing") + final val SameAs = Role(s"$owl#sameAs") + + final val DifferentFrom = Role(s"$owl#differentFrom") + } final case class Conjunction(left: Concept, right: Concept) extends Concept { @@ -112,6 +116,30 @@ final case class ExistentialRestriction(role: Role, concept: Concept) extends Co } +final case class UniversalRestriction(role: Role, concept: Concept) extends Concept { + + def conceptSignature: Set[Concept] = concept.conceptSignature + this + + def signature: Set[Entity] = concept.signature + role + + def isAnonymous: Boolean = true + + override val hashCode: Int = scala.util.hashing.MurmurHash3.productHash(this) + +} + +final case class MaxCardinalityRestriction(role: Role, concept: Concept, cardinality: Int) extends Concept { + + def conceptSignature: Set[Concept] = concept.conceptSignature + this + + def signature: Set[Entity] = concept.signature + role + + def isAnonymous: Boolean = true + + override val hashCode: Int = scala.util.hashing.MurmurHash3.productHash(this) + +} + final case class SelfRestriction(role: Role) extends Concept { def conceptSignature: Set[Concept] = Set(this) @@ -260,6 +288,22 @@ final case class RoleAtom(predicate: Role, subject: IndividualArgument, target: } +final case class SameIndividualsAtom(left: IndividualArgument, right: IndividualArgument) extends RuleAtom { + + def signature: Set[Entity] = left.signature ++ right.signature + + def variables: Set[Variable] = Set(left, right).collect { case v: Variable => v } + +} + +final case class DifferentIndividualsAtom(left: IndividualArgument, right: IndividualArgument) extends RuleAtom { + + def signature: Set[Entity] = left.signature ++ right.signature + + def variables: Set[Variable] = Set(left, right).collect { case v: Variable => v } + +} + final case class Rule(body: List[RuleAtom], head: List[RuleAtom]) extends Axiom { def signature: Set[Entity] = body.flatMap(_.signature).toSet ++ head.flatMap(_.signature).toSet 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 2362592..38cf3fd 100644 --- a/modules/core/src/main/scala/org/geneontology/whelk/Reasoner.scala +++ b/modules/core/src/main/scala/org/geneontology/whelk/Reasoner.scala @@ -26,6 +26,9 @@ final case class ReasonerState( negExistsMapByConcept: Map[Concept, Set[ExistentialRestriction]] = Map.empty, propagations: Map[Concept, Map[Role, List[ExistentialRestriction]]] = Map.empty, assertedNegativeSelfRestrictionsByRole: Map[Role, SelfRestriction] = Map.empty, + universalRestrictionTypesByRole: Map[Nominal, Map[Role, List[Concept]]] = Map.empty, + maxOneCardinalityRestrictionTypesByRole: Map[Nominal, Map[Role, List[Concept]]] = Map.empty, + maxOneCardinalityRestrictionTypesByFiller: Map[Concept, Map[Role, List[Nominal]]] = Map.empty, ruleEngine: RuleEngine = RuleEngine.empty, wm: WorkingMemory = RuleEngine.empty.emptyMemory, disableBottom: Boolean = false, @@ -123,7 +126,7 @@ object Reasoner { val hier: Map[Role, Set[Role]] = saturateRoles(allRoleInclusions) |+| allRoles.map(r => r -> Set(r)).toMap val hierList = hier.map { case (k, v) => k -> v.toList } val hierComps = indexRoleCompositions(hier, axioms.collect { case rc: RoleComposition => rc }) - val rules = axioms.collect { case r: Rule => r } //TODO create rules from certain Whelk axioms + val rules = axioms.collect { case r: Rule => r } val anonymousRulePredicates = rules.flatMap(_.body.collect { case ConceptAtom(concept, _) if concept.isAnonymous => ConceptInclusion(concept, Top) }) @@ -134,10 +137,17 @@ object Reasoner { } def assert(axioms: Set[ConceptInclusion], reasoner: ReasonerState): ReasonerState = { + val ruleConcepts = reasoner.ruleEngine.rules.flatMap(_.signature).collect { + case ac: AtomicConcept => ac + case i: Individual => Nominal(i) + } val distinctConcepts = axioms.flatMap { case ConceptInclusion(subclass, superclass) => Set(subclass, superclass) }.flatMap(_.conceptSignature) - val atomicConcepts = distinctConcepts.collect { case a: AtomicConcept => a } + val conceptsToQueue = distinctConcepts.collect { + case a: AtomicConcept => a + case n: Nominal => n + } val additionalAxioms = distinctConcepts.flatMap { case d @ Disjunction(_) => `R⊔`(d) case c @ Complement(_) => `R¬`(c, reasoner.disableBottom) @@ -145,7 +155,7 @@ object Reasoner { } val negativeSelfRestrictions = axioms.flatMap(_.subclass.conceptSignature).collect { case sr: SelfRestriction => sr.role -> sr }.toMap val updatedAssertions = additionalAxioms.toList ::: axioms.toList - val todo = mutable.Stack.from[QueueExpression](atomicConcepts.toList ::: updatedAssertions) + val todo = mutable.Stack.from[QueueExpression](ruleConcepts.toList ::: conceptsToQueue.toList ::: updatedAssertions) computeClosure(reasoner.copy( assertions = reasoner.assertions ::: updatedAssertions, assertedNegativeSelfRestrictionsByRole = negativeSelfRestrictions), @@ -199,10 +209,12 @@ object Reasoner { val closureSubsBySuperclass = reasoner.closureSubsBySuperclass.updated(superclass, subs + subclass) val supers = reasoner.closureSubsBySubclass.getOrElse(subclass, Set.empty) val closureSubsBySubclass = reasoner.closureSubsBySubclass.updated(subclass, supers + superclass) - val updatedReasoner = `R⊔right`(ci, `R+⟲`(ci, `R-⟲`(ci, `R⊑right`(ci, `R+∃b-right`(ci, `R-∃`(ci, `R+⨅left`(ci, `R+⨅right`(ci, `R-⨅`(ci, `R⊥left`(ci, reasoner.copy(closureSubsBySuperclass = closureSubsBySuperclass, closureSubsBySubclass = closureSubsBySubclass), todo), todo), todo), todo), todo), todo), todo), todo), todo)) + val updatedReasoner = `R≤1-c`(ci, `R≤1-a`(ci, `R∀-left`(ci, `R∃ind-left`(ci, `R⊔right`(ci, `R+⟲`(ci, `R-⟲`(ci, `R⊑right`(ci, `R+∃b-right`(ci, `R-∃`(ci, `R+⨅left`(ci, `R+⨅right`(ci, `R-⨅`(ci, `R⊥left`(ci, reasoner.copy(closureSubsBySuperclass = closureSubsBySuperclass, closureSubsBySubclass = closureSubsBySubclass), todo), todo), todo), todo), todo), todo), todo), todo), todo)), todo), todo), todo), todo) val newState = ci match { - case ConceptInclusion(Nominal(ind), concept) => reasoner.ruleEngine.processConceptAssertion(ConceptAssertion(concept, ind), updatedReasoner, todo) - case _ => updatedReasoner + case ConceptInclusion(Nominal(left), Nominal(right)) => + reasoner.ruleEngine.processRoleAssertion(RoleAssertion(BuiltIn.SameAs, left, right), updatedReasoner, todo) + case ConceptInclusion(Nominal(ind), concept) => reasoner.ruleEngine.processConceptAssertion(ConceptAssertion(concept, ind), updatedReasoner, todo) + case _ => updatedReasoner } newState.queueDelegates.keysIterator.foldLeft(newState) { (state, delegateKey) => state.queueDelegates(delegateKey).processConceptInclusion(ci, state) @@ -218,10 +230,11 @@ object Reasoner { val closureSubsBySuperclass = reasoner.closureSubsBySuperclass.updated(superclass, subs + subclass) val supers = reasoner.closureSubsBySubclass.getOrElse(subclass, Set.empty) val closureSubsBySubclass = reasoner.closureSubsBySubclass.updated(subclass, supers + superclass) - val updatedReasoner = `R⊔right`(ci, `R-⟲`(ci, `R⊑right`(ci, `R+∃b-right`(ci, `R+⨅left`(ci, `R+⨅right`(ci, `R⊥left`(ci, reasoner.copy(closureSubsBySuperclass = closureSubsBySuperclass, closureSubsBySubclass = closureSubsBySubclass), todo), todo), todo), todo), todo), todo)) + val updatedReasoner = `R≤1-c`(ci, `R≤1-a`(ci, `R∀-left`(ci, `R∃ind-left`(ci, `R⊔right`(ci, `R-⟲`(ci, `R⊑right`(ci, `R+∃b-right`(ci, `R+⨅left`(ci, `R+⨅right`(ci, `R⊥left`(ci, reasoner.copy(closureSubsBySuperclass = closureSubsBySuperclass, closureSubsBySubclass = closureSubsBySubclass), todo), todo), todo), todo), todo), todo)), todo), todo), todo), todo) val newState = ci match { - case ConceptInclusion(Nominal(ind), concept) => updatedReasoner.ruleEngine.processConceptAssertion(ConceptAssertion(concept, ind), updatedReasoner, todo) - case _ => updatedReasoner + case ConceptInclusion(Nominal(left), Nominal(right)) => reasoner.ruleEngine.processRoleAssertion(RoleAssertion(BuiltIn.SameAs, left, right), updatedReasoner, todo) + case ConceptInclusion(Nominal(ind), concept) => updatedReasoner.ruleEngine.processConceptAssertion(ConceptAssertion(concept, ind), updatedReasoner, todo) + case _ => updatedReasoner } newState.queueDelegates.keysIterator.foldLeft(newState) { (state, delegateKey) => state.queueDelegates(delegateKey).processSubPlus(ci, state) @@ -242,7 +255,7 @@ object Reasoner { val updatedSubjects = subject :: subjects val updatedRolesToSubjects = rolesToSubjects.updated(role, updatedSubjects) val linksByTarget = reasoner.linksByTarget.updated(target, updatedRolesToSubjects) - val updatedReasoner = `R+⟲𝒪`(link, `R⤳`(link, `R∘left`(link, `R∘right`(link, `R+∃right`(link, `R⊥right`(link, reasoner.copy(linksBySubject = linksBySubject, linksByTarget = linksByTarget), todo), todo), todo), todo), todo), todo) + val updatedReasoner = `R≤1-b`(link, `R∀-right`(link, `R∃ind-right`(link, `R+⟲𝒪`(link, `R⤳`(link, `R∘left`(link, `R∘right`(link, `R+∃right`(link, `R⊥right`(link, reasoner.copy(linksBySubject = linksBySubject, linksByTarget = linksByTarget), todo), todo), todo), todo), todo), todo), todo), todo), todo) val newState = link match { case Link(Nominal(subjectInd), aRole, Nominal(targetInd)) => updatedReasoner.ruleEngine.processRoleAssertion(RoleAssertion(aRole, subjectInd, targetInd), updatedReasoner, todo) case _ => updatedReasoner @@ -383,7 +396,7 @@ object Reasoner { // better for GO for { (right, conjunction) <- conjunctionsMatchingLeft - if (d2s(right)) + if d2s(right) } todo.push(`Sub+`(ConceptInclusion(c, conjunction))) } reasoner @@ -403,7 +416,7 @@ object Reasoner { } else { for { (left, conjunction) <- conjunctionsMatchingRight - if (d1s(left)) + if d1s(left) } todo.push(`Sub+`(ConceptInclusion(c, conjunction))) } reasoner @@ -474,6 +487,154 @@ object Reasoner { reasoner } + // Implementation of OWL RL eq-rep-o + private[this] def `R∃ind-left`(ci: ConceptInclusion, reasoner: ReasonerState, todo: mutable.Stack[QueueExpression]): ReasonerState = { + ci match { + case ConceptInclusion(subclass: Nominal, superclass: Nominal) => + for { + (r, subjects) <- reasoner.linksByTarget.getOrElse(superclass, Map.empty) + s <- subjects + } todo.push(Link(s, r, subclass)) + case _ => () + } + reasoner + } + + // Implementation of OWL RL eq-rep-o + private[this] def `R∃ind-right`(link: Link, reasoner: ReasonerState, todo: mutable.Stack[QueueExpression]): ReasonerState = { + link match { + case Link(s: Nominal, r, o: Nominal) => + for { + same <- reasoner.closureSubsBySuperclass.getOrElse(o, Set.empty).collect { + case n: Nominal => n + } + } todo.push(Link(s, r, same)) + case _ => () + } + reasoner + } + + // Implementation of OWL RL cls-avf + private[this] def `R∀-left`(ci: ConceptInclusion, reasoner: ReasonerState, todo: mutable.Stack[QueueExpression]): ReasonerState = { + val updatedRestrictionTypesByRole = ci match { + case ConceptInclusion(n: Nominal, UniversalRestriction(role, filler)) => + for { + target @ Nominal(_) <- reasoner.linksBySubject.getOrElse(n, Map.empty).getOrElse(role, Set.empty) + } todo.push(ConceptInclusion(target, filler)) + reasoner.universalRestrictionTypesByRole.updatedWith(n) { + case Some(rolesToFillers) => Some(rolesToFillers.updatedWith(role) { + case Some(fillers) => Some(filler :: fillers) + case None => Some(List(filler)) + }) + case None => Some(Map(role -> List(filler))) + } + case _ => reasoner.universalRestrictionTypesByRole + } + reasoner.copy(universalRestrictionTypesByRole = updatedRestrictionTypesByRole) + } + + // Implementation of OWL RL cls-avf + private[this] def `R∀-right`(link: Link, reasoner: ReasonerState, todo: mutable.Stack[QueueExpression]): ReasonerState = { + link match { + case Link(s: Nominal, role, o: Nominal) => + for { + filler <- reasoner.universalRestrictionTypesByRole.getOrElse(s, Map.empty).getOrElse(role, Nil) + } todo.push(ConceptInclusion(o, filler)) + case _ => () + } + reasoner + } + + // Max 1 restrictions rules may need some optimization to reduce repeated joins + // x ⊑ ≤1R.C ∧ x R y ∧ x R z ∧ y ⊑ C ∧ z ⊑ C + // assuming all role subsumptions get materialized for individuals + // this rule: x ⊑ ≤1R.C + private[this] def `R≤1-a`(ci: ConceptInclusion, reasoner: ReasonerState, todo: mutable.Stack[QueueExpression]): ReasonerState = + ci match { + case ConceptInclusion(subject: Nominal, MaxCardinalityRestriction(role, filler, cardinality)) + if cardinality == 1 => + val fillerSubclasses = reasoner.closureSubsBySuperclass.getOrElse(filler, Set.empty) + val targets = reasoner.linksBySubject.getOrElse(subject, Map.empty).getOrElse(role, Set.empty) + .collect { + case n: Nominal => n + } + .filter(fillerSubclasses) + if (targets.size > 1) { + for { + pair <- targets.toList.combinations(2) + } todo.push(ConceptInclusion(pair(0), pair(1)), ConceptInclusion(pair(1), pair(0))) + } + val updatedRestrictionTypesByRole = reasoner.maxOneCardinalityRestrictionTypesByRole.updatedWith(subject) { + case Some(rolesToFillers) => Some(rolesToFillers.updatedWith(role) { + case Some(fillers) => Some(filler :: fillers) + case None => Some(List(filler)) + }) + case None => Some(Map(role -> List(filler))) + } + val updatedRestrictionTypesByFiller = reasoner.maxOneCardinalityRestrictionTypesByFiller.updatedWith(filler) { + case Some(rolesToInstances) => Some(rolesToInstances.updatedWith(role) { + case Some(instances) => Some(subject :: instances) + case None => Some(List(subject)) + }) + case None => Some(Map(role -> List(subject))) + } + reasoner.copy(maxOneCardinalityRestrictionTypesByRole = updatedRestrictionTypesByRole, + maxOneCardinalityRestrictionTypesByFiller = updatedRestrictionTypesByFiller) + + case _ => reasoner + } + + // x ⊑ ≤1R.C ∧ x R y ∧ x R z ∧ y ⊑ C ∧ z ⊑ C + // this rule: x R y or x R z + private[this] def `R≤1-b`(link: Link, reasoner: ReasonerState, todo: mutable.Stack[QueueExpression]): ReasonerState = { + link match { + case Link(s: Nominal, role, o: Nominal) => + val fillers = reasoner.maxOneCardinalityRestrictionTypesByRole.getOrElse(s, Map.empty).getOrElse(role, Nil) + if (fillers.nonEmpty) { + val targets = reasoner.linksBySubject.getOrElse(s, Map.empty).getOrElse(role, Set.empty) + .collect { + case n: Nominal => n + } + for { + filler <- fillers + fillerSubclasses = reasoner.closureSubsBySuperclass.getOrElse(filler, Set.empty) + target <- targets.iterator.filter(fillerSubclasses) + if target != o + } { + todo.push(ConceptInclusion(o, target)) + todo.push(ConceptInclusion(target, o)) + } + } + case _ => () + } + reasoner + } + + // x ⊑ ≤1R.C ∧ x R y ∧ x R z ∧ y ⊑ C ∧ z ⊑ C + // this rule: y ⊑ C or z ⊑ C + private[this] def `R≤1-c`(ci: ConceptInclusion, reasoner: ReasonerState, todo: mutable.Stack[QueueExpression]): ReasonerState = { + ci match { + case ConceptInclusion(target: Nominal, filler) => + val restrictionInstancesByRole = reasoner.maxOneCardinalityRestrictionTypesByFiller.getOrElse(filler, Map.empty) + if (restrictionInstancesByRole.nonEmpty) { + for { + (role, subjects) <- restrictionInstancesByRole + subject <- subjects + otherTarget <- reasoner.linksBySubject.getOrElse(subject, Map.empty).getOrElse(role, Set.empty).iterator + .collect { + case n: Nominal => n + } + if otherTarget != target + } { + todo.push(ConceptInclusion(otherTarget, target)) + todo.push(ConceptInclusion(target, otherTarget)) + } + } + case _ => () + } + reasoner + } + private[this] def `R-⟲`(ci: ConceptInclusion, reasoner: ReasonerState, todo: mutable.Stack[QueueExpression]): ReasonerState = ci match { case ConceptInclusion(sub, SelfRestriction(role)) => todo.push(Link(sub, role, sub)) diff --git a/modules/core/src/main/scala/org/geneontology/whelk/ReteNodes.scala b/modules/core/src/main/scala/org/geneontology/whelk/ReteNodes.scala index e84313c..1d12798 100644 --- a/modules/core/src/main/scala/org/geneontology/whelk/ReteNodes.scala +++ b/modules/core/src/main/scala/org/geneontology/whelk/ReteNodes.scala @@ -187,8 +187,18 @@ final case class RoleAtomJoinNode(atom: RoleAtom, children: List[BetaNode], spec if ((individualSubject == assertion.subject) && (individualTarget == assertion.target)) parentMem.tokens else Nil } - //FIXME missing ind with unbound var? both directions - case (_, _) => (assertion: RoleAssertion, parentMem: BetaMemory) => parentMem.tokens.map(t => t.extend(makeBindings(assertion))) + case (individualSubject: Individual, _: Variable) => + (assertion: RoleAssertion, parentMem: BetaMemory) => { + if (individualSubject == assertion.subject) parentMem.tokens.map(t => t.extend(makeBindings(assertion))) + else Nil + } + case (_: Variable, individualTarget: Individual) => + (assertion: RoleAssertion, parentMem: BetaMemory) => { + if (individualTarget == assertion.target) parentMem.tokens.map(t => t.extend(makeBindings(assertion))) + else Nil + } + case (_, _) => (assertion: RoleAssertion, parentMem: BetaMemory) => + parentMem.tokens.map(t => t.extend(makeBindings(assertion))) } @@ -270,10 +280,21 @@ final case class ProductionNode(rule: Rule) extends BetaNode { atom <- rule.head } { atom match { - case RoleAtom(role, subj, obj) => + case RoleAtom(BuiltIn.SameAs, subj, obj) => + todo.push(ConceptInclusion(Nominal(fillVariable(subj, token)), Nominal(fillVariable(obj, token)))) + todo.push(ConceptInclusion(Nominal(fillVariable(obj, token)), Nominal(fillVariable(subj, token)))) + case RoleAtom(BuiltIn.DifferentFrom, subj, obj) => + todo.push(ConceptInclusion(Conjunction(Nominal(fillVariable(subj, token)), Nominal(fillVariable(obj, token))), BuiltIn.Bottom)) + case RoleAtom(role, subj, obj) => todo.push(Link(Nominal(fillVariable(subj, token)), role, Nominal(fillVariable(obj, token)))) - case ConceptAtom(concept, arg) => + case ConceptAtom(concept, arg) => todo.push(ConceptInclusion(Nominal(fillVariable(arg, token)), concept)) + case SameIndividualsAtom(_, _) => + ??? + // Should never happen; SameIndividualsAtom should be internally handled as RoleAtom + case DifferentIndividualsAtom(_, _) => + ??? + // Should never happen; SameIndividualsAtom should be internally handled as RoleAtom } } reasoner diff --git a/modules/core/src/main/scala/org/geneontology/whelk/RuleEngine.scala b/modules/core/src/main/scala/org/geneontology/whelk/RuleEngine.scala index 925e816..2c9ceb1 100644 --- a/modules/core/src/main/scala/org/geneontology/whelk/RuleEngine.scala +++ b/modules/core/src/main/scala/org/geneontology/whelk/RuleEngine.scala @@ -4,9 +4,16 @@ import scala.collection.mutable final case class RuleEngine(rules: Set[Rule]) { + val equalityRules = Set( + Rule( + body = List(RoleAtom(BuiltIn.SameAs, Variable("x"), Variable("y"))), + head = List(RoleAtom(BuiltIn.SameAs, Variable("y"), Variable("x"))) + ) + ) + val (conceptAlphaIndex: Map[Concept, ConceptAtomAlphaNode], roleAlphaIndex: Map[Role, RoleAtomAlphaNode], - allJoinSpecs: Set[JoinNodeSpec]) = constructReteNetwork(rules) + allJoinSpecs: Set[JoinNodeSpec]) = constructReteNetwork(rules ++ equalityRules) def emptyMemory: WorkingMemory = WorkingMemory( conceptAlphaIndex.keys.map(c => c -> ConceptAlphaMemory.empty).toMap, @@ -17,11 +24,26 @@ final case class RuleEngine(rules: Set[Rule]) { def processConceptAssertion(assertion: ConceptAssertion, reasoner: ReasonerState, todo: mutable.Stack[QueueExpression]): ReasonerState = conceptAlphaIndex.get(assertion.concept).map(node => node.activate(assertion.individual, reasoner, todo)).getOrElse(reasoner) - def processRoleAssertion(assertion: RoleAssertion, reasoner: ReasonerState, todo: mutable.Stack[QueueExpression]): ReasonerState = + def processRoleAssertion(assertion: RoleAssertion, reasoner: ReasonerState, todo: mutable.Stack[QueueExpression]): ReasonerState = { roleAlphaIndex.get(assertion.role).map(node => node.activate(assertion, reasoner, todo)).getOrElse(reasoner) + } private def constructReteNetwork(rules: Set[Rule]): (Map[Concept, ConceptAtomAlphaNode], Map[Role, RoleAtomAlphaNode], Set[JoinNodeSpec]) = { - val nodes = rules.foldLeft(Set.empty[BetaNode]) { (nodes, rule) => + // We handle sameAs and differentFrom using role atom join nodes + val normalized = rules.map { rule => + Rule( + rule.body.map { + case SameIndividualsAtom(left, right) => RoleAtom(BuiltIn.SameAs, left, right) + case DifferentIndividualsAtom(left, right) => RoleAtom(BuiltIn.DifferentFrom, left, right) + case other => other + }, + rule.head.map { + case SameIndividualsAtom(left, right) => RoleAtom(BuiltIn.SameAs, left, right) + case DifferentIndividualsAtom(left, right) => RoleAtom(BuiltIn.DifferentFrom, left, right) + case other => other + }) + } + val nodes = normalized.foldLeft(Set.empty[BetaNode]) { (nodes, rule) => val (_, allConceptNodes, allRoleNodes) = processRuleAtoms(rule.body, Nil, rule, Map.empty, Map.empty) nodes ++ allConceptNodes.values ++ allRoleNodes.values } @@ -48,14 +70,20 @@ final case class RuleEngine(rules: Set[Rule]) { val spec = JoinNodeSpec(thisPatternSequence) val (child, updatedExistingC, updatedExistingR) = processRuleAtoms(rest, thisPatternSequence, rule, existingC, existingR) atom match { - case ca: ConceptAtom => + case ca: ConceptAtom => val node = existingC.getOrElse(spec, ConceptAtomJoinNode(ca, Nil, spec)) val updatedNode = node.copy(children = child :: node.children) (updatedNode, updatedExistingC.updated(spec, updatedNode), updatedExistingR) - case ra: RoleAtom => + case ra: RoleAtom => val node = existingR.getOrElse(spec, RoleAtomJoinNode(ra, Nil, spec)) val updatedNode = node.copy(children = child :: node.children) (updatedNode, updatedExistingC, updatedExistingR.updated(spec, updatedNode)) + case _: SameIndividualsAtom => + // Should never happen; SameIndividualsAtom should be internally handled as RoleAtom + ??? + case _: DifferentIndividualsAtom => + // Should never happen; SameIndividualsAtom should be internally handled as RoleAtom + ??? } case Nil => (ProductionNode(rule), existingC, existingR) } diff --git a/modules/owlapi/src/main/scala/org/geneontology/whelk/Bridge.scala b/modules/owlapi/src/main/scala/org/geneontology/whelk/Bridge.scala index 6eaa8bc..ac2236d 100644 --- a/modules/owlapi/src/main/scala/org/geneontology/whelk/Bridge.scala +++ b/modules/owlapi/src/main/scala/org/geneontology/whelk/Bridge.scala @@ -32,7 +32,7 @@ object Bridge { case first :: second :: Nil => Set(ConceptInclusion(first, second), ConceptInclusion(second, first)) case _ => ??? //impossible }.toSet - case DisjointClasses(_, operands) if operands.size == 2 => //FIXME handle >2 + case DisjointClasses(_, operands) => val converted = operands.map(convertExpression).toList.collect { case Some(concept) => concept } converted.combinations(2).flatMap { case first :: second :: Nil => Set(ConceptInclusion(Conjunction(first, second), Bottom)) @@ -45,9 +45,18 @@ object Bridge { case ObjectPropertyAssertion(_, ObjectInverseOf(ObjectProperty(prop)), NamedIndividual(obj), NamedIndividual(subj)) => Set(ConceptInclusion(Nominal(WIndividual(subj.toString)), ExistentialRestriction(Role(prop.toString), Nominal(WIndividual(obj.toString))))) case EquivalentObjectProperties(_, propertyExpressions) => + //FIXME handle inverse property expression? val properties = propertyExpressions.collect { case p @ ObjectProperty(_) => p }.toList properties.combinations(2).flatMap { - case ObjectProperty(first) :: ObjectProperty(second) :: Nil => Set(RoleInclusion(Role(first.toString), Role(second.toString))) + case ObjectProperty(first) :: ObjectProperty(second) :: Nil => + val role1 = Role(first.toString) + val role2 = Role(second.toString) + Set( + RoleInclusion(role1, role2), + RoleInclusion(role2, role1), + Rule(body = List(RoleAtom(role1, WVariable("x"), WVariable("y"))), head = List(RoleAtom(role2, WVariable("x"), WVariable("y")))), + Rule(body = List(RoleAtom(role2, WVariable("x"), WVariable("y"))), head = List(RoleAtom(role1, WVariable("x"), WVariable("y")))) + ) case _ => ??? //impossible }.toSet case SubObjectPropertyOf(_, ObjectProperty(subproperty), ObjectProperty(superproperty)) => @@ -75,6 +84,22 @@ object Bridge { val compositionProperty: OWLObjectProperty = ObjectProperty(s"$CompositionRolePrefix${first.getIRI}${second.getIRI}") convertAxiom(SubObjectPropertyChainOf(List(first, second), compositionProperty)) ++ convertAxiom(SubObjectPropertyChainOf(compositionProperty :: more, superproperty)) + case DisjointObjectProperties(_, properties) => + properties.toList.combinations(2).flatMap { + case first :: second :: Nil => + val firstRoleAtom = first match { + case ObjectProperty(iri) => RoleAtom(Role(iri.toString), WVariable("x"), WVariable("y")) + case ObjectInverseOf(ObjectProperty(iri)) => RoleAtom(Role(iri.toString), WVariable("y"), WVariable("x")) + } + val secondRoleAtom = second match { + case ObjectProperty(iri) => RoleAtom(Role(iri.toString), WVariable("x"), WVariable("y")) + case ObjectInverseOf(ObjectProperty(iri)) => RoleAtom(Role(iri.toString), WVariable("y"), WVariable("x")) + } + Set( + Rule(body = List(firstRoleAtom, secondRoleAtom), head = List(ConceptAtom(Bottom, WVariable("x")), ConceptAtom(Bottom, WVariable("y")))) + ) + case _ => ??? //impossible + }.toSet case TransitiveObjectProperty(_, ObjectProperty(property)) => val role = Role(property.toString) Set( @@ -83,6 +108,31 @@ object Bridge { case ReflexiveObjectProperty(_, ObjectProperty(property)) => Set( ConceptInclusion(Top, SelfRestriction(Role(property.toString))) ) + case FunctionalObjectProperty(_, ObjectProperty(property)) => + val role = Role(property.toString) + Set( + Rule(body = List(RoleAtom(role, WVariable("x"), WVariable("y1")), RoleAtom(role, WVariable("x"), WVariable("y2"))), head = List(SameIndividualsAtom(WVariable("y1"), WVariable("y2")))) + ) + case InverseFunctionalObjectProperty(_, ObjectProperty(property)) => + val role = Role(property.toString) + Set( + Rule(body = List(RoleAtom(role, WVariable("x1"), WVariable("y")), RoleAtom(role, WVariable("x2"), WVariable("y"))), head = List(SameIndividualsAtom(WVariable("x1"), WVariable("x2")))) + ) + case IrreflexiveObjectProperty(_, ObjectProperty(property)) => + val role = Role(property.toString) + Set( + Rule(body = List(RoleAtom(role, WVariable("x"), WVariable("x"))), head = List(ConceptAtom(BuiltIn.Bottom, WVariable("x")))) + ) + case SymmetricObjectProperty(_, ObjectProperty(property)) => + val role = Role(property.toString) + Set( + Rule(body = List(RoleAtom(role, WVariable("x"), WVariable("y"))), head = List(RoleAtom(role, WVariable("y"), WVariable("x")))) + ) + case AsymmetricObjectProperty(_, ObjectProperty(property)) => + val role = Role(property.toString) + Set( + Rule(body = List(RoleAtom(role, WVariable("x"), WVariable("y")), RoleAtom(role, WVariable("y"), WVariable("x"))), head = List(ConceptAtom(BuiltIn.Bottom, WVariable("x")), ConceptAtom(BuiltIn.Bottom, WVariable("y")))) + ) case ObjectPropertyDomain(_, ObjectProperty(property), ce) => convertExpression(ce).map(concept => ConceptInclusion(ExistentialRestriction(Role(property.toString), Top), concept)).toSet case ObjectPropertyRange(_, ObjectProperty(property), ce) => convertExpression(ce).map(concept => @@ -94,6 +144,27 @@ object Bridge { Set( Rule(body = List(RoleAtom(roleP, x1, x2)), head = List(RoleAtom(roleQ, x2, x1))), Rule(body = List(RoleAtom(roleQ, x1, x2)), head = List(RoleAtom(roleP, x2, x1)))) + case NegativeObjectPropertyAssertion(_, ObjectProperty(p), NamedIndividual(x), NamedIndividual(y)) => + val xInd = WIndividual(x.toString) + val yInd = WIndividual(y.toString) + Set( + Rule(body = List(RoleAtom(Role(p.toString), xInd, yInd)), head = List(ConceptAtom(Bottom, xInd), ConceptAtom(Bottom, yInd))) + ) + case SameIndividual(_, individuals) => + individuals.toList.combinations(2).flatMap { + case NamedIndividual(first) :: NamedIndividual(second) :: Nil => Set( + ConceptInclusion(Nominal(WIndividual(first.toString)), Nominal(WIndividual(second.toString))), + ConceptInclusion(Nominal(WIndividual(second.toString)), Nominal(WIndividual(first.toString))) + ) + case _ => ??? //impossible + }.toSet + case DifferentIndividuals(_, individuals) => + individuals.toList.combinations(2).flatMap { + case NamedIndividual(first) :: NamedIndividual(second) :: Nil => Set( + ConceptInclusion(Conjunction(Nominal(WIndividual(first.toString)), Nominal(WIndividual(second.toString))), Bottom), + ) + case _ => ??? //impossible + }.toSet case DLSafeRule(_, body, head) => (for { bodyAtoms <- convertAtomSet(body) headAtoms <- convertAtomSet(head) @@ -103,13 +174,15 @@ object Bridge { Set.empty } - //TODO ObjectOneOf def convertExpression(expression: OWLClassExpression): Option[Concept] = { expression match { case OWLThing => Some(Top) case OWLNothing => Some(Bottom) case Class(iri) => Some(AtomicConcept(iri.toString)) case ObjectSomeValuesFrom(ObjectProperty(prop), filler) => convertExpression(filler).map(ExistentialRestriction(Role(prop.toString), _)) + case ObjectAllValuesFrom(ObjectProperty(prop), filler) => convertExpression(filler).map(UniversalRestriction(Role(prop.toString), _)) + case ObjectMaxCardinality(0, ObjectProperty(prop), filler) => convertExpression(filler).map(c => Complement(ExistentialRestriction(Role(prop.toString), c))) + case ObjectMaxCardinality(1, ObjectProperty(prop), filler) => convertExpression(filler).map(MaxCardinalityRestriction(Role(prop.toString), _, 1)) case ObjectHasSelf(ObjectProperty(prop)) => Some(SelfRestriction(Role(prop.toString))) case ObjectIntersectionOf(operands) if operands.nonEmpty => def convert(items: List[Concept]): Concept = items match { @@ -122,7 +195,13 @@ object Bridge { case ObjectUnionOf(operands) => operands.toList.map(convertExpression).sequence.map(_.toSet).map(Disjunction) case ObjectComplementOf(concept) => convertExpression(concept).map(Complement) - case ObjectOneOf(individuals) if individuals.size == 1 => individuals.collectFirst { case NamedIndividual(iri) => Nominal(WIndividual(iri.toString)) } + case ObjectOneOf(individuals) => + val operands = individuals.collect { + case NamedIndividual(iri) => Nominal(WIndividual(iri.toString)) + } + if (operands.isEmpty) None + else if (operands.size == 1) operands.headOption + else Some(Disjunction(operands.toSet[Concept])) case ObjectHasValue(ObjectProperty(prop), NamedIndividual(ind)) => Some(ExistentialRestriction(Role(prop.toString), Nominal(WIndividual(ind.toString)))) case DataSomeValuesFrom(DataProperty(prop), range) => Some(DataRestriction(DataRole(prop.toString), DataRange(range))) //scowl is missing DataHasValue @@ -145,7 +224,16 @@ object Bridge { subject <- convertAtomArg(subj) target <- convertAtomArg(obj) } yield RoleAtom(Role(iri.toString), subject, target) - case ObjectPropertyAtom(ObjectInverseOf(prop @ ObjectProperty(_)), subj, obj) => convertRuleAtom(ObjectPropertyAtom(prop, obj, subj)) + case ObjectPropertyAtom(ObjectInverseOf(prop @ ObjectProperty(_)), subj, obj) => + convertRuleAtom(ObjectPropertyAtom(prop, obj, subj)) + case swrl.SameIndividualAtom(left, right) => for { + subject <- convertAtomArg(left) + target <- convertAtomArg(right) + } yield RoleAtom(SameAs, subject, target) + case swrl.DifferentIndividualsAtom(left, right) => for { + subject <- convertAtomArg(left) + target <- convertAtomArg(right) + } yield RoleAtom(DifferentFrom, subject, target) case _ => None } diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/incremental-allvaluesfrom.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/incremental-allvaluesfrom.ofn new file mode 100644 index 0000000..e90729c --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/incremental-allvaluesfrom.ofn @@ -0,0 +1,42 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) + +############################ +# Classes +############################ + +# Class: () + +EquivalentClasses( ObjectAllValuesFrom( )) + +# Class: () + +SubClassOf( ) + + +############################ +# Named Individuals +############################ + +# Individual: () + +ClassAssertion( ) +ObjectPropertyAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cax-dw-adc.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cax-dw-adc.ofn new file mode 100644 index 0000000..58e5466 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cax-dw-adc.ofn @@ -0,0 +1,57 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +############################ +# Object Properties +############################ + +# Object Property: () + +SubObjectPropertyOf( ) + + +############################ +# Classes +############################ + +# Class: () + + +# Class: () + + +# Class: () + + +# Class: () + +SubClassOf( ) + + +############################ +# Named Individuals +############################ + +# Individual: () + +ClassAssertion( ) +ClassAssertion( ) + + +DisjointClasses( ) +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cax-eqc.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cax-eqc.ofn new file mode 100644 index 0000000..73784fd --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cax-eqc.ofn @@ -0,0 +1,45 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) + +############################ +# Classes +############################ + +# Class: () + +EquivalentClasses( ) + +# Class: () + +EquivalentClasses( ) + + +############################ +# Named Individuals +############################ + +# Individual: () + +ClassAssertion( ) + +# Individual: () + +ClassAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cax-sco.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cax-sco.ofn new file mode 100644 index 0000000..9482e84 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cax-sco.ofn @@ -0,0 +1,39 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) + +############################ +# Classes +############################ + +# Class: () + +SubClassOf( ) + +# Class: () + +SubClassOf( ) + + +############################ +# Named Individuals +############################ + +# Individual: () + +ClassAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-avf.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-avf.ofn new file mode 100644 index 0000000..6f39a6a --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-avf.ofn @@ -0,0 +1,36 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) + +############################ +# Classes +############################ + +# Class: () + +EquivalentClasses( ObjectAllValuesFrom( )) + + +############################ +# Named Individuals +############################ + +# Individual: () + +ClassAssertion( ) +ObjectPropertyAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-com.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-com.ofn new file mode 100644 index 0000000..c3eda86 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-com.ofn @@ -0,0 +1,35 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) + +############################ +# Classes +############################ + +# Class: () + +EquivalentClasses( ObjectComplementOf()) + + +############################ +# Named Individuals +############################ + +# Individual: () + +ClassAssertion( ) +ClassAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-hv.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-hv.ofn new file mode 100644 index 0000000..61da4d3 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-hv.ofn @@ -0,0 +1,39 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) + +############################ +# Classes +############################ + +# Class: () + +EquivalentClasses( ObjectHasValue( )) + + +############################ +# Named Individuals +############################ + +# Individual: () + +ClassAssertion( ) + +# Individual: () + +ObjectPropertyAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-int.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-int.ofn new file mode 100644 index 0000000..88d3a13 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-int.ofn @@ -0,0 +1,43 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) + +############################ +# Classes +############################ + +# Class: () + +EquivalentClasses( ObjectIntersectionOf( )) + + +############################ +# Named Individuals +############################ + +# Individual: () + +ClassAssertion( ) +ClassAssertion( ) +ClassAssertion( ) + +# Individual: () + +ClassAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-maxqc0.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-maxqc0.ofn new file mode 100644 index 0000000..465accd --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-maxqc0.ofn @@ -0,0 +1,54 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +############################ +# Object Properties +############################ + +# Object Property: () + +SubObjectPropertyOf( ) + + +############################ +# Classes +############################ + +# Class: () + +SubClassOf( ObjectMaxCardinality(0 )) + +# Class: () + +SubClassOf( ) + + +############################ +# Named Individuals +############################ + +# Individual: () + +ClassAssertion( ) +ObjectPropertyAssertion( ) + +# Individual: () + +ClassAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-maxqc1.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-maxqc1.ofn new file mode 100644 index 0000000..02cda34 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-maxqc1.ofn @@ -0,0 +1,48 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) + +############################ +# Classes +############################ + +# Class: () + +SubClassOf( ObjectMaxCardinality(1 )) + + +############################ +# Named Individuals +############################ + +# Individual: () + +ClassAssertion( ) +ObjectPropertyAssertion( ) +ObjectPropertyAssertion( ) + +# Individual: () + +ClassAssertion( ) +ClassAssertion( ) + +# Individual: () + +ClassAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-oo.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-oo.ofn new file mode 100644 index 0000000..8f4c55b --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-oo.ofn @@ -0,0 +1,38 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) + +############################ +# Classes +############################ + +# Class: () + +SubClassOf( ObjectMaxCardinality(1 )) + +# Class: () + +SubClassOf( ) + +# Class: () + +EquivalentClasses( ObjectOneOf( )) +SubClassOf( ) + + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-svf.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-svf.ofn new file mode 100644 index 0000000..6201db1 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-svf.ofn @@ -0,0 +1,50 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) + +############################ +# Classes +############################ + +# Class: () + +EquivalentClasses( ObjectSomeValuesFrom( )) + +# Class: () + +EquivalentClasses( ObjectSomeValuesFrom( owl:Thing)) + + +############################ +# Named Individuals +############################ + +# Individual: () + +ObjectPropertyAssertion( ) + +# Individual: () + +ObjectPropertyAssertion( ) + +# Individual: () + +ClassAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-uni.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-uni.ofn new file mode 100644 index 0000000..31d716a --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/cls-uni.ofn @@ -0,0 +1,41 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) + +############################ +# Classes +############################ + +# Class: () + +EquivalentClasses( ObjectUnionOf( )) + + +############################ +# Named Individuals +############################ + +# Individual: () + +ClassAssertion( ) + +# Individual: () + +ClassAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/eq-diff.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/eq-diff.ofn new file mode 100644 index 0000000..3e64159 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/eq-diff.ofn @@ -0,0 +1,28 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) + +############################ +# Named Individuals +############################ + +# Individual: () + +SameIndividual( ) + +# Individual: () + + + +DifferentIndividuals( ) +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/eq-ref.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/eq-ref.ofn new file mode 100644 index 0000000..58ce049 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/eq-ref.ofn @@ -0,0 +1,28 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) + + +############################ +# Named Individuals +############################ + +# Individual: () + +ObjectPropertyAssertion( ) + + +DLSafeRule(Body(SameIndividualAtom(Variable() Variable()))Head(ClassAtom( Variable()))) +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/eq-rep.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/eq-rep.ofn new file mode 100644 index 0000000..8dcb738 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/eq-rep.ofn @@ -0,0 +1,38 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) + + +############################ +# Named Individuals +############################ + +# Individual: () + +SameIndividual( ) + +# Individual: () + +ObjectPropertyAssertion( ) + +# Individual: () + +SameIndividual( ) + + +DLSafeRule(Body(SameIndividualAtom(Variable() ))Head(ClassAtom( Variable()))) +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/eq-sym.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/eq-sym.ofn new file mode 100644 index 0000000..dec8cb7 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/eq-sym.ofn @@ -0,0 +1,28 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) + + +############################ +# Named Individuals +############################ + +# Individual: () + +SameIndividual( ) + + +DLSafeRule(Body(SameIndividualAtom(Variable() ))Head(ClassAtom( Variable()))) +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/eq-trans.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/eq-trans.ofn new file mode 100644 index 0000000..ac7af98 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/eq-trans.ofn @@ -0,0 +1,33 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) + + +############################ +# Named Individuals +############################ + +# Individual: () + +SameIndividual( ) + +# Individual: () + +SameIndividual( ) + + +DLSafeRule(Body(SameIndividualAtom(Variable() ))Head(ClassAtom( Variable()))) +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-asyp.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-asyp.ofn new file mode 100644 index 0000000..8808e12 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-asyp.ofn @@ -0,0 +1,43 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +############################ +# Object Properties +############################ + +# Object Property: () + +SubObjectPropertyOf( ) +AsymmetricObjectProperty() + + + +############################ +# Named Individuals +############################ + +# Individual: () + +ClassAssertion( ) +ObjectPropertyAssertion( ) + +# Individual: () + +ObjectPropertyAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-dom.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-dom.ofn new file mode 100644 index 0000000..39ce6df --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-dom.ofn @@ -0,0 +1,41 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +############################ +# Object Properties +############################ + +# Object Property: () + +SubObjectPropertyOf( ) +ObjectPropertyDomain( ) + +# Object Property: () + +ObjectPropertyDomain( ) + + + +############################ +# Named Individuals +############################ + +# Individual: () + +ObjectPropertyAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-eqp.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-eqp.ofn new file mode 100644 index 0000000..c6cf3b2 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-eqp.ofn @@ -0,0 +1,50 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +############################ +# Object Properties +############################ + +# Object Property: () + +EquivalentObjectProperties( ) + +# Object Property: () + +EquivalentObjectProperties( ) + + + +############################ +# Named Individuals +############################ + +# Individual: () + +ObjectPropertyAssertion( ) + +# Individual: () + +ObjectPropertyAssertion( ) + +# Individual: () + +ObjectPropertyAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-fp.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-fp.ofn new file mode 100644 index 0000000..803dcd7 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-fp.ofn @@ -0,0 +1,43 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +############################ +# Object Properties +############################ + +# Object Property: () + +SubObjectPropertyOf( ) +FunctionalObjectProperty() + + + +############################ +# Named Individuals +############################ + +# Individual: () + +ObjectPropertyAssertion( ) +ObjectPropertyAssertion( ) + +# Individual: () + +ClassAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-ifp.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-ifp.ofn new file mode 100644 index 0000000..8caddbb --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-ifp.ofn @@ -0,0 +1,43 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +############################ +# Object Properties +############################ + +# Object Property: () + +SubObjectPropertyOf( ) +InverseFunctionalObjectProperty() + + + +############################ +# Named Individuals +############################ + +# Individual: () + +ClassAssertion( ) +ObjectPropertyAssertion( ) + +# Individual: () + +ObjectPropertyAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-inv.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-inv.ofn new file mode 100644 index 0000000..7f8dde8 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-inv.ofn @@ -0,0 +1,46 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +############################ +# Object Properties +############################ + +# Object Property: () + +InverseObjectProperties( ) + + + +############################ +# Named Individuals +############################ + +# Individual: () + +ObjectPropertyAssertion( ) + +# Individual: () + +ObjectPropertyAssertion( ) + +# Individual: () + +ObjectPropertyAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-irp.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-irp.ofn new file mode 100644 index 0000000..6110642 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-irp.ofn @@ -0,0 +1,41 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +############################ +# Object Properties +############################ + +# Object Property: () + +SubObjectPropertyOf( ) +IrreflexiveObjectProperty() + + + +############################ +# Named Individuals +############################ + +# Individual: () + +ObjectPropertyAssertion( ) + +# Individual: () + +ObjectPropertyAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-npa.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-npa.ofn new file mode 100644 index 0000000..2660d7a --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-npa.ofn @@ -0,0 +1,47 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +############################ +# Object Properties +############################ + +# Object Property: () + +InverseObjectProperties( ) + + + +############################ +# Named Individuals +############################ + +# Individual: () + +ObjectPropertyAssertion( ) +NegativeObjectPropertyAssertion( ) + +# Individual: () + +ObjectPropertyAssertion( ) + +# Individual: () + +ObjectPropertyAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-pdw-adp.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-pdw-adp.ofn new file mode 100644 index 0000000..f409d56 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-pdw-adp.ofn @@ -0,0 +1,53 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +############################ +# Object Properties +############################ + +# Object Property: () + +SubObjectPropertyOf( ) +ObjectPropertyDomain( ) +DisjointObjectProperties( ) + +# Object Property: () + +ObjectPropertyDomain( ) +DisjointObjectProperties( ) + + + +############################ +# Named Individuals +############################ + +# Individual: () + +ObjectPropertyAssertion( ) +ObjectPropertyAssertion( ) + +# Individual: () + +ObjectPropertyAssertion( ) +ObjectPropertyAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-rng.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-rng.ofn new file mode 100644 index 0000000..ee08382 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-rng.ofn @@ -0,0 +1,41 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +############################ +# Object Properties +############################ + +# Object Property: () + +SubObjectPropertyOf( ) +ObjectPropertyRange( ) + +# Object Property: () + +ObjectPropertyRange( ) + + + +############################ +# Named Individuals +############################ + +# Individual: () + +ObjectPropertyAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-spo.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-spo.ofn new file mode 100644 index 0000000..fb87419 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-spo.ofn @@ -0,0 +1,51 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +############################ +# Object Properties +############################ + +# Object Property: () + +SubObjectPropertyOf( ) + + + +############################ +# Named Individuals +############################ + +# Individual: () + +ClassAssertion( ) +ObjectPropertyAssertion( ) + +# Individual: () + +ObjectPropertyAssertion( ) + +# Individual: () + +ObjectPropertyAssertion( ) + + +SubObjectPropertyOf(ObjectPropertyChain( ) ) +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-symp.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-symp.ofn new file mode 100644 index 0000000..94b1316 --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-symp.ofn @@ -0,0 +1,39 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +############################ +# Object Properties +############################ + +# Object Property: () + +SubObjectPropertyOf( ) +SymmetricObjectProperty() + + + +############################ +# Named Individuals +############################ + +# Individual: () + +ClassAssertion( ) +ObjectPropertyAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-trp.ofn b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-trp.ofn new file mode 100644 index 0000000..8c45fdb --- /dev/null +++ b/modules/owlapi/src/test/resources/org/geneontology/whelk/owlrl/prp-trp.ofn @@ -0,0 +1,50 @@ +Prefix(:=) +Prefix(owl:=) +Prefix(rdf:=) +Prefix(xml:=) +Prefix(xsd:=) +Prefix(rdfs:=) + + +Ontology( + +Declaration(Class()) +Declaration(Class()) +Declaration(ObjectProperty()) +Declaration(ObjectProperty()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +Declaration(NamedIndividual()) +############################ +# Object Properties +############################ + +# Object Property: () + +SubObjectPropertyOf( ) + +# Object Property: () + +TransitiveObjectProperty() + + + +############################ +# Named Individuals +############################ + +# Individual: () + +ClassAssertion( ) +ObjectPropertyAssertion( ) + +# Individual: () + +ObjectPropertyAssertion( ) + +# Individual: () + +ObjectPropertyAssertion( ) + + +) \ No newline at end of file diff --git a/modules/owlapi/src/test/scala/org/geneontology/whelk/TestIncrementalAllValuesFrom.scala b/modules/owlapi/src/test/scala/org/geneontology/whelk/TestIncrementalAllValuesFrom.scala new file mode 100644 index 0000000..5e9608e --- /dev/null +++ b/modules/owlapi/src/test/scala/org/geneontology/whelk/TestIncrementalAllValuesFrom.scala @@ -0,0 +1,26 @@ +package org.geneontology.whelk + +import org.semanticweb.owlapi.apibinding.OWLManager +import utest._ + +object TestIncrementalAllValuesFrom extends TestSuite { + + val tests: Tests = Tests { + "New allValuesFrom subsumptions should classify individuals" - { + val manager = OWLManager.createOWLOntologyManager() + val ontology = manager.loadOntologyFromOntologyDocument(this.getClass.getResourceAsStream("incremental-allvaluesfrom.ofn")) + val axioms = Bridge.ontologyToAxioms(ontology) + val whelk = Reasoner.assert(axioms) + val B = AtomicConcept("http://example.org/B") + val C = AtomicConcept("http://example.org/C") + val D = AtomicConcept("http://example.org/D") + val x = Nominal(Individual("http://example.org/x")) + val y = Nominal(Individual("http://example.org/y")) + val newAxioms = Set(ConceptInclusion(C, D)) + val newWhelk = Reasoner.assert(newAxioms, whelk) + val subclasses = newWhelk.closureSubsBySuperclass(B) + assert(subclasses(y)) + } + } + +} diff --git a/modules/owlapi/src/test/scala/org/geneontology/whelk/TestIncrementalIntersections.scala b/modules/owlapi/src/test/scala/org/geneontology/whelk/TestIncrementalIntersections.scala index 3023738..e9b728c 100644 --- a/modules/owlapi/src/test/scala/org/geneontology/whelk/TestIncrementalIntersections.scala +++ b/modules/owlapi/src/test/scala/org/geneontology/whelk/TestIncrementalIntersections.scala @@ -5,7 +5,7 @@ import utest._ object TestIncrementalIntersections extends TestSuite { - val tests = Tests { + val tests: Tests = Tests { "New intersection definitions should be properly classified" - { val manager = OWLManager.createOWLOntologyManager() val ontology = manager.loadOntologyFromOntologyDocument(this.getClass.getResourceAsStream("incremental-intersections.ofn")) diff --git a/modules/owlapi/src/test/scala/org/geneontology/whelk/TestIncrementalMaxOneCardinality.scala b/modules/owlapi/src/test/scala/org/geneontology/whelk/TestIncrementalMaxOneCardinality.scala new file mode 100644 index 0000000..cc05f12 --- /dev/null +++ b/modules/owlapi/src/test/scala/org/geneontology/whelk/TestIncrementalMaxOneCardinality.scala @@ -0,0 +1,78 @@ +package org.geneontology.whelk + +import scala.util.chaining._ +import utest._ + +object TestIncrementalMaxOneCardinality extends TestSuite { + + val tests: Tests = Tests { + "Max 1 cardinality inferences should work in any order" - { + val A = AtomicConcept("http://example.org/A") + val B = AtomicConcept("http://example.org/B") + val C = AtomicConcept("http://example.org/C") + val D = AtomicConcept("http://example.org/D") + val r = Role("http://example.org/r") + val Max1rC = MaxCardinalityRestriction(r, C, 1) + val x = Nominal(Individual("http://example.org/x")) + val y = Nominal(Individual("http://example.org/y")) + val z = Nominal(Individual("http://example.org/z")) + val ASubMax1rC = ConceptInclusion(A, Max1rC) + val BSubA = ConceptInclusion(B, A) + val xTypeB = ConceptInclusion(x, B) + val DSubC = ConceptInclusion(D, C) + val yTypeD = ConceptInclusion(y, D) + val zTypeD = ConceptInclusion(z, D) + val xry = ConceptInclusion(x, ExistentialRestriction(r, y)) + val xrz = ConceptInclusion(x, ExistentialRestriction(r, z)) + Reasoner.assert(Set(ASubMax1rC)) + .pipe(Reasoner.assert(Set(BSubA), _)) + .pipe(Reasoner.assert(Set(xTypeB), _)) + .pipe(Reasoner.assert(Set(DSubC), _)) + .pipe(Reasoner.assert(Set(xry), _)) + .pipe(Reasoner.assert(Set(xrz), _)) + .pipe(Reasoner.assert(Set(yTypeD), _)) + .pipe(Reasoner.assert(Set(zTypeD), _)) + .pipe { whelk => + assert(whelk.closureSubsBySubclass(y)(z)) + assert(whelk.closureSubsBySubclass(z)(y)) + } + Reasoner.assert(Set(ASubMax1rC)) + .pipe(Reasoner.assert(Set(yTypeD), _)) + .pipe(Reasoner.assert(Set(zTypeD), _)) + .pipe(Reasoner.assert(Set(BSubA), _)) + .pipe(Reasoner.assert(Set(xTypeB), _)) + .pipe(Reasoner.assert(Set(xry), _)) + .pipe(Reasoner.assert(Set(xrz), _)) + .pipe(Reasoner.assert(Set(DSubC), _)) + .pipe { whelk => + assert(whelk.closureSubsBySubclass(y)(z)) + assert(whelk.closureSubsBySubclass(z)(y)) + } + Reasoner.assert(Set(DSubC)) + .pipe(Reasoner.assert(Set(yTypeD), _)) + .pipe(Reasoner.assert(Set(zTypeD), _)) + .pipe(Reasoner.assert(Set(BSubA), _)) + .pipe(Reasoner.assert(Set(xTypeB), _)) + .pipe(Reasoner.assert(Set(xry), _)) + .pipe(Reasoner.assert(Set(xrz), _)) + .pipe(Reasoner.assert(Set(ASubMax1rC), _)) + .pipe { whelk => + assert(whelk.closureSubsBySubclass(y)(z)) + assert(whelk.closureSubsBySubclass(z)(y)) + } + Reasoner.assert(Set(ASubMax1rC)) + .pipe(Reasoner.assert(Set(yTypeD), _)) + .pipe(Reasoner.assert(Set(zTypeD), _)) + .pipe(Reasoner.assert(Set(BSubA), _)) + .pipe(Reasoner.assert(Set(xTypeB), _)) + .pipe(Reasoner.assert(Set(DSubC), _)) + .pipe(Reasoner.assert(Set(xry), _)) + .pipe(Reasoner.assert(Set(xrz), _)) + .pipe { whelk => + assert(whelk.closureSubsBySubclass(y)(z)) + assert(whelk.closureSubsBySubclass(z)(y)) + } + } + } + +} diff --git a/modules/owlapi/src/test/scala/org/geneontology/whelk/TestInferences.scala b/modules/owlapi/src/test/scala/org/geneontology/whelk/TestInferences.scala index aee5bb7..48481c4 100644 --- a/modules/owlapi/src/test/scala/org/geneontology/whelk/TestInferences.scala +++ b/modules/owlapi/src/test/scala/org/geneontology/whelk/TestInferences.scala @@ -31,6 +31,35 @@ object TestInferences extends TestSuite { "taxon-unions.ofn" - compareWhelkAndHermiT(false) "unions.ofn" - compareWhelkAndHermiT(false) "long-chains.ofn" - compareWhelkAndELK() + "owlrl/eq-ref.ofn" - compareWhelkAndHermiT(true) + "owlrl/eq-sym.ofn" - compareWhelkAndHermiT(true) + "owlrl/eq-trans.ofn" - compareWhelkAndHermiT(true) + "owlrl/eq-rep.ofn" - compareWhelkAndHermiT(true) + "owlrl/eq-diff.ofn" - compareWhelkAndHermiT(true, true) + "owlrl/prp-dom.ofn" - compareWhelkAndHermiT(true) + "owlrl/prp-rng.ofn" - compareWhelkAndHermiT(true) + "owlrl/prp-fp.ofn" - compareWhelkAndHermiT(true) + "owlrl/prp-ifp.ofn" - compareWhelkAndHermiT(true) + "owlrl/prp-irp.ofn" - compareWhelkAndHermiT(true, true) + "owlrl/prp-symp.ofn" - compareWhelkAndHermiT(true) + "owlrl/prp-asyp.ofn" - compareWhelkAndHermiT(true, true) + "owlrl/prp-trp.ofn" - compareWhelkAndHermiT(true) + "owlrl/prp-spo.ofn" - compareWhelkAndHermiT(true) + "owlrl/prp-eqp.ofn" - compareWhelkAndHermiT(true) + "owlrl/prp-pdw-adp.ofn" - compareWhelkAndHermiT(true, true) + "owlrl/prp-inv.ofn" - compareWhelkAndHermiT(true) + "owlrl/prp-npa.ofn" - compareWhelkAndHermiT(true, true) + "owlrl/cls-int.ofn" - compareWhelkAndHermiT(true) + "owlrl/cls-uni.ofn" - compareWhelkAndHermiT(true) + "owlrl/cls-com.ofn" - compareWhelkAndHermiT(true, true) + "owlrl/cls-svf.ofn" - compareWhelkAndHermiT(true) + "owlrl/cls-avf.ofn" - compareWhelkAndHermiT(true) + "owlrl/cls-hv.ofn" - compareWhelkAndHermiT(true) + "owlrl/cls-maxqc0.ofn" - compareWhelkAndHermiT(true, true) + "owlrl/cls-maxqc1.ofn" - compareWhelkAndHermiT(true) + "owlrl/cls-oo.ofn" - compareWhelkAndHermiT(true) + "owlrl/cax-sco.ofn" - compareWhelkAndHermiT(true) + "owlrl/cax-dw-adc.ofn" - compareWhelkAndHermiT(true, true) } } @@ -57,38 +86,46 @@ object TestInferences extends TestSuite { assert(whelkUnsatisfiable == elkUnsatisfiable) } - def compareWhelkAndHermiT(aboxOnly: Boolean)(implicit path: utest.framework.TestPath): Unit = { + def compareWhelkAndHermiT(aboxOnly: Boolean, inconsistent: Boolean = false)(implicit path: utest.framework.TestPath): Unit = { val fileName = path.value.last val manager = OWLManager.createOWLOntologyManager() val ontology = manager.loadOntologyFromOntologyDocument(this.getClass.getResourceAsStream(fileName)) val axioms = Bridge.ontologyToAxioms(ontology) val terms = axioms.collect { case ax: ConceptInclusion => ax }.flatMap(_.signature).collect { case e: AtomicConcept => e } - val done = Reasoner.assert(axioms) + val whelk = Reasoner.assert(axioms) val hermit = new ReasonerFactory().createReasoner(ontology) - val generator = new InferredOntologyGenerator(hermit, List[InferredAxiomGenerator[_]]( - new InferredSubClassAxiomGenerator(), - new InferredEquivalentClassAxiomGenerator(), - new InferredPropertyAssertionGenerator(), - new InferredClassAssertionAxiomGenerator()).asJava) - generator.fillOntology(manager.getOWLDataFactory, ontology) - val hermitConceptInclusions = terms.filterNot(_ == Top).flatMap(t => hermit.getSubClasses(Class(t.id), false).getFlattened.asScala.map(sub => ConceptInclusion(AtomicConcept(sub.getIRI.toString), t))).filterNot(_.subclass == Bottom) - val hermitClassAssertions = (for { - ClassAssertion(_, Class(cls), NamedIndividual(ind)) <- ontology.getAxioms(Imports.INCLUDED).asScala - } yield ConceptAssertion(AtomicConcept(cls.toString), WIndividual(ind.toString))).toSet - .filterNot(_.concept == Top) - val hermitRoleAssertions = (for { - ObjectPropertyAssertion(_, ObjectProperty(prop), NamedIndividual(subject), NamedIndividual(target)) <- ontology.getAxioms(Imports.INCLUDED).asScala - } yield RoleAssertion(Role(prop.toString), WIndividual(subject.toString), WIndividual(target.toString))).toSet - hermit.dispose() - val whelkClassAssertions = done.classAssertions.filterNot(_.concept == Top) - val whelkRoleAssertions = done.roleAssertions - val whelkSubClassAxioms = done.subs.filter { - case ConceptInclusion(sub: AtomicConcept, sup: AtomicConcept) if (sub != sup && sub != Bottom && sup != Top) => true - case _ => false + if (inconsistent) { + assert(!hermit.isConsistent) + assert(!whelk.closureSubsBySuperclass(Bottom).forall { + case n: Nominal => false + case _ => true + }) + } else { + val generator = new InferredOntologyGenerator(hermit, List[InferredAxiomGenerator[_]]( + new InferredSubClassAxiomGenerator(), + new InferredEquivalentClassAxiomGenerator(), + new InferredPropertyAssertionGenerator(), + new InferredClassAssertionAxiomGenerator()).asJava) + generator.fillOntology(manager.getOWLDataFactory, ontology) + val hermitConceptInclusions = terms.filterNot(_ == Top).flatMap(t => hermit.getSubClasses(Class(t.id), false).getFlattened.asScala.map(sub => ConceptInclusion(AtomicConcept(sub.getIRI.toString), t))).filterNot(_.subclass == Bottom) + val hermitClassAssertions = (for { + ClassAssertion(_, Class(cls), NamedIndividual(ind)) <- ontology.getAxioms(Imports.INCLUDED).asScala + } yield ConceptAssertion(AtomicConcept(cls.toString), WIndividual(ind.toString))).toSet + .filterNot(_.concept == Top) + val hermitRoleAssertions = (for { + ObjectPropertyAssertion(_, ObjectProperty(prop), NamedIndividual(subject), NamedIndividual(target)) <- ontology.getAxioms(Imports.INCLUDED).asScala + } yield RoleAssertion(Role(prop.toString), WIndividual(subject.toString), WIndividual(target.toString))).toSet + hermit.dispose() + val whelkClassAssertions = whelk.classAssertions.filterNot(_.concept == Top) + val whelkRoleAssertions = whelk.roleAssertions + val whelkSubClassAxioms = whelk.subs.filter { + case ConceptInclusion(sub: AtomicConcept, sup: AtomicConcept) if (sub != sup && sub != Bottom && sup != Top) => true + case _ => false + } + if (!aboxOnly) assert(whelkSubClassAxioms == hermitConceptInclusions) + assert(whelkClassAssertions == hermitClassAssertions) + assert(whelkRoleAssertions == hermitRoleAssertions) } - if (!aboxOnly) assert(whelkSubClassAxioms == hermitConceptInclusions) - assert(whelkClassAssertions == hermitClassAssertions) - assert(whelkRoleAssertions == hermitRoleAssertions) } } \ No newline at end of file