2
votes

I have an ontology with a lot of individuals and use the Jena reasoner to get information about them. My goal is to create new individuals based on the given information inside of that rules and assign properties to them. The individuals don't have to be named, but they need a type and have to be part of a few properties. At the moment I can create anonymous individuals (with the help of a mailing list post), but I can only give them one type, or one property.

Here's a little example of my problem; my rule looks like this (the ontology and inferred result can be found at the bottom):

[test2: (?X rdf:type NS:Test1) ->
    [(?Y rdf:type NS:Test2) <- makeSkolem(?Y, ?X)]]

It means when a Test1 individual is found, then a new blank node is created and then the type Test2 is given to that node. It works fine, but i want to give this new individuals a classification and a pointer(property) to ?X (the Test1 individuals).

Something like the following doesn't work, since "backward rules only allow one head clause". Every clause for its one works perfectly fine though.

[test2: (?X rdf:type NS:Test1) ->
    [(?Y rdf:type NS:Test2), (?Y NS:hasClassification 'test'), <- makeSkolem(?Y, ?X)]]

This is my ontology:

<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:owl="http://www.w3.org/2002/07/owl#"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
    xmlns="file:/Test#"
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" > 
  <rdf:Description rdf:about="file:/Test#hasClassification">
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#DatatypeProperty"/>
  </rdf:Description>
  <rdf:Description rdf:about="file:/Test#TestProp">
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#ObjectProperty"/>
  </rdf:Description>
  <rdf:Description rdf:about="file:/Test#testInd">
    <rdf:type rdf:resource="file:/Test#Test1"/>
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#NamedIndividual"/>
  </rdf:Description>
  <rdf:Description rdf:about="file:/Test#testInd2">
    <rdf:type rdf:resource="file:/Test#Test1"/>
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#NamedIndividual"/>
  </rdf:Description>
  <rdf:Description rdf:about="file:/Test#Test1">
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Class"/>
  </rdf:Description>
  <rdf:Description rdf:about="file:/Test#Test2">
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Class"/>
  </rdf:Description>
  <rdf:Description rdf:about="file:/Test">
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Ontology"/>
  </rdf:Description>
</rdf:RDF>

This is the result with the first rule (blank nodes with IDs A0 and A1 are the new individuals):

<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:owl="http://www.w3.org/2002/07/owl#"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
    xmlns="file:/Test#"
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" > 
  <rdf:Description rdf:about="file:/Test#hasClassification">
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#DatatypeProperty"/>
  </rdf:Description>
  <rdf:Description rdf:nodeID="A0">
    <rdf:type rdf:resource="file:/Test#Test2"/>
  </rdf:Description>
  <rdf:Description rdf:about="file:/Test#TestProp">
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#ObjectProperty"/>
  </rdf:Description>
  <rdf:Description rdf:about="file:/Test#testInd">
    <rdf:type rdf:resource="file:/Test#Test1"/>
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#NamedIndividual"/>
  </rdf:Description>
  <rdf:Description rdf:about="file:/Test#testInd2">
    <rdf:type rdf:resource="file:/Test#Test1"/>
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#NamedIndividual"/>
  </rdf:Description>
  <rdf:Description rdf:about="file:/Test#Test1">
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Class"/>
  </rdf:Description>
  <rdf:Description rdf:about="file:/Test#Test2">
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Class"/>
  </rdf:Description>
  <rdf:Description rdf:nodeID="A1">
    <rdf:type rdf:resource="file:/Test#Test2"/>
  </rdf:Description>
  <rdf:Description rdf:about="file:/Test">
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Ontology"/>
  </rdf:Description>
</rdf:RDF>
1
I used the backward-chaining-rule because previous to makeSkolum() I worked with makeInstance() which is only usable in backward/hybrid rules. After trying to use makeInstance() for a whole day I didnt think about using a simple forward rule with makeSkolum(). It works perfectly fine though, so ty very much (also for your second explanation). I still have a lot to learn about Jena and am thankful u helped me a second time. Hopefully I wont need help in the near future, again. Do u want to answer the question so i can mark it as solved and give you your deserved credit?ImmaCute
I'm glad this helped! I've added the essence of it as an answer.Joshua Taylor

1 Answers

1
votes

First, note that your rule doesn't do exactly what you've said it does.

[test2: (?X rdf:type NS:Test1) &rightarrow;
    [(?Y rdf:type NS:Test2) &leftarrow; makeSkolem(?Y, ?X)]]

It means when a Test1 individual is found, then a new blank node is created and then the type Test2 is given to that node.

The rule matches when an instanceof NS:Test1 is found, and adds a new backward chaining rule that can be used when determining whether something has type NS:Test2: if it's the skolem of ?X, then it has that type. It doesn't give anything the type NS:Test2 unless you ask for it. (Writing the whole model, of course, does ask for such triples.)

If you find that type of behavior acceptable, you could simply use a forward chaining rule that adds multiple backwards chaining rules, e.g.:

[test2:
  (?X rdf:type NS:Test1) &rightarrow;
    [(?Y rdf:type NS:Test2) &leftarrow; makeSkolem(?Y, ?X)],
    [(?Y NS:hasClassification 'test') &leftarrow; makeSkolem(?Y, ?X)]
]

I think that that's a bit more complicated than it needs to be. Skolem objects are simply objects that are uniquely determined by some other values, so you can use makeSkolem in the preconditions of a rule as well as the head of a rule. This means that you can do this:

[test2: (?X rdf:type NS:Test1), makeSkolem(?Y, ?X) &rightarrow;
    (?Y rdf:type NS:Test2), (?Y rdf:type NS:hasClassification 'test')]

It's worth noting that makeSkolem takes an arbitrary number of arguments. It might be worthwhile to add some sort of indicator so that you don't accidentally get the same skolem object in multiple places. E.g., if you had something like

[(?child rdf:type :Child), makeSkolem(?mother, ?child) &rightarrow;
    (?mother rdf:type :Mother), (?child :hasMother ?mother)]

[(?child rdf:type :Child), makeSkolem(?father, ?child) &rightarrow;
    (?father rdf:type :Father), (?child :hasFather ?father)]

then you'd actually be creating just one skolem object for each Child and calling it the Child's Mother and Father, which probably isn't what you want. Instead, you could do something like:

[(?child rdf:type :Child), makeSkolem(?mother, ?child, 'mother') &rightarrow;
    (?mother rdf:type :Mother), (?child :hasMother ?mother)]

[(?child rdf:type :Child), makeSkolem(?father, ?child, 'father') &rightarrow;
    (?father rdf:type :Father), (?child :hasFather ?father)]

since (?child, 'mother') and (?child, 'father') are always different, you get two skolem objects instead of just one. You might use something like this in your rule to get, e.g.,

[test2: (?X rdf:type NS:Test1), makeSkolem(?Y, ?X, 'test2') &rightarrow;
    (?Y rdf:type NS:Test2), (?Y NS:hasClassification 'test')]