4

I'm currently discovering all the possibilities of the Owlready library. Right now I'm trying to process some SWRL rules and so far it's been going very good, but I'm stuck at one point.

I've defined some rules in my ontology and now I want to see all the results (so, everything inferred from a rule).

For example, if I had a rule

has_brother(David, ?b) ^ has_child(?b, ?s) -> has_uncle(?s, David)

and David has two brothers, John and Pete, and John's kid is Anna, Pete's kid is Simon, I would like too see something like:

has_brother(David, John) ^ has_child(John, Anna) -> has_uncle(Anna, David)

has_brother(David, Pete) ^ has_child(Pete, Simon) -> has_uncle(Simon, David)

Is this possible in any way? I thought that maybe if I run the reasoner, I could see it in its output, but I can't find this anywhere.

I appreciate any help possible!

mathbbandstuff
  • 215
  • 1
  • 5
  • I cannot follow. What exactly is the SWRL rule in your ontology now? And which reasoner did you use? – UninformedUser Sep 17 '21 at 17:36
  • @UninformedUser the rule is as defined in the question. I've used Pellet with inferring both property values and data property values. – mathbbandstuff Sep 19 '21 at 18:43
  • not sure what you mean, the SWRL rule should be generic and not hard-coded to a value like `David` - so the rule in the ontology should be `hasBrother(?X, ?Y), hasChild(?Y, ?Z) -> hasUncle(?Z, ?X)` - and it works as expected, both uncle facts are being inferred. I checked it in Protege with Pellet reasoner. How did you check if the axioms are there? – UninformedUser Sep 20 '21 at 09:41

1 Answers1

2

This is my solution:



import owlready2 as owl

onto = owl.get_ontology("http://test.org/onto.owl")

with onto:
    class Person(owl.Thing):
        pass

    class has_brother(owl.ObjectProperty, owl.SymmetricProperty, owl.IrreflexiveProperty):
        domain = [Person]
        range = [Person]
    
    class has_child(Person >> Person):
        pass
    
    class has_uncle(Person >> Person):
        pass

    rule1 = owl.Imp()
    rule1.set_as_rule(
        "has_brother(?p, ?b), has_child(?p, ?c) -> has_uncle(?c, ?b)"
    )

    # This rule gives "irreflexive transitivity",
    # i.e. transitivity, as long it does not lead to has_brother(?a, ?a)"
    rule2 = owl.Imp()
    rule2.set_as_rule(
        "has_brother(?a, ?b), has_brother(?b, ?c), differentFrom(?a, ?c) -> has_brother(?a, ?c)"
    )
    
david = Person("David")
john = Person("John")
pete = Person("Pete")
anna = Person("Anna")
simon = Person("Simon")

owl.AllDifferent([david, john, pete, anna, simon])

david.has_brother.extend([john, pete])

john.has_child.append(anna)
pete.has_child.append(simon)

print("Uncles of Anna:", anna.has_uncle) # -> []
print("Uncles of Simon:", simon.has_uncle) # -> []
owl.sync_reasoner(infer_property_values=True)
print("Uncles of Anna:", anna.has_uncle) # -> [onto.Pete, onto.David]
print("Uncles of Simon:", simon.has_uncle) # -> [onto.John, onto.David]

Notes:

One might think has_brother is

  • symmetric, i.e. has_brother(A, B)has_brother(B, A)
  • transitive, i.e. has_brother(A, B) + has_brother(B, C) ⇒ has_brother(A, C)
  • irreflexive, i.e. no one is his own brother.

However, transitivity only holds if the unique name assumption holds. Otherwise A could be the same individual as C and this conflicts irreflexivity. Thus I used a rule for this kind of "weak transitivity".

Once, has_brother works as expected the uncle rule also does. Of course, the reasoner must run before.

Update: I published the solution in this Jupyter notebook (which also contains the output of the execution).

cknoll
  • 2,130
  • 4
  • 18
  • 34