Skip to content

Commit 407e525

Browse files
committed
Improve the decoupling and the API #1
1 parent 29397f0 commit 407e525

21 files changed

Lines changed: 694 additions & 1643 deletions

AGENTS.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ Priorities, in order:
4141
- `src/zodd/extend.zig`: Leaper-based extension primitives (`ExtendWith`, `FilterAnti`, `ExtendAnti`, `extendInto`).
4242
- `src/zodd/index.zig`: Indexes for keyed lookups.
4343
- `src/zodd/aggregate.zig`: Group-by and aggregation operations.
44-
- `src/zodd/context.zig`: `ExecutionContext` (allocator and shared state for a Datalog run).
4544
- `tests/`: Non-unit tests (`integration_tests.zig`, `regression_tests.zig`, `property_tests.zig`, `incremental_tests.zig`).
4645
- `examples/`: Self-contained example programs (`e1_network_reachability.zig` through `e6_dependency_resolution.zig`) built as executables via
4746
`build.zig`.
@@ -54,10 +53,10 @@ Priorities, in order:
5453

5554
### Evaluation Pipeline
5655

57-
A Datalog program flows through: `ExecutionContext` (`context.zig`) owns the allocator and shared state. Base data is loaded into a `Relation`
58-
(`relation.zig`). Derived predicates use a `Variable` (`variable.zig`) driven by an `Iteration` (`iteration.zig`) loop that calls `changed()` until a
59-
fixed point. Each iteration extends tuples via `join` (`join.zig`) or `extend` (`extend.zig`), optionally using indexes (`index.zig`) or aggregates
60-
(`aggregate.zig`).
56+
A Datalog program flows through: base data is loaded into a `Relation` (`relation.zig`). Derived predicates use a `Variable` (`variable.zig`) driven
57+
by an `Iteration` (`iteration.zig`) loop that calls `changed()` until a fixed point. Each iteration extends tuples via `join` (`join.zig`) or `extend`
58+
(`extend.zig`), optionally using indexes (`index.zig`) or aggregates (`aggregate.zig`). Every primitive takes a `std.mem.Allocator` directly; there is
59+
no wrapper context type.
6160

6261
### Relations and Variables Split
6362

