From 913f1eec19da9a8b8c80217013c6488bcf9d79cd Mon Sep 17 00:00:00 2001 From: Alex Ivliev Date: Tue, 3 Dec 2024 14:59:27 +0100 Subject: [PATCH 1/2] Add reduce function to terms --- nemo-physical/src/function.rs | 2 +- nemo-physical/src/function/evaluation.rs | 8 +-- nemo/src/execution/planning/operations.rs | 2 +- .../planning/operations/operation.rs | 4 +- nemo/src/rule_model/components/parse.rs | 58 +++++++++------- nemo/src/rule_model/components/tag.rs | 5 ++ nemo/src/rule_model/components/term.rs | 62 ++++++++++++++++- .../rule_model/components/term/aggregate.rs | 39 +++++++++++ .../rule_model/components/term/function.rs | 50 +++++++++++++- nemo/src/rule_model/components/term/map.rs | 54 +++++++++++++++ .../rule_model/components/term/operation.rs | 68 ++++++++++++++++++- .../rule_model/components/term/primitive.rs | 18 ++++- .../components/term/primitive/ground.rs | 16 +++++ .../components/term/primitive/variable.rs | 18 +++++ nemo/src/rule_model/components/term/tuple.rs | 29 +++++++- .../translation/complex/function.rs | 2 +- 16 files changed, 391 insertions(+), 44 deletions(-) diff --git a/nemo-physical/src/function.rs b/nemo-physical/src/function.rs index 3a0a6c59a..76f75ae92 100644 --- a/nemo-physical/src/function.rs +++ b/nemo-physical/src/function.rs @@ -1,7 +1,7 @@ //! This module contains data structures and implementations //! for realizing the evaluation of functions on columnar data. +pub mod evaluation; pub mod tree; pub(crate) mod definitions; -pub(crate) mod evaluation; diff --git a/nemo-physical/src/function/evaluation.rs b/nemo-physical/src/function/evaluation.rs index 96d7b0af8..84ad35313 100644 --- a/nemo-physical/src/function/evaluation.rs +++ b/nemo-physical/src/function/evaluation.rs @@ -52,7 +52,7 @@ pub(crate) enum StackOperation { /// Representation of a [FunctionTree] as a stack program #[derive(Debug, Clone)] -pub(crate) struct StackProgram { +pub struct StackProgram { /// Maximmum size of the stack size: usize, /// List of instructions @@ -115,7 +115,7 @@ impl StackProgram { } /// Construct a [StackProgram] from [FunctionTree]. - pub(crate) fn from_function_tree( + pub fn from_function_tree( tree: &FunctionTree, reference_map: &HashMap, this: Option, @@ -263,7 +263,7 @@ impl StackProgram { /// /// # Panics /// Panics if the [StackProgram] is not valid. - pub(crate) fn evaluate_data(&self, referenced_values: &[AnyDataValue]) -> Option { + pub fn evaluate_data(&self, referenced_values: &[AnyDataValue]) -> Option { self.evaluate(referenced_values, None) } @@ -274,7 +274,7 @@ impl StackProgram { /// /// # Panics /// Panics if the [StackProgram] is not valid. - pub(crate) fn evaluate_bool( + pub fn evaluate_bool( &self, referenced_values: &[AnyDataValue], this: Option, diff --git a/nemo/src/execution/planning/operations.rs b/nemo/src/execution/planning/operations.rs index 6f513a356..ec4e70472 100644 --- a/nemo/src/execution/planning/operations.rs +++ b/nemo/src/execution/planning/operations.rs @@ -7,5 +7,5 @@ pub(crate) mod filter; pub(crate) mod functions; pub(crate) mod join; pub(crate) mod negation; -pub(super) mod operation; +pub(crate) mod operation; pub(crate) mod union; diff --git a/nemo/src/execution/planning/operations/operation.rs b/nemo/src/execution/planning/operations/operation.rs index 24c91c10a..4a1c6cae0 100644 --- a/nemo/src/execution/planning/operations/operation.rs +++ b/nemo/src/execution/planning/operations/operation.rs @@ -11,7 +11,7 @@ use crate::{ }; /// Helper function to translate a [OperationTerm] into a [FunctionTree]. -pub(super) fn operation_term_to_function_tree( +pub(crate) fn operation_term_to_function_tree( translation: &VariableTranslation, operation_term: &OperationTerm, ) -> FunctionTree { @@ -56,7 +56,7 @@ macro_rules! unary { } /// Helper function to translate a [Operation] into a [FunctionTree]. -pub(super) fn operation_to_function_tree( +pub(crate) fn operation_to_function_tree( translation: &VariableTranslation, operation: &Operation, ) -> FunctionTree { diff --git a/nemo/src/rule_model/components/parse.rs b/nemo/src/rule_model/components/parse.rs index 5995f0331..414d3bdd3 100644 --- a/nemo/src/rule_model/components/parse.rs +++ b/nemo/src/rule_model/components/parse.rs @@ -23,35 +23,41 @@ impl Display for ComponentParseError { #[macro_export] macro_rules! parse_component { - ($string:expr, $parser:expr, $builder:expr) => {{ - use nom::InputLength; - - let input = $crate::parser::input::ParserInput::new( - $string, - $crate::parser::ParserState::default(), - ); - let ast = match $parser(input) { - Ok((input, ast)) => { - if input.input_len() == 0 { - ast - } else { - return Err( + ($string:expr, $parser:expr, $builder:expr) => { + 'parse: { + use nom::InputLength; + + let input = $crate::parser::input::ParserInput::new( + $string, + $crate::parser::ParserState::default(), + ); + let ast = match $parser(input) { + Ok((input, ast)) => { + if input.input_len() == 0 { + ast + } else { + break 'parse Err( + $crate::rule_model::components::parse::ComponentParseError::ParseError, + ); + } + } + Err(_) => { + break 'parse Err( $crate::rule_model::components::parse::ComponentParseError::ParseError, - ); + ) } - } - Err(_) => { - return Err($crate::rule_model::components::parse::ComponentParseError::ParseError) - } - }; + }; - let mut translation = ASTProgramTranslation::initialize($string, String::default()); + let mut translation = ASTProgramTranslation::initialize($string, String::default()); - match $builder(&mut translation, &ast) { - Ok(component) => Ok(component), - Err(error) => Err( - $crate::rule_model::components::parse::ComponentParseError::TranslationError(error), - ), + match $builder(&mut translation, &ast) { + Ok(component) => Ok(component), + Err(error) => Err( + $crate::rule_model::components::parse::ComponentParseError::TranslationError( + error, + ), + ), + } } - }}; + }; } diff --git a/nemo/src/rule_model/components/tag.rs b/nemo/src/rule_model/components/tag.rs index 0f21d8294..433d81215 100644 --- a/nemo/src/rule_model/components/tag.rs +++ b/nemo/src/rule_model/components/tag.rs @@ -40,6 +40,11 @@ impl Tag { self.origin = origin; self } + + /// Return the name of [Tag]. + pub fn name(&self) -> &str { + &self.tag + } } impl Display for Tag { diff --git a/nemo/src/rule_model/components/term.rs b/nemo/src/rule_model/components/term.rs index 832e26731..5ff65dd1d 100644 --- a/nemo/src/rule_model/components/term.rs +++ b/nemo/src/rule_model/components/term.rs @@ -31,7 +31,13 @@ use tuple::Tuple; use value_type::ValueType; use crate::{ + chase_model::translation::ProgramChaseTranslation, + execution::{ + planning::operations::operation::operation_term_to_function_tree, + rule_execution::VariableTranslation, + }, parse_component, + parser::ast::ProgramAST, rule_model::{ error::ValidationErrorBuilder, origin::Origin, translation::ASTProgramTranslation, }, @@ -132,6 +138,31 @@ impl Term { Term::Tuple(term) => Box::new(term.arguments()), } } + + /// Return whether this term is ground, + /// i.e. whether it does not contain any variables. + pub fn is_ground(&self) -> bool { + match self { + Term::Primitive(term) => term.is_ground(), + Term::Aggregate(term) => term.is_ground(), + Term::FunctionTerm(term) => term.is_gound(), + Term::Map(term) => term.is_gound(), + Term::Operation(term) => term.is_gound(), + Term::Tuple(term) => term.is_gound(), + } + } + + /// Reduce (potential) constant expressions contained and return a copy of the resulting [Term]. + pub fn reduce(&self) -> Term { + match self { + Term::Operation(operation) => operation.reduce(), + Term::Primitive(_) => self.clone(), + Term::Aggregate(term) => Term::Aggregate(term.reduce()), + Term::FunctionTerm(term) => Term::FunctionTerm(term.reduce()), + Term::Map(term) => Term::Map(term.reduce()), + Term::Tuple(term) => Term::Tuple(term.reduce()), + } + } } impl From for Term { @@ -250,7 +281,7 @@ impl ProgramComponent for Term { { parse_component!( string, - crate::parser::ast::expression::Expression::parse_complex, + crate::parser::ast::expression::Expression::parse, ASTProgramTranslation::build_inner_term ) } @@ -353,3 +384,32 @@ impl IterablePrimitives for Term { } } } + +#[cfg(test)] +mod test { + use crate::rule_model::components::ProgramComponent; + + use super::Term; + + #[test] + fn term_reduce_ground() { + let constant = Term::parse("2 * (3 + 7)").unwrap(); + let term = Term::from(tuple!(5, constant)); + + let reduced = term.reduce(); + let expected_term = Term::from(tuple!(5, 20)); + + assert_eq!(reduced, expected_term); + } + + #[test] + fn term_reduce_nonground() { + let expression = Term::parse("?x * (3 + 7)").unwrap(); + let term = Term::from(tuple!(5, expression)); + + let reduced = term.reduce(); + let expected_term = Term::parse("(5, ?x * 10)").unwrap(); + + assert_eq!(reduced, expected_term); + } +} diff --git a/nemo/src/rule_model/components/term/aggregate.rs b/nemo/src/rule_model/components/term/aggregate.rs index f225e47af..39dd55c0d 100644 --- a/nemo/src/rule_model/components/term/aggregate.rs +++ b/nemo/src/rule_model/components/term/aggregate.rs @@ -160,6 +160,21 @@ impl Aggregate { pub fn distinct(&self) -> impl Iterator { self.distinct.iter() } + + /// Return whether the aggregate expression is ground. + pub fn is_ground(&self) -> bool { + self.aggregate.is_ground() + } + + /// Reduce the [Term] in the aggregate expression returning a copy. + pub fn reduce(&self) -> Self { + Self { + origin: self.origin, + kind: self.kind, + aggregate: Box::new(self.aggregate.reduce()), + distinct: self.distinct.clone(), + } + } } impl Display for Aggregate { @@ -319,3 +334,27 @@ impl IterablePrimitives for Aggregate { self.aggregate.primitive_terms_mut() } } + +#[cfg(test)] +mod test { + use crate::rule_model::components::{ + term::{aggregate::AggregateKind, primitive::variable::Variable, Term}, + ProgramComponent, + }; + + use super::Aggregate; + + #[test] + fn parse_aggregate() { + let aggregate = Aggregate::parse("#sum(?x, ?y)").unwrap(); + + assert_eq!( + Aggregate::new( + AggregateKind::SumOfNumbers, + Term::from(Variable::universal("x")), + vec![Variable::universal("y")] + ), + aggregate + ); + } +} diff --git a/nemo/src/rule_model/components/term/function.rs b/nemo/src/rule_model/components/term/function.rs index 768f64893..9c11296b1 100644 --- a/nemo/src/rule_model/components/term/function.rs +++ b/nemo/src/rule_model/components/term/function.rs @@ -51,7 +51,7 @@ macro_rules! function { let mut terms = Vec::new(); term_list!(terms; $($tt)*); $crate::rule_model::components::term::function::FunctionTerm::new( - $crate::rule_model::components::tag::Tag::from($name), terms + $name, terms ) } }}; @@ -59,7 +59,16 @@ macro_rules! function { impl FunctionTerm { /// Create a new [FunctionTerm]. - pub fn new>(tag: Tag, subterms: Terms) -> Self { + pub fn new>(name: &str, subterms: Terms) -> Self { + Self { + origin: Origin::Created, + tag: Tag::new(name.to_string()), + terms: subterms.into_iter().collect(), + } + } + + /// Create a new [FunctionTerm] with a [Tag]. + pub(crate) fn new_tag>(tag: Tag, subterms: Terms) -> Self { Self { origin: Origin::Created, tag, @@ -91,6 +100,21 @@ impl FunctionTerm { pub fn tag(&self) -> &Tag { &self.tag } + + /// Return whether this term is ground, + /// i.e. if it does not contain any variables. + pub fn is_gound(&self) -> bool { + self.terms.iter().all(Term::is_ground) + } + + /// Reduce each sub [Term] in the function returning a copy. + pub fn reduce(&self) -> Self { + Self { + origin: self.origin, + tag: self.tag.clone(), + terms: self.terms.iter().map(Term::reduce).collect(), + } + } } impl Display for FunctionTerm { @@ -209,7 +233,10 @@ impl IterablePrimitives for FunctionTerm { #[cfg(test)] mod test { - use crate::rule_model::components::{term::primitive::variable::Variable, IterableVariables}; + use crate::rule_model::components::{ + term::{function::FunctionTerm, primitive::variable::Variable, Term}, + IterableVariables, ProgramComponent, + }; #[test] fn function_basic() { @@ -226,4 +253,21 @@ mod test { ] ); } + + #[test] + fn parse_function() { + let function = FunctionTerm::parse("f(?x, 2, ?y)").unwrap(); + + assert_eq!( + FunctionTerm::new( + "f", + vec![ + Term::from(Variable::universal("x")), + Term::from(2), + Term::from(Variable::universal("y")) + ] + ), + function + ); + } } diff --git a/nemo/src/rule_model/components/term/map.rs b/nemo/src/rule_model/components/term/map.rs index a63b81969..07ef4cf0b 100644 --- a/nemo/src/rule_model/components/term/map.rs +++ b/nemo/src/rule_model/components/term/map.rs @@ -57,6 +57,18 @@ impl Map { } } + /// Create a new [Map] with a given (optional) [Tag]. + pub fn new_tagged>( + tag: Option, + map: Pairs, + ) -> Self { + Self { + origin: Origin::Created, + tag, + map: map.into_iter().collect(), + } + } + /// Create a new empty [Map]. pub fn empty(name: &str) -> Self { Self { @@ -99,6 +111,25 @@ impl Map { pub fn is_empty(&self) -> bool { self.len() == 0 } + + /// Return whether this term is ground, + /// i.e. if it does not contain any variables. + pub fn is_gound(&self) -> bool { + self.key_value() + .all(|(key, value)| key.is_ground() && value.is_ground()) + } + + /// Reduce the [Term]s in each key-value pair returning a copy. + pub fn reduce(&self) -> Self { + Self { + origin: self.origin, + tag: self.tag.clone(), + map: self + .key_value() + .map(|(key, value)| (key.reduce(), value.reduce())) + .collect(), + } + } } impl Display for Map { @@ -217,3 +248,26 @@ impl IterablePrimitives for Map { ) } } + +#[cfg(test)] +mod test { + use crate::rule_model::components::{ + term::{primitive::variable::Variable, Term}, + ProgramComponent, + }; + + use super::Map; + + #[test] + fn parse_map() { + let map = Map::parse("m { ?x = 5 }").unwrap(); + + assert_eq!( + Map::new( + "m", + vec![(Term::from(Variable::universal("x")), Term::from(5)),] + ), + map + ); + } +} diff --git a/nemo/src/rule_model/components/term/operation.rs b/nemo/src/rule_model/components/term/operation.rs index 998805ea8..576c195d3 100644 --- a/nemo/src/rule_model/components/term/operation.rs +++ b/nemo/src/rule_model/components/term/operation.rs @@ -2,7 +2,7 @@ pub mod operation_kind; -use std::{fmt::Display, hash::Hash}; +use std::{collections::HashMap, fmt::Display, hash::Hash}; use operation_kind::OperationKind; @@ -21,9 +21,10 @@ use crate::{ }; use super::{ - primitive::{variable::Variable, Primitive}, + operation_term_to_function_tree, + primitive::{ground::GroundTerm, variable::Variable, Primitive}, value_type::ValueType, - Term, + ProgramChaseTranslation, Term, VariableTranslation, }; /// Operation @@ -87,6 +88,37 @@ impl Operation { None } + + /// Return whether this term is ground, + /// i.e. if it does not contain any variables. + pub fn is_gound(&self) -> bool { + self.subterms.iter().all(Term::is_ground) + } + + /// Reduce constant expressions returning a copy of the reduced [Term]. + pub fn reduce(&self) -> Term { + if !self.is_gound() { + return Term::Operation(Self { + origin: self.origin, + kind: self.kind, + subterms: self.subterms.iter().map(Term::reduce).collect(), + }); + } + + let chase_operation_term = ProgramChaseTranslation::build_operation_term(self); + + let empty_translation = VariableTranslation::new(); + let function_tree = + operation_term_to_function_tree(&empty_translation, &chase_operation_term); + let stack_program = nemo_physical::function::evaluation::StackProgram::from_function_tree( + &function_tree, + &HashMap::default(), + None, + ); + let result = stack_program.evaluate_data(&[]).expect("term is ground"); + + Term::from(GroundTerm::new(result)) + } } // Helper functions related to the display implementation @@ -213,6 +245,13 @@ impl ProgramComponent for Operation { crate::parser::ast::expression::complex::operation::Operation::parse, ASTProgramTranslation::build_operation ) + .or_else(|_| { + parse_component!( + string, + crate::parser::ast::expression::complex::arithmetic::Arithmetic::parse, + ASTProgramTranslation::build_arithmetic + ) + }) } fn origin(&self) -> &Origin { @@ -282,3 +321,26 @@ impl IterablePrimitives for Operation { ) } } + +#[cfg(test)] +mod test { + use crate::rule_model::components::{ + term::{operation::operation_kind::OperationKind, Term}, + ProgramComponent, + }; + + use super::Operation; + + #[test] + fn parse_operation() { + let operaton = Operation::parse("2 * 5").unwrap(); + + assert_eq!( + Operation::new( + OperationKind::NumericProduct, + vec![Term::from(2), Term::from(5)] + ), + operaton + ); + } +} diff --git a/nemo/src/rule_model/components/term/primitive.rs b/nemo/src/rule_model/components/term/primitive.rs index 4620c39bc..6dcd5cb52 100644 --- a/nemo/src/rule_model/components/term/primitive.rs +++ b/nemo/src/rule_model/components/term/primitive.rs @@ -126,7 +126,7 @@ impl ProgramComponent for Primitive { { let term = parse_component!( string, - crate::parser::ast::expression::Expression::parse_complex, + crate::parser::ast::expression::Expression::parse_basic, ASTProgramTranslation::build_inner_term )?; @@ -193,3 +193,19 @@ impl IterableVariables for Primitive { ) } } + +#[cfg(test)] +mod test { + use crate::rule_model::components::{term::primitive::variable::Variable, ProgramComponent}; + + use super::Primitive; + + #[test] + fn parse_primitive() { + let variable = Primitive::parse("?x").unwrap(); + let ground = Primitive::parse("2").unwrap(); + + assert_eq!(Primitive::from(Variable::universal("x")), variable); + assert_eq!(Primitive::from(2), ground); + } +} diff --git a/nemo/src/rule_model/components/term/primitive/ground.rs b/nemo/src/rule_model/components/term/primitive/ground.rs index f67c6e2aa..877eea1ec 100644 --- a/nemo/src/rule_model/components/term/primitive/ground.rs +++ b/nemo/src/rule_model/components/term/primitive/ground.rs @@ -193,3 +193,19 @@ impl ProgramComponent for GroundTerm { } } } + +#[cfg(test)] +mod test { + use crate::rule_model::components::ProgramComponent; + + use super::GroundTerm; + + #[test] + fn parse_ground_term() { + let integer = GroundTerm::parse("2").unwrap(); + let string = GroundTerm::parse("\"abc\"").unwrap(); + + assert_eq!(GroundTerm::from(2), integer); + assert_eq!(GroundTerm::from("abc"), string); + } +} diff --git a/nemo/src/rule_model/components/term/primitive/variable.rs b/nemo/src/rule_model/components/term/primitive/variable.rs index 3dd47c5b0..02dafbb1a 100644 --- a/nemo/src/rule_model/components/term/primitive/variable.rs +++ b/nemo/src/rule_model/components/term/primitive/variable.rs @@ -171,3 +171,21 @@ impl ProgramComponent for Variable { ProgramComponentKind::Variable } } + +#[cfg(test)] +mod test { + use crate::rule_model::components::ProgramComponent; + + use super::Variable; + + #[test] + fn parse_variable() { + let universal = Variable::parse("?x").unwrap(); + let existential = Variable::parse("!v").unwrap(); + let anonymous = Variable::parse("_").unwrap(); + + assert_eq!(Variable::universal("x"), universal); + assert_eq!(Variable::existential("v"), existential); + assert_eq!(Variable::anonymous(), anonymous); + } +} diff --git a/nemo/src/rule_model/components/term/tuple.rs b/nemo/src/rule_model/components/term/tuple.rs index 3ea46b668..4089ec98d 100644 --- a/nemo/src/rule_model/components/term/tuple.rs +++ b/nemo/src/rule_model/components/term/tuple.rs @@ -69,6 +69,20 @@ impl Tuple { pub fn arguments(&self) -> impl Iterator { self.terms.iter() } + + /// Return whether this term is ground, + /// i.e. if it does not contain any variables. + pub fn is_gound(&self) -> bool { + self.terms.iter().all(Term::is_ground) + } + + /// Reduce each sub [Term] in the tuple returning a copy. + pub fn reduce(&self) -> Self { + Self { + origin: self.origin, + terms: self.terms.iter().map(Term::reduce).collect(), + } + } } impl Display for Tuple { @@ -171,7 +185,10 @@ impl IterablePrimitives for Tuple { #[cfg(test)] mod test { - use crate::rule_model::components::{term::primitive::variable::Variable, IterableVariables}; + use crate::rule_model::components::{ + term::{primitive::variable::Variable, tuple::Tuple, Term}, + IterableVariables, ProgramComponent, + }; #[test] fn tuple_basic() { @@ -188,4 +205,14 @@ mod test { ] ); } + + #[test] + fn parse_tuple() { + let tuple = Tuple::parse("(?x, 2)").unwrap(); + + assert_eq!( + Tuple::new(vec![Term::from(Variable::universal("x")), Term::from(2)]), + tuple + ); + } } diff --git a/nemo/src/rule_model/translation/complex/function.rs b/nemo/src/rule_model/translation/complex/function.rs index fbf97674a..3a7cfd0fe 100644 --- a/nemo/src/rule_model/translation/complex/function.rs +++ b/nemo/src/rule_model/translation/complex/function.rs @@ -22,6 +22,6 @@ impl<'a> ASTProgramTranslation<'a> { subterms.push(self.build_inner_term(expression)?); } - Ok(self.register_component(FunctionTerm::new(tag, subterms), function)) + Ok(self.register_component(FunctionTerm::new_tag(tag, subterms), function)) } } From 245a5b991cbdbacda17e5332d34bb6d86034a67f Mon Sep 17 00:00:00 2001 From: Alex Ivliev Date: Tue, 3 Dec 2024 15:45:18 +0100 Subject: [PATCH 2/2] Generalize constructors for substitutions --- nemo-python/src/lib.rs | 4 +- nemo/src/execution/execution_engine.rs | 8 +- nemo/src/execution/tracing/trace.rs | 12 +-- nemo/src/rule_model.rs | 2 +- nemo/src/rule_model/components/term.rs | 15 +++- nemo/src/rule_model/substitution.rs | 106 +++++++++++++++++++++++++ nemo/src/rule_model/term_map.rs | 71 ----------------- 7 files changed, 133 insertions(+), 85 deletions(-) create mode 100644 nemo/src/rule_model/substitution.rs delete mode 100644 nemo/src/rule_model/term_map.rs diff --git a/nemo-python/src/lib.rs b/nemo-python/src/lib.rs index af09ebf3a..db27182aa 100644 --- a/nemo-python/src/lib.rs +++ b/nemo-python/src/lib.rs @@ -13,7 +13,7 @@ use nemo::{ rule_model::{ components::{fact::Fact, tag::Tag, term::primitive::Primitive, ProgramComponent}, error::ValidationErrorBuilder, - term_map::PrimitiveTermMap, + substitution::Substitution, }, }; @@ -269,7 +269,7 @@ impl NemoTrace { } } -fn assignement_to_dict(assignment: &PrimitiveTermMap, py: Python) -> PyResult { +fn assignement_to_dict(assignment: &Substitution, py: Python) -> PyResult { let dict = PyDict::new_bound(py); for (variable, term) in assignment { if let Primitive::Ground(ground) = term { diff --git a/nemo/src/execution/execution_engine.rs b/nemo/src/execution/execution_engine.rs index bd3788c33..7a2997571 100644 --- a/nemo/src/execution/execution_engine.rs +++ b/nemo/src/execution/execution_engine.rs @@ -28,7 +28,7 @@ use crate::{ term::primitive::{ground::GroundTerm, variable::Variable, Primitive}, }, program::Program, - term_map::PrimitiveTermMap, + substitution::Substitution, }, table_manager::{MemoryUsage, SubtableExecutionPlan, TableManager}, }; @@ -452,9 +452,9 @@ impl ExecutionEngine { let rule_application = TraceRuleApplication::new( rule_index, - PrimitiveTermMap::new(variable_assignment.into_iter().map( - |(variable, value)| (Primitive::from(variable), Primitive::from(value)), - )), + Substitution::new(variable_assignment.into_iter().map(|(variable, value)| { + (Primitive::from(variable), Primitive::from(value)) + })), head_index, ); diff --git a/nemo/src/execution/tracing/trace.rs b/nemo/src/execution/tracing/trace.rs index 8ea1ec35f..433cbdc8e 100644 --- a/nemo/src/execution/tracing/trace.rs +++ b/nemo/src/execution/tracing/trace.rs @@ -15,7 +15,7 @@ use crate::{ rule_model::{ components::{fact::Fact, rule::Rule}, program::Program, - term_map::PrimitiveTermMap, + substitution::Substitution, }, }; @@ -49,14 +49,14 @@ pub(crate) struct TraceRuleApplication { /// Index of the rule that was applied rule_index: RuleIndex, /// Variable assignment used during the rule application - assignment: PrimitiveTermMap, + assignment: Substitution, /// Index of the head atom which produced the fact under consideration _position: usize, } impl TraceRuleApplication { /// Create new [TraceRuleApplication]. - pub fn new(rule_index: RuleIndex, assignment: PrimitiveTermMap, _position: usize) -> Self { + pub fn new(rule_index: RuleIndex, assignment: Substitution, _position: usize) -> Self { Self { rule_index, assignment, @@ -201,7 +201,7 @@ pub struct TraceTreeRuleApplication { /// Rule that was applied pub rule: Rule, /// Variable assignment used during the rule application - pub assignment: PrimitiveTermMap, + pub assignment: Substitution, /// Index of the head atom which produced the fact under consideration _position: usize, } @@ -562,7 +562,7 @@ mod test { ProgramComponent, }, program::ProgramBuilder, - term_map::PrimitiveTermMap, + substitution::Substitution, }, }; @@ -579,7 +579,7 @@ mod test { ) }); - PrimitiveTermMap::new(terms) + Substitution::new(terms) }}; } diff --git a/nemo/src/rule_model.rs b/nemo/src/rule_model.rs index bd6e37928..95e14b151 100644 --- a/nemo/src/rule_model.rs +++ b/nemo/src/rule_model.rs @@ -7,5 +7,5 @@ pub mod components; pub mod error; pub mod origin; pub mod program; -pub mod term_map; +pub mod substitution; pub mod translation; diff --git a/nemo/src/rule_model/components/term.rs b/nemo/src/rule_model/components/term.rs index 5ff65dd1d..392a327d2 100644 --- a/nemo/src/rule_model/components/term.rs +++ b/nemo/src/rule_model/components/term.rs @@ -39,7 +39,8 @@ use crate::{ parse_component, parser::ast::ProgramAST, rule_model::{ - error::ValidationErrorBuilder, origin::Origin, translation::ASTProgramTranslation, + error::ValidationErrorBuilder, origin::Origin, substitution::Substitution, + translation::ASTProgramTranslation, }, }; @@ -163,6 +164,18 @@ impl Term { Term::Tuple(term) => Term::Tuple(term.reduce()), } } + + /// Reduce (potential) constant expressions + /// contained while replacing terms according to the provided substition + /// and return a copy of the resulting reduced [Term] + pub fn reduce_substitution(&self, substitution: &Substitution) -> Term { + // TODO: A more efficient implementation would propagate the substituion + // into the subterms and reduce grounded subterms + let mut cloned = self.clone(); + substitution.apply(&mut cloned); + + cloned.reduce() + } } impl From for Term { diff --git a/nemo/src/rule_model/substitution.rs b/nemo/src/rule_model/substitution.rs new file mode 100644 index 000000000..53f8e5362 --- /dev/null +++ b/nemo/src/rule_model/substitution.rs @@ -0,0 +1,106 @@ +//! This module defines [Substitution]. + +use std::collections::{ + hash_map::{IntoIter, Iter, IterMut}, + HashMap, +}; + +use super::components::{ + term::primitive::{variable::Variable, Primitive}, + IterablePrimitives, +}; + +/// Map from [Primitive] terms to each other +/// that can be used to uniformly replace terms +#[derive(Debug, Default, Clone)] +pub struct Substitution { + map: HashMap, +} + +impl Substitution { + /// Create a new [Substitution]. + pub fn new(iter: Iterator) -> Self + where + From: Into, + To: Into, + Iterator: IntoIterator, + { + Self { + map: iter + .into_iter() + .map(|(from, to)| (from.into(), to.into())) + .collect(), + } + } + + /// Add a new mapping. + pub fn insert(&mut self, from: From, to: To) + where + From: Into, + To: Into, + { + self.map.insert(from.into(), to.into()); + } + + /// Apply mapping to a program component. + pub fn apply(&self, component: &mut Component) { + for primitive in component.primitive_terms_mut() { + if let Some(term) = self.map.get(primitive) { + *primitive = term.clone(); + } + } + } + + /// Return an iterator over all mapped variables in this substition. + pub fn variables(&self) -> impl Iterator { + self.map.keys().filter_map(|term| { + if let Primitive::Variable(variable) = term { + Some(variable) + } else { + None + } + }) + } +} + +impl From> for Substitution +where + TypeFrom: Into, + TypeTo: Into, +{ + fn from(value: HashMap) -> Self { + Self { + map: value + .into_iter() + .map(|(key, value)| (key.into(), value.into())) + .collect(), + } + } +} + +impl IntoIterator for Substitution { + type Item = (Primitive, Primitive); + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.map.into_iter() + } +} + +impl<'a> IntoIterator for &'a Substitution { + type Item = (&'a Primitive, &'a Primitive); + type IntoIter = Iter<'a, Primitive, Primitive>; + + fn into_iter(self) -> Self::IntoIter { + self.map.iter() + } +} + +impl<'a> IntoIterator for &'a mut Substitution { + type Item = (&'a Primitive, &'a mut Primitive); + type IntoIter = IterMut<'a, Primitive, Primitive>; + + fn into_iter(self) -> Self::IntoIter { + self.map.iter_mut() + } +} diff --git a/nemo/src/rule_model/term_map.rs b/nemo/src/rule_model/term_map.rs deleted file mode 100644 index 8efd9f8be..000000000 --- a/nemo/src/rule_model/term_map.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! This module defines [PrimitiveTermMap]. - -use std::collections::{ - hash_map::{IntoIter, Iter, IterMut}, - HashMap, -}; - -use super::components::{term::primitive::Primitive, IterablePrimitives}; - -/// Map from [Primitive] terms to each other -/// that can be used to uniformly replace terms -#[derive(Debug, Default, Clone)] -pub struct PrimitiveTermMap { - map: HashMap, -} - -impl PrimitiveTermMap { - /// Create a new [PrimitiveTermMap]. - pub fn new>(iter: Iterator) -> Self { - Self { - map: iter.into_iter().collect(), - } - } - - /// Add a new mapping. - pub fn insert(&mut self, from: Primitive, to: Primitive) { - self.map.insert(from, to); - } - - /// Apply mapping to a program component. - pub fn apply(&self, component: &mut Component) { - for primitive in component.primitive_terms_mut() { - if let Some(term) = self.map.get(primitive) { - *primitive = term.clone(); - } - } - } -} - -impl From> for PrimitiveTermMap { - fn from(value: HashMap) -> Self { - Self { map: value } - } -} - -impl IntoIterator for PrimitiveTermMap { - type Item = (Primitive, Primitive); - type IntoIter = IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.map.into_iter() - } -} - -impl<'a> IntoIterator for &'a PrimitiveTermMap { - type Item = (&'a Primitive, &'a Primitive); - type IntoIter = Iter<'a, Primitive, Primitive>; - - fn into_iter(self) -> Self::IntoIter { - self.map.iter() - } -} - -impl<'a> IntoIterator for &'a mut PrimitiveTermMap { - type Item = (&'a Primitive, &'a mut Primitive); - type IntoIter = IterMut<'a, Primitive, Primitive>; - - fn into_iter(self) -> Self::IntoIter { - self.map.iter_mut() - } -}