diff --git a/Makefile b/Makefile index f229115..58df5ea 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ SHELL := /usr/bin/env bash # Targets ################################################################################ -.PHONY: all build rebuild example test cov lint format docs docs-serve clean install-deps release help coverage \ +.PHONY: all build rebuild example test lint format docs docs-serve clean install-deps release help coverage \ setup-hooks test-hooks .DEFAULT_GOAL := help diff --git a/README.md b/README.md index 12fb015..f7f59b2 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![Tests](https://img.shields.io/github/actions/workflow/status/CogitatorTech/zodd/tests.yml?label=tests&style=flat&labelColor=282c34&logo=github)](https://github.com/CogitatorTech/zodd/actions/workflows/tests.yml) [![License](https://img.shields.io/badge/license-MIT-007ec6?label=license&style=flat&labelColor=282c34&logo=open-source-initiative)](https://github.com/CogitatorTech/zodd/blob/main/LICENSE) [![Examples](https://img.shields.io/badge/examples-view-green?style=flat&labelColor=282c34&logo=zig)](https://github.com/CogitatorTech/zodd/tree/main/examples) -[![Docs](https://img.shields.io/badge/docs-read-blue?style=flat&labelColor=282c34&logo=read-the-docs)](https://CogitatorTech.github.io/zodd/#zodd.lib) +[![Docs](https://img.shields.io/badge/docs-read-blue?style=flat&labelColor=282c34&logo=read-the-docs)](https://CogitatorTech.github.io/zodd/) [![Zig Version](https://img.shields.io/badge/Zig-0.15.2-orange?logo=zig&labelColor=282c34)](https://ziglang.org/download/) [![Release](https://img.shields.io/github/release/CogitatorTech/zodd.svg?label=release&style=flat&labelColor=282c34&logo=github)](https://github.com/CogitatorTech/zodd/releases/latest) @@ -76,9 +76,10 @@ For example: ### Why Zodd? - Written in pure Zig with a simple API -- Supports a subset of relational algebra with sorted, deduplicated relations -- Supports fast incremental rule computation -- Supports multi-way joins and anti-join operations +- Implements semi-naive evaluation for efficient recursive query processing +- Uses immutable, sorted, and deduplicated relations as core data structures +- Supports parallel execution for joins and variable updates +- Provides primitives for multi-way joins, anti-joins, secondary indexes, and aggregation See [ROADMAP.md](ROADMAP.md) for the list of implemented and planned features. @@ -183,7 +184,7 @@ pub fn main() !void { ### Documentation -You can find the API documentation for the latest release of Zodd [here](https://CogitatorTech.github.io/zodd/#zodd.lib). +You can find the API documentation for the latest release of Zodd [here](https://CogitatorTech.github.io/zodd/). Alternatively, you can use the `make docs` command to generate the documentation for the current version of Zodd. This will generate HTML documentation in the `docs/api` directory, which you can serve locally with `make docs-serve` and view in a web browser. diff --git a/build.zig.zon b/build.zig.zon index 04dfa27..1cbb5a0 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = .zodd, - .version = "0.1.0-alpha.2", + .version = "0.1.0-alpha.3", .fingerprint = 0x2d03181bdd24914c, // Changing this has security and trust implications. .minimum_zig_version = "0.15.2", .dependencies = .{ diff --git a/src/lib.zig b/src/lib.zig index fd785bb..76d05de 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -1,4 +1,55 @@ -//! Zodd: datalog engine for Zig. +//! # Zodd +//! +//! Zodd is a Datalog engine for Zig. +//! It implements semi-naive evaluation with parallel execution support. +//! +//! ## Quickstart +//! +//! ```zig +//! const std = @import("std"); +//! const zodd = @import("zodd"); +//! +//! pub fn main() !void { +//! var gpa = std.heap.GeneralPurposeAllocator(.{}){}; +//! defer _ = gpa.deinit(); +//! const allocator = gpa.allocator(); +//! +//! // 1. Create execution context +//! var ctx = zodd.ExecutionContext.init(allocator); +//! defer ctx.deinit(); +//! +//! // 2. Define relation (e.g., edge(x, y)) +//! const Edge = struct { u32, u32 }; +//! var edge = try zodd.Relation(Edge).fromSlice(&ctx, &[_]Edge{ +//! .{ 1, 2 }, .{ 2, 3 }, .{ 3, 4 } +//! }); +//! defer edge.deinit(); +//! +//! // 3. Define variable for transitive closure path(x, y) +//! var path = try zodd.Variable(Edge).init(&ctx, &edge); +//! defer path.deinit(); +//! +//! // 4. Run semi-naive evaluation +//! while (try path.changed()) { +//! // path(x, z) :- path(x, y), edge(y, z). +//! const new_paths = try zodd.joinInto(Edge, Edge, u32, &path, &edge, 1, 0, struct { +//! fn f(x: u32, y: u32, z: u32) Edge { return .{ x, z }; } +//! }.f); +//! try path.insert(new_paths); +//! } +//! +//! std.debug.print("Path count: {}\n", .{path.complete().len}); +//! } +//! ``` +//! +//! ## Components +//! +//! - `Relation`: The immutable data structure (sorted, deduplicated tuples). +//! - `Variable`: The mutable relation for fixed-point iterations. +//! - `join`: The merge-join algorithms. +//! - `extend`: The primitives for extending tuples (semi-joins, anti-joins). +//! - `index`: The indexes for lookups. +//! - `aggregate`: The group-by and aggregation operations. /// Relation module. pub const relation = @import("zodd/relation.zig"); diff --git a/src/zodd/aggregate.zig b/src/zodd/aggregate.zig index 4a4c180..d264cfb 100644 --- a/src/zodd/aggregate.zig +++ b/src/zodd/aggregate.zig @@ -1,4 +1,10 @@ -//! Group-by and aggregation primitives for relations. +//! # Aggregation +//! +//! The module provides primitives for grouping and aggregating tuples. +//! +//! It supports standard operations like sum, count, min, max via a generic folder interface. +//! The algorithm sorts tuples by the grouping key, then folds values. +//! The pre-processing step supports parallel execution. const std = @import("std"); const Allocator = std.mem.Allocator; diff --git a/src/zodd/context.zig b/src/zodd/context.zig index 644e783..52aa563 100644 --- a/src/zodd/context.zig +++ b/src/zodd/context.zig @@ -1,4 +1,9 @@ -//! Context management for parallel execution. +//! # Execution Context +//! +//! The context manages shared resources for query execution, primarily the memory allocator +//! and optional thread pool. +//! +//! Users pass it to Zodd operations to access resources. const std = @import("std"); const Allocator = std.mem.Allocator; diff --git a/src/zodd/extend.zig b/src/zodd/extend.zig index 386c01a..a8a4e5a 100644 --- a/src/zodd/extend.zig +++ b/src/zodd/extend.zig @@ -1,4 +1,15 @@ -//! Primitives for extending tuples with new values. +//! # Extension Primitives +//! +//! The module provides mechanisms to extend tuples with new values, implementing semi-joins and anti-joins. +//! +//! A `Leaper` represents a strategy for finding matching values for a prefix. +//! +//! Components: +//! - `ExtendWith`: The struct extends a tuple by looking up values in a relation (semi-join). +//! - `FilterAnti`: The struct filters tuples that DO NOT match values in a relation (anti-join). +//! - `ExtendAnti`: The struct extends a tuple but excludes values present in a relation. +//! +//! The mechanics support "Leapfrog Trie Join" style execution. const std = @import("std"); const Allocator = std.mem.Allocator; @@ -7,6 +18,9 @@ const Variable = @import("variable.zig").Variable; const gallop = @import("variable.zig").gallop; const ExecutionContext = @import("context.zig").ExecutionContext; +/// Creates a Leaper interface type for a Tuple and Value type. +/// +/// The Leaper proposes candidate values and verifies/intersects them based on a prefix tuple. pub fn Leaper(comptime Tuple: type, comptime Val: type) type { return struct { const Self = @This(); diff --git a/src/zodd/index.zig b/src/zodd/index.zig index f607435..4b53461 100644 --- a/src/zodd/index.zig +++ b/src/zodd/index.zig @@ -1,4 +1,9 @@ -//! Secondary index implementation for relations using B-trees. +//! # Secondary Index +//! +//! The module implements B-tree based secondary indexes for relations. +//! +//! The index allows efficient lookups and range queries on attributes other than the primary sort key. +//! It uses an `id` -> `Relation` mapping, where `id` is the indexed attribute. const std = @import("std"); const Allocator = std.mem.Allocator; diff --git a/src/zodd/iteration.zig b/src/zodd/iteration.zig index 3f97db5..fab17de 100644 --- a/src/zodd/iteration.zig +++ b/src/zodd/iteration.zig @@ -1,4 +1,9 @@ -//! Fixpoint iteration logic for semi-naive evaluation. +//! # Iteration Manager +//! +//! The manager handles the fixpoint iteration loop for semi-naive evaluation. +//! +//! It orchestrates the evolution of multiple `Variable` instances, checking for convergence +//! (when no new facts are added). It supports parallel "changed" checks. const std = @import("std"); const Allocator = std.mem.Allocator; diff --git a/src/zodd/join.zig b/src/zodd/join.zig index de4f5b5..0e227aa 100644 --- a/src/zodd/join.zig +++ b/src/zodd/join.zig @@ -1,4 +1,13 @@ -//! Join algorithms including merge-join and leapfrog trie join. +//! # Join Algorithms +//! +//! The module implements join operations for sorted relations. +//! +//! Functions: +//! - `joinHelper`: The function performs a sort-merge join between two relations. +//! - `joinInto`: The function joins two relations (including Variable deltas) and projects results. +//! - `joinAnti`: The function performs an anti-join operation (subtracting tuples). +//! +//! These functions use the sorted property of relations. const std = @import("std"); const Allocator = std.mem.Allocator; @@ -7,7 +16,18 @@ const Variable = @import("variable.zig").Variable; const gallop = @import("variable.zig").gallop; const ExecutionContext = @import("context.zig").ExecutionContext; -/// Helper function for joining two sorted relations. +/// Performs a merge-join between two sorted relations on a common key. +/// +/// The function iterates through both relations. When keys match, it invokes the callback. +/// +/// Arguments: +/// - `Key`: The type of join key. +/// - `Val1`: The value type of the first relation. +/// - `Val2`: The value type of the second relation. +/// - `input1`: The first relation (sorted by Key). +/// - `input2`: The second relation (sorted by Key). +/// - `context`: The context passed to the result callback. +/// - `result`: The comparison callback function `fn(ctx, key, val1, val2) void`. pub fn joinHelper( comptime Key: type, comptime Val1: type, diff --git a/src/zodd/relation.zig b/src/zodd/relation.zig index 27c4e5d..0b32046 100644 --- a/src/zodd/relation.zig +++ b/src/zodd/relation.zig @@ -1,4 +1,25 @@ -//! Core relation data structure: sorted list of unique tuples. +//! # Relation +//! +//! Structure representing a set of tuples. +//! The relation is immutable, sorted, and deduplicated. +//! +//! ## Usage +//! +//! ```zig +//! const zodd = @import("zodd"); +//! +//! // Define tuple type +//! const Edge = struct { u32, u32 }; +//! +//! // Create from slice +//! var rel = try zodd.Relation(Edge).fromSlice(ctx, &[_]Edge{ +//! .{ 1, 2 }, .{ 1, 2 }, .{ 2, 3 } +//! }); +//! defer rel.deinit(); +//! +//! // Elements are sorted and deduplicated +//! std.debug.assert(rel.elements.len == 2); +//! ``` const std = @import("std"); const mem = std.mem; @@ -10,14 +31,24 @@ pub fn Relation(comptime Tuple: type) type { return struct { const Self = @This(); - /// Elements of the relation. + /// The underlying sorted, deduplicated slice of tuples. + /// The slice is owned by the Relation. elements: []Tuple, - /// Allocator for the relation. + /// The allocator for the relation. allocator: Allocator, - /// Execution context. + /// The execution context. ctx: *ExecutionContext, - /// Creates a relation from a slice of tuples. + /// Creates a `Relation` from a slice of tuples. + /// + /// The function copies, sorts, and deduplicates the input slice. + /// It uses a thread pool for sorting if one is available. + /// + /// Arguments: + /// - `ctx`: The execution context. + /// - `input`: The slice of tuples. + /// + /// Returns: A new `Relation`. pub fn fromSlice(ctx: *ExecutionContext, input: []const Tuple) Allocator.Error!Self { if (input.len == 0) { return Self{ diff --git a/src/zodd/variable.zig b/src/zodd/variable.zig index b8e6b10..e0900a1 100644 --- a/src/zodd/variable.zig +++ b/src/zodd/variable.zig @@ -1,4 +1,26 @@ -//! Datalog variable representing a relation that evolves during iteration. +//! # Variable +//! +//! A Variable represents a relation that evolves during fixed-point iteration (semi-naive evaluation). +//! +//! The Variable maintains three states: +//! - **Stable**: The tuples processed in previous iterations. +//! - **Recent**: The tuples added in the current iteration (delta). +//! - **To Add**: The new tuples discovered in the current iteration. +//! +//! This structure allows join processing where only `recent` tuples are joined +//! with other relations to discover new facts. +//! +//! ## Usage +//! +//! ```zig +//! var v = try Variable(Edge).init(ctx, &initial_edges); +//! defer v.deinit(); +//! +//! while (try v.changed()) { +//! // Join logic here, populating v.next +//! try v.insert(new_facts); +//! } +//! ``` const std = @import("std"); const Allocator = std.mem.Allocator; @@ -11,18 +33,22 @@ pub fn Variable(comptime Tuple: type) type { const Rel = Relation(Tuple); const RelList = std.ArrayListUnmanaged(Rel); - /// Stable batches of the variable. + /// The list of "stable" relations. stable: RelList, - /// Recent batch of the variable. + /// The "recent" relation (added in the last round). recent: Rel, - /// Batches to be added to the variable. + /// The list of relations to be added for the next round. to_add: RelList, - /// Allocator for the variable. + /// The allocator for internal structures. allocator: Allocator, - /// Execution context. + /// The execution context. ctx: *ExecutionContext, /// Initializes a new variable. + /// + /// Arguments: + /// - `ctx`: The execution context. + /// - `initial_data`: (Optional) The initial relation. pub fn init(ctx: *ExecutionContext) Self { return Self{ .stable = RelList{},