README.md

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -132,46 +132,39 @@ pub fn main() !void {
132132
var gpa = std.heap.DebugAllocator(.{}){};
133133
defer _ = gpa.deinit();
134134
const allocator = gpa.allocator();
135-
var ctx = zodd.ExecutionContext.init(allocator);
136-
defer ctx.deinit();
137135
138136
const Edge = struct { u32, u32 };
139137
140-
// Create base relation: edges in a graph
141-
var edges = try zodd.Relation(Edge).fromSlice(&ctx, &[_]Edge{
138+
// Base relation: edges in a graph
139+
var edges = try zodd.Relation(Edge).fromSlice(allocator, &[_]Edge{
142140
.{ 1, 2 },
143141
.{ 2, 3 },
144142
.{ 3, 4 },
145143
});
146144
defer edges.deinit();
147145
148-
// Create variable for reachability (transitive closure)
149-
var reachable = zodd.Variable(Edge).init(&ctx);
146+
// Variable holding the reachability closure
147+
var reachable = zodd.Variable(Edge).init(allocator);
150148
defer reachable.deinit();
149+
try reachable.insertSlice(edges.elements);
151150
152-
// Initialize with base edges
153-
try reachable.insertSlice(&ctx, edges.elements);
154-
155-
// Fixed-point iteration: reachable(X,Z) :- reachable(X,Y), edge(Y,Z)
151+
// Fixed-point iteration: reachable(X, Z) :- reachable(X, Y), edge(Y, Z)
156152
while (try reachable.changed()) {
157-
var new_tuples: std.ArrayList(Edge) = .empty;
158-
defer new_tuples.deinit(allocator);
153+
var batch: std.ArrayList(Edge) = .empty;
154+
defer batch.deinit(allocator);
159155
160156
for (reachable.recent.elements) |r| {
161157
for (edges.elements) |e| {
162-
if (e[0] == r[1]) {
163-
try new_tuples.append(allocator, .{ r[0], e[1] });
164-
}
158+
if (e[0] == r[1]) try batch.append(allocator, .{ r[0], e[1] });
165159
}
166160
}
167161
168-
if (new_tuples.items.len > 0) {
169-
const rel = try zodd.Relation(Edge).fromSlice(&ctx, new_tuples.items);
162+
if (batch.items.len > 0) {
163+
const rel = try zodd.Relation(Edge).fromSlice(allocator, batch.items);
170164
try reachable.insert(rel);
171165
}
172166
}
173167
174-
// Get final result
175168
var result = try reachable.complete();
176169
defer result.deinit();
177170

examples/e1_network_reachability.zig

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ pub fn main() !void {
1818
var gpa = std.heap.DebugAllocator(.{}){};
1919
defer _ = gpa.deinit();
2020
const allocator = gpa.allocator();
21-
var ctx = zodd.ExecutionContext.init(allocator);
2221

2322
std.debug.print("Zodd Datalog Engine - Network Reachability Analysis\n", .{});
2423
std.debug.print("===================================================\n\n", .{});
@@ -87,20 +86,20 @@ pub fn main() !void {
8786

8887
// -- Build relations --
8988

90-
var links = try zodd.Relation(Pair).fromSlice(&ctx, &link_data);
89+
var links = try zodd.Relation(Pair).fromSlice(allocator, &link_data);
9190
defer links.deinit();
9291

93-
var blocked = try zodd.Relation(Pair).fromSlice(&ctx, &blocked_data);
92+
var blocked = try zodd.Relation(Pair).fromSlice(allocator, &blocked_data);
9493
defer blocked.deinit();
9594

9695
// -- Step 1: Compute reachable zones (transitive routing) --
9796
// reachable(A, B) :- link(A, B).
9897
// reachable(A, C) :- reachable(A, B), link(B, C).
9998

100-
var reachable = zodd.Variable(Pair).init(&ctx);
99+
var reachable = zodd.Variable(Pair).init(allocator);
101100
defer reachable.deinit();
102101

103-
try reachable.insertSlice(&ctx, links.elements);
102+
try reachable.insertSlice(links.elements);
104103

105104
std.debug.print("\nComputing transitive reachability...\n", .{});
106105

@@ -119,7 +118,7 @@ pub fn main() !void {
119118
}
120119

121120
if (results.items.len > 0) {
122-
const rel = try zodd.Relation(Pair).fromSlice(&ctx, results.items);
121+
const rel = try zodd.Relation(Pair).fromSlice(allocator, results.items);
123122
try reachable.insert(rel);
124123
}
125124

examples/e2_knowledge_graph.zig

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ pub fn main() !void {
1818
var gpa = std.heap.DebugAllocator(.{}){};
1919
defer _ = gpa.deinit();
2020
const allocator = gpa.allocator();
21-
var ctx = zodd.ExecutionContext.init(allocator);
2221

2322
std.debug.print("Zodd Datalog Engine - Knowledge Graph Reasoning\n", .{});
2423
std.debug.print("================================================\n\n", .{});
@@ -117,24 +116,24 @@ pub fn main() !void {
117116

118117
// -- Build relations --
119118

120-
var is_a_rel = try zodd.Relation(Pair).fromSlice(&ctx, &is_a_data);
119+
var is_a_rel = try zodd.Relation(Pair).fromSlice(allocator, &is_a_data);
121120
defer is_a_rel.deinit();
122121

123-
var symptom_rel = try zodd.Relation(Pair).fromSlice(&ctx, &symptom_data);
122+
var symptom_rel = try zodd.Relation(Pair).fromSlice(allocator, &symptom_data);
124123
defer symptom_rel.deinit();
125124

126-
var targets_rel = try zodd.Relation(Pair).fromSlice(&ctx, &targets_data);
125+
var targets_rel = try zodd.Relation(Pair).fromSlice(allocator, &targets_data);
127126
defer targets_rel.deinit();
128127

129-
var assoc_rel = try zodd.Relation(Pair).fromSlice(&ctx, &assoc_data);
128+
var assoc_rel = try zodd.Relation(Pair).fromSlice(allocator, &assoc_data);
130129
defer assoc_rel.deinit();
131130

132131
// -- Step 1: Compute transitive type hierarchy --
133132
// is_a(X, Z) :- is_a(X, Y), is_a(Y, Z).
134133

135-
var is_a = zodd.Variable(Pair).init(&ctx);
134+
var is_a = zodd.Variable(Pair).init(allocator);
136135
defer is_a.deinit();
137-
try is_a.insertSlice(&ctx, is_a_rel.elements);
136+
try is_a.insertSlice(is_a_rel.elements);
138137

139138
std.debug.print("\nComputing transitive type hierarchy...\n", .{});
140139

@@ -153,7 +152,7 @@ pub fn main() !void {
153152
}
154153

155154
if (results.items.len > 0) {
156-
const rel = try zodd.Relation(Pair).fromSlice(&ctx, results.items);
155+
const rel = try zodd.Relation(Pair).fromSlice(allocator, results.items);
157156
try is_a.insert(rel);
158157
}
159158
if (iter > 50) break;
@@ -170,9 +169,9 @@ pub fn main() !void {
170169
// -- Step 2: Inherit symptoms through type hierarchy --
171170
// has_symptom(D, S) :- is_a(D, D2), has_symptom(D2, S).
172171

173-
var has_symptom = zodd.Variable(Pair).init(&ctx);
172+
var has_symptom = zodd.Variable(Pair).init(allocator);
174173
defer has_symptom.deinit();
175-
try has_symptom.insertSlice(&ctx, symptom_rel.elements);
174+
try has_symptom.insertSlice(symptom_rel.elements);
176175

177176
// For each is_a(D, D2), propagate symptoms from D2 to D
178177
{
@@ -198,7 +197,7 @@ pub fn main() !void {
198197
}
199198

200199
if (inherited.items.len > 0) {
201-
try has_symptom.insertSlice(&ctx, inherited.items);
200+
try has_symptom.insertSlice(inherited.items);
202201
}
203202
}
204203
_ = try has_symptom.changed();
@@ -217,29 +216,29 @@ pub fn main() !void {
217216
// Join key = Protein. targets is (Drug, Protein), assoc is (Protein, Disease).
218217
// Rekey targets as (Protein, Drug) to align the join key.
219218

220-
var targets_by_protein = zodd.Variable(Pair).init(&ctx);
219+
var targets_by_protein = zodd.Variable(Pair).init(allocator);
221220
defer targets_by_protein.deinit();
222221
{
223222
var flipped = PairList.empty;
224223
defer flipped.deinit(allocator);
225224
for (targets_rel.elements) |t| {
226225
try flipped.append(allocator, .{ t[1], t[0] }); // (Protein, Drug)
227226
}
228-
try targets_by_protein.insertSlice(&ctx, flipped.items);
227+
try targets_by_protein.insertSlice(flipped.items);
229228
_ = try targets_by_protein.changed();
230229
}
231230

232-
var assoc_var = zodd.Variable(Pair).init(&ctx);
231+
var assoc_var = zodd.Variable(Pair).init(allocator);
233232
defer assoc_var.deinit();
234-
try assoc_var.insertSlice(&ctx, assoc_rel.elements);
233+
try assoc_var.insertSlice(assoc_rel.elements);
235234
_ = try assoc_var.changed();
236235

237236
const Triple = struct { u32, u32, u32 };
238-
var treats_triple = zodd.Variable(Triple).init(&ctx);
237+
var treats_triple = zodd.Variable(Triple).init(allocator);
239238
defer treats_triple.deinit();
240239

241240
// joinInto: key=Protein, val1=Drug, val2=Disease
242-
try zodd.joinInto(u32, u32, u32, Triple, &ctx, &targets_by_protein, &assoc_var, &treats_triple, struct {
241+
try zodd.joinInto(u32, u32, u32, Triple, &targets_by_protein, &assoc_var, &treats_triple, struct {
243242
fn logic(_: *const u32, drug: *const u32, disease: *const u32) Triple {
244243
return .{ drug.*, disease.*, 0 };
245244
}

examples/e3_data_lineage.zig

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ pub fn main() !void {
1818
var gpa = std.heap.DebugAllocator(.{}){};
1919
defer _ = gpa.deinit();
2020
const allocator = gpa.allocator();
21-
var ctx = zodd.ExecutionContext.init(allocator);
2221

2322
std.debug.print("Zodd Datalog Engine - Data Lineage for Compliance\n", .{});
2423
std.debug.print("=================================================\n\n", .{});
@@ -130,25 +129,25 @@ pub fn main() !void {
130129

131130
// -- Build relations --
132131

133-
var transforms = try zodd.Relation(Pair).fromSlice(&ctx, &transform_data);
132+
var transforms = try zodd.Relation(Pair).fromSlice(allocator, &transform_data);
134133
defer transforms.deinit();
135134

136-
var anonymizes = try zodd.Relation(Pair).fromSlice(&ctx, &anonymize_data);
135+
var anonymizes = try zodd.Relation(Pair).fromSlice(allocator, &anonymize_data);
137136
defer anonymizes.deinit();
138137

139138
// -- Step 1: Propagate PII through the pipeline --
140139
// contains_pii(D) :- source_pii(D).
141140
// contains_pii(D2) :- contains_pii(D1), transform(D1, D2),
142141
// NOT anonymizes(D1, D2).
143142

144-
var contains_pii = zodd.Variable(Scalar).init(&ctx);
143+
var contains_pii = zodd.Variable(Scalar).init(allocator);
145144
defer contains_pii.deinit();
146-
try contains_pii.insertSlice(&ctx, &source_pii_data);
145+
try contains_pii.insertSlice(&source_pii_data);
147146

148147
std.debug.print("\nPropagating PII through ETL pipeline...\n", .{});
149148

150149
// Use ExtendWith to propose destinations for PII-containing datasets
151-
var extend = zodd.ExtendWith(Scalar, u32, u32).init(&ctx, &transforms, &struct {
150+
var extend = zodd.ExtendWith(Scalar, u32, u32).init(allocator, &transforms, &struct {
152151
fn key(tuple: *const Scalar) u32 {
153152
return tuple[0];
154153
}
@@ -159,7 +158,7 @@ pub fn main() !void {
159158
std.debug.print(" Iteration {}: {} datasets with PII\n", .{ iteration, contains_pii.recent.len() });
160159

161160
// Use extendInto to find downstream datasets
162-
var proposed = zodd.Variable(Pair).init(&ctx);
161+
var proposed = zodd.Variable(Pair).init(allocator);
163162
defer proposed.deinit();
164163

165164
const leaper = extend.leaper();
@@ -169,7 +168,6 @@ pub fn main() !void {
169168
Scalar,
170169
u32,
171170
Pair,
172-
&ctx,
173171
&contains_pii,
174172
&leapers,
175173
&proposed,
@@ -203,7 +201,7 @@ pub fn main() !void {
203201
}
204202

205203
if (new_pii.items.len > 0) {
206-
const rel = try zodd.Relation(Scalar).fromSlice(&ctx, new_pii.items);
204+
const rel = try zodd.Relation(Scalar).fromSlice(allocator, new_pii.items);
207205
try contains_pii.insert(rel);
208206
}
209207

0 commit comments

Comments
 (0)