Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e49d184
more general strategies
matzemathics Jul 9, 2025
4d512fe
add saturator
matzemathics Aug 5, 2025
ccad5ba
saturation_model
matzemathics Aug 5, 2025
08056ae
Merge branch 'main' into feature/saturation
matzemathics Aug 5, 2025
6974593
trigger and join
matzemathics Aug 5, 2025
7ae4e38
saturation with nested iterators
matzemathics Aug 20, 2025
1780c1e
basic tests
matzemathics Aug 20, 2025
eb69699
generalize dependency graph generation
matzemathics Aug 20, 2025
3f5aa19
wrap database expected by saturation
matzemathics Aug 20, 2025
740c4f5
expose iterator over StorageValueT
matzemathics Aug 20, 2025
9645533
simplify matching loop
matzemathics Sep 3, 2025
2ec0ca7
add age flag to saturation
matzemathics Sep 10, 2025
c8a09ff
integration
matzemathics Sep 10, 2025
92781fb
fix missindexing of rules
matzemathics Sep 10, 2025
4cf22ca
disallow atoms with multiple occurences of the same term
matzemathics Sep 10, 2025
14aaee8
actually use interner
matzemathics Sep 10, 2025
38347e2
fix warnings
matzemathics Sep 10, 2025
7869800
clippy
matzemathics Sep 10, 2025
38ca698
add timing, saturate single rules
matzemathics Sep 12, 2025
49b4972
add support for single equality constraints
matzemathics Sep 12, 2025
9aa4376
cleanup
matzemathics Sep 12, 2025
bf4b219
clippy (I)
matzemathics Sep 12, 2025
6b8cdf6
clippy (II)
matzemathics Sep 14, 2025
df9eaa4
cargo fmt
matzemathics Sep 15, 2025
7f2c491
Merge branch 'main' into feature/saturation
matzemathics Sep 22, 2025
aa17a69
fix warnings
matzemathics Sep 22, 2025
94969ea
fix pathological performance
matzemathics Sep 23, 2025
8fa1e37
prepare for long-lived tables
matzemathics Sep 23, 2025
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
18 changes: 6 additions & 12 deletions nemo-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use nemo::{
datavalues::AnyDataValue,
error::Error,
execution::{
DefaultExecutionEngine, ExecutionEngine,
DefaultExecutionStrategy, ExecutionEngine,
execution_parameters::ExecutionParameters,
tracing::{node_query::TableEntriesForTreeNodesQuery, tree_query::TreeForTableQuery},
},
Expand Down Expand Up @@ -150,7 +150,7 @@ fn print_timing_details() {
}

/// Prints detailed memory information.
fn print_memory_details(engine: &DefaultExecutionEngine) {
fn print_memory_details(engine: &ExecutionEngine) {
println!("\nMemory report:\n\n{}", engine.memory_usage());
}

Expand All @@ -169,7 +169,7 @@ fn parse_trace_facts(cli: &CliApp) -> Result<Vec<String>, Error> {
}

/// Deal with tracing
async fn handle_tracing(cli: &CliApp, engine: &mut DefaultExecutionEngine) -> Result<(), CliError> {
async fn handle_tracing(cli: &CliApp, engine: &mut ExecutionEngine) -> Result<(), CliError> {
let tracing_facts = parse_trace_facts(cli)?;
if !tracing_facts.is_empty() {
log::info!("Starting tracing of {} facts...", tracing_facts.len());
Expand Down Expand Up @@ -214,10 +214,7 @@ async fn handle_tracing(cli: &CliApp, engine: &mut DefaultExecutionEngine) -> Re
Ok(())
}

async fn handle_tracing_tree(
cli: &CliApp,
engine: &mut DefaultExecutionEngine,
) -> Result<(), CliError> {
async fn handle_tracing_tree(cli: &CliApp, engine: &mut ExecutionEngine) -> Result<(), CliError> {
if let Some(query_json) = &cli.tracing_tree.trace_tree_json {
let tree_query: TreeForTableQuery =
serde_json::from_str(query_json).map_err(|_| CliError::TracingInvalidFact {
Expand All @@ -233,10 +230,7 @@ async fn handle_tracing_tree(
Ok(())
}

async fn handle_tracing_node(
cli: &CliApp,
engine: &mut DefaultExecutionEngine,
) -> Result<(), CliError> {
async fn handle_tracing_node(cli: &CliApp, engine: &mut ExecutionEngine) -> Result<(), CliError> {
if let Some(query_file) = &cli.tracing_node.trace_node_json {
let query_string = read_to_string(query_file).expect("Unable to read file");

Expand Down Expand Up @@ -297,7 +291,7 @@ async fn run(mut cli: CliApp) -> Result<(), CliError> {

TimedCode::instance().sub("Reasoning").start();
log::info!("Reasoning ... ");
engine.execute().await?;
engine.execute::<DefaultExecutionStrategy>().await?;
log::info!("Reasoning done");
TimedCode::instance().sub("Reasoning").stop();

Expand Down
2 changes: 1 addition & 1 deletion nemo-physical/src/datatypes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub(crate) mod storage_type_name;
pub(crate) use storage_type_name::StorageTypeName;
/// Module for defining [StorageValueT]
pub(crate) mod storage_value;
pub(crate) use storage_value::StorageValueT;
pub use storage_value::StorageValueT;
/// Module for defining [Double]
pub mod double;
pub use double::Double;
Expand Down
2 changes: 1 addition & 1 deletion nemo-physical/src/datatypes/storage_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@
/// Ord and PartialOrd assume U32 < U64 < I64 < Float < Double.
/// More information at <https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html#derivable>
#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub(crate) enum StorageValueT {
pub enum StorageValueT {
/// A value of type [StorageTypeName::Id32]. Such values always refer to an entry in a

Check failure on line 16 in nemo-physical/src/datatypes/storage_value.rs

View workflow job for this annotation

GitHub Actions / Verify crate documentation

public documentation for `Id32` links to private item `StorageTypeName::Id32`
/// dictionary, rather than to the literal numerical integer value.
Id32(u32),
/// A value of type [StorageTypeName::Id64]. Such values always refer to an entry in a

Check failure on line 19 in nemo-physical/src/datatypes/storage_value.rs

View workflow job for this annotation

GitHub Actions / Verify crate documentation

public documentation for `Id64` links to private item `StorageTypeName::Id64`
/// dictionary, rather than to the literal numerical integer value.
Id64(u64),
/// A value of type [StorageTypeName::Int64]. Such values always refer to a literal

Check failure on line 22 in nemo-physical/src/datatypes/storage_value.rs

View workflow job for this annotation

GitHub Actions / Verify crate documentation

public documentation for `Int64` links to private item `StorageTypeName::Int64`
/// numerical integer value rather than to an entry in a dictionary.
Int64(i64),
/// A value of type [StorageTypeName::Float].

Check failure on line 25 in nemo-physical/src/datatypes/storage_value.rs

View workflow job for this annotation

GitHub Actions / Verify crate documentation

public documentation for `Float` links to private item `StorageTypeName::Float`
Float(Float),
/// A value of type [StorageTypeName::Double].

Check failure on line 27 in nemo-physical/src/datatypes/storage_value.rs

View workflow job for this annotation

GitHub Actions / Verify crate documentation

public documentation for `Double` links to private item `StorageTypeName::Double`
Double(Double),
}

Expand Down
2 changes: 1 addition & 1 deletion nemo-physical/src/datavalues/any_datavalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ impl AnyDataValue {
/// The correct process in this case is to use the dictionary to create any null value on which this
/// method will later be called. It is not possible to newly create a dictionary id for an arbitrary
/// null value (in such a way that the same ID will be returned if an equal null value is converted).
pub(crate) fn to_storage_value_t_dict(&self, dictionary: &mut Dict) -> StorageValueT {
pub fn to_storage_value_t_dict(&self, dictionary: &mut Dict) -> StorageValueT {
match self.value_domain() {
ValueDomain::Tuple
| ValueDomain::Map
Expand Down
22 changes: 21 additions & 1 deletion nemo-physical/src/management/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use ascii_tree::write_tree;

use crate::{
datasources::table_providers::TableProvider,
datatypes::StorageValueT,
datavalues::AnyDataValue,
error::Error,
management::{bytesized::ByteSized, database::execution_series::ExecutionTreeNode},
Expand Down Expand Up @@ -150,6 +151,26 @@ impl DatabaseInstance {
self.reference_manager.count_rows_in_memory(id)
}

/// Provide an iterator over the rows of the table with the given [PermanentTableId]
/// which directly yields rows of [`StorageValueT`], without translation through a
/// dictionary.
///
/// # Panics
/// Panics if the given id does not exist.
pub async fn table_raw_row_iterator(
&mut self,
id: PermanentTableId,
) -> Result<impl Iterator<Item = Vec<StorageValueT>> + '_, Error> {
// Make sure trie is loaded
let storage_id = self
.reference_manager
.trie_id(&self.dictionary, id, ColumnOrder::default())
.await.unwrap_or_else(|err| panic!("No table with the id {id} exists: {err}"));
let trie = self.reference_manager.trie(storage_id);

Ok(trie.row_iterator())
}

/// Provide an iterator over the rows of the table with the given [PermanentTableId].
///
/// # Panics
Expand All @@ -158,7 +179,6 @@ impl DatabaseInstance {
&mut self,
id: PermanentTableId,
) -> Result<impl Iterator<Item = Vec<AnyDataValue>> + '_, Error> {
// Make sure trie is loaded
let storage_id = self
.reference_manager
.trie_id(&self.dictionary, id, ColumnOrder::default())
Expand Down
2 changes: 1 addition & 1 deletion nemo-physical/src/tabular.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ pub mod operations;
pub mod trie;
pub(crate) mod triescan;

pub(crate) mod buffer;
pub mod buffer;
pub(crate) mod rowscan;
2 changes: 2 additions & 0 deletions nemo-physical/src/tabular/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@

pub(crate) mod sorted_tuple_buffer;
pub(crate) mod tuple_buffer;

pub use tuple_buffer::TupleBuffer;
2 changes: 1 addition & 1 deletion nemo-physical/src/tabular/buffer/sorted_tuple_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use super::tuple_buffer::TupleBuffer;

/// Read-only wrapper for [TupleBuffer] which allows the retrieval of its tuples in a sorted manner
#[derive(Debug)]
pub(crate) struct SortedTupleBuffer {
pub struct SortedTupleBuffer {
/// Underlying [TupleBuffer] containing the actual values
tuple_buffer: TupleBuffer,
/// We imagine the tuple of the `tuple_buffer` to be arranged one after another in the order of its subtables.
Expand Down
10 changes: 5 additions & 5 deletions nemo-physical/src/tabular/buffer/tuple_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@

/// Represents a row-based table containing values of arbitrary data types
#[derive(Debug)]
pub(crate) struct TupleBuffer {
pub struct TupleBuffer {
/// Conceptionally, one may imagine the table represented by the [TupleBuffer]
/// to be split into several subtables that only contain rows with certain fixed types.
/// E.g. one subtable might contain tuples of type ([StorageTypeName::Id32], [StorageTypeName::Int64])
Expand Down Expand Up @@ -282,7 +282,7 @@

impl TupleBuffer {
/// Create a new [TupleBuffer].
pub(crate) fn new(column_number: usize) -> Self {
pub fn new(column_number: usize) -> Self {
Self::with_patterns(column_number, Vec::new())
}

Expand Down Expand Up @@ -333,8 +333,8 @@
/// When the value for the last column was provided, the tuple is committed to the buffer.
/// Alternatively, a partially built tuple can be abandonded by calling `drop_current_tuple`.
///
/// This must not be mixed with [add_tuple_data_value] on the same tuple.

Check failure on line 336 in nemo-physical/src/tabular/buffer/tuple_buffer.rs

View workflow job for this annotation

GitHub Actions / Verify crate documentation

unresolved link to `add_tuple_data_value`
pub(crate) fn add_tuple_value(&mut self, value: StorageValueT) {
pub fn add_tuple_value(&mut self, value: StorageValueT) {
self.current_tuple_types[self.current_tuple_index] = value.get_type();
self.current_tuple[self.current_tuple_index] = value;
self.current_tuple_index += 1;
Expand Down Expand Up @@ -375,8 +375,8 @@
self.current_tuple_index = 0;
}

/// Finish writing to the [TupleBuffer] and return a [SortedTupleBuffer].

Check failure on line 378 in nemo-physical/src/tabular/buffer/tuple_buffer.rs

View workflow job for this annotation

GitHub Actions / Verify crate documentation

public documentation for `finalize` links to private item `SortedTupleBuffer`
pub(crate) fn finalize(self) -> SortedTupleBuffer {
pub fn finalize(self) -> SortedTupleBuffer {
SortedTupleBuffer::new(self)
}

Expand All @@ -387,7 +387,7 @@
}

/// Returns the number of rows in the [TupleBuffer]
pub(crate) fn size(&self) -> usize {
pub fn size(&self) -> usize {
self.typed_subtables
.iter()
.map(|record| record.current_length)
Expand Down
4 changes: 2 additions & 2 deletions nemo-physical/src/tabular/trie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
}

/// Return a row based iterator over this trie.
pub(crate) fn row_iterator(&self) -> impl Iterator<Item = Vec<StorageValueT>> + '_ {
pub fn row_iterator(&self) -> impl Iterator<Item = Vec<StorageValueT>> + use<'_> {
RowScan::new_full(self.partial_iterator())
}

Expand Down Expand Up @@ -179,8 +179,8 @@
}
}

/// Create a new [Trie] from a [SortedTupleBuffer].

Check failure on line 182 in nemo-physical/src/tabular/trie.rs

View workflow job for this annotation

GitHub Actions / Verify crate documentation

public documentation for `from_tuple_buffer` links to private item `SortedTupleBuffer`
pub(crate) fn from_tuple_buffer(buffer: SortedTupleBuffer) -> Self {
pub fn from_tuple_buffer(buffer: SortedTupleBuffer) -> Self {
let mut intervalcolumn_builders = (0..buffer.column_number())
.map(|_| IntervalColumnTBuilderMatrix::<IntervalLookupMethod>::default())
.collect::<Vec<_>>();
Expand Down
7 changes: 4 additions & 3 deletions nemo-python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use nemo::{
chase_model::ChaseAtom,
datavalues::{AnyDataValue, DataValue},
error::Error,
execution::{ExecutionEngine, tracing::trace::ExecutionTraceTree},
execution::{DefaultExecutionStrategy, ExecutionEngine, tracing::trace::ExecutionTraceTree},
io::{ExportManager, ImportManager, resource_providers::ResourceProviders},
meta::timing::TimedCode,
rule_model::{
Expand Down Expand Up @@ -331,7 +331,7 @@ impl NemoResults {

#[pyclass(unsendable)]
struct NemoEngine {
engine: nemo::execution::DefaultExecutionEngine,
engine: nemo::execution::ExecutionEngine,
}

#[pyclass]
Expand Down Expand Up @@ -426,7 +426,8 @@ impl NemoEngine {
.enable_all()
.build()?;

rt.block_on(self.engine.execute()).py_res()?;
rt.block_on(self.engine.execute::<DefaultExecutionStrategy>())
.py_res()?;

TimedCode::instance().sub("Reasoning").stop();
TimedCode::instance().stop();
Expand Down
8 changes: 5 additions & 3 deletions nemo-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ use web_sys::{Blob, FileReaderSync};
use nemo::{
datavalues::{AnyDataValue, DataValue},
error::ReadingError,
execution::{ExecutionEngine, execution_parameters::ExecutionParameters},
execution::{
DefaultExecutionStrategy, ExecutionEngine, execution_parameters::ExecutionParameters,
},
io::{
ImportManager,
resource_providers::{ResourceProvider, ResourceProviders, http},
Expand Down Expand Up @@ -126,7 +128,7 @@ impl ResourceProvider for BlobResourceProvider {

#[wasm_bindgen]
pub struct NemoEngine {
engine: nemo::execution::DefaultExecutionEngine,
engine: nemo::execution::ExecutionEngine,
}

#[cfg(feature = "web_sys_unstable_apis")]
Expand Down Expand Up @@ -264,7 +266,7 @@ impl NemoEngine {
#[wasm_bindgen]
pub async fn reason(&mut self) -> Result<(), NemoError> {
self.engine
.execute()
.execute::<DefaultExecutionStrategy>()
.await
.map_err(WasmOrInternalNemoError::Nemo)
.map_err(NemoError)
Expand Down
6 changes: 3 additions & 3 deletions nemo/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use std::{fs::read_to_string, path::PathBuf};
use crate::{
error::{Error, ReadingError, report::ProgramReport},
execution::{
DefaultExecutionEngine, ExecutionEngine, execution_parameters::ExecutionParameters,
DefaultExecutionStrategy, ExecutionEngine, execution_parameters::ExecutionParameters,
},
rule_file::RuleFile,
rule_model::{
Expand All @@ -41,7 +41,7 @@ use crate::{
use nemo_physical::resource::Resource;

/// Reasoning Engine exposed by the API
pub type Engine = DefaultExecutionEngine;
pub type Engine = ExecutionEngine;

/// Load the given `file` and load the program from the file.
///
Expand Down Expand Up @@ -116,7 +116,7 @@ pub fn validate(input: String, label: String) -> ProgramReport {
/// parsed rules, all relative paths are resolved with the current
/// working directory
pub async fn reason(engine: &mut Engine) -> Result<(), Error> {
engine.execute().await
engine.execute::<DefaultExecutionStrategy>().await
}

/// Get a [Vec] of all output predicates that are computed by the engine.
Expand Down
4 changes: 1 addition & 3 deletions nemo/src/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ use self::selection_strategy::{
pub mod execution_parameters;
pub mod planning;
pub mod rule_execution;
pub mod saturation;
pub mod selection_strategy;
pub mod tracing;

/// The default strategy that will be used for reasoning
pub type DefaultExecutionStrategy = StrategyStratifiedNegation<
StrategyDependencyGraph<GraphConstructorPositive, StrategyRoundRobin>,
>;

/// Shorthand for an execution engine using the default strategy
pub type DefaultExecutionEngine = ExecutionEngine<DefaultExecutionStrategy>;
Loading
Loading