[ https://issues.apache.org/jira/browse/RYA-292?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16090788#comment-16090788 ]
Jesse Hatfield commented on RYA-292: ------------------------------------ h4. Basic Logic If A is the intersection of (B, C, D), this means: bq. (x is an A) *if and only if* (x is a B) AND (x is a C) AND (x is a D) . Or: {quote}(1) *If* (x is an A) *then* (x is a B) AND (x is a C) AND (x is a D) . (2) *If* (x is a B) AND (x is a C) AND (x is a D) *then (x is an A) .{quote} Implication (1) can be written: {quote}*If* x is an A *then* x is a B . *If* x is an A *then* x is a C . *If* x is an A *then* x is a D .{quote} Which is equivalent to: {quote}A subClassOf B . A subClassOf C . A subClassOf D .{quote} (For example, see [https://www.w3.org/TR/owl2-profiles/#Reasoning_in_OWL_2_RL_and_RDF_Graphs_using_Rules], rule *scm-int* .) h4. Advanced Cases A may itself be some bnode, and we may need to answer queries about its subclass(es) or superclass(es). For example, when "Mother subClassOf [ intersectionOf (Parent, Woman) ]", implication (1) extends to: {quote}(1) *If* x is a Mother *then* x is a _bnode_ *and therefore* x is a Parent and a Woman.{quote} Using the subclass formulation, this is straightforward: Mother is a subclass of the bnode, which in turn is both a subclass of Parent and a subclass of Woman. Existing logic handles the implications of the subClassOf graph. If, on the other hand, we have "[ intersectionOf (Parent, Woman) ] subClassOf Mother ", then implication (2) extends to: {quote}*If* x is a Parent and a Woman *then* x is a _bnode_ *and therefore* x is a Mother.{quote} This means if the query is for instances of type Mother, it isn't sufficient to check whether Mother is an intersection, but instead we must check whether there is any intersection that should imply Mother, even indirectly. In principle, there could be multiple such intersections, if there were multiple subclasses, e.g. suppose we define Father similarly and then have some superclass shared by both. A query for all the MotherOrFather instances should include anything that fits either the Mother intersection or the Father intersection. Theoretically, one could declare multiple intersections for the same type, such as "A is the intersection of (B C D); A is the intersection of (E F)."That would mean the two intersections represent the same set, and therefore: {quote}(1) *If* (x is an A) *then* (x is a B) AND (x is a C) AND (x is a D) AND (x is an E) AND (x is an F) . (2a) *If* (x is a B) AND (x is a C) AND (x is a D) *then* (x is an A) AND (x is an E) AND (x is an F). (2b) *If* (x is an E) AND (x is an F) *then* (x is an A) AND (x is a B) AND (x is a C) AND (x is a D). {quote} Implication (1) results in the same subClassOf expressions that would be derived from each intersection individually. Implications (2a) and (2b) require explicitly recording, for each class in either intersection, that the other intersection can imply that class. h4. Proposed Strategy: Treat the two implications separately. Use existing subclass logic to handle (1) and a new visitor to handle (2). The InferenceEngine, at schema refresh time, should query for all intersection expressions and produce a mapping from individual type to a list of intersections that should imply that type. At the same time, it should add the subclass relationships to the subclass graph. An intersection expression might look something like: {code}:A owl:intersectionOf _:bnode1 . _:bnode1 rdf:first :B . _:bnode1 rdf:rest _:bnode2 . _:bnode2 rdf:first :C . _:bnode2 rdf:rest _:bnode3 . _:bnode3 rdf:first :D . _:bnode3 rdf:rest rdf:nil .{code} And the refresh logic: {code} intersections <- new Map<Resource, List<Set<Resource>>>() for (type, head) in query(?type, owl:intersectionOf, ?head): if type not in intersections: intersections[type] = new List<Set<Resource>>() intersection <- new Set<Resource>() while (head != rdf:nil): intersection += query(head, rdf:first, ?)[0] head = query(head, rdf:rest, ?)[0] intersections[type].append(intersection) for (type, intersectionList) in intersections: otherTypes <- distinct types in intersections in intersectionList for other in otherTypes: AddSubClass(type subClassOf other). for (intersection in intersectionList unless containing other): AddIntersection(intersection implies other) for (intersection in intersectionList): AddIntersection(intersection implies type) {code} At query time, implication (1) will be handled by existing subclass logic. Implication (2) will require a new visitor to rewrite appropriate queries. An input query {code}SELECT ?x { ?x rdf:type :A . }{code} should be rewritten to: {code}SELECT ?x { { ?x rdf:type :B . ?x rdf:type :C . ?x rdf:type :D . } UNION { ?x rdf:type :A . } }{code} Or in the query algebra, {{StatementPattern(?x, rdf:type, :A)}} should be replaced with: {code}Union( Join( StatementPattern(?x, rdf:type, :B), Join( StatementPattern(?x, rdf:type, :C), StatementPattern(?x, rdf:type, :D) ), ), StatementPattern(?x, rdf:type, :A) ){code} Visitor logic might be: {code} meet(StatementPattern originalSP): if originalSP like (?individual, rdf:type, :c1): // Assume the type in question is explicitly given in the query, not a variable intersectionJoins <- new List<InferJoin>() for intersection in inferenceEngine.getIntersectionsImplying(c1): left <- new InferJoin(intersection.head, intersection.tail.head) current <- intersection.tail.tail while current != nil: left <- new InferJoin(left, current.head) current <- current.tail intersectionJoins.add(left) tree <- InferUnion(intersectionJoins.head, originalSP) current <- intersectionJoins.tail while (current != nil): tree <- InferUnion(node, current.head) current <- current.tail originalSP.replaceWith(tree) {code} > Implement owl:intersectionOf inference > -------------------------------------- > > Key: RYA-292 > URL: https://issues.apache.org/jira/browse/RYA-292 > Project: Rya > Issue Type: Sub-task > Components: sail > Reporter: Jesse Hatfield > > An *{{owl:intersectionOf}}* expression defines the set of resources who > belong to all of a particular set of classes. > A basic implementation, if the ontology states that {{:Mother}} is the > intersection of {{:Parent}} and {{:Woman}}, should cause the inference engine > to: > 1. Rewrite query patterns {{?x rdf:type :Mother}} (the intersection type) to > check for resources that have both types {{:Parent}} and {{:Woman}} (as well > as check for resources that are explicitly stated to be {{:Mother}} s) > 2. Rewrite query patterns {{?y rdf:type :Parent}} (one of the intersecting > sets) to check for resources that are stated to be either {{:Mother}} or > {{:Parent}} , since belonging to the intersection type implies membership in > the component types. (Equivalent logic applies to {{Woman}} ) -- This message was sent by Atlassian JIRA (v6.4.14#64029)