سؤال

Consider the following RDF:

semapi:BaseClass     a rdfs:Class; 
                     rdfs:subClassOf rdfs:Class .

semapi:hasChainTo a rdf:Property; 
                   rdfs:domain semapi:BaseClass;
                   rdfs:range  semapi:BaseClass .                   

semapi:DerivedClass  a rdfs:Class; rdfs:subClassOf semapi:BaseClass .                  

instances:Instance1 a semapi:DerivedClass;
                    semapi:hasChainTo (
                        [
                            a semapi:DerivedClass;
                                    semapi:hasChainTo (
                                           [C1]
                                           [C2]
                                    )
                            ]
                     )

If semapi:hasChainTo rdfs:range semapi:BaseClass then it implies the list is rdf:type semapi:BaseClass.

What I really mean to say is each item in the list is rdf:type (ei. [C1] rdf:type semapi:BaseClass, [C2] rdf:type semapi:BaseClass, ...)

How can I do this? Do I need Owl (preferably not)?

هل كانت مفيدة؟

المحلول

Depending on how you want to do this, you have a few options. I think you're trying to stick to non-OWL reasoning, so we'll make sure to include such a solution, but I do want to touch on an OWL solution too, since for some similar situations, it works very well.

Using OWL and a custom ObjectList

If you do have the option of using an OWL reasoner, then this is a nice case in which you can create your own list vocabulary and use some property chains. The idea is that you introduce a class List with an individual nil, and properties first and rest. You're really just copying the vocabulary in your own namespace. Then lets say you define two properties

  • likes: relates an individual X to another individual Y; "X likes Y".
  • likesList: relates an individual X to a List (not an RDF list, though) of individuals that X likes.

Then you can introduce two property chain axioms

  • likesList subPropertyChain likesList o rest: if X likesList (_ ...), then X likesList (...).

This way, from X likes (A B C) we get X likes (A B C), X likes (B C), X likes (C), and X likes nil.

  • likes subPropertyChain likesList o first: if X likesList (A ...), then X likes A.

Then, from all those inferred statements above, we get X likes A, X likes B, and X likes C.

In Turtle, this looks like:

@prefix :        <http://www.example.org/distributing#> .
@prefix rdfs:    <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl:     <http://www.w3.org/2002/07/owl#> .
@prefix xsd:     <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

<http://www.example.org/distributing>
      a       owl:Ontology .

:List
      a       owl:Class .

:nil  a       :List , owl:NamedIndividual .

:first
      a       owl:ObjectProperty .

:rest
      a       owl:ObjectProperty .

:likes
      a       owl:ObjectProperty ;
      owl:propertyChainAxiom
              (:likesList :first) .

[]    a       owl:Axiom ;
      rdfs:comment "If X likesList (A ...), then X likes A." ;
      owl:annotatedProperty
              owl:propertyChainAxiom ;
      owl:annotatedSource :likes ;
      owl:annotatedTarget (:likesList :first) .

:likesList
      a       owl:ObjectProperty ;
      rdfs:comment "Relates an individual I1 to a ObjectList of individuals that I1 likes." ;
      owl:propertyChainAxiom
              (:likesList :rest) .

[]    a       owl:Axiom ;
      rdfs:comment "If X likesList (A B C), then since (B C) is the rest of (A B C), X likesList (B C), too." ;
      owl:annotatedProperty
              owl:propertyChainAxiom ;
      owl:annotatedSource :likesList ;
      owl:annotatedTarget (:likesList :rest) .

This gets a bit inconvenient if you have to write the RDF manually, since you have to do

X :likesList [ :first A ;
               :rest [ :first B ;
                       :rest [ :first C ;
                               :rest nil ] ] ] .

and can't use the nice (...) syntax that Turtle includes. This also really doesn't help for the case that you've got, since OWL classes aren't individuals, so they can't be the object of object properties, and rdf:type isn't an object property. I just wanted to include this because it's a nice way for an object property to distribute over a (non-RDF) list of individuals, and because the approach makes the following solutions clearer.

Using SPARQL queries

Given data like:

@prefix : <urn:ex:> .

:X :pList (:A :B :C :D) .

A SPARQL query like

prefix : <http://example.org/>
prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>

construct { 
  ?x :p ?y 
} 
where { 
  ?x :pList/rdf:rest*/rdf:first ?y
}

produces

@prefix :        <http://example.org/> .
@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

:X    :p            :A ;
      :p            :C ;
      :p            :B ;
      :p            :D .

In imitation of the the OWL based approach above, I used two properties pList and p, but they could be the same, in which case p would be "distributed" over the list.

With a datastore somewhere, you should be able to do a SPARQL update using insert/where:

prefix : <http://example.org/>
prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>

insert { 
  ?x :p ?y 
} 
where { 
  ?x :pList/rdf:rest*/rdf:first ?y
}

to add the data to the store.

Using a Prolog like syntax

If you want to actually get this reasoning to be performed with a reasoner, you'll be in the domain of reasoner specific stuff. However, lots of reasoners support a Prolog-like query language, and you can write these rules there, too. I don't know AllegoGraph's RDFS++ syntax, but the general structure would include some definitions like:

?x p ?y :- ?x pList ?list, ?list rdf:first ?y

?x pList ?l :- ?x pList ?list, ?list rdf:rest ?l
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top