TypeDB Fundamentals Lecture Series: from Nov 16 to Jan 9

Inferring data

How to use inference

Inference is enabled as a Read transaction option.

TypeDB Studio

To send a request with inference enabled in TypeDB Studio follow these steps:

  1. Ensure the Session type (schema / data) switch is set to data (next to the database dropdown).

  2. Ensure the Transaction type (write / read) switch is set to read.

  3. Ensure the infer button (next to the transaction type switch) is pushed (shown in green text).

  4. Click the + icon at the top of the Text editor panel.

  5. Copy the TypeQL query into the Text editor panel.

  6. Click the green "play" button.

TypeDB Console

Set the infer option to true when starting the read transaction. For example:

transaction typedb data read --infer true

TypeDB Drivers

Set the infer option to True when starting the read transaction.

For example (in Python):

typedb_options = TypeDBOptions()  # Initialising a new set of options
typedb_options.infer = True  # Enabling inference in this new set of options
with session.transaction(TransactionType.READ, typedb_options) as transaction:

Example

In the IAM database there are multiple rules, but we will use the one described at the IAM schema explanation page.

rule add-view-permission:
    when {
        $modify isa action, has name "modify_file";
        $view isa action, has name "view_file";
        $ac_modify (object: $obj, action: $modify) isa access;
        $ac_view (object: $obj, action: $view) isa access;
        (subject: $subj, access: $ac_modify) isa permission;
    } then {
        (subject: $subj, access: $ac_view) isa permission;
    };

By default, the IAM dataset used in the Quickstart guide sets permissions only for the modify_file action.

But the IAM schema has the rule called add-view-permission (see above) to infer permission to view_file access from the modify_file permission for the same file. The view_file action access permission does not exist in the database, but it can be inferred by the rule.

To do so, let’s use the following query:

match
    $p isa person, has full-name "Kevin Morrison";
    $o isa object, has path $o-path;
    $a isa action, has name "view_file";
    $ac(object: $o, action: $a) isa access;
    $pe(subject: $p, access: $ac) isa permission;
get $o-path;

The above query should return path values for most files if the inference option is enabled. Without the inference enabled the query should not return any results (as the default IAM dataset contains no permissions for access with view_file action).

Query explanation

The example is a get query that does the following:

  1. Finds person entity ($p) with an attribute full-name equal to the Kevin Morrison string value.

  2. Finds all object entities ($o) with a path attribute ($o-path).

  3. Finds action entity ($a) with name of view-file.

  4. Finds access relation ($ac) that relates $o (as object) to $a (as action).

  5. Finds all permission relations that relate $p (as subject) to $ac (as access).

  6. Filters the results to receive only the $o-path variable that complies with all the statements above.

In short, it returns path attributes for every object that person with a full-name of Kevin Morrison has permission to access with action with the name of view_file.

Explain query

There is specific type of query to use with the inferred results if we want them explained by a TypeDB server.

An Explain query does not use any TypeQL syntax.

When we get a result of a Get query that was performed with both inference and explain options enabled we can use an Explain query to request additional information on exactly how the instances of data were inferred.

To do that use the explainables method on the get query result (ConceptMap). It returns a stream of explainable objects. Send an explainable object with an Explain query to get a stream of explanations.

Every explanation include information about what rule was used to obtain this inferred result, condition of the rule, conclusion of the rule, and variable mapping.

Python example

The following code (provided with a TypeDB server with IAM database from Quickstart guide) can infer view_file permission for multiple files for a single user and explain every inferred result. Basically, we repeat the previous example and then request explanation of relation $pe.

from typedb.driver import TypeDB, SessionType, TransactionType, TypeDBOptions

with TypeDB.core_driver("0.0.0.0:1729") as driver:  # Connect to TypeDB server
    with driver.session("iam", SessionType.DATA) as session:
        print("\nRequest #1: Explain query — Files that Kevin Morrison has view access to (with explanation)")
        typedb_options = TypeDBOptions()  # Initialising a new set of options
        typedb_options.infer = True  # Enabling inference in this new set of options
        typedb_options.explain = True
        with session.transaction(TransactionType.READ, typedb_options) as transaction:
            typeql_read_query = "match $p isa person, has full-name $p-fname; $o isa object, has path $o-path;" \
                                "$a isa action, has name 'view_file'; $ac(object: $o, action: $a) isa access;" \
                                "$pe(subject: $p, access: $ac) isa permission; $p-fname = 'Kevin Morrison';" \
                                "get $o-path; sort $o-path asc;"
            iterator = transaction.query().get(typeql_read_query)
            i = 0
            for item in iterator:  # Iterating through results
                i += 1
                explainable_relations = item.explainables().relations()
                e = 0
                for explainable in explainable_relations:
                    e += 1
                    explain_iterator = transaction.query().explain(explainable_relations[explainable])
                    ex = 0
                    for explanation in explain_iterator:
                        ex += 1

                        print("\nRead result #:", i, ", File path:", item.get(cVar("o-path")).as_attribute().get_value())
                        print("Explainable #:", e, ", Explained variable:", explainable)
                        print("Explainable object:", explainable_relations[explainable])
                        print("Explainable part of query:", explainable_relations[explainable].conjunction())
                        print("Explanation #:", ex)

                        print("\nRule: ", explanation.rule().get_label())
                        print("Condition: ", explanation.condition())
                        print("Conclusion: ", explanation.conclusion())
                        print("Variables used in explanation: ", explanation.variable_mapping())
                        print("----------------------------------------------------------")

