Explanation objects
In this guide, you’ll learn how to use driver API methods and stateful objects to programmatically work with explanations. This page assumes you’ve already followed instructions from the Inference page.
Understanding explanations
TypeDB has explainable rule-based data inference feature: explanations represent chains of reasoning that yield a conclusions. The Driver API provides methods to programmatically work with explanations as objects. To learn how to use explanations in TypeDB Studio, see the explanation section of the TypeDB Studio Manual.
Explainables
To get an explanation on infered data, you need to infer some data with a Get query.
If the explain
transaction option is enabled,
the response to the query includes an Explainables object as a part of every ConceptMap.
We will use the Explainables object later to get explanations.
-
Rust
-
Python
-
Node.js
-
Java
-
C#
-
C++
-
C
let response = tx.query().get(get_query)?;
for (i, cmap) in response.enumerate() {
let explainable_relations = cmap.clone()?.explainables.relations;
}
For more information, see the Explainables and Explainable classes.
response = tx.query.get(get_query)
for i, ConceptMap in enumerate(response, start=1):
explainable_relations = ConceptMap.explainables().relations()
For more information, see the Explainables and Explainable classes.
let response = await tx.query.get(get_query).collect();
for(let i = 0; i < response.length; i++) {
let explainable_relations = await response[i].explainables.relations;
}
For more information, see the Explainables and Explainable classes.
Stream<ConceptMap> response = tx.query().get(getQuery);
response.forEach(result -> {
Stream<Pair<String, ConceptMap.Explainable>> explainable_relations = result.explainables().relations();
});
For more information, see the Explainables and Explainable classes.
IList<IConceptMap> response = tx.Query.Get(getQuery).ToList();
foreach (IConceptMap ConceptMap in response) {
IEnumerable<KeyValuePair<string, IConceptMap.IExplainable>> explainableRelations = ConceptMap.AllExplainables.GetRelations();
}
For more information, see the IExplainables and IExplainable classes.
TypeDB::ConceptMapIterable results = tx.query.get(getQuery);
for (TypeDB::ConceptMap& cm : results) {
TypeDB::StringIterable explainableRelations = cm.explainables().relations();
}
For more information, see the Explainables and Explainable classes.
ConceptMapIterator* response = query_get(tx, query, opts);
ConceptMap* CM = NULL;
Concept* nameConcept = NULL;
while ((CM = concept_map_iterator_next(response)) != NULL) {
Explainables* explainables = concept_map_get_explainables(CM);
StringIterator* explainableRelations = explainables_get_relations_keys(explainables);
}
Explain query
An Explain query is a special type of query that can be sent with driver API to get explanations. It is the only type of query that doesn’t use TypeQL. Instead, it uses an explainable object as an argument. See an example below.
-
Rust
-
Python
-
Node.js
-
Java
-
C#
-
C++
-
C
for (var, explainable) in explainable_relations {
let explain_iterator = tx.query().explain(&explainable)?;
}
For more information, see the Explain query and Explainable class methods.
for var, explainable in explainable_relations:
explain_iterator = tx.query.explain(explainable)
For more information, see the Explain query and Explainable class methods.
for await (const explainable of explainable_relations) {
explain_iterator = tx.query.explain(explainable);
}
For more information, see the Explain query and Explainable class methods.
explainable_relations.forEach(explainable -> {
Stream<Explanation> explain_iterator = tx.query().explain(explainable.second());
});
For more information, see the Explain query and Explainable class methods.
foreach (KeyValuePair<string, IConceptMap.IExplainable> explainable in explainableRelations) {
IEnumerable<IExplanation> explainIterator = tx.Query.Explain(ConceptMap.AllExplainables.Relation(explainable.Key));
}
For more information, see the Explain query and IExplainable class methods.
for (std::string& explainable : explainableRelations) {
TypeDB::ExplanationIterable explainIterator = tx.query.explain(cm.explainables().relation(explainable));
}
For more information, see the Explain query and Explainable class methods.
char* explainableKey;
while ((explainableKey = string_iterator_next(explainableRelations)) != NULL) {
ExplanationIterator* explainIterator = query_explain(tx, explainables_get_relation(explainables, explainableKey), opts);
}
Explanation
An explanation object has information on which rule was used for inferring the explained concept, the condition of the rule, the conclusion of the rule, and the mapping of variables between the query and the rule’s conclusion.
-
Rust
-
Python
-
Node.js
-
Java
-
C#
-
C++
-
C
for explanation in explain_iterator {
let exp = explanation?;
println!("Rule: {}", exp.rule.label);
println!("Condition: {}", exp.rule.when.to_string());
println!("Conclusion: {}", exp.rule.then.to_string());
println!("Variable mapping:");
for qvar in exp.variable_mapping.keys() {
println!(
"Query variable {} maps to the rule variable {}",
*qvar,
exp.variable_mapping.get(qvar).unwrap().concat().to_string()
);
}
}
For more information, see the Explanation class methods.
for explanation in explain_iterator:
print("\nRule: ", explanation.rule().label)
print("Condition: ", explanation.condition())
print("Conclusion: ", explanation.conclusion())
print("Variable mapping: ")
for qvar in explanation.query_variables():
print(
f" Query variable {qvar} maps to the rule variable {explanation.query_variable_mapping(var)}")
For more information, see the Explanation class methods.
for (explanation of explain_iterator) {
console.log("Rule: " + explanation.rule.label)
console.log("Condition: " + explanation.condition.toString())
console.log("Conclusion " + explanation.conclusion.toString())
for (qvar of explanation.variableMapping.keys()) {
console.log("Query variable " + qvar + " maps to the rule variable " + explanation.variableMapping.get(qvar))
}
}
For more information, see the Explanation class methods.
explain_iterator.forEach(explanation -> {
System.out.println("Rule: " + explanation.rule().getLabel());
System.out.println(" Condition: " + explanation.rule().getWhen().toString());
System.out.println(" Conclusion: " + explanation.rule().getThen().toString());
explanation.queryVariables().forEach(var ->
System.out.println("Query variable " + var + "maps to the rule variable " + explanation.queryVariableMapping(var)));
});
For more information, see the Explanation class methods.
foreach (IExplanation explanation in explainIterator) {
Console.WriteLine("Rule: " + explanation.Rule.Label);
Console.WriteLine("Condition: " + explanation.Rule.When);
Console.WriteLine("Conclusion: " + explanation.Rule.Then);
Console.WriteLine("Variable mapping: ");
foreach (string var in explanation.GetQueryVariables()) {
Console.WriteLine("Query variable: " + var + " maps to the rule variable " + explanation.QueryVariableMapping(var).ToString());
}
}
For more information, see the IExplanation class methods.
for (TypeDB::Explanation& explanation : explainIterator) {
std::cout << "Rule: " << explanation.rule().label() << std::endl;
std::cout << "Condition: " << explanation.rule().when() << std::endl;
std::cout << "Conclusion: " << explanation.rule().then() << std::endl;
std::cout << "Variable mapping: " << std::endl;
for (std::string& var : explanation.queryVariables()) {
std::cout << "Query variable " << var << " maps to the rule variable " << explanation.queryVariableMapping(var)[1] << std::endl;
}
}
For more information, see the Explanation class methods.
Explanation* explanation;
while ((explanation = explanation_iterator_next(explainIterator)) != NULL) {
Rule* rule = explanation_get_rule(explanation);
printf("Rule: %s\nCondition: %s\nConclusion: %s\nVariable mapping:\n", rule_get_label(rule), rule_get_when(rule), rule_get_then(rule));
char* var;
while ((var = string_iterator_next(explanation_get_mapped_variables(explanation))) != NULL) {
printf("Query variable: %s maps to the rule variable(s)", var);
char* ruleVar;
while ((ruleVar = string_iterator_next(explanation_get_mapping(explanation, var))) != NULL) {
printf(" %s", ruleVar);
}
}
}
Full example
See below a comprehensive example of using explanation of inferred results.
In this example we get all names of a user, matched by its email containing substring alice
.
There should be only one such user in the database so far, and it should have only one name,
but according to the users
rule, it should have an additional infered name: 'User`.
So, we turn on inference, retrieve both names, and proceed to retrieve explainables for both names.
Since only one name is infered, we can only have one explainable.
So we iterate through explainables and send an Explain query to retrieve an explanation.
We then print the information from the explanation object, including rule label, condition, conclusion,
and variable mapping between the query and the rule.
-
Rust
-
Python
-
Node.js
-
Java
-
C#
-
C++
-
C
let databases = DatabaseManager::new(driver);
let db = databases.get(DB_NAME)?;
let options = Options::new().infer(true).explain(true);
{
let session = Session::new(db, SessionType::Data)?;
{
let tx = session.transaction_with_options(TransactionType::Read, options)?;
let get_query = "
match
$u isa user, has email $e, has name $n;
$e contains 'Alice';
get
$u, $n;
";
let response = tx.query().get(get_query)?;
for (i, cmap) in response.enumerate() {
let ncmap = cmap.clone();
let name_concept = ncmap?.get("n").unwrap().clone();
let name = match name_concept {
Concept::Attribute(Attribute { value: Value::String(value), .. }) => value,
_ => unreachable!(),
};
println!("Name #{}: {}", (i + 1).to_string(), name);
let explainable_relations = cmap?.explainables.relations;
for (var, explainable) in explainable_relations {
println!("{}", var);
println!("{}", explainable.conjunction);
let explain_iterator = tx.query().explain(&explainable)?;
for explanation in explain_iterator {
let exp = explanation?;
println!("Rule: {}", exp.rule.label);
println!("Condition: {}", exp.rule.when.to_string());
println!("Conclusion: {}", exp.rule.then.to_string());
println!("Variable mapping:");
for qvar in exp.variable_mapping.keys() {
println!(
"Query variable {} maps to the rule variable {}",
*qvar,
exp.variable_mapping.get(qvar).unwrap().concat().to_string()
);
}
}
}
}
}
}
For more information, see the Explanation class methods.
with driver.session(DB_NAME, SessionType.DATA) as session:
with session.transaction(TransactionType.READ, TypeDBOptions(infer=True, explain=True)) as tx:
get_query = """
match
$u isa user, has email $e, has name $n;
$e contains 'alice';
get
$u, $n;
"""
response = tx.query.get(get_query)
for i, ConceptMap in enumerate(response, start=1):
name = ConceptMap.get("n").as_attribute().get_value()
print(f"Name #{i}: {name}")
explainable_relations = ConceptMap.explainables().relations()
for var, explainable in explainable_relations:
print("Explained variable:", explainable)
print("Explainable object:", explainable_relations[explainable])
print("Explainable part of query:", explainable_relations[explainable].conjunction())
explain_iterator = tx.query.explain(explainable)
for explanation in explain_iterator:
print("\nRule: ", explanation.rule().label)
print("Condition: ", explanation.condition())
print("Conclusion: ", explanation.conclusion())
print("Variable mapping: ")
for qvar in explanation.query_variables():
print(
f" Query variable {qvar} maps to the rule variable {explanation.query_variable_mapping(var)}")
print("----------------------------------------------------------")
For more information, see the Explanation class methods.
try {
session = await driver.session(DB_NAME, SessionType.DATA);
try {
let options = new TypeDBOptions();
options.infer = true;
options.explain = true;
tx = await session.transaction(TransactionType.READ, options);
const get_query = `
match
$u isa user, has email $e, has name $n;
$e contains 'Alice';
get
$u, $n;
`;
let response = await tx.query.get(get_query).collect();
for(let i = 0; i < response.length; i++) {
console.log("Name #" + (i + 1) + ": " + response[i].get("n").value);
let explainable_relations = await response[i].explainables.relations;
for await (const explainable of explainable_relations) {
console.log("Explainable part of the query: " + explainable.conjunction())
explain_iterator = tx.query.explain(explainable);
for (explanation of explain_iterator) {
console.log("Rule: " + explanation.rule.label)
console.log("Condition: " + explanation.condition.toString())
console.log("Conclusion " + explanation.conclusion.toString())
for (qvar of explanation.variableMapping.keys()) {
console.log("Query variable " + qvar + " maps to the rule variable " + explanation.variableMapping.get(qvar))
}
}
}
}
}
finally {if (tx.isOpen()) {await tx.close()};}
}
finally {await session?.close();}
For more information, see the Explanation class methods.
try (TypeDBSession session = driver.session(DB_NAME, TypeDBSession.Type.DATA)) {
TypeDBOptions options = new TypeDBOptions().infer(true).explain(true);
try (TypeDBTransaction tx = session.transaction(TypeDBTransaction.Type.READ, options)) {
String getQuery = """
match
$u isa user, has email $e, has name $n;
$e contains 'Alice';
get
$u, $n;
""";
int[] ctr = new int[1];
tx.query().get(getQuery).forEach(result -> {
String name = result.get("n").asAttribute().getValue().toString();
System.out.println("Email #" + (++ctr[0]) + ": " + name);
Stream<Pair<String, ConceptMap.Explainable>> explainable_relations = result.explainables().relations();
explainable_relations.forEach(explainable -> {
System.out.println("Explainable variable:" + explainable.first());
System.out.println("Explainable part of the query:" + explainable.second().conjunction());
Stream<Explanation> explain_iterator = tx.query().explain(explainable.second());
explain_iterator.forEach(explanation -> {
System.out.println("Rule: " + explanation.rule().getLabel());
System.out.println(" Condition: " + explanation.rule().getWhen().toString());
System.out.println(" Conclusion: " + explanation.rule().getThen().toString());
explanation.queryVariables().forEach(var ->
System.out.println("Query variable " + var + "maps to the rule variable " + explanation.queryVariableMapping(var)));
});
});
});
}
}
For more information, see the Explanation class methods.
using (ITypeDBSession session = driver.Session(DB_NAME, SessionType.Data)) {
using (ITypeDBTransaction tx = session.Transaction(TransactionType.Read, options.Infer(true).Explain(true))) {
string getQuery = @"match
$u isa user, has email $e, has name $n;
$e contains 'Alice';
get
$u, $n;";
IList<IConceptMap> response = tx.Query.Get(getQuery).ToList();
Int16 i = 0;
foreach (IConceptMap ConceptMap in response) {
i++;
Console.WriteLine("Name #" + i.ToString() + ": " + ConceptMap.Get("n").AsAttribute().Value.AsString());
IEnumerable<KeyValuePair<string, IConceptMap.IExplainable>> explainableRelations = ConceptMap.AllExplainables.GetRelations();
foreach (KeyValuePair<string, IConceptMap.IExplainable> explainable in explainableRelations) {
Console.WriteLine("Explained variable: " + explainable.Key);
Console.WriteLine("Explainable part of the query: " + ConceptMap.AllExplainables.Relation(explainable.Key).Conjunction);
IEnumerable<IExplanation> explainIterator = tx.Query.Explain(ConceptMap.AllExplainables.Relation(explainable.Key));
foreach (IExplanation explanation in explainIterator) {
Console.WriteLine("Rule: " + explanation.Rule.Label);
Console.WriteLine("Condition: " + explanation.Rule.When);
Console.WriteLine("Conclusion: " + explanation.Rule.Then);
Console.WriteLine("Variable mapping: ");
foreach (string var in explanation.GetQueryVariables()) {
Console.WriteLine("Query variable: " + var + " maps to the rule variable " + explanation.QueryVariableMapping(var).ToString());
}
}
}
}
}
}
For more information, see the Explanation class methods.
TypeDB::Options inferOptions;
inferOptions.infer(true);
inferOptions.explain(true);
TypeDB::Session session = driver.session(DB_NAME, TypeDB::SessionType::DATA, inferOptions);
{
TypeDB::Transaction tx = session.transaction(TypeDB::TransactionType::READ, inferOptions);
std::string getQuery = R"(
match
$u isa user, has email $e, has name $n;
$e contains 'Alice';
get
$u, $n;
)";
TypeDB::ConceptMapIterable results = tx.query.get(getQuery);
int16_t i = 0;
for (TypeDB::ConceptMap& cm : results) {
i += 1;
std::cout << "Name #" << std::to_string(i) << ": " << cm.get("n")->asAttribute()->getValue()->asString() << std::endl;
TypeDB::StringIterable explainableRelations = cm.explainables().relations();
for (std::string& explainable : explainableRelations) {
std::cout << "Explained variable " << explainable << std::endl;
std::cout << "Explainable part of the query " << cm.explainables().relation(explainable).conjunction() << std::endl;
TypeDB::ExplanationIterable explainIterator = tx.query.explain(cm.explainables().relation(explainable));
for (TypeDB::Explanation& explanation : explainIterator) {
std::cout << "Rule: " << explanation.rule().label() << std::endl;
std::cout << "Condition: " << explanation.rule().when() << std::endl;
std::cout << "Conclusion: " << explanation.rule().then() << std::endl;
std::cout << "Variable mapping: " << std::endl;
for (std::string& var : explanation.queryVariables()) {
std::cout << "Query variable " << var << " maps to the rule variable " << explanation.queryVariableMapping(var)[1] << std::endl;
}
}
}
}
}
For more information, see the Explanation class methods.
session = session_new(databaseManager, DB_NAME, Data, opts);
Options* optsInferExplain = options_new();
options_set_infer(optsInferExplain, true);
options_set_explain(optsInferExplain, true);
tx = transaction_new(session, Read, optsInferExplain);
if ((tx == NULL) || FAILED()) {
handle_error("Transaction failed to start.");
goto cleanup;
}
char query[512];
snprintf(query, sizeof(query), "match $u isa user, has email $e, has name $n; $e contains 'Alice'; get $u, $n;");
ConceptMapIterator* response = query_get(tx, query, opts);
if (FAILED()) {
handle_error("Query execution failed.");
goto cleanup;
}
ConceptMap* CM;
int16_t i = 0;
while ((CM = concept_map_iterator_next(response)) != NULL) {
i++;
Concept* nameConcept = concept_map_get(CM, "n");
printf("%s %d: %s\n", "Name#", i, value_get_string(attribute_get_value(nameConcept)));
Explainables* explainables = concept_map_get_explainables(CM);
StringIterator* explainableRelations = explainables_get_relations_keys(explainables);
char* explainableKey;
while ((explainableKey = string_iterator_next(explainableRelations)) != NULL) {
printf("Explained variable: %s\n Explainable part of the query: %s\n", explainableKey, explainable_get_conjunction( explainables_get_relation(explainables, explainableKey)));
ExplanationIterator* explainIterator = query_explain(tx, explainables_get_relation(explainables, explainableKey), opts);
Explanation* explanation;
while ((explanation = explanation_iterator_next(explainIterator)) != NULL) {
Rule* rule = explanation_get_rule(explanation);
printf("Rule: %s\nCondition: %s\nConclusion: %s\nVariable mapping:\n", rule_get_label(rule), rule_get_when(rule), rule_get_then(rule));
char* var;
while ((var = string_iterator_next(explanation_get_mapped_variables(explanation))) != NULL) {
printf("Query variable: %s maps to the rule variable(s)", var);
char* ruleVar;
while ((ruleVar = string_iterator_next(explanation_get_mapping(explanation, var))) != NULL) {
printf(" %s", ruleVar);
}
}
}
}
}
transaction_close(tx);
session_close(session);
options_drop(optsInferExplain);