Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tracing for head arithmetic #374

Merged
merged 4 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion nemo/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub use nemo_physical::error::ReadingError;
pub enum Error {
/// Currently tracing doesn't work for all language features
#[error(
"Tracing is currently not supported for rules with arithmetic operations in the head."
"Tracing is currently not supported for some rules with arithmetic operations in the head."
)]
TraceUnsupportedFeature(),
/// Error which implies a needed Rollback
Expand Down
121 changes: 95 additions & 26 deletions nemo/src/execution/execution_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,10 +443,14 @@ impl<Strategy: RuleSelectionStrategy> ExecutionEngine<Strategy> {
}

// Unify the head atom with the given fact
// If unification is possible `compatible` remain true
// and `assignment` will contain the match which is responsible for the fact

// If unification is possible `compatible` remains true
let mut compatible = true;
let mut assignment = HashMap::<Variable, Constant>::new();
// Contains the head variable and the constant it aligns with.
let mut assignment_constant = HashMap::<Variable, Constant>::new();
// For each constructor variable, contains the term which describes its calculation
// and the constant it should equal to based on the input fact
let mut assignment_constructor = HashMap::<Variable, (Constant, Term)>::new();

for (ty, (head_term, fact_term)) in predicate_types
.iter()
Expand All @@ -468,22 +472,77 @@ impl<Strategy: RuleSelectionStrategy> ExecutionEngine<Strategy> {
continue;
}

