This document describes Shapes Constraint Language (SHACL) Inference Rules.
This specification is published by the Data Shapes Working Group .
Inference Rules
Connect to definitions in RDF 1.2 Concepts.
Some examples in this document use Turtle [[turtle]]. The reader is expected to be familiar with SHACL [[shacl]] and SPARQL [[sparql-query]].
Within this document, the following namespace prefix bindings are used:
Prefix | Namespace |
---|---|
rdf: |
http://www.w3.org/1999/02/22-rdf-syntax-ns# |
rdfs: |
http://www.w3.org/2000/01/rdf-schema# |
sh: |
http://www.w3.org/ns/shacl# |
xsd: |
http://www.w3.org/2001/XMLSchema# |
ex: |
http://example.com/ns# |
Throughout the document, color-coded boxes containing RDF graphs in Turtle will appear. These fragments of Turtle documents use the prefix bindings given above.
# This box represents a shapes graph
# This box represents a data graph.
# This box represents an output results graph
Formal definitions appear in blue boxes:
# This box contains textual definitions.
Grey boxes such as this include syntax rules that apply to the shapes graph.
true
denotes the RDF term
"true"^^xsd:boolean
.
false
denotes the RDF term
"false"^^xsd:boolean
.
TODO
RFC 2119 language should autmatically be inserted here.
SHACL rules infer new triples. The input is a data graph and a shape graph with rules, the output is a graph of inferred triples that do not occur in the data graph.
Data:
:A :fatherOf :X . :B :motherOf :X . :C :motherOf :A .
Rules:
RULE { ?x :childOf ?y } WHERE { ?y :fatherOf ?x } RULE { ?x :childOf ?y } WHERE { ?y :motherOf ?x } RULE { ?x :descendedFrom ?y } WHERE { ?x :childOf ?y } RULE { ?x :descendedFrom ?y } WHERE { ?x :childOf ?z . ?z :childOf ?y }
will conclude that: :X is the child of :a and :B. and that :X is descendedFrom :C
The Shape Rules Data Model
"Abstract syntax" and "data model" are synonymous so this could be "Rules Abstract Syntax". "Rules Data model" has the advantage that "syntax" is left for the concrete syntaxes. Downside - it's "rules data" not "data".
[ sh:var "name" ]
In a [=triple pattern=] or a [=triple template=], position 1 of the tuple is informally called the subject; position 2 is informally called the predicate position 3 is informally called the object.
Well-formedness is a set of conditions on abstract data model of shapes rules. Together, these rules ensure that a [=variable=] in a [=head=] of a rule has a value defined in the [=body=] of the rule; that each variable in an condition expression or assignment expression has a value at the point of evaluation, and that each assignment in a rule only introduces a new variable, not used earlier in the rule body.
A [=rule=] is a well-formed rule if all the following conditions are met:
A [=rule set=] is "well-formed" if and only all the [=rules=] of the rule set are "well-formed".
The compact syntax has an equivalent RDF syntax form.
Well-formed RDF syntax can be translated to the compact syntax.
Compact:
PREFIX : <http://example/> DATA { :x :p 1 ; :q 2 . } RULE { ?x :bothPositive true . } WHERE { ?x :p ?v1 FILTER ( ?v1 > 0 ) ?x :q ?v2 FILTER ( ?v2 > 0 ) } RULE { ?x :oneIsZero true . } WHERE { ?x :p ?v1 ; :q ?v2 FILTER ( ( ?v1 = 0 ) || ( ?v2 = 0 ) ) }
RDF:
PREFIX : <http://example/> PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> PREFIX sh: <http://www.w3.org/ns/shacl#> PREFIX sparql: <http://www.w3.org/ns/sparql#> :ruleSet-1 rdf:type sh:RuleSet; sh:data ( <<( :x :p 1 )>> <<( :x :q 2 )>> ); sh:ruleSet ( [ rdf:type sh:Rule; sh:head ( [ sh:subject [ sh:var "x" ] ; sh:predicate :bothPositive; sh:object true ] ) sh:body ( [ sh:subject [ sh:var "x" ]; sh:predicate :p; sh:object [ sh:var "v1" ] ] [ sh:expr [ sparql:greaterThan ( [ sh:var "v1" ] 0 ) ] ] [ sh:subject [ sh:var "x" ] ; sh:predicate :q; sh:object [ sh:var "v2" ] ] [ sh:expr [ sparql:greaterThan ( [ sh:var "v2" ] 0 ) ] ] ); ] [ rdf:type sh:Rule; sh:head ( [ sh:subject [ sh:var "x" ] ; sh:predicate :oneIsZero ; sh:object true ] ) sh:body ( [ sh:subject [ sh:var "x" ] ; sh:predicate :p ; sh:object [ sh:var "v1" ] ] [ sh:subject [ sh:var "x" ] ; sh:predicate :q ; sh:object [ sh:var "v2" ] ] [ sh:expr [ sparql:function-or ( [ sparql:equals ( [ sh:var "v1" ] 0 ) ] [ sparql:equals ( [ sh:var "v2" ] 0 ) ] ) ] ] ); ] ) .
Well -formed ness:
Describe how the data model maps to triples??. way round - copes with extra triples. Output is the instance of the dada model that generate the triples - but need to define "maximal".
Process : accumulators, bottom up/ Walk the structure.
All triples not in the syntax are ignored. No other "sh:" predicates are allowed (??).
@@ link to SHACL constraints@@
The grammar is given below.
Mapping the AST to the abstrat data model.
Additional helpers (short-hand abbreviations) @@which also allow specialised implementations for basic engines@@.
This section defines the outcome of evaluating a rule set on given data. It does not prescribe the algorithm as the method of implementation. An implementation can use any algorithm that generates the same outcome.
Inputs: data graph G and a rule set RS. Output: an RDF graph GI of inferred triples
The inferred triples does not include any triple present in the set of triples of G.
Let F(arg1, arg2, ...) be an expression. where arg1, arg2 are RDF terms. Let [x/row] be if x is an RDF term, the [x/row] is x if x is a variable then [x/row] is the value of x in the row ## By well-formedness, it is an error if x is not in the row. eval(F(expr1, expr2), row) = F(eval(expr1, row), eval(expr2, row)) eval(FF(expr1, expr2) , row) = ... things that are not functions like IF
let R be a well-formed rule. let rule R = (head, body) where H is the sequence of triple templates in the head B is the sequence of triple patterns, condition expressions, and assignments in the body let R : map variable to RDF term as a set of pairs (variable, RDF term) # "solution" let T :list of B Initial T = empty list # Evaluate rule body for each rule element rElt: if rElt is a triple pattern: T1 = empty list for each row in T: T1 = empty list for each matches m set of (var,term) row1 = row union (var, term) add row1 to T1 endfor T = T1 endif if rElt is a condition expression F: T1 = empty list for each row in T: if ( eval(F,B) ) add R to T1 endif endfor T = T1 endif if rElt is an assignment(V, expr) T1 = empty list for each row in T: let x = eval(expr, row) let R1 = R union (V, x) add R1 to T1 endfor T = T1 endif endfor # Evaluate rule head let H = empty set for each R in T: Let S = set of triples obtained by replacing variables in the triple templates of the head with values from R define [head(R)/row] H = H union S endfor result eval(R, G) is H
Note that `H` may contain triples that are also in the data grapoh.
Let G be the input RDF graph. Let I be the set of triples generated by evaluation Let RS be a rule set Let finished = false while !finished: finished = true foreach rule in RS let GI = G union I let X = eval(rule, GI) let Y = those triples in X that are not in GI if Y is not empty: finished = false endif let I = Y union I endfor endwhile result is GI
[1] |
RuleSet |
::= | ( Prologue ( Rule | Data ) )* |
[2] |
Rule |
::= | Rule1 | Rule2 | Rule3 | Declaration |
[3] |
Rule1 |
::= | 'RULE' HeadTemplate 'WHERE' BodyPattern |
[4] |
Rule2 |
::= | 'IF' BodyPattern 'THEN' HeadTemplate |
[5] |
Rule3 |
::= | HeadTemplate ':-' BodyPattern |
[6] |
Declaration |
::= | ( 'TRANSITIVE' '(' iri ')' | 'SYMMETRIC' '(' iri ')' | 'INVERSE' '(' iri ',' iri ')' ) |
[7] |
Data |
::= | 'DATA' TriplesTemplateBlock |
[8] |
HeadTemplate |
::= | TriplesTemplateBlock |
[9] |
BodyPattern |
::= | '{' BodyPattern1? Assignment* '}' |
[10] |
BodyPattern1 |
::= | ( Filter | BodyPatternSub ( Filter BodyPatternSub? )* ) |
[11] |
BodyPatternSub |
::= | TriplesBlock |
[12] |
Prologue |
::= | ( BaseDecl | PrefixDecl | VersionDecl | ImportsDecl )* |
[13] |
BaseDecl |
::= | 'BASE' IRIREF |
[14] |
PrefixDecl |
::= | 'PREFIX' PNAME_NS IRIREF |
[15] |
VersionDecl |
::= | 'VERSION' VersionSpecifier |
[16] |
VersionSpecifier |
::= | STRING_LITERAL1 | STRING_LITERAL2 |
[17] |
ImportsDecl |
::= | 'IMPORTS' iri |
[18] |
TriplesTemplateBlock |
::= | '{' TriplesTemplate? '}' |
[19] |
TriplesTemplate |
::= | TriplesSameSubject ( '.' TriplesTemplate? )? |
[20] |
TriplesBlock |
::= | TriplesSameSubjectPath ( '.' TriplesBlock? )? |
[21] |
ReifiedTripleBlock |
::= | ReifiedTriple PropertyList |
[22] |
ReifiedTripleBlockPath |
::= | ReifiedTriple PropertyListPath |
[23] |
Assignment |
::= | Bind |
[24] |
Bind |
::= | 'BIND' '(' Expression 'AS' Var ')' |
[25] |
Let |
::= | 'LET' '(' Var ':=' Expression ')' |
[26] |
Reifier |
::= | '~' VarOrReifierId? |
[27] |
VarOrReifierId |
::= | Var | iri | BlankNode |
[28] |
Filter |
::= | 'FILTER' Constraint |
[29] |
Constraint |
::= | BrackettedExpression | BuiltInCall | FunctionCall |
[30] |
FunctionCall |
::= | iri ArgList |
[31] |
ArgList |
::= | NIL | '(' Expression ( ',' Expression )* ')' |
[32] |
ExpressionList |
::= | NIL | '(' Expression ( ',' Expression )* ')' |
[33] |
TriplesSameSubject |
::= | VarOrTerm PropertyListNotEmpty | TriplesNode PropertyList | ReifiedTripleBlock |
[34] |
PropertyList |
::= | PropertyListNotEmpty? |
[35] |
PropertyListNotEmpty |
::= | Verb ObjectList ( ';' ( Verb ObjectList )? )* |
[36] |
Verb |
::= | VarOrIri | 'a' |
[37] |
ObjectList |
::= | Object ( ',' Object )* |
[38] |
Object |
::= | GraphNode Annotation |
[39] |
TriplesSameSubjectPath |
::= | VarOrTerm PropertyListPathNotEmpty | TriplesNodePath PropertyListPath | ReifiedTripleBlockPath |
[40] |
PropertyListPath |
::= | PropertyListPathNotEmpty? |
[41] |
PropertyListPathNotEmpty |
::= | ( VerbPath | VerbSimple ) ObjectListPath ( ';' ( ( VerbPath | VerbSimple ) ObjectListPath )? )* |
[42] |
VerbPath |
::= | Path |
[43] |
VerbSimple |
::= | Var |
[44] |
ObjectListPath |
::= | ObjectPath ( ',' ObjectPath )* |
[45] |
ObjectPath |
::= | GraphNodePath AnnotationPath |
[46] |
Path |
::= | PathSequence |
[47] |
PathSequence |
::= | PathEltOrInverse ( '/' PathEltOrInverse )* |
[48] |
PathEltOrInverse |
::= | PathElt | '^' PathElt |
[49] |
PathElt |
::= | PathPrimary |
[50] |
PathPrimary |
::= | iri | 'a' | '(' Path ')' |
[51] |
TriplesNode |
::= | Collection | BlankNodePropertyList |
[52] |
BlankNodePropertyList |
::= | '[' PropertyListNotEmpty ']' |
[53] |
TriplesNodePath |
::= | CollectionPath | BlankNodePropertyListPath |
[54] |
BlankNodePropertyListPath |
::= | '[' PropertyListPathNotEmpty ']' |
[55] |
Collection |
::= | '(' GraphNode+ ')' |
[56] |
CollectionPath |
::= | '(' GraphNodePath+ ')' |
[57] |
AnnotationPath |
::= | ( Reifier | AnnotationBlockPath )* |
[58] |
AnnotationBlockPath |
::= | '{|' PropertyListPathNotEmpty '|}' |
[59] |
Annotation |
::= | ( Reifier | AnnotationBlock )* |
[60] |
AnnotationBlock |
::= | '{|' PropertyListNotEmpty '|}' |
[61] |
GraphNode |
::= | VarOrTerm | TriplesNode | ReifiedTriple |
[62] |
GraphNodePath |
::= | VarOrTerm | TriplesNodePath | ReifiedTriple |
[63] |
VarOrTerm |
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | NIL | TripleTerm |
[64] |
ReifiedTriple |
::= | '<<' ReifiedTripleSubject Verb ReifiedTripleObject Reifier? '>>' |
[65] |
ReifiedTripleSubject |
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | ReifiedTriple |
[66] |
ReifiedTripleObject |
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | ReifiedTriple | TripleTerm |
[67] |
TripleTerm |
::= | '<<(' TripleTermSubject Verb TripleTermObject ')>>' |
[68] |
TripleTermSubject |
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode |
[69] |
TripleTermObject |
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | TripleTerm |
[70] |
TripleTermData |
::= | '<<(' TripleTermDataSubject ( iri | 'a' ) TripleTermDataObject ')>>' |
[71] |
TripleTermDataSubject |
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral |
[72] |
TripleTermDataObject |
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | TripleTermData |
[73] |
VarOrIri |
::= | Var | iri |
[74] |
Var |
::= | VAR1 | VAR2 |
[75] |
Expression |
::= | ConditionalOrExpression |
[76] |
ConditionalOrExpression |
::= | ConditionalAndExpression ( '||' ConditionalAndExpression )* |
[77] |
ConditionalAndExpression |
::= | ValueLogical ( '&&' ValueLogical )* |
[78] |
ValueLogical |
::= | RelationalExpression |
[79] |
RelationalExpression |
::= | NumericExpression ( '=' NumericExpression | '!=' NumericExpression | '<' NumericExpression | '>' NumericExpression | '<=' NumericExpression | '>=' NumericExpression | 'IN' ExpressionList | 'NOT' 'IN' ExpressionList )? |
[80] |
NumericExpression |
::= | AdditiveExpression |
[81] |
AdditiveExpression |
::= | MultiplicativeExpression ( '+' MultiplicativeExpression | '-' MultiplicativeExpression | ( NumericLiteralPositive | NumericLiteralNegative ) ( ( '*' UnaryExpression ) | ( '/' UnaryExpression ) )* )* |
[82] |
MultiplicativeExpression |
::= | UnaryExpression ( '*' UnaryExpression | '/' UnaryExpression )* |
[83] |
UnaryExpression |
::= | '!' PrimaryExpression |
[84] |
PrimaryExpression |
::= | BrackettedExpression | BuiltInCall | iriOrFunction | RDFLiteral | NumericLiteral | BooleanLiteral | Var | ExprTripleTerm |
[85] |
ExprTripleTerm |
::= | '<<(' ExprTripleTermSubject Verb ExprTripleTermObject ')>>' |
[86] |
ExprTripleTermSubject |
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | Var |
[87] |
ExprTripleTermObject |
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | Var | ExprTripleTerm |
[88] |
BrackettedExpression |
::= | '(' Expression ')' |
[89] |
BuiltInCall |
::= | 'STR' '(' Expression ')' |
[90] |
iriOrFunction |
::= | iri ArgList? |
[91] |
RDFLiteral |
::= | String ( LANG_DIR | '^^' iri )? |
[92] |
NumericLiteral |
::= | NumericLiteralUnsigned | NumericLiteralPositive | NumericLiteralNegative |
[93] |
NumericLiteralUnsigned |
::= | INTEGER | DECIMAL | DOUBLE |
[94] |
NumericLiteralPositive |
::= | INTEGER_POSITIVE | DECIMAL_POSITIVE | DOUBLE_POSITIVE |
[95] |
NumericLiteralNegative |
::= | INTEGER_NEGATIVE | DECIMAL_NEGATIVE | DOUBLE_NEGATIVE |
[96] |
BooleanLiteral |
::= | 'true' | 'false' |
[97] |
String |
::= | STRING_LITERAL1 | STRING_LITERAL2 | STRING_LITERAL_LONG1 | STRING_LITERAL_LONG2 |
[98] |
iri |
::= | IRIREF | PrefixedName |
[99] |
PrefixedName |
::= | PNAME_LN | PNAME_NS |
[100] |
BlankNode |
::= | BLANK_NODE_LABEL | ANON |
Productions for terminals:
[101] |
IRIREF |
::= | '<' ([^<>"{}|^`\]-[#x00-#x20])* '>' |
[102] |
PNAME_NS |
::= | PN_PREFIX? ':' |
[103] |
PNAME_LN |
::= | PNAME_NS PN_LOCAL |
[104] |
BLANK_NODE_LABEL |
::= | '_:' ( PN_CHARS_U | [0-9] ) ((PN_CHARS|'.')* PN_CHARS)? |
[105] |
VAR1 |
::= | '?' VARNAME |
[106] |
VAR2 |
::= | '$' VARNAME |
[107] |
LANG_DIR |
::= | '@' [a-zA-Z]+ ('-' [a-zA-Z0-9]+)* ('--' [a-zA-Z]+)? |
[108] |
INTEGER |
::= | [0-9]+ |
[109] |
DECIMAL |
::= | [0-9]* '.' [0-9]+ |
[110] |
DOUBLE |
::= | ( ([0-9]+ ('.'[0-9]*)? ) | ( '.' ([0-9])+ ) ) [eE][+-]?[0-9]+ |
[111] |
INTEGER_POSITIVE |
::= | '+' INTEGER |
[112] |
DECIMAL_POSITIVE |
::= | '+' DECIMAL |
[113] |
DOUBLE_POSITIVE |
::= | '+' DOUBLE |
[114] |
INTEGER_NEGATIVE |
::= | '-' INTEGER |
[115] |
DECIMAL_NEGATIVE |
::= | '-' DECIMAL |
[116] |
DOUBLE_NEGATIVE |
::= | '-' DOUBLE |
[117] |
STRING_LITERAL1 |
::= | "'" ( ([^#x27#x5C#xA#xD]) | ECHAR )* "'" |
[118] |
STRING_LITERAL2 |
::= | '"' ( ([^#x22#x5C#xA#xD]) | ECHAR )* '"' |
[119] |
STRING_LITERAL_LONG1 |
::= | "'''" ( ( "'" | "''" )? ( [^'\] | ECHAR ) )* "'''" |
[120] |
STRING_LITERAL_LONG2 |
::= | '"""' ( ( '"' | '""' )? ( [^"\] | ECHAR ) )* '"""' |
[121] |
ECHAR |
::= | '\' [tbnrf\"'] |
[122] |
NIL |
::= | '(' WS* ')' |
[123] |
WS |
::= | #x20 | #x9 | #xD | #xA |
[124] |
ANON |
::= | '[' WS* ']' |
[125] |
PN_CHARS_BASE |
::= | [A-Z] | [a-z] | [#x00C0-#x00D6] | [#x00D8-#x00F6] | [#x00F8-#x02FF] | [#x0370-#x037D] | [#x037F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF] |
[126] |
PN_CHARS_U |
::= | PN_CHARS_BASE | '_' |
[127] |
VARNAME |
::= | ( PN_CHARS_U | [0-9] ) ( PN_CHARS_U | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040] )* |
[128] |
PN_CHARS |
::= | PN_CHARS_U | '-' | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040] |
[129] |
PN_PREFIX |
::= | PN_CHARS_BASE ((PN_CHARS|'.')* PN_CHARS)? |
[130] |
PN_LOCAL |
::= | (PN_CHARS_U | ':' | [0-9] | PLX ) ((PN_CHARS | '.' | ':' | PLX)* (PN_CHARS | ':' | PLX) )? |
[131] |
PLX |
::= | PERCENT | PN_LOCAL_ESC |
[132] |
PERCENT |
::= | '%' HEX HEX |
[133] |
HEX |
::= | [0-9] | [A-F] | [a-f] |
[134] |
PN_LOCAL_ESC |
::= | '\' ( '_' | '~' | '.' | '-' | '!' | '$' | '&' | "'" | '(' | ')' | '*' | '+' | ',' | ';' | '=' | '/' | '?' | '#' | '@' | '%' ) |
TODO
TODO
TODO
Many people contributed to this document, including members of the RDF Data Shapes Working Group.