The script above runs the query from the example in the previous section. The inference option provides the result of 10 files (by default in the IAM database from the Quickstart guide). And explain option enables the explainables to be received and used in the explain queries (one explain query for each result that needs to be explained).

Output

The result should be similar to the following:

Read result #: 10 , File path: zlckt.ts
Explainable #: 1 , Explained variable: pe
Explainable object: <typedb.concept.answer.concept_map._ConceptMap.Explainable object at 0x105cb34f0>
Explainable part of query: { $pe (subject:$p, access:$ac); $pe isa permission; }
Explanation #: 1

Rule:  add-view-permission

Condition:  [_1/_StringAttribute[name:0x836f800328000b6d6f646966795f66696c65]][_2/_StringAttribute[name:0x836f8003280009766965775f66696c65]][_3/_Relation[permission:0x847080038000000000000001]][ac_modify/_Relation[access:0x8470800a8000000000000003]][ac_view/_Relation[access:0x8470800a8000000000000011]][modify/_Entity[operation:0x826e800c8000000000000001]][obj/_Entity[file:0x826e80098000000000000004]][subj/_Entity[person:0x826e80018000000000000001]][view/_Entity[operation:0x826e800c8000000000000000]]

Conclusion:  [_/_Relation[permission:0x847080037fffffffffffffff]][_permission/_RelationType[label: permission]][_permission:access/_RoleType[label: permission:access]][_permission:subject/_RoleType[label: permission:subject]][ac_view/_Relation[access:0x8470800a8000000000000011]][subj/_Entity[person:0x826e80018000000000000001]]

Variables used in explanation:  {'p': {'subj'}, 'ac': {'ac_view'}, 'pe': {'_'}}

----------------------------------------------------------

Explanation parsing

Get label

The explanation.rule().get_label() method returns the name of the rule that was used for this particular inference, hence the result of which is being explained:

add-view-permission
Variable mapping

The explanation.variable_mapping() method returns mapping of the variable names in the query with variable names in the rule:

{'p': {'subj'}, 'ac': {'ac_view'}, 'pe': {'_'}}
Condition

The explanation.condition()method returns the condition of the rule written with the exact matched instances of data.

For the rule condition defined as:

$modify isa action, has name "modify_file";
$view isa action, has name "view_file";
$ac_modify (object: $obj, action: $modify) isa access;
$ac_view (object: $obj, action: $view) isa access;
(subject: $subj, access: $ac_modify) isa permission; \

We got the condition explained with particular instances from the IAM dataset:

[_1/_StringAttribute[name:0x836f800328000b6d6f646966795f66696c65]]
[_2/_StringAttribute[name:0x836f8003280009766965775f66696c65]]
[_3/_Relation[permission:0x847080038000000000000001]]
[ac_modify/_Relation[access:0x8470800a8000000000000003]]
[ac_view/_Relation[access:0x8470800a8000000000000011]]
[modify/_Entity[operation:0x826e800c8000000000000001]]
[obj/_Entity[file:0x826e80098000000000000004]]
[subj/_Entity[person:0x826e80018000000000000001]]
[view/_Entity[operation:0x826e800c8000000000000000]]

The example above contains additional line breakers for convenience. The syntax of the condition is similar to the following pattern:

[<rule_variable_label>/<RootType>[<Type>:<IID>]]

Instead of a <RootType> placeholder we can get one of this:

  • _Entity

  • _Relation

  • _Attribute

  • _EntityType

  • _RelationType

  • _AttributeType

  • RoleType

Those are exact instances of data that were matched by the rule. For example, obj is a file type entity that has an attribute of path type with the value zlckt.ts. We didn’t get the path in the explanation because it wasn’t mentioned in the rule, but was able to obtain it by the API call from the get query result:

item.get(cVar("o-path")).as_attribute().get_value())
Conclusion

The explanation.conclusion() method returns the conclusion of the rule written with the exact instances of data (including the inferred instance of data that exists only virtually — as a result of the inference).

For the rule condition defined as:

(subject: $subj, access: $ac_view) isa permission;

We got the conclusion explained with particular instances from the IAM dataset:

[_/_Relation[permission:0x847080037fffffffffffffff]]
[_permission/_RelationType[label: permission]]
[_permission:access/_RoleType[label: permission:access]]
[_permission:subject/_RoleType[label: permission:subject]]
[ac_view/_Relation[access:0x8470800a8000000000000011]]
[subj/_Entity[person:0x826e80018000000000000001]]

The example above contains additional line breakers for convenience. The syntax of the condition is similar to the following pattern:

[<rule_variable>/<RootType>[<Type>:<IID>]]

Instead of a <RootType> placeholder we can get one of this:

  • _Entity

  • _Relation

  • _Attribute

  • _EntityType

  • _RelationType

  • _AttributeType

  • RoleType

Inference optimization

These are general tips for making queries with reasoning execute faster:

  1. Adding a limit to the query. Without a limit, the reasoning engine is forced to explore all possible ways to answer the query exhaustively. If we only need 1 answer, adding limit 1; to the get query can significantly improve query times.

  2. Using the same transaction for multiple reasoning queries. Because inferred data is cleared between transactions, running the same or similar queries within one transaction can reuse previously inferred data. Combined with a limit on the query, it might be possible to avoid having to do any new reasoning at all.

  3. For complex queries, it can also be beneficial to add more CPU cores, as the reasoning engine is able to explore more paths in the database concurrently.

Provide Feedback