if rule.get_constructor(variable).is_some() {
// TODO: Support arbitrary operations in the head
return Err(Error::TraceUnsupportedFeature());
}
if let Some(constructor) = rule.get_constructor(variable) {
match assignment_constructor.entry(variable.clone()) {
Entry::Occupied(entry) => {
let (stored_constant, _) = entry.get();

match assignment.entry(variable.clone()) {
Entry::Occupied(entry) => {
if ty.ground_term_to_data_value_t(entry.get().clone())
!= ty.ground_term_to_data_value_t(fact_term.clone())
{
compatible = false;
break;
if stored_constant != fact_term {
compatible = false;
break;
}
}
Entry::Vacant(entry) => {
if constructor.term().variables().next().is_none() {
if let Term::Primitive(PrimitiveTerm::Constant(constant)) =
constructor.term()
{
if ty.ground_term_to_data_value_t(constant.clone())
!= ty.ground_term_to_data_value_t(fact_term.clone())
{
compatible = false;
break;
}
} else {
if let Ok(fact_term_data) =
ty.ground_term_to_data_value_t(fact_term.clone())
{
if let Some(fact_term_storage) = fact_term_data
.to_storage_value(
&self.table_manager.get_dict(),
)
{
if let Some(constructor_value_storage) =
constructor
.term()
.evaluate_constant_numeric(
ty,
&self.table_manager.get_dict(),
)
{
if fact_term_storage
== constructor_value_storage
{
continue;
}
}
}
}

compatible = false;
break;
}
} else {
entry.insert((
fact_term.clone(),
constructor.term().clone(),
));
}
}
}
Entry::Vacant(entry) => {
entry.insert(fact_term.clone());
} else {
match assignment_constant.entry(variable.clone()) {
Entry::Occupied(entry) => {
if ty.ground_term_to_data_value_t(entry.get().clone())
!= ty.ground_term_to_data_value_t(fact_term.clone())
{
compatible = false;
break;
}
}
Entry::Vacant(entry) => {
entry.insert(fact_term.clone());
}
}
}
}
Expand All @@ -498,18 +557,28 @@ impl<Strategy: RuleSelectionStrategy> ExecutionEngine<Strategy> {
// The goal of this part of the code is to apply the rule which led to the given fact
// but with the variable binding derived from the unification above

let new_constraints: Vec<Constraint> = assignment
.iter()
.map(|(variable, term)| {
Constraint::Equals(
Term::Primitive(PrimitiveTerm::Variable(variable.clone())),
Term::Primitive(PrimitiveTerm::Constant(term.clone())),
)
})
.collect();
let unification_constraints: Vec<Constraint> =
assignment_constant
.into_iter()
.map(|(variable, term)| {
Constraint::Equals(
Term::Primitive(PrimitiveTerm::Variable(variable)),
Term::Primitive(PrimitiveTerm::Constant(term)),
)
})
.chain(assignment_constructor.into_iter().map(
|(_variable, (constant, term))| {
Constraint::Equals(
Term::Primitive(PrimitiveTerm::Constant(constant)),
term,
)
},
))
.collect();

let mut rule = self.program.rules()[rule_index].clone();
rule.positive_constraints_mut().extend(new_constraints);
rule.positive_constraints_mut()
.extend(unification_constraints);
let analysis = &self.analysis.rule_analysis[rule_index];

let body_execution = SeminaiveStrategy::initialize(&rule, analysis);
Expand Down
2 changes: 1 addition & 1 deletion nemo/src/execution/planning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ pub mod plan_util;

pub mod negation;

mod arithmetic;
pub mod arithmetic;
4 changes: 3 additions & 1 deletion nemo/src/execution/planning/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ use crate::{
program_analysis::variable_order::VariableOrder,
};

pub(super) fn termtree_to_arithmetictree(
/// Builds an [`ArithmeticTree`] with [`DataValueT`]
/// from a given [`Term`].
pub fn termtree_to_arithmetictree(
term: &Term,
order: &VariableOrder,
logical_type: &PrimitiveType,
Expand Down
16 changes: 14 additions & 2 deletions nemo/src/model/rule_model/numeric_literal.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use nemo_physical::datatypes::Double;

/// A numerical literal.
#[derive(Debug, Eq, PartialEq, Copy, Clone, PartialOrd, Ord)]
#[derive(Eq, PartialEq, Copy, Clone, PartialOrd, Ord)]
pub enum NumericLiteral {
/// An integer literal.
Integer(i64),
Expand All @@ -11,7 +11,7 @@ pub enum NumericLiteral {
Double(Double),
}

impl std::fmt::Display for NumericLiteral {
impl std::fmt::Debug for NumericLiteral {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
NumericLiteral::Integer(value) => write!(f, "{value}"),
Expand All @@ -20,3 +20,15 @@ impl std::fmt::Display for NumericLiteral {
}
}
}

impl std::fmt::Display for NumericLiteral {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
NumericLiteral::Integer(value) => write!(f, "{value}"),
NumericLiteral::Decimal(left, right) => write!(f, "{left}.{right}"),
NumericLiteral::Double(value) => {
f.write_str(format!("{:.4}", f64::from(*value)).trim_end_matches(['.', '0']))
}
}
}
}
58 changes: 57 additions & 1 deletion nemo/src/model/rule_model/term.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
use std::fmt::{Debug, Display};

use crate::model::{types::primitive_logical_value::LOGICAL_NULL_PREFIX, VariableAssignment};
use nemo_physical::{
columnar::operations::arithmetic::expression::ArithmeticTreeLeaf,
datatypes::{DataValueT, StorageTypeName, StorageValueT},
management::database::Dict,
};

use crate::{
execution::planning::arithmetic::termtree_to_arithmetictree,
model::{
types::primitive_logical_value::LOGICAL_NULL_PREFIX, PrimitiveType, VariableAssignment,
},
program_analysis::variable_order::VariableOrder,
};

use super::{Aggregate, Identifier, NumericLiteral, RdfLiteral};

Expand Down Expand Up @@ -367,6 +379,50 @@ impl Term {
Term::Function(sub) => sub.subterms.iter().any(Self::aggregate_subterm_recursive),
}
}

/// Evaluates a constant (numeric) term.
pub fn evaluate_constant_numeric(
&self,
ty: &PrimitiveType,
dict: &Dict,
) -> Option<StorageValueT> {
let arithmetic_tree = termtree_to_arithmetictree(self, &VariableOrder::new(), ty);
let storage_type = ty.datatype_name().to_storage_type_name();

macro_rules! translate_data_type {
($variant:ident, $type:ty) => {{
let translate_function = |l: ArithmeticTreeLeaf<DataValueT>| match l {
ArithmeticTreeLeaf::Constant(t) => {
if let StorageValueT::$variant(value) = t
.to_storage_value(dict)
.expect("We don't have string operations so this cannot fail.")
{
ArithmeticTreeLeaf::Constant(value)
} else {
panic!(
"Expected a operation tree value of type {}",
stringify!($src_name)
);
}
}
ArithmeticTreeLeaf::Reference(index) => ArithmeticTreeLeaf::Reference(index),
};

let arithmetic_tree_typed = arithmetic_tree.map(&translate_function);
Some(StorageValueT::$variant(
arithmetic_tree_typed.evaluate(&[])?,
))
}};
}

match storage_type {
StorageTypeName::U32 => translate_data_type!(U32, u32),
StorageTypeName::U64 => translate_data_type!(U64, u64),
StorageTypeName::I64 => translate_data_type!(I64, i64),
StorageTypeName::Float => translate_data_type!(Float, f32),
StorageTypeName::Double => translate_data_type!(Double, f64),
}
}
}

impl From<PrimitiveTerm> for Term {
Expand Down