How to debug N3 rules
© J.M. Vanel - 2009-03-25
© Jean-Marc Vanel - $Date: 2012-08-01$ - under Creative Commons License
Here are some tricks I used in the last months developing Déductions rules.
A companion document is N3 rules design good practices .
TOC
@prefix e: <http://eulersharp.sourceforge.net/2003/03swap/log-rules#> . @prefix eg: <http://eulergui.sourceforge.net/engine.owl#> .
(for Euler, use prefix eu: with prefix.cc)
Do not spoil your valuable sources with debugging lines.
With EulerGUI, it is very quick to:
Add a debug predicate in the consequent of the rule. This will add verbose printings, but will not change the expected results . Using a list, you can print whatever you want in a single triple.
Example:
{ ?UML_CLASS uml:ownedAttribute ?ATTRIBUTE . ?ATTRIBUTE uml:name ?NAME . (?SCOPE ?SPAN) e:findall ( ?TYPE { ?ATTRIBUTE uml:type ?TYPE } ?TYPES ). } => { ?PROPERTY a owl:DatatypeProperty . ?PROPERTY rdfs:domain ?OWL_CLASS . ?PROPERTY rdfs:range xsd:string . ?PROPERTY rdfs:label ?NAME . ?PROPERTY :translatedFromUML ?ATTRIBUTE . ?PROPERTY <urn:DEBUG> ( :last_rule_UML_CLASS ?UML_CLASS "\n" :last_rule_ATTRIBUTE ?ATTRIBUTE "\n" :last_rule_TYPES ?TYPES "\n" ). } .
You need an extra rule in the query (again, it will not disturb and can even stay in production code) :
{ ?X <urn:DEBUG> ?Y } => { ?X <urn:DEBUG> ?Y } .
What one also can use is the builtin e:trace , defined thus :
e:trace a rdf:Property; rdfs:comment "builtin that outputs the object"; rdfs:domain rdfs:Resource; rdfs:range rdfs:Resource .
As its name implies, e:trace prints stuff at runtime. It can be put in the antecedent only, as it is a builtin in Euler engine .
Moreover, you can intersperse the antecedent with trace triples, so one can see which part of the rule is causing problems in which application (instantiation).
Example with Euler processor :
{ ?OWL_CLASS :translatedFromUML ?UML_CLASS . _:d e:trace ( "step1" ?OWL_CLASS :translatedFromUML ?UML_CLASS ) . ?UML_CLASS uml:ownedAttribute ?ATTRIBUTE . _:d e:trace ( "step2" "ATTRIBUTE" ?ATTRIBUTE ) . ?ATTRIBUTE uml:name ?NAME . _:d e:trace ( "step3" "NAME" ?NAME ) . } => { # some consequences .... }.
Result:
One can see that for some instantiations, step2 is not reached :
#TRACE ("""step1""" _:sk2 umlowl:translatedFromUML def:id223205208) #TRACE ("""step2""" """ATTRIBUTE""" def:id1563093566) #TRACE ("""step3""" """NAME""" """name""") #TRACE ("""step1""" _:sk3 umlowl:translatedFromUML def:id1563093566) #TRACE ("""step1""" _:sk5 umlowl:translatedFromUML def:id1975260912) #TRACE ("""step1""" _:sk7 umlowl:translatedFromUML def:id1563093566) #TRACE ("""step1""" _:sk0 umlowl:translatedFromUML def:id223205208)
In Drools N3 engine, contrary to above, tracing can only be used in the consequent side. Use a similar syntax, but a different builtin, e.g. :
_:d eg:trace ( ":x :p" ?listModel ?V ).
The e:trace
builtin of Euler is taken in account : it generates
no test in the antecedent, so that it can stay in the rule file while using
Drools/N3 engine .
This is especially useful for "chains" of rules , with intermediary results firing other rules.
You can add ad-hoc queries to print these.
It is more convenient to again use the <urn:DEBUG>
trick
to print these, because you intervene in a single place: the rule you are
working at.
Using previous trick, when you see a rule is not fired, you can remove conditions until that rule is actually fired.
By comparing the conditions that are actually met with the extra conditions that cause the rule to fail, you can see what is happening.
Very often, some predicates are not present, like rdsf:label , or rdfs:range, but a rule is relying on it .
In this case , it is necessary to provide a fallback rule using
e:findall
or log:notIncludes
.
There is also the builtin e:optional, defined thus :
e:optional a rdf:Property;
rdfs:comment "builtin to call object formula and to succeed anyway";
rdfs:domain rdfs:Resource;
rdfs:range log:Formula .
Example:
??????????