2026-03-09 09:59:10 +01:00
|
|
|
# chase-rs
|
|
|
|
|
|
|
|
|
|
An efficient implementation of the chase algorithm in Rust for advanced reasoning engines.
|
|
|
|
|
|
|
|
|
|
## Overview
|
|
|
|
|
|
|
|
|
|
The chase algorithm is a fundamental technique in database theory and knowledge representation used for:
|
|
|
|
|
|
|
|
|
|
- Query answering under tuple-generating dependencies (TGDs)
|
|
|
|
|
- Computing universal models
|
|
|
|
|
- Ontology-based data access (OBDA)
|
|
|
|
|
- Datalog with existential rules
|
|
|
|
|
|
|
|
|
|
This implementation provides a **restricted chase** that ensures termination even with existential rules by tracking applied triggers.
|
|
|
|
|
|
|
|
|
|
## Features
|
|
|
|
|
|
|
|
|
|
- **Core Data Types**: Terms, Atoms, Rules, Instances
|
|
|
|
|
- **Existential Quantification**: Automatic generation of labeled nulls
|
|
|
|
|
- **Restricted Chase**: Termination guarantees via trigger tracking
|
|
|
|
|
- **Fluent API**: `RuleBuilder` for readable rule construction
|
|
|
|
|
- **Zero Dependencies**: Pure Rust with no external runtime dependencies
|
|
|
|
|
|
|
|
|
|
## Quick Start
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
use chase_rs::{chase, Atom, Instance, Term};
|
|
|
|
|
use chase_rs::chase::rule::RuleBuilder;
|
|
|
|
|
|
|
|
|
|
// Create initial facts
|
|
|
|
|
let instance: Instance = vec![
|
|
|
|
|
Atom::new("Parent", vec![Term::constant("alice"), Term::constant("bob")]),
|
|
|
|
|
Atom::new("Parent", vec![Term::constant("bob"), Term::constant("carol")]),
|
|
|
|
|
].into_iter().collect();
|
|
|
|
|
|
|
|
|
|
// Define rules
|
|
|
|
|
// Parent(X, Y) -> Ancestor(X, Y)
|
|
|
|
|
let rule1 = RuleBuilder::new()
|
|
|
|
|
.when("Parent", vec![Term::var("X"), Term::var("Y")])
|
|
|
|
|
.then("Ancestor", vec![Term::var("X"), Term::var("Y")])
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
// Ancestor(X, Y), Parent(Y, Z) -> Ancestor(X, Z)
|
|
|
|
|
let rule2 = RuleBuilder::new()
|
|
|
|
|
.when("Ancestor", vec![Term::var("X"), Term::var("Y")])
|
|
|
|
|
.when("Parent", vec![Term::var("Y"), Term::var("Z")])
|
|
|
|
|
.then("Ancestor", vec![Term::var("X"), Term::var("Z")])
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
// Run the chase
|
|
|
|
|
let result = chase(instance, &[rule1, rule2]);
|
|
|
|
|
|
|
|
|
|
assert!(result.terminated);
|
|
|
|
|
println!("Derived {} facts", result.instance.len());
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Existential Rules
|
|
|
|
|
|
|
|
|
|
Rules with head-only variables (existential quantification) automatically generate fresh labeled nulls:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
// Every person has an SSN: Person(X) -> HasSSN(X, Y)
|
|
|
|
|
let rule = RuleBuilder::new()
|
|
|
|
|
.when("Person", vec![Term::var("X")])
|
|
|
|
|
.then("HasSSN", vec![Term::var("X"), Term::var("Y")]) // Y is existential
|
|
|
|
|
.build();
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Project Structure
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
src/
|
|
|
|
|
├── lib.rs # Library root and re-exports
|
|
|
|
|
├── main.rs # Binary entry point
|
|
|
|
|
└── chase/
|
|
|
|
|
├── mod.rs # Module exports
|
|
|
|
|
├── term.rs # Terms: Constants, Nulls, Variables
|
|
|
|
|
├── atom.rs # Atoms: Predicate(term1, term2, ...)
|
|
|
|
|
├── instance.rs # Database instance (set of facts)
|
|
|
|
|
├── rule.rs # TGDs with RuleBuilder
|
|
|
|
|
├── substitution.rs # Variable bindings and unification
|
|
|
|
|
└── engine.rs # Core chase algorithm
|
|
|
|
|
tests/
|
|
|
|
|
├── integration_tests.rs
|
|
|
|
|
├── regression_tests.rs
|
|
|
|
|
└── property_tests.rs
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Building and Testing
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Run all tests
|
|
|
|
|
cargo test
|
|
|
|
|
|
|
|
|
|
# Run with optimizations
|
|
|
|
|
cargo build --release
|
|
|
|
|
|
|
|
|
|
# Check for lint warnings
|
|
|
|
|
cargo clippy
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## License
|
|
|
|
|
|
|
|
|
|
This project is licensed under [BSD-3](LICENSE).
|