Accountability in RDF (AIR) is a policy language represented in Turtle + quoting. The vocabulary –classes and properties- used for declaring policies is defined in the AIR ontology (ttl, owl 1, owl 2, class taxonomy). AIR supports generation of explanations for policy decisions and provides efficient and expressive reasoning.
In AIR, policy is modeled as production-rule containers. Rules are encoded as pattern-action pairs. Conditions for a rule is represented in form of graph pattern. The actions- assertions, triggering of a new rule- can be declared to be executed either when pattern is matched or when no match is found. A matched subgraph can be referenced and reused to generate justifications. Every action is associated with a justification- rule-id and matched pattern- at the run-time. A natural language description of the rule can also be included. Rules are of 2 types- Belief-rules and Hidden-rules. When an explanation for policy decision is generated the justification for actions in Hidden rules are not included. Compliance and non-compliance with policies can be asserted using compliant-with and non-compliant-with predicates. Detailed AIR policy specifications are provided here.
More examples and demos can be found here
The latest release of the Tabulator extension for Firefox is available here.
Figure 1. Data Log
Data used for examples in the tutorial: Demo Data ( in RDF )
Description of the Data:
The general flavor of policies is that people belonging to NY state- because of their place of residence and/ or if they hold a NY state id- are policy compliant.
Policy contain one or more rules. The preconditions for the rule are described as graph patterns where variable may occur at any of the 3 positions- subject, object or predicate- and is declared using the air:pattern property. The global variables are declared at the beginning of the document. The assertions are made using air:assert (or air:assertion) property.
@forAll :PERSON, :CITY.
:ny_state_residency_policy a air:Policy;
rdfs:label "NY State residency policy";
air:rule :state-residency-rule.
:state-residency-rule a air:Belief-rule;
rdfs:label "state residency rule";
air:if {
:PERSON tamip:Lives_in_city :CITY.
:CITY tamip:Has_state :NY.
};
air:then [air:assert [air:statement {:PERSON air:compliant-with :ny_state_residency_policy.}]].
Execute (Output of the Policy Engine)
Conclusions: Bob and Alice are compliant with :ny_state_residency.
(Bob and Alice live in Troy, which is in NY state)
The same policy can be written without explicitly naming the rule. However, for justification justification of actions taken when this rule is applied the policy engine assigns a random identifier for the rule.
@forAll :PERSON, :CITY.
:ny_state_residency_policy a air:Policy;
rdfs:label "NY State residency policy";
air:then
[ air:rule
[
rdfs:label "state residency rule";
air:if {
:PERSON tamip:Lives_in_city :CITY.
:CITY tamip:Has_state :NY.
};
air:then [air:assert [air:statement {:PERSON air:compliant-with :ny_state_residency_policy.}]] ]].
A rule with an empty pattern can also be asserted as shown in the example below.
TODO
Tutorial Policy 21:
@forAll :PERSON, :CITY.
:rpi_location_based_policy a air:Policy;
air:rule :troy-rule;
air:rule :hartford-rule.
:troy-rule a air:Belief-rule;
rdfs:label "rpi in troy rule";
air:if { };
air:then [air:assert [air:statement {:rpi tamip:Located_In :troy.}]].
:hartford-rule a air:Belief-rule;
rdfs:label "rpi in hartford rule";
air:if { };
air:then [air:assert [air:statement {:rpi tamip:Located_In :hartford.}]].
Conclusions: Alice is compliant with '':ny_state_residency_and_id_policy.
(Alice lives in Troy which is in NY state and has NY state id 307 578 001)
@forAll :PERSON, :CITY, :STATE.
:ny_neighbor_state_residency_policy a air:Policy;
air:rule :non-ny-residency-rule.
:non-ny-residency-rule a air:Belief-rule;
rdfs:label "Non NY residency rule";
air:if {:PERSON tamip:Lives_in_city :CITY.};
air:then
[ air:rule [
air:if {:CITY tamip:Has_state :NY.};
air:else [air:rule :neighbor-state-rule]]].
:neighbor-state-rule a air:Belief-rule;
rdfs:label "neighbor state rule";
air:if { :CITY tamip:Has_state :STATE.
:NY tamip:Neighbor_state :STATE.};
air:then [air:assert [air:statement { :PERSON air:compliant-with :ny_neighbor_state_residency_policy. }]].
Conclusion: George is compliant with :ny_neighbor_state_residency_policy.
(George lives in Boston, Boston is in MA (not NY), NY is a neighbor state of MA.)
@forAll :PERSON, :CITY, :NY_STATE_ID.
:ny_state_residency_or_id_policy a air:Policy;
air:rule :state-residency-rule;
air:rule :state-id-check.
:state-residency-rule a air:Belief-rule;
rdfs:label "state residency rule";
air:if {
:PERSON tamip:Lives_in_city :CITY.
:CITY tamip:Has_state :NY.
};
air:then [air:assert [air:statement {:PERSON air:compliant-with :ny_state_residency_or_id_policy.}]].
:state-id-check a air:Belief-rule;
rdfs:label "state id check rule";
air:if { :PERSON tamip:Has_ny_state_id :NY_STATE_ID. };
air:then [air:assert [air:statement {:PERSON air:compliant-with :ny_state_residency_or_id_policy.}]].
Conclusions: Alice, Bob and David comply with the :ny_state_residency_or_id_policy.
(Bob live in Troy, which is in NY. David has NY state ID 307 578 002. Alice lives in Troy, in NY and has a NY state ID, 307 578 001, as well.)
In Negatively Nested Rules air:alt had been used to activate a nested rule when the pattern in the nesting rule did not match. However, air:alt can also be used to make simply an alternate assertion.
@forAll :PERSON, :CITY.
:ny_state_residency_policy a air:Policy;
air:rule :state-residency-rule.
:state-residency-rule a air:Belief-rule;
rdfs:label "state residency rule";
air:if {
:PERSON tamip:Lives_in_city :CITY.
};
air:then [air:rule [
air:if {
:CITY tamip:Has_state :NY.
};
air:then [air:assert [air:statement {:PERSON air:compliant-with :ny_state_residency_policy.}]];
air:else [air:assert [air:statement {:PERSON air:non-compliant-with :ny_state_residency_policy.}]] ]
].
Conclusions: Alice and Bob comply with the :ny_state_residency_policy. George is not compliant with the :ny_state_residency_policy.
(Alice and Bob live in Troy which is in NY state. George lives in Boston, in MA, which is not in NY.)
All variables in the examples covered so far were declared globally.
@forAll :PERSON.
:ny_state_residency_policy a air:Policy;
air:rule :state-residency-rule.
:state-residency-rule a air:Belief-rule;
rdfs:label "state residency rule";
air:if {
@forSome :CITY.
:PERSON tamip:Lives_in_city :CITY.
:CITY tamip:Has_state :NY.
};
air:then [air:assert [air:statement {:PERSON air:compliant-with :ny_state_residency_policy.}]].
Conclusions: Bob and Alice comply with :ny_state_residency_policy.
Instead, if :CITY was universally qualified, the :state-residency-rule would become: for all persons, if person lives in :CITY and for all values that :CITY can take :CITY is in state NY, the person is compliant with :ny_state_residency_policy. Since all URIs in the data are possible values for :CITY, empty set is returned as conclusion. (Tutorial Policy 7, Execute)
Note that the existentially quantified variable :CITY can be replaced with a blank node.
@forAll :PERSON.
:ny_state_residency_policy a air:Policy;
air:rule :state-residency-rule.
:state-residency-rule a air:Belief-rule;
rdfs:label "state residency rule";
air:if {
:PERSON tamip:Lives_in_city [tamip:Has_state :NY].
};
air:then [air:assert [air:statement {:PERSON air:compliant-with :ny_state_residency_policy.}]].
TBD
@forAll :PERSON, :CITY, :NY_STATE_ID. :ny_state_residency_policy a air:Policy; air:rule :state-residency-rule. :state-residency-rule a air:Belief-rule; rdfs:label "state residency rule"; air:if { :PERSON tamip:Lives_in_city :CITY. :CITY tamip:Has_state :NY. }; air:then [air:assert [air:statement {:PERSON air:compliant-with :ny_state_residency_policy.}]]. :ny_state_id_policy a air:Policy; air:rule :state-id-check. :state-id-check a air:Belief-rule; rdfs:label "state id check rule"; air:if { :PERSON tamip:Has_ny_state_id :NY_STATE_ID. }; air:then [air:assert [air:statement {:PERSON air:compliant-with :ny_state_id_policy.}]].
Here we see that we can define two different policies :ny_state_residency_policy and :ny_state_id_policy in the same document. Rules listed in a document are considered together, therefore an assertion made in :state-id-check can potentially impact the execution of :state-residency-rule and vice-versa. Therefore, carefully check for any side effects from rules contained in other policies, in the same document.
Conclusions: Alice and Bob comply with :ny_state_residency_policy. Alice and David comply with :ny_state_id_policy.
(Alice and Bob stay in Troy, which is in NY state. Alice and David have NY state ids "307 578 001" and "307 578 002")
@forAll :PERSON, :CITY.
:ny_state_residency_policy a air:Policy;
air:rule :state-residency-rule.
:state-residency-rule a air:Belief-rule;
rdfs:label "state residency rule";
air:if {
:PERSON tamip:Lives_in_city :CITY.
:CITY tamip:Has_state :NY.
};
air:then [air:assert [air:statement {:PERSON air:compliant-with :ny_state_residency_policy.}]].
@forAll :PERSON, :NY_STATE_ID.
:ny_state_id_policy a air:Policy;
air:rule :state-id-check.
:state-id-check a air:Belief-rule;
rdfs:label "state id check rule";
air:if { :PERSON tamip:Has_ny_state_id :NY_STATE_ID. };
air:then [air:assert [air:statement {:PERSON air:compliant-with :ny_state_id_policy.}]].
Conclusions: Alice and Bob comply with :ny_state_residency_policy. Alice and David comply with :ny_state_id_policy.
@forAll :PERSON, :CITY.
:ny_state_residency_and_id_policy a air:Policy;
air:rule :state-residency-rule.
:state-residency-rule a air:Belief-rule;
rdfs:label "state residency rule";
air:if {
:PERSON tamip:Lives_in_city :CITY.
:CITY tamip:Has_state :NY.
};
air:then [air:rule :state-id-check].
@forAll :PERSON, :NY_STATE_ID.
:state-id-check a air:Belief-rule;
rdfs:label "state id check rule";
air:if { :PERSON tamip:Has_ny_state_id :NY_STATE_ID. };
air:then [air:assert [air:statement {:PERSON air:compliant-with :ny_state_residency_and_id_policy.}]].
Conclusions: Alice is compliant with ':ny_state_residency_and_id_policy'.
Variable bindings take place at the time of pattern matching. Unsafe rule refers to the condition when a statement is asserted by the rule when not all of its variables are bound. It may arise for at-least one of these two reasons.
1. PERSON1 variable doesn't occur in the pattern of the rule or that of it's parent or any ancestor rules.
@forAll :PERSON, :CITY, :PERSON1.
:ny_state_residency_and_id_policy a air:Policy;
air:rule :state-residency-rule.
:state-residency-rule a air:Belief-rule;
rdfs:label "state residency rule";
air:if {
:PERSON tamip:Lives_in_city :CITY.
:CITY tamip:Has_state :NY.
};
air:then [air:assert [air:statement {:PERSON1 air:compliant-with :ny_state_residency_and_id_policy.}]].
@forAll :PERSON, :CITY.
:ny_state_residency_and_id_policy a air:Policy;
air:rule :state-residency-rule.
:state-residency-rule a air:Belief-rule;
rdfs:label "state residency rule";
air:if {
:PERSON tamip:Lives_in_city :CITY.
:CITY tamip:Has_state :NY.
};
air:else [air:assert [air:statement {:PERSON air:non-compliant-with :ny_state_residency_and_id_policy.}]].
Though this can rarely be detected before policy reasoning is performed, but while performing reasoning it can be inferred explicitly using following policy:
@forAll :RES, :POL.
:contains_contradiction_policy a air:Policy;
air:rule [
rdfs:label "rule to detect contradiction";
air:if {
:RES air:compliant-with :POL.
:RES air:non-compliant-with :POL.
};
air:then [air:assert air:statement [{:RES air:compliant-with :contains_contradiction_policy.}]]
].
Every action is associated with justification(s) at the run time. By default the conjunction of matched graphs (antecedent) and the rule-id are given as justification for the actions. These justifications (i.e. antecedent & rule-id) can be explicitly modified or suppressed.
A natural language description of the rule can also be provided.
@forAll :PERSON, :CITY.
:ny_state_residency_policy a air:Policy;
air:rule :state-residency-rule.
:state-residency-rule a air:Belief-rule;
rdfs:label "state residency rule";
air:if {
:PERSON tamip:Lives_in_city :CITY.
:CITY tamip:Has_state :NY.
};
air:then
[ air:description(:PERSON "lives in the NY state city -" :CITY);
air:assert [air:statement {:PERSON air:compliant-with :ny_state_residency_policy.}]].
, <:Alice> "lives in the NY state city -" <:Troy>.
@forAll :PERSON, :CITY, :NY_STATE_ID.
:ny_state_residency_and_id_policy a air:Policy;
air:rule :state-residency-rule.
:state-residency-rule a air:Belief-rule;
rdfs:label "state residency rule";
air:if {
:PERSON tamip:Lives_in_city :CITY.
:CITY tamip:Has_state :NY.
};
air:then [air:rule :state-id-check].
:state-id-check a air:Hidden-rule;
rdfs:label "state id check rule";
air:if { :PERSON tamip:Has_ny_state_id :NY_STATE_ID. };
air:then [air:assert [air:statement {:PERSON air:compliant-with :ny_state_residency_and_id_policy.}]].
:justification [
:antecedent-expr [
a :And-justification;
:sub-expr [
air:instanceOf <:state-id-check>;
:justification [
:antecedent-expr [
a :And-justification;
:sub-expr <:state-residency-rule>,
{<:Alice> <:Lives_in_city> <:Troy> . <:Troy> <:Has_state> <:NY> .} ];
:rule-name <:state-residency-rule> ] ],
{<:Alice> <:Has_ny_state_id> <:307_578_001> .
} ];
:rule-name <:state-id-check> ] .
:justification [
:antecedent-expr [
a :And-justification;
:sub-expr <:state-residency-rule>,
{<:Alice> <:Lives_in_city> <:Troy>. <:Troy> <:Has_state> <:NY>.} ];
:rule-name <:state-residency-rule> ].
@forAll :PERSON, :CITY, :NY_STATE_ID, :G1 .
:ny_state_residency_and_id_policy a air:Policy;
air:rule :state-residency-rule.
:state-residency-rule a air:Belief-rule;
rdfs:label "state residency rule";
air:if {
:PERSON tamip:Lives_in_city :CITY.
:CITY tamip:Has_state :NY.
};
air:matched-graph :G1;
air:then [air:rule :state-id-check].
:state-id-check a air:Belief-rule;
rdfs:label "state id check rule";
air:if { :PERSON tamip:Has_ny_state_id :NY_STATE_ID. };
air:then
[ air:description ( :PERSON "is a new york state resident" );
air:assert
[ air:statement { :PERSON air:compliant-with :ny_state_residency_and_id_policy.};
air:justification
[ air:rule-id :state-residency-id-rule;
air:antecedent :G1 ]]].
Justification for Tutorial Policy 13:
:justification [
:antecedent-expr [
a :And-justification;
:sub-expr <:state-residency-rule>,
{<:Alice> <:Lives_in_city> <:Troy>. <:Troy> <:Has_state> <:NY>.} ];
:rule-name <:state-residency-rule> ].
In section 3.1.3 we had seen how unmatched cases can be explicitly handled using air:alt, like in Tutorial Policy 16_v2. This explicit handling also provides explanations for failed (non-compliant) policy decisions.
Section 4: Policy Engine Features
The policy engine supports reasoning for following owl (& rdfs) constructs:
The inference rules are encoded in AIR (base-rules).
@forAll :PERSON, :CITY, :STATE.
:ny_neighbor_state_residency_policy a air:Policy;
air:rule :non-ny-residency-rule.
:non-ny-residency-rule a air:Belief-rule;
rdfs:label "Non NY residency rule";
air:if {:PERSON tamip:Lives_in_city :CITY.};
air:then
[ air:rule
[ air:if {:CITY tamip:Has_state :NY.};
air:else [air:rule :neighbor-state-rule]]].
:neighbor-state-rule a air:Belief-rule;
rdfs:label "neighbor state rule";
air:if { :CITY tamip:Has_state :STATE.
:STATE tamip:Neighbor_state :NY.};
air:then [air:assert [air:statement { :PERSON air:compliant-with :ny_neighbor_state_residency_policy. }]].
Conclusions: George is compliant with :ny_neighbor_state_residency_policy.
(George lives in Boston. Boston is in MA, not in NY. NY is neighbor state of MA, but since neighbor state is a symmetric property, MA is neighbor state of NY also holds.)
Consider a new data file AIR Demo Data1_v2 (RDF)- contains single triple :Bill tamip:lives_in_city :Troy.
The 2 data files, AIR Demo Data and AIR Demo Data1_v2, are checked against Tutorial Policy 1_v2.
Conclusions: Alice, Bob and Bill comply with :ny_state_residency policy.
(Alice & Bob live in Troy, which is in NY. Bill also lives in Troy. Troy is in NY is stated in other log.)
For example, a list of concluded predicates to be displayed in the reasoner's output can be passed to the engine. This is discussed in detail in 4.4.1.
Policy engine employs a forward chaining reasoner. On finding the deductive closure (of conclusions) the reasoner filters some conclusions to be displayed in the results, along with their justification. Other characteristics of the reasoners output:
@forAll :PERSON, :CITY.
:ny_state_residency_policy a air:Policy;
air:rule :state-residency-rule.
:state-residency-rule a air:Belief-rule;
rdfs:label "state residency rule";
air:if {
:PERSON tamip:Lives_in_city :CITY.
:CITY tamip:Has_state :NY.
};
air:then [air:assert [air:statement {:PERSON air:compliant-with :ny_state_residency_policy.
:PERSON tamip:Lives_in_state :NY.}]].
Conclusions: Bob and Alice are compliant with :ny_state_residency policy. However, the result does not include the triples- <:Bob> tamip:Lives_in_state <:NY> and <:Alice> tamip:Lives_in_state <:NY>- that we know have been asserted as well.
By default the policy engine chooses to filter all the conclusions with the predicates air:compliant-with and air:non-compliant-with. We can pass a list of URIs of predicates to the argument named filterProperties, directing the policy engine to filter all the conclusions with these predicates (in addition to air:compliant-with and air:non-compliant-with predicates).
We will run data by Tutorial Policy 15, and pass tamip:Lives_in_state as argument to filterProperties.