rust-kgdb 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,478 @@
1
+ /**
2
+ * Datalog Example for rust-kgdb TypeScript SDK
3
+ *
4
+ * Demonstrates Datalog reasoning capabilities including:
5
+ * - Adding facts and rules
6
+ * - Semi-naive evaluation
7
+ * - Recursive rule evaluation
8
+ * - Stratified negation
9
+ * - Query evaluation
10
+ * - Integration with RDF graphs
11
+ */
12
+
13
+ import { DatalogEngine, GraphDb } from 'rust-kgdb';
14
+
15
+ // =============================================================================
16
+ // Example 1: Basic Facts and Rules
17
+ // =============================================================================
18
+
19
+ async function basicDatalogExample() {
20
+ console.log('=== Basic Datalog Facts and Rules ===\n');
21
+
22
+ const engine = new DatalogEngine();
23
+
24
+ // Add facts: parent(X, Y) means X is parent of Y
25
+ engine.addFact('parent', ['alice', 'bob']);
26
+ engine.addFact('parent', ['alice', 'carol']);
27
+ engine.addFact('parent', ['bob', 'david']);
28
+ engine.addFact('parent', ['bob', 'eve']);
29
+ engine.addFact('parent', ['carol', 'frank']);
30
+
31
+ console.log('Facts added:');
32
+ console.log(' parent(alice, bob)');
33
+ console.log(' parent(alice, carol)');
34
+ console.log(' parent(bob, david)');
35
+ console.log(' parent(bob, eve)');
36
+ console.log(' parent(carol, frank)\n');
37
+
38
+ // Add rule: grandparent(X, Z) :- parent(X, Y), parent(Y, Z)
39
+ engine.addRule(
40
+ 'grandparent', // head predicate
41
+ ['X', 'Z'], // head variables
42
+ [
43
+ { predicate: 'parent', args: ['X', 'Y'] },
44
+ { predicate: 'parent', args: ['Y', 'Z'] }
45
+ ]
46
+ );
47
+
48
+ console.log('Rule added:');
49
+ console.log(' grandparent(X, Z) :- parent(X, Y), parent(Y, Z)\n');
50
+
51
+ // Evaluate to derive all facts
52
+ engine.evaluate();
53
+
54
+ // Query grandparents
55
+ const grandparents = engine.query('grandparent', ['?who', '?grandchild']);
56
+ console.log('Query: grandparent(?who, ?grandchild)');
57
+ console.log('Results:');
58
+ for (const result of grandparents) {
59
+ console.log(` ${result.who} is grandparent of ${result.grandchild}`);
60
+ }
61
+ console.log();
62
+ }
63
+
64
+ // =============================================================================
65
+ // Example 2: Recursive Rules (Transitive Closure)
66
+ // =============================================================================
67
+
68
+ async function recursiveDatalogExample() {
69
+ console.log('=== Recursive Rules (Transitive Closure) ===\n');
70
+
71
+ const engine = new DatalogEngine();
72
+
73
+ // Add edge facts for a graph
74
+ engine.addFact('edge', ['a', 'b']);
75
+ engine.addFact('edge', ['b', 'c']);
76
+ engine.addFact('edge', ['c', 'd']);
77
+ engine.addFact('edge', ['d', 'e']);
78
+ engine.addFact('edge', ['a', 'c']); // shortcut
79
+
80
+ console.log('Graph edges: a->b, b->c, c->d, d->e, a->c\n');
81
+
82
+ // Base case: path(X, Y) :- edge(X, Y)
83
+ engine.addRule(
84
+ 'path',
85
+ ['X', 'Y'],
86
+ [{ predicate: 'edge', args: ['X', 'Y'] }]
87
+ );
88
+
89
+ // Recursive case: path(X, Z) :- edge(X, Y), path(Y, Z)
90
+ engine.addRule(
91
+ 'path',
92
+ ['X', 'Z'],
93
+ [
94
+ { predicate: 'edge', args: ['X', 'Y'] },
95
+ { predicate: 'path', args: ['Y', 'Z'] }
96
+ ]
97
+ );
98
+
99
+ console.log('Rules added:');
100
+ console.log(' path(X, Y) :- edge(X, Y) % base case');
101
+ console.log(' path(X, Z) :- edge(X, Y), path(Y, Z) % recursive case\n');
102
+
103
+ // Evaluate using semi-naive algorithm
104
+ const iterations = engine.evaluateSemiNaive();
105
+ console.log(`Semi-naive evaluation completed in ${iterations} iterations\n`);
106
+
107
+ // Query all paths from 'a'
108
+ const paths = engine.query('path', ['a', '?end']);
109
+ console.log('Query: path(a, ?end)');
110
+ console.log('Results (nodes reachable from a):');
111
+ for (const result of paths) {
112
+ console.log(` a can reach ${result.end}`);
113
+ }
114
+ console.log();
115
+ }
116
+
117
+ // =============================================================================
118
+ // Example 3: Stratified Negation
119
+ // =============================================================================
120
+
121
+ async function negationDatalogExample() {
122
+ console.log('=== Stratified Negation ===\n');
123
+
124
+ const engine = new DatalogEngine();
125
+
126
+ // Add employee facts
127
+ engine.addFact('employee', ['alice', 'engineering']);
128
+ engine.addFact('employee', ['bob', 'engineering']);
129
+ engine.addFact('employee', ['carol', 'sales']);
130
+ engine.addFact('employee', ['david', 'hr']);
131
+
132
+ // Add manager facts
133
+ engine.addFact('manager', ['alice']);
134
+ engine.addFact('manager', ['carol']);
135
+
136
+ console.log('Facts:');
137
+ console.log(' employee(alice, engineering), employee(bob, engineering)');
138
+ console.log(' employee(carol, sales), employee(david, hr)');
139
+ console.log(' manager(alice), manager(carol)\n');
140
+
141
+ // Rule: non_manager(X) :- employee(X, _), NOT manager(X)
142
+ engine.addRuleWithNegation(
143
+ 'non_manager',
144
+ ['X'],
145
+ [{ predicate: 'employee', args: ['X', '_'] }],
146
+ [{ predicate: 'manager', args: ['X'] }] // negated atoms
147
+ );
148
+
149
+ console.log('Rule with negation:');
150
+ console.log(' non_manager(X) :- employee(X, _), NOT manager(X)\n');
151
+
152
+ // Stratified evaluation (handles negation correctly)
153
+ engine.evaluateStratified();
154
+
155
+ // Query non-managers
156
+ const nonManagers = engine.query('non_manager', ['?person']);
157
+ console.log('Query: non_manager(?person)');
158
+ console.log('Results:');
159
+ for (const result of nonManagers) {
160
+ console.log(` ${result.person} is not a manager`);
161
+ }
162
+ console.log();
163
+ }
164
+
165
+ // =============================================================================
166
+ // Example 4: Aggregate Functions
167
+ // =============================================================================
168
+
169
+ async function aggregateDatalogExample() {
170
+ console.log('=== Aggregate Functions ===\n');
171
+
172
+ const engine = new DatalogEngine();
173
+
174
+ // Add sales facts: sale(product, amount, region)
175
+ engine.addFact('sale', ['laptop', '1200', 'west']);
176
+ engine.addFact('sale', ['laptop', '1100', 'east']);
177
+ engine.addFact('sale', ['phone', '800', 'west']);
178
+ engine.addFact('sale', ['phone', '850', 'east']);
179
+ engine.addFact('sale', ['tablet', '500', 'west']);
180
+ engine.addFact('sale', ['tablet', '450', 'east']);
181
+
182
+ console.log('Sales facts added\n');
183
+
184
+ // Rule with aggregation: total_by_product(P, sum(A)) :- sale(P, A, _)
185
+ engine.addAggregateRule(
186
+ 'total_by_product',
187
+ ['Product', 'Total'],
188
+ { predicate: 'sale', args: ['Product', 'Amount', '_'] },
189
+ { function: 'sum', variable: 'Amount', output: 'Total' },
190
+ ['Product'] // group by
191
+ );
192
+
193
+ // Rule: avg_by_region(R, avg(A)) :- sale(_, A, R)
194
+ engine.addAggregateRule(
195
+ 'avg_by_region',
196
+ ['Region', 'Average'],
197
+ { predicate: 'sale', args: ['_', 'Amount', 'Region'] },
198
+ { function: 'avg', variable: 'Amount', output: 'Average' },
199
+ ['Region']
200
+ );
201
+
202
+ console.log('Aggregate rules added:');
203
+ console.log(' total_by_product(P, sum(A)) :- sale(P, A, _)');
204
+ console.log(' avg_by_region(R, avg(A)) :- sale(_, A, R)\n');
205
+
206
+ engine.evaluate();
207
+
208
+ // Query totals by product
209
+ const totals = engine.query('total_by_product', ['?product', '?total']);
210
+ console.log('Total sales by product:');
211
+ for (const result of totals) {
212
+ console.log(` ${result.product}: $${result.total}`);
213
+ }
214
+
215
+ // Query averages by region
216
+ const avgs = engine.query('avg_by_region', ['?region', '?avg']);
217
+ console.log('\nAverage sale by region:');
218
+ for (const result of avgs) {
219
+ console.log(` ${result.region}: $${result.avg}`);
220
+ }
221
+ console.log();
222
+ }
223
+
224
+ // =============================================================================
225
+ // Example 5: RDF Graph Integration
226
+ // =============================================================================
227
+
228
+ async function rdfIntegrationExample() {
229
+ console.log('=== RDF Graph Integration ===\n');
230
+
231
+ // Create RDF graph
232
+ const db = new GraphDb('http://example.org/company');
233
+
234
+ // Load company data
235
+ const ttl = `
236
+ @prefix ex: <http://example.org/> .
237
+ @prefix foaf: <http://xmlns.com/foaf/0.1/> .
238
+ @prefix org: <http://www.w3.org/ns/org#> .
239
+
240
+ ex:alice a foaf:Person ;
241
+ foaf:name "Alice" ;
242
+ org:memberOf ex:engineering .
243
+
244
+ ex:bob a foaf:Person ;
245
+ foaf:name "Bob" ;
246
+ org:memberOf ex:engineering ;
247
+ org:reportsTo ex:alice .
248
+
249
+ ex:carol a foaf:Person ;
250
+ foaf:name "Carol" ;
251
+ org:memberOf ex:sales .
252
+
253
+ ex:engineering a org:OrganizationalUnit ;
254
+ org:name "Engineering Department" .
255
+
256
+ ex:sales a org:OrganizationalUnit ;
257
+ org:name "Sales Department" .
258
+ `;
259
+
260
+ db.loadTtl(ttl, null);
261
+ console.log('RDF graph loaded with company data\n');
262
+
263
+ // Create Datalog engine from RDF
264
+ const engine = db.toDatalog();
265
+
266
+ // The RDF triples become facts:
267
+ // triple(subject, predicate, object)
268
+ console.log('RDF triples converted to Datalog facts\n');
269
+
270
+ // Add reasoning rules
271
+ // colleague(X, Y) :- memberOf(X, Dept), memberOf(Y, Dept), X != Y
272
+ engine.addRule(
273
+ 'colleague',
274
+ ['X', 'Y'],
275
+ [
276
+ { predicate: 'triple', args: ['X', 'http://www.w3.org/ns/org#memberOf', 'Dept'] },
277
+ { predicate: 'triple', args: ['Y', 'http://www.w3.org/ns/org#memberOf', 'Dept'] }
278
+ ],
279
+ { filter: 'X != Y' }
280
+ );
281
+
282
+ // manager(X, Y) :- reportsTo(Y, X)
283
+ engine.addRule(
284
+ 'manager',
285
+ ['Manager', 'Employee'],
286
+ [
287
+ { predicate: 'triple', args: ['Employee', 'http://www.w3.org/ns/org#reportsTo', 'Manager'] }
288
+ ]
289
+ );
290
+
291
+ console.log('Reasoning rules added:');
292
+ console.log(' colleague(X, Y) :- memberOf(X, Dept), memberOf(Y, Dept), X != Y');
293
+ console.log(' manager(M, E) :- reportsTo(E, M)\n');
294
+
295
+ engine.evaluate();
296
+
297
+ // Query colleagues
298
+ const colleagues = engine.query('colleague', ['?person1', '?person2']);
299
+ console.log('Colleagues (same department):');
300
+ for (const result of colleagues) {
301
+ console.log(` ${result.person1.split('/').pop()} and ${result.person2.split('/').pop()}`);
302
+ }
303
+
304
+ // Query managers
305
+ const managers = engine.query('manager', ['?manager', '?employee']);
306
+ console.log('\nManagement relationships:');
307
+ for (const result of managers) {
308
+ console.log(` ${result.manager.split('/').pop()} manages ${result.employee.split('/').pop()}`);
309
+ }
310
+ console.log();
311
+ }
312
+
313
+ // =============================================================================
314
+ // Example 6: Access Control Rules (Practical Application)
315
+ // =============================================================================
316
+
317
+ async function accessControlExample() {
318
+ console.log('=== Access Control Rules ===\n');
319
+
320
+ const engine = new DatalogEngine();
321
+
322
+ // User roles
323
+ engine.addFact('role', ['alice', 'admin']);
324
+ engine.addFact('role', ['bob', 'developer']);
325
+ engine.addFact('role', ['carol', 'developer']);
326
+ engine.addFact('role', ['david', 'viewer']);
327
+
328
+ // Resource permissions by role
329
+ engine.addFact('permission', ['admin', 'read']);
330
+ engine.addFact('permission', ['admin', 'write']);
331
+ engine.addFact('permission', ['admin', 'delete']);
332
+ engine.addFact('permission', ['developer', 'read']);
333
+ engine.addFact('permission', ['developer', 'write']);
334
+ engine.addFact('permission', ['viewer', 'read']);
335
+
336
+ // Resource ownership
337
+ engine.addFact('owns', ['alice', 'resource1']);
338
+ engine.addFact('owns', ['bob', 'resource2']);
339
+
340
+ console.log('Access control facts loaded\n');
341
+
342
+ // Rule: can_access(User, Resource, Action) :- role(User, Role), permission(Role, Action)
343
+ engine.addRule(
344
+ 'can_access',
345
+ ['User', 'Resource', 'Action'],
346
+ [
347
+ { predicate: 'role', args: ['User', 'Role'] },
348
+ { predicate: 'permission', args: ['Role', 'Action'] }
349
+ ]
350
+ );
351
+
352
+ // Rule: owner_override(User, Resource, delete) :- owns(User, Resource)
353
+ engine.addRule(
354
+ 'can_access',
355
+ ['User', 'Resource', 'delete'],
356
+ [
357
+ { predicate: 'owns', args: ['User', 'Resource'] }
358
+ ]
359
+ );
360
+
361
+ console.log('Access control rules:');
362
+ console.log(' can_access(U, R, A) :- role(U, Role), permission(Role, A)');
363
+ console.log(' can_access(U, R, delete) :- owns(U, R) % owner override\n');
364
+
365
+ engine.evaluate();
366
+
367
+ // Check specific access
368
+ const checkAccess = (user: string, action: string) => {
369
+ const results = engine.query('can_access', [user, '?resource', action]);
370
+ return results.length > 0;
371
+ };
372
+
373
+ const checks = [
374
+ ['alice', 'delete'],
375
+ ['bob', 'write'],
376
+ ['bob', 'delete'],
377
+ ['david', 'read'],
378
+ ['david', 'write']
379
+ ];
380
+
381
+ console.log('Access checks:');
382
+ for (const [user, action] of checks) {
383
+ const allowed = checkAccess(user, action);
384
+ const icon = allowed ? '✓' : '✗';
385
+ console.log(` ${icon} ${user} can ${action}: ${allowed}`);
386
+ }
387
+
388
+ // Bob can delete resource2 because he owns it
389
+ const bobCanDelete = engine.query('can_access', ['bob', 'resource2', 'delete']);
390
+ console.log(`\n Bob can delete resource2 (owner): ${bobCanDelete.length > 0}`);
391
+ console.log();
392
+ }
393
+
394
+ // =============================================================================
395
+ // Example 7: Incremental Updates
396
+ // =============================================================================
397
+
398
+ async function incrementalExample() {
399
+ console.log('=== Incremental Updates ===\n');
400
+
401
+ const engine = new DatalogEngine();
402
+
403
+ // Initial facts
404
+ engine.addFact('friend', ['alice', 'bob']);
405
+ engine.addFact('friend', ['bob', 'carol']);
406
+
407
+ // Bidirectional friendship rule
408
+ engine.addRule(
409
+ 'friends_with',
410
+ ['X', 'Y'],
411
+ [{ predicate: 'friend', args: ['X', 'Y'] }]
412
+ );
413
+ engine.addRule(
414
+ 'friends_with',
415
+ ['X', 'Y'],
416
+ [{ predicate: 'friend', args: ['Y', 'X'] }]
417
+ );
418
+
419
+ // Transitive friends-of-friends
420
+ engine.addRule(
421
+ 'fof',
422
+ ['X', 'Z'],
423
+ [
424
+ { predicate: 'friends_with', args: ['X', 'Y'] },
425
+ { predicate: 'friends_with', args: ['Y', 'Z'] }
426
+ ],
427
+ { filter: 'X != Z' }
428
+ );
429
+
430
+ engine.evaluate();
431
+
432
+ console.log('Initial state:');
433
+ let fofs = engine.query('fof', ['alice', '?friend']);
434
+ console.log(` Alice's friends-of-friends: ${fofs.map(r => r.friend).join(', ')}\n`);
435
+
436
+ // Incremental update: add new friendship
437
+ console.log('Adding new fact: friend(carol, david)');
438
+ engine.addFact('friend', ['carol', 'david']);
439
+
440
+ // Incremental evaluation (only processes new facts)
441
+ engine.evaluateIncremental();
442
+
443
+ console.log('\nAfter incremental update:');
444
+ fofs = engine.query('fof', ['alice', '?friend']);
445
+ console.log(` Alice's friends-of-friends: ${fofs.map(r => r.friend).join(', ')}`);
446
+
447
+ console.log('\nIncremental evaluation is much faster for dynamic graphs!');
448
+ console.log();
449
+ }
450
+
451
+ // =============================================================================
452
+ // Run All Examples
453
+ // =============================================================================
454
+
455
+ async function main() {
456
+ console.log('========================================');
457
+ console.log(' Datalog Reasoning Examples');
458
+ console.log('========================================\n');
459
+
460
+ try {
461
+ await basicDatalogExample();
462
+ await recursiveDatalogExample();
463
+ await negationDatalogExample();
464
+ await aggregateDatalogExample();
465
+ await rdfIntegrationExample();
466
+ await accessControlExample();
467
+ await incrementalExample();
468
+
469
+ console.log('========================================');
470
+ console.log(' All examples completed successfully!');
471
+ console.log('========================================');
472
+ } catch (error) {
473
+ console.error('Error running examples:', error);
474
+ process.exit(1);
475
+ }
476
+ }
477
+
478
+ main();