chase-rs/README.md

166 lines
4.2 KiB
Markdown
Raw Normal View History

2026-03-09 09:59:10 +01:00
## Chase-rs
2026-03-06 10:52:32 +00:00
2026-03-16 13:11:50 +01:00
An implementation of the chase algorithm in Rust for reasoning engines.
2026-03-09 09:59:10 +01:00
### Overview
2026-03-16 13:11:50 +01:00
The chase algorithm is a technique in context of database theory and knowledge representation used for:
2026-03-09 09:59:10 +01:00
- Query answering under tuple-generating dependencies (TGDs)
- Computing universal models
- Datalog with existential rules
2026-03-06 10:52:32 +00:00
2026-03-13 10:20:52 +01:00
This implementation provides a restricted-chase style materialization with active-trigger checks.
The default entrypoints use a `10_000` step safeguard and report incomplete results with
`terminated == false` if that limit is reached.
2026-03-06 10:52:32 +00:00
### Features
2026-03-09 09:59:10 +01:00
- **Core Data Types**: Terms, Atoms, Rules, and Instances
- **Existential Quantification**: Automatic generation of labeled nulls
2026-03-13 10:20:52 +01:00
- **Restricted Chase Style**: Active-trigger checks for existential rules
- **Configurable Step Limit**: `chase_with_config` exposes the step bound
2026-03-09 09:59:10 +01:00
- **Fluent API**: `RuleBuilder` for readable rule construction
2026-03-10 12:41:33 +01:00
- **Interactive Frontends**: REPL, script runner, and a local GUI
2026-03-09 09:59:10 +01:00
- **Zero Dependencies**: Pure Rust with no external runtime dependencies
2026-03-06 10:52:32 +00:00
2026-03-09 09:59:10 +01:00
See [ROADMAP.md](ROADMAP.md) for the list of implemented and planned features.
2026-03-06 10:52:32 +00:00
2026-03-09 09:59:10 +01:00
> [!IMPORTANT]
> This project is still in early development, so bugs and breaking changes are expected.
> Please use the [issues page](https://code.obsidian.systems/habedi-work/chase-rs/issues) to report bugs or request features.
2026-03-06 10:52:32 +00:00
2026-03-09 09:59:10 +01:00
---
### Quickstart
#### Example
```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]);
2026-03-06 10:52:32 +00:00
2026-03-09 09:59:10 +01:00
assert!(result.terminated);
println!("Derived {} facts", result.instance.len());
```
2026-03-13 10:20:52 +01:00
Use `chase_with_config` to change the default step bound:
```rust
use chase_rs::{Atom, ChaseConfig, Instance, Term, chase_with_config};
use chase_rs::chase::rule::RuleBuilder;
let instance: Instance = vec![Atom::new("P", vec![Term::constant("a")])]
.into_iter()
.collect();
let rule = RuleBuilder::new()
.when("P", vec![Term::var("X")])
.then("Q", vec![Term::var("X")])
.build();
let result = chase_with_config(
instance,
&[rule],
ChaseConfig { max_steps: 100 },
);
assert!(result.terminated);
```
2026-03-09 09:59:10 +01:00
#### 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();
```
2026-03-10 12:41:33 +01:00
#### Frontends
The binary now supports three entrypoints:
```bash
# Start the interactive REPL
cargo run -- repl
# Start the local GUI at http://127.0.0.1:7878
cargo run -- gui
# Run a script file
2026-03-10 12:48:25 +01:00
cargo run -- script examples/scripts/ancestor.chase
2026-03-10 12:41:33 +01:00
```
The REPL and GUI share a minimal command language:
```text
fact Parent(alice, bob).
rule Parent(?X, ?Y) -> Ancestor(?X, ?Y).
run.
query Ancestor(?X, ?Y)?
2026-03-10 13:02:19 +01:00
explain Ancestor(alice, carol)?
2026-03-10 12:41:33 +01:00
show facts
show rules
reset
help
```
Rules:
- facts and rules end with `.`
- queries end with `?`
2026-03-10 13:02:19 +01:00
- `explain ... ?` shows one derivation tree per matching answer
2026-03-10 12:41:33 +01:00
- variables are prefixed with `?`
- quoted constants are supported, for example `"alice smith"`
#### Useful Commands
2026-03-09 09:59:10 +01:00
```bash
2026-03-09 13:25:25 +01:00
# Install dependencies
nix develop -c make install-deps
2026-03-09 09:59:10 +01:00
# Run all tests
make test
# Run linter checks
make lint
# Run with optimizations
make build
# Run CLI
make run
```
---
### License
2026-03-06 10:52:32 +00:00
2026-03-09 09:59:10 +01:00
This project is licensed under [BSD-3](LICENSE).