Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for ObjectPropertyRange in EL reasoner. #250

Merged
merged 1 commit into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions modules/core/src/main/scala/org/geneontology/whelk/Model.scala
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,6 @@ final case class Individual(id: String) extends Entity with IndividualArgument {

}

sealed trait Axiom extends HasSignature

final case class Nominal(individual: Individual) extends Concept {

def conceptSignature: Set[Concept] = Set(this)
Expand All @@ -219,6 +217,21 @@ final case class Nominal(individual: Individual) extends Concept {

}


final case class RoleTarget(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)

}

sealed trait Axiom extends HasSignature

final case class ConceptInclusion(subclass: Concept, superclass: Concept) extends Axiom with QueueExpression {

def signature: Set[Entity] = subclass.signature ++ superclass.signature
Expand All @@ -237,6 +250,12 @@ final case class RoleComposition(first: Role, second: Role, superproperty: Role)

}

final case class RoleHasRange(role: Role, range: Concept) extends Axiom {

def signature: Set[Entity] = range.signature + role

}

final case class ConceptAssertion(concept: Concept, individual: Individual) extends Axiom {

def signature: Set[Entity] = concept.signature + individual
Expand Down
34 changes: 31 additions & 3 deletions modules/core/src/main/scala/org/geneontology/whelk/Reasoner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ final case class ReasonerState(
hier: Map[Role, Set[Role]] = Map.empty, // initial
hierList: Map[Role, List[Role]] = Map.empty, // initial
hierComps: Map[Role, Map[Role, List[Role]]] = Map.empty, // initial
roleRanges: Map[Role, Concept] = Map.empty, // initial
assertions: List[ConceptInclusion] = Nil,
inits: Set[Concept] = Set.empty, // closure
assertedConceptInclusionsBySubclass: Map[Concept, List[ConceptInclusion]] = Map.empty,
Expand Down Expand Up @@ -126,14 +127,21 @@ 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 allRoleRangeAxioms = axioms.collect { case rr: RoleHasRange => rr }
val assertedRoleRanges = allRoleRangeAxioms.groupBy(_.role).view.mapValues(_.map(_.range)).toMap
val roleRanges = for {
(subprop, supers) <- hier
ranges = supers.flatMap(sup => assertedRoleRanges.get(sup).toSet.flatten)
if ranges.nonEmpty
} yield subprop -> (if (ranges.size == 1) ranges.head else ranges.reduce(Conjunction))
val rules = axioms.collect { case r: Rule => r }
val anonymousRulePredicates = rules.flatMap(_.body.collect {
case ConceptAtom(concept, _) if concept.isAnonymous => ConceptInclusion(concept, Top)
})
val concIncs = axioms.collect { case ci: ConceptInclusion => ci } ++ anonymousRulePredicates
val ruleEngine = RuleEngine(rules)
val wm = ruleEngine.emptyMemory
assert(concIncs, ReasonerState.empty.copy(hier = hier, hierList = hierList, hierComps = hierComps, ruleEngine = ruleEngine, wm = wm, queueDelegates = delegates, disableBottom = disableBottom))
assert(concIncs, ReasonerState.empty.copy(hier = hier, hierList = hierList, hierComps = hierComps, roleRanges = roleRanges, ruleEngine = ruleEngine, wm = wm, queueDelegates = delegates, disableBottom = disableBottom))
}

def assert(axioms: Set[ConceptInclusion], reasoner: ReasonerState): ReasonerState = {
Expand Down Expand Up @@ -267,7 +275,16 @@ object Reasoner {
}

private[this] def R0(concept: Concept, reasoner: ReasonerState, todo: mutable.Stack[QueueExpression]): ReasonerState = {
todo.push(ConceptInclusion(concept, concept))
concept match {
case rt @ RoleTarget(role, target) =>
for {
range <- reasoner.roleRanges.get(role)
} {
todo.push(ConceptInclusion(rt, range))
todo.push(ConceptInclusion(rt, target))
}
case _ => todo.push(ConceptInclusion(concept, concept))
}
reasoner
}

Expand Down Expand Up @@ -424,7 +441,15 @@ object Reasoner {

private[this] def `R-∃`(ci: ConceptInclusion, reasoner: ReasonerState, todo: mutable.Stack[QueueExpression]): ReasonerState = ci match {
case ConceptInclusion(c, ExistentialRestriction(role, filler)) =>
todo.push(Link(c, role, filler))
val target = (c, filler) match {
case (Nominal(_), Nominal(_)) =>
// ranges will be handled by rete rules
filler
case _ =>
if (reasoner.roleRanges.contains(role)) RoleTarget(role, filler)
else filler
}
todo.push(Link(c, role, target))
reasoner
case _ => reasoner
}
Expand Down Expand Up @@ -637,6 +662,9 @@ object Reasoner {

private[this] def `R-⟲`(ci: ConceptInclusion, reasoner: ReasonerState, todo: mutable.Stack[QueueExpression]): ReasonerState = ci match {
case ConceptInclusion(sub, SelfRestriction(role)) =>
reasoner.roleRanges.get(role).foreach { range =>
todo.push(ConceptInclusion(sub, range))
}
todo.push(Link(sub, role, sub))
reasoner
case _ => reasoner
Expand Down
16 changes: 11 additions & 5 deletions modules/owlapi/src/main/scala/org/geneontology/whelk/Bridge.scala
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,17 @@ object Bridge {
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 =>
//TODO only supporting in rules for now
Rule(body = List(RoleAtom(Role(property.toString), WVariable("x1"), WVariable("x2"))), head = List(ConceptAtom(concept, WVariable("x2"))))).toSet
case ObjectPropertyDomain(_, ObjectProperty(property), ce) =>
convertExpression(ce).map(concept =>
ConceptInclusion(ExistentialRestriction(Role(property.toString), Top), concept)).toSet
case ObjectPropertyRange(_, ObjectProperty(property), ce) =>
convertExpression(ce).to(Set).flatMap { concept =>
val role = Role(property.toString)
Set(
RoleHasRange(role, concept),
Rule(body = List(RoleAtom(role, WVariable("x1"), WVariable("x2"))), head = List(ConceptAtom(concept, WVariable("x2"))))
)
}
case InverseObjectProperties(_, ObjectProperty(p), ObjectProperty(q)) =>
val (roleP, roleQ) = (Role(p.toString), Role(q.toString))
val (x1, x2) = (WVariable("x1"), WVariable("x2"))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
Prefix(:=<http://example.org/uberon#>)
Prefix(owl:=<http://www.w3.org/2002/07/owl#>)
Prefix(rdf:=<http://www.w3.org/1999/02/22-rdf-syntax-ns#>)
Prefix(xml:=<http://www.w3.org/XML/1998/namespace>)
Prefix(xsd:=<http://www.w3.org/2001/XMLSchema#>)
Prefix(rdfs:=<http://www.w3.org/2000/01/rdf-schema#>)


Ontology(<http://example.org/uberon>

Declaration(Class(:A))
Declaration(Class(:B))
Declaration(Class(:C))
Declaration(Class(:D))
Declaration(Class(:arm))
Declaration(Class(:finger))
Declaration(Class(:hand))
Declaration(Class(:part_of_arm))
Declaration(Class(:part_of_some_A))
Declaration(Class(:q_some_C))
Declaration(Class(:r_some_hand))
Declaration(Class(:reflexive_part_of_some_arm))
Declaration(Class(:s_some_Self))
Declaration(Class(:special_arm))
Declaration(Class(:special_hand))
Declaration(Class(:t_some_Self))
Declaration(ObjectProperty(:p))
Declaration(ObjectProperty(:part_of))
Declaration(ObjectProperty(:q))
Declaration(ObjectProperty(:r))
Declaration(ObjectProperty(:reflexive_part_of))
Declaration(ObjectProperty(:s))
Declaration(ObjectProperty(:t))
Declaration(NamedIndividual(:a))
############################
# Object Properties
############################

# Object Property: :p (:p)

ObjectPropertyRange(:p :C)

# Object Property: :part_of (:part_of)

SubObjectPropertyOf(:part_of :reflexive_part_of)
TransitiveObjectProperty(:part_of)
ObjectPropertyRange(:part_of :A)

# Object Property: :q (:q)

SubObjectPropertyOf(:q :p)

# Object Property: :r (:r)

ObjectPropertyRange(:r :B)

# Object Property: :reflexive_part_of (:reflexive_part_of)

ReflexiveObjectProperty(:reflexive_part_of)

# Object Property: :t (:t)

SubObjectPropertyOf(:t :s)


############################
# Classes
############################

# Class: :A (:A)

SubClassOf(:A ObjectSomeValuesFrom(:q :B))

# Class: :D (:D)

SubClassOf(:D ObjectSomeValuesFrom(:t :D))

# Class: :arm (:arm)

SubClassOf(:arm ObjectSomeValuesFrom(:r :hand))

# Class: :finger (:finger)

SubClassOf(:finger ObjectSomeValuesFrom(:part_of :special_hand))

# Class: :hand (:hand)

SubClassOf(:hand ObjectSomeValuesFrom(:part_of :special_arm))
SubClassOf(:hand ObjectHasSelf(:r))

# Class: :part_of_arm (:part_of_arm)

EquivalentClasses(:part_of_arm ObjectSomeValuesFrom(:part_of :arm))

# Class: :part_of_some_A (:part_of_some_A)

EquivalentClasses(:part_of_some_A ObjectSomeValuesFrom(:part_of :A))

# Class: :q_some_C (:q_some_C)

EquivalentClasses(:q_some_C ObjectSomeValuesFrom(:q :C))

# Class: :r_some_hand (:r_some_hand)

EquivalentClasses(:r_some_hand ObjectSomeValuesFrom(:r :hand))

# Class: :reflexive_part_of_some_arm (:reflexive_part_of_some_arm)

EquivalentClasses(:reflexive_part_of_some_arm ObjectSomeValuesFrom(:reflexive_part_of :arm))

# Class: :s_some_Self (:s_some_Self)

EquivalentClasses(:s_some_Self ObjectHasSelf(:s))

# Class: :special_arm (:special_arm)

SubClassOf(:special_arm :arm)

# Class: :special_hand (:special_hand)

SubClassOf(:special_hand :hand)

# Class: :t_some_Self (:t_some_Self)

EquivalentClasses(:t_some_Self ObjectHasSelf(:t))


############################
# Named Individuals
############################

# Individual: :a (:a)

ObjectPropertyAssertion(:t :a :a)


)
Loading
Loading