@suss/datalog 0.33.0 → 0.34.0
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.
- package/README.md +17 -17
- package/dist/index.cjs +515 -101
- package/dist/index.d.cts +121 -61
- package/dist/index.d.ts +121 -61
- package/dist/index.js +514 -101
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -71,28 +71,35 @@ interface Relation {
|
|
|
71
71
|
* lookup and buy nothing.
|
|
72
72
|
*/
|
|
73
73
|
columns: (Map<Atom, Tuple[]> | undefined)[];
|
|
74
|
+
/**
|
|
75
|
+
* Per set of columns, the tuples under each combination of values at
|
|
76
|
+
* those columns, keyed by a bitmask of the column positions. Built and
|
|
77
|
+
* kept up to date the same way as `columns`.
|
|
78
|
+
*/
|
|
79
|
+
combined: Map<number, CombinedIndex>;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* A tree of maps with one level per column in `columns`, lowest position
|
|
83
|
+
* first, and the matching tuples at the leaves.
|
|
84
|
+
*/
|
|
85
|
+
interface CombinedIndex {
|
|
86
|
+
columns: readonly number[];
|
|
87
|
+
root: Map<Atom, unknown>;
|
|
74
88
|
}
|
|
75
89
|
|
|
76
90
|
/**
|
|
77
91
|
* Confidence levels as a tag algebra: how sure the run is of each
|
|
78
92
|
* derived fact, given how sure it is of the facts and rules behind it.
|
|
79
93
|
*
|
|
80
|
-
* A
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* that is the intended reading rather than a loss of information.
|
|
85
|
-
*
|
|
86
|
-
* A rule can be a heuristic itself, and then its conclusions should
|
|
87
|
-
* not outrank it however sure its premises are. `confidenceWith`
|
|
88
|
-
* takes a level per rule and folds it into the minimum. The plain
|
|
89
|
-
* `confidence` algebra treats every rule as exact.
|
|
94
|
+
* A rule firing takes the minimum level across its body, and a fact
|
|
95
|
+
* reached two ways keeps the better level. Both are idempotent, so ten
|
|
96
|
+
* medium steps come out medium. A rule that is itself a heuristic caps
|
|
97
|
+
* its conclusions through `confidenceWith`.
|
|
90
98
|
*
|
|
91
|
-
* An asserted fact without a tag counts as `"high"
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
* database as computed, not a guess about the world.
|
|
99
|
+
* An asserted fact without a tag counts as `"high"`; assert it with a
|
|
100
|
+
* level, as in `db.add(relation, tuple, "medium")`, to say less. A
|
|
101
|
+
* matched negation also counts as `"high"`, because negation is exact
|
|
102
|
+
* over the database as computed. DESIGN.md has a worked example.
|
|
96
103
|
*/
|
|
97
104
|
|
|
98
105
|
/** How sure the run is of one fact. The order is high > medium > low. */
|
|
@@ -106,6 +113,21 @@ declare function confidenceWith(levelOf: (rule: Rule) => ConfidenceLevel | undef
|
|
|
106
113
|
/** The confidence algebra with every rule counted as exact. */
|
|
107
114
|
declare const confidence: TagAlgebra<ConfidenceLevel>;
|
|
108
115
|
|
|
116
|
+
/**
|
|
117
|
+
* The magic sets rewrite, which derives only what somebody asked for.
|
|
118
|
+
* Each derived relation gets a companion relation recording which of its
|
|
119
|
+
* rows something is waiting on. That companion becomes the first literal
|
|
120
|
+
* of each rule, and demand travels down each body the way the join binds
|
|
121
|
+
* variables. The caller lists the relations that must come out complete;
|
|
122
|
+
* every other derived relation is filled in only as far as those need.
|
|
123
|
+
*
|
|
124
|
+
* The rewritten program is positive, so it keeps the semi-naive resume
|
|
125
|
+
* that a store evaluating after every wave of facts depends on. Demand
|
|
126
|
+
* is an ordinary fact, so a new question is one more fact arriving, and
|
|
127
|
+
* `clearRelations` can take the question back once its answer is read.
|
|
128
|
+
* DESIGN.md has the worked examples and the costs.
|
|
129
|
+
*/
|
|
130
|
+
|
|
109
131
|
/** One demand relation the rewrite created, and what its rows say. */
|
|
110
132
|
interface Demand {
|
|
111
133
|
/** The relation whose rows are being waited on. */
|
|
@@ -155,6 +177,17 @@ interface OnDemandRules {
|
|
|
155
177
|
*/
|
|
156
178
|
declare function deriveOnDemand(rules: Rule[], complete: readonly string[]): OnDemandRules;
|
|
157
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Per-rule timing and row counts for an evaluation. A rule's cost is
|
|
182
|
+
* spread across joins no caller can see, and a CPU profile stops at
|
|
183
|
+
* `unify` and `lookup` without saying which rule asked for the work, so
|
|
184
|
+
* the engine records these itself.
|
|
185
|
+
*
|
|
186
|
+
* Nothing here runs unless a caller wraps its evaluation in
|
|
187
|
+
* `profileEvaluation`. The engine checks whether a scope is open once
|
|
188
|
+
* per rule attempt and once per round, never per tuple, and allocates
|
|
189
|
+
* only inside that branch, so an unprofiled run pays one check per rule.
|
|
190
|
+
*/
|
|
158
191
|
/** What one rule cost, summed over every attempt across every round. */
|
|
159
192
|
interface RuleCost {
|
|
160
193
|
/** The relation this rule derives. */
|
|
@@ -181,8 +214,8 @@ interface AbandonedQuestion {
|
|
|
181
214
|
examined: number;
|
|
182
215
|
}
|
|
183
216
|
/**
|
|
184
|
-
* How a caller that asks one question at a time got on. `skipped`
|
|
185
|
-
* the
|
|
217
|
+
* How a caller that asks one question at a time got on. `skipped` counts
|
|
218
|
+
* the questions it never asked because the run's row budget was spent.
|
|
186
219
|
*/
|
|
187
220
|
interface QuestionTally {
|
|
188
221
|
asked: number;
|
|
@@ -214,11 +247,20 @@ interface RuleSetCost {
|
|
|
214
247
|
rounds: number;
|
|
215
248
|
rules: RuleCost[];
|
|
216
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* The engine entry points a profile times whole. `evaluate` includes the
|
|
252
|
+
* rules, so what it has past `datalogMs` is the engine's own work: the
|
|
253
|
+
* round loop and the bookkeeping around it. `clear` is `clearRelations`
|
|
254
|
+
* emptying relations between questions.
|
|
255
|
+
*/
|
|
256
|
+
type EnginePart = "evaluate" | "clear";
|
|
217
257
|
interface EvaluationProfile {
|
|
218
258
|
/** Wall time for everything the profiled scope did, datalog or not. */
|
|
219
259
|
wallMs: number;
|
|
220
|
-
/** Wall time inside rule evaluation,
|
|
260
|
+
/** Wall time inside rule evaluation, summed over the rules. */
|
|
221
261
|
datalogMs: number;
|
|
262
|
+
/** Wall time inside each of the engine's entry points. */
|
|
263
|
+
engineMs: Record<EnginePart, number>;
|
|
222
264
|
/** Semi-naive rounds, summed over strata. The seed round counts as one. */
|
|
223
265
|
rounds: number;
|
|
224
266
|
/**
|
|
@@ -254,8 +296,8 @@ interface EvaluationProfile {
|
|
|
254
296
|
*/
|
|
255
297
|
declare function chargeAbandoned(question: string, examined: number): void;
|
|
256
298
|
/**
|
|
257
|
-
*
|
|
258
|
-
*
|
|
299
|
+
* Count one question from a caller. Pass `outcome` when the question was
|
|
300
|
+
* abandoned part way or skipped, and leave it out when it was answered.
|
|
259
301
|
*/
|
|
260
302
|
declare function chargeQuestion(outcome?: QuestionOutcome): void;
|
|
261
303
|
/**
|
|
@@ -281,6 +323,19 @@ declare function profileEvaluationAsync<T>(fn: () => Promise<T>): Promise<{
|
|
|
281
323
|
/** Render a profile as the table a human reads in a terminal. */
|
|
282
324
|
declare function formatProfile(profile: EvaluationProfile): string;
|
|
283
325
|
|
|
326
|
+
/**
|
|
327
|
+
* Turns several values into one string key, reversibly.
|
|
328
|
+
*
|
|
329
|
+
* A tuple value can be any string a caller interns, so no separator
|
|
330
|
+
* character is safe to join on. Pick one, and the day a module
|
|
331
|
+
* specifier or an identifier contains it, two different tuples produce
|
|
332
|
+
* the same key and a lookup returns the wrong facts. Each part is
|
|
333
|
+
* written with its own length instead, which no value can forge.
|
|
334
|
+
*
|
|
335
|
+
* Any caller that joins values into a map key has the same problem, so
|
|
336
|
+
* the encoding is exported for them to share.
|
|
337
|
+
*/
|
|
338
|
+
|
|
284
339
|
declare const tupleKey: (parts: readonly Atom[]) => string;
|
|
285
340
|
/**
|
|
286
341
|
* Throws on a string `tupleKey` did not produce. A key that does not
|
|
@@ -296,11 +351,10 @@ declare function tupleKeyParts(key: string): string[];
|
|
|
296
351
|
* Evaluate under the `witnesses` algebra and every derived fact stores
|
|
297
352
|
* a `Witness`: the rule that fired and one entry per body literal. The
|
|
298
353
|
* merge keeps whatever is already stored, so a fact derived nine ways
|
|
299
|
-
* keeps its first witness and the fixpoint
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
* the
|
|
303
|
-
* Soufflé's provenance mode does, and never re-runs a rule.
|
|
354
|
+
* keeps its first witness and the fixpoint runs the same as untagged.
|
|
355
|
+
* The proof that comes back is therefore valid but may not be the
|
|
356
|
+
* shortest. `proofOf` walks the stored witnesses into a tree when asked,
|
|
357
|
+
* the way Soufflé's provenance mode does, and never re-runs a rule.
|
|
304
358
|
*/
|
|
305
359
|
|
|
306
360
|
/**
|
|
@@ -356,11 +410,11 @@ interface ProofOptions {
|
|
|
356
410
|
* Rebuild the proof of one fact from stored witnesses, without
|
|
357
411
|
* re-running any evaluation. A `fact` leaf has no witness: the caller
|
|
358
412
|
* asserted it, or it was derived without the `witnesses` algebra. An
|
|
359
|
-
* `absence` leaf is a tuple missing from the database
|
|
360
|
-
* when the asked
|
|
361
|
-
* where a negated literal relied on it being missing,
|
|
362
|
-
* evaluation saw it
|
|
363
|
-
*
|
|
413
|
+
* `absence` leaf is a tuple missing from the database. It appears at the
|
|
414
|
+
* root when the fact asked about was never derived, and under a
|
|
415
|
+
* derivation where a negated literal relied on it being missing, as
|
|
416
|
+
* evaluation saw it at the time. `truncated` marks where the depth cap
|
|
417
|
+
* or the cycle guard stopped the walk.
|
|
364
418
|
*/
|
|
365
419
|
declare function proofOf(db: Database, relation: string, tuple: Tuple, options?: ProofOptions): Proof;
|
|
366
420
|
|
|
@@ -431,8 +485,8 @@ declare class Database {
|
|
|
431
485
|
private relation;
|
|
432
486
|
/**
|
|
433
487
|
* The key this relation gives a tuple, whether or not the fact is
|
|
434
|
-
* present. Evaluation keys its ledger of derived facts by it
|
|
435
|
-
*
|
|
488
|
+
* present. Evaluation keys its ledger of derived facts by it. Nothing
|
|
489
|
+
* outside this module accepts a key.
|
|
436
490
|
*/
|
|
437
491
|
keyFor(relationName: string, tuple: Tuple): FactKey;
|
|
438
492
|
/**
|
|
@@ -467,6 +521,12 @@ declare class Database {
|
|
|
467
521
|
* time. Callers rarely need it.
|
|
468
522
|
*/
|
|
469
523
|
retract(relationName: string, tuples: Iterable<Tuple>): number;
|
|
524
|
+
/**
|
|
525
|
+
* Remove every fact in a relation, and return how many there were. The
|
|
526
|
+
* database ends up as `retract` leaves it when handed every tuple, but
|
|
527
|
+
* nothing looks a tuple up to remove it.
|
|
528
|
+
*/
|
|
529
|
+
retractAll(relationName: string): number;
|
|
470
530
|
size(relationName: string): number;
|
|
471
531
|
relationNames(): string[];
|
|
472
532
|
}
|
|
@@ -524,25 +584,32 @@ interface Derivation {
|
|
|
524
584
|
rule: Rule;
|
|
525
585
|
body: readonly BodyMatch[];
|
|
526
586
|
}
|
|
587
|
+
/**
|
|
588
|
+
* The rules that can derive any of `relations`: the ones with such a
|
|
589
|
+
* head, and every rule behind a relation their bodies read, negated
|
|
590
|
+
* literals included. Evaluating only these derives the same tuples for
|
|
591
|
+
* `relations` as evaluating all of `rules`, and skips the relations
|
|
592
|
+
* nothing asked about.
|
|
593
|
+
*/
|
|
594
|
+
declare function rulesDeriving(rules: readonly Rule[], relations: readonly string[]): Rule[];
|
|
527
595
|
/**
|
|
528
596
|
* Assign each derived relation a stratum such that positive
|
|
529
597
|
* dependencies never decrease the stratum and negative dependencies
|
|
530
|
-
* strictly increase it
|
|
598
|
+
* strictly increase it, iterating to a fixpoint. A stratum above the
|
|
531
599
|
* relation count means there is a negation cycle, which cannot be
|
|
532
600
|
* stratified, so this throws.
|
|
533
601
|
*/
|
|
534
602
|
declare function stratify(rules: Rule[]): Rule[][];
|
|
535
603
|
/**
|
|
536
604
|
* How many rows the joins of one evaluation may read, and how many they
|
|
537
|
-
* have read so far.
|
|
538
|
-
*
|
|
539
|
-
*
|
|
605
|
+
* have read so far. The budget counts rows read rather than new tuples.
|
|
606
|
+
* A question that runs away keeps finding conclusions it already has, so
|
|
607
|
+
* the tuple count stays flat while the rows read keep growing.
|
|
540
608
|
*
|
|
541
|
-
* The caller builds one with `rowBudget` and reads `examined`
|
|
542
|
-
*
|
|
543
|
-
*
|
|
544
|
-
*
|
|
545
|
-
* compare either way.
|
|
609
|
+
* The caller builds one with `rowBudget` and reads `examined` after the
|
|
610
|
+
* call to learn what the question cost, whether or not it settled. An
|
|
611
|
+
* evaluation with no budget gets a limit of Infinity, so the hot loop
|
|
612
|
+
* does the same increment and the same compare either way.
|
|
546
613
|
*/
|
|
547
614
|
interface RowBudget {
|
|
548
615
|
examined: number;
|
|
@@ -576,31 +643,24 @@ declare class BudgetExhausted extends Error {
|
|
|
576
643
|
* database: a resumed run tags only what the new facts reach.
|
|
577
644
|
*
|
|
578
645
|
* With a `budget`, evaluation gives up once its joins have read that
|
|
579
|
-
* many rows and throws `BudgetExhausted`. Either way
|
|
580
|
-
* afterwards what the evaluation cost.
|
|
646
|
+
* many rows and throws `BudgetExhausted`. Either way, `budget.examined`
|
|
647
|
+
* afterwards gives what the evaluation cost.
|
|
581
648
|
*/
|
|
582
649
|
declare function evaluate<Tag = never>(db: Database, rules: Rule[], algebra?: TagAlgebra<Tag>, budget?: RowBudget): Database;
|
|
583
650
|
/**
|
|
584
|
-
* Empty these relations
|
|
585
|
-
* it got to.
|
|
586
|
-
*
|
|
587
|
-
* `retract` cannot do this. A fact leaving the database can take away a
|
|
588
|
-
* conclusion drawn anywhere, so it sends the next run back to the base
|
|
589
|
-
* facts. A caller here is saying something stronger than "these facts
|
|
590
|
-
* are gone": nothing outside `relations` was derived from them, so what
|
|
591
|
-
* is left is already the fixpoint and the next run has only the facts
|
|
592
|
-
* that arrive after this to work through.
|
|
651
|
+
* Empty these relations and let `rules` resume from its last fixpoint.
|
|
593
652
|
*
|
|
594
|
-
*
|
|
595
|
-
*
|
|
596
|
-
*
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
* contents, which are the answers a caller has already read.
|
|
653
|
+
* After `retract`, the next run starts again from the base facts, since
|
|
654
|
+
* a fact leaving the database can take away a conclusion drawn anywhere.
|
|
655
|
+
* A caller of this function promises more: nothing outside `relations`
|
|
656
|
+
* was derived from these facts. What is left is already the fixpoint,
|
|
657
|
+
* and the next run works through only the facts added after this.
|
|
600
658
|
*
|
|
601
|
-
*
|
|
602
|
-
*
|
|
659
|
+
* A demand-driven caller clears `deriveOnDemand`'s demand-driven
|
|
660
|
+
* relations between questions, and the complete ones keep the answers
|
|
661
|
+
* already read (DESIGN.md has an example). Any other rule set over the
|
|
662
|
+
* same database starts over, since it may have derived from these.
|
|
603
663
|
*/
|
|
604
664
|
declare function clearRelations(db: Database, rules: Rule[], relations: readonly string[]): void;
|
|
605
665
|
|
|
606
|
-
export { type AbandonedQuestion, type AddOutcome, type Atom, type BodyMatch, BudgetExhausted, type ConfidenceLevel, Database, type Demand, type Derivation, type EvaluationProfile, type Literal, type OnDemandRules, type Proof, type ProofDerived, type ProofOptions, type QuestionOutcome, type QuestionTally, type RowBudget, type Rule, type RuleCost, type TagAlgebra, type Term, type Tuple, Witness, type WitnessTag, chargeAbandoned, chargeQuestion, clearRelations, confidence, confidenceWith, constant, deriveOnDemand, evaluate, formatProfile, lit, notLit, profileEvaluation, profileEvaluationAsync, proofOf, rowBudget, rule, ruleLabel, stratify, tupleKey, tupleKeyParts, variable, witnesses };
|
|
666
|
+
export { type AbandonedQuestion, type AddOutcome, type Atom, type BodyMatch, BudgetExhausted, type ConfidenceLevel, Database, type Demand, type Derivation, type EvaluationProfile, type Literal, type OnDemandRules, type Proof, type ProofDerived, type ProofOptions, type QuestionOutcome, type QuestionTally, type RowBudget, type Rule, type RuleCost, type TagAlgebra, type Term, type Tuple, Witness, type WitnessTag, chargeAbandoned, chargeQuestion, clearRelations, confidence, confidenceWith, constant, deriveOnDemand, evaluate, formatProfile, lit, notLit, profileEvaluation, profileEvaluationAsync, proofOf, rowBudget, rule, ruleLabel, rulesDeriving, stratify, tupleKey, tupleKeyParts, variable, witnesses };
|
package/dist/index.d.ts
CHANGED
|
@@ -71,28 +71,35 @@ interface Relation {
|
|
|
71
71
|
* lookup and buy nothing.
|
|
72
72
|
*/
|
|
73
73
|
columns: (Map<Atom, Tuple[]> | undefined)[];
|
|
74
|
+
/**
|
|
75
|
+
* Per set of columns, the tuples under each combination of values at
|
|
76
|
+
* those columns, keyed by a bitmask of the column positions. Built and
|
|
77
|
+
* kept up to date the same way as `columns`.
|
|
78
|
+
*/
|
|
79
|
+
combined: Map<number, CombinedIndex>;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* A tree of maps with one level per column in `columns`, lowest position
|
|
83
|
+
* first, and the matching tuples at the leaves.
|
|
84
|
+
*/
|
|
85
|
+
interface CombinedIndex {
|
|
86
|
+
columns: readonly number[];
|
|
87
|
+
root: Map<Atom, unknown>;
|
|
74
88
|
}
|
|
75
89
|
|
|
76
90
|
/**
|
|
77
91
|
* Confidence levels as a tag algebra: how sure the run is of each
|
|
78
92
|
* derived fact, given how sure it is of the facts and rules behind it.
|
|
79
93
|
*
|
|
80
|
-
* A
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* that is the intended reading rather than a loss of information.
|
|
85
|
-
*
|
|
86
|
-
* A rule can be a heuristic itself, and then its conclusions should
|
|
87
|
-
* not outrank it however sure its premises are. `confidenceWith`
|
|
88
|
-
* takes a level per rule and folds it into the minimum. The plain
|
|
89
|
-
* `confidence` algebra treats every rule as exact.
|
|
94
|
+
* A rule firing takes the minimum level across its body, and a fact
|
|
95
|
+
* reached two ways keeps the better level. Both are idempotent, so ten
|
|
96
|
+
* medium steps come out medium. A rule that is itself a heuristic caps
|
|
97
|
+
* its conclusions through `confidenceWith`.
|
|
90
98
|
*
|
|
91
|
-
* An asserted fact without a tag counts as `"high"
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
* database as computed, not a guess about the world.
|
|
99
|
+
* An asserted fact without a tag counts as `"high"`; assert it with a
|
|
100
|
+
* level, as in `db.add(relation, tuple, "medium")`, to say less. A
|
|
101
|
+
* matched negation also counts as `"high"`, because negation is exact
|
|
102
|
+
* over the database as computed. DESIGN.md has a worked example.
|
|
96
103
|
*/
|
|
97
104
|
|
|
98
105
|
/** How sure the run is of one fact. The order is high > medium > low. */
|
|
@@ -106,6 +113,21 @@ declare function confidenceWith(levelOf: (rule: Rule) => ConfidenceLevel | undef
|
|
|
106
113
|
/** The confidence algebra with every rule counted as exact. */
|
|
107
114
|
declare const confidence: TagAlgebra<ConfidenceLevel>;
|
|
108
115
|
|
|
116
|
+
/**
|
|
117
|
+
* The magic sets rewrite, which derives only what somebody asked for.
|
|
118
|
+
* Each derived relation gets a companion relation recording which of its
|
|
119
|
+
* rows something is waiting on. That companion becomes the first literal
|
|
120
|
+
* of each rule, and demand travels down each body the way the join binds
|
|
121
|
+
* variables. The caller lists the relations that must come out complete;
|
|
122
|
+
* every other derived relation is filled in only as far as those need.
|
|
123
|
+
*
|
|
124
|
+
* The rewritten program is positive, so it keeps the semi-naive resume
|
|
125
|
+
* that a store evaluating after every wave of facts depends on. Demand
|
|
126
|
+
* is an ordinary fact, so a new question is one more fact arriving, and
|
|
127
|
+
* `clearRelations` can take the question back once its answer is read.
|
|
128
|
+
* DESIGN.md has the worked examples and the costs.
|
|
129
|
+
*/
|
|
130
|
+
|
|
109
131
|
/** One demand relation the rewrite created, and what its rows say. */
|
|
110
132
|
interface Demand {
|
|
111
133
|
/** The relation whose rows are being waited on. */
|
|
@@ -155,6 +177,17 @@ interface OnDemandRules {
|
|
|
155
177
|
*/
|
|
156
178
|
declare function deriveOnDemand(rules: Rule[], complete: readonly string[]): OnDemandRules;
|
|
157
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Per-rule timing and row counts for an evaluation. A rule's cost is
|
|
182
|
+
* spread across joins no caller can see, and a CPU profile stops at
|
|
183
|
+
* `unify` and `lookup` without saying which rule asked for the work, so
|
|
184
|
+
* the engine records these itself.
|
|
185
|
+
*
|
|
186
|
+
* Nothing here runs unless a caller wraps its evaluation in
|
|
187
|
+
* `profileEvaluation`. The engine checks whether a scope is open once
|
|
188
|
+
* per rule attempt and once per round, never per tuple, and allocates
|
|
189
|
+
* only inside that branch, so an unprofiled run pays one check per rule.
|
|
190
|
+
*/
|
|
158
191
|
/** What one rule cost, summed over every attempt across every round. */
|
|
159
192
|
interface RuleCost {
|
|
160
193
|
/** The relation this rule derives. */
|
|
@@ -181,8 +214,8 @@ interface AbandonedQuestion {
|
|
|
181
214
|
examined: number;
|
|
182
215
|
}
|
|
183
216
|
/**
|
|
184
|
-
* How a caller that asks one question at a time got on. `skipped`
|
|
185
|
-
* the
|
|
217
|
+
* How a caller that asks one question at a time got on. `skipped` counts
|
|
218
|
+
* the questions it never asked because the run's row budget was spent.
|
|
186
219
|
*/
|
|
187
220
|
interface QuestionTally {
|
|
188
221
|
asked: number;
|
|
@@ -214,11 +247,20 @@ interface RuleSetCost {
|
|
|
214
247
|
rounds: number;
|
|
215
248
|
rules: RuleCost[];
|
|
216
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* The engine entry points a profile times whole. `evaluate` includes the
|
|
252
|
+
* rules, so what it has past `datalogMs` is the engine's own work: the
|
|
253
|
+
* round loop and the bookkeeping around it. `clear` is `clearRelations`
|
|
254
|
+
* emptying relations between questions.
|
|
255
|
+
*/
|
|
256
|
+
type EnginePart = "evaluate" | "clear";
|
|
217
257
|
interface EvaluationProfile {
|
|
218
258
|
/** Wall time for everything the profiled scope did, datalog or not. */
|
|
219
259
|
wallMs: number;
|
|
220
|
-
/** Wall time inside rule evaluation,
|
|
260
|
+
/** Wall time inside rule evaluation, summed over the rules. */
|
|
221
261
|
datalogMs: number;
|
|
262
|
+
/** Wall time inside each of the engine's entry points. */
|
|
263
|
+
engineMs: Record<EnginePart, number>;
|
|
222
264
|
/** Semi-naive rounds, summed over strata. The seed round counts as one. */
|
|
223
265
|
rounds: number;
|
|
224
266
|
/**
|
|
@@ -254,8 +296,8 @@ interface EvaluationProfile {
|
|
|
254
296
|
*/
|
|
255
297
|
declare function chargeAbandoned(question: string, examined: number): void;
|
|
256
298
|
/**
|
|
257
|
-
*
|
|
258
|
-
*
|
|
299
|
+
* Count one question from a caller. Pass `outcome` when the question was
|
|
300
|
+
* abandoned part way or skipped, and leave it out when it was answered.
|
|
259
301
|
*/
|
|
260
302
|
declare function chargeQuestion(outcome?: QuestionOutcome): void;
|
|
261
303
|
/**
|
|
@@ -281,6 +323,19 @@ declare function profileEvaluationAsync<T>(fn: () => Promise<T>): Promise<{
|
|
|
281
323
|
/** Render a profile as the table a human reads in a terminal. */
|
|
282
324
|
declare function formatProfile(profile: EvaluationProfile): string;
|
|
283
325
|
|
|
326
|
+
/**
|
|
327
|
+
* Turns several values into one string key, reversibly.
|
|
328
|
+
*
|
|
329
|
+
* A tuple value can be any string a caller interns, so no separator
|
|
330
|
+
* character is safe to join on. Pick one, and the day a module
|
|
331
|
+
* specifier or an identifier contains it, two different tuples produce
|
|
332
|
+
* the same key and a lookup returns the wrong facts. Each part is
|
|
333
|
+
* written with its own length instead, which no value can forge.
|
|
334
|
+
*
|
|
335
|
+
* Any caller that joins values into a map key has the same problem, so
|
|
336
|
+
* the encoding is exported for them to share.
|
|
337
|
+
*/
|
|
338
|
+
|
|
284
339
|
declare const tupleKey: (parts: readonly Atom[]) => string;
|
|
285
340
|
/**
|
|
286
341
|
* Throws on a string `tupleKey` did not produce. A key that does not
|
|
@@ -296,11 +351,10 @@ declare function tupleKeyParts(key: string): string[];
|
|
|
296
351
|
* Evaluate under the `witnesses` algebra and every derived fact stores
|
|
297
352
|
* a `Witness`: the rule that fired and one entry per body literal. The
|
|
298
353
|
* merge keeps whatever is already stored, so a fact derived nine ways
|
|
299
|
-
* keeps its first witness and the fixpoint
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
* the
|
|
303
|
-
* Soufflé's provenance mode does, and never re-runs a rule.
|
|
354
|
+
* keeps its first witness and the fixpoint runs the same as untagged.
|
|
355
|
+
* The proof that comes back is therefore valid but may not be the
|
|
356
|
+
* shortest. `proofOf` walks the stored witnesses into a tree when asked,
|
|
357
|
+
* the way Soufflé's provenance mode does, and never re-runs a rule.
|
|
304
358
|
*/
|
|
305
359
|
|
|
306
360
|
/**
|
|
@@ -356,11 +410,11 @@ interface ProofOptions {
|
|
|
356
410
|
* Rebuild the proof of one fact from stored witnesses, without
|
|
357
411
|
* re-running any evaluation. A `fact` leaf has no witness: the caller
|
|
358
412
|
* asserted it, or it was derived without the `witnesses` algebra. An
|
|
359
|
-
* `absence` leaf is a tuple missing from the database
|
|
360
|
-
* when the asked
|
|
361
|
-
* where a negated literal relied on it being missing,
|
|
362
|
-
* evaluation saw it
|
|
363
|
-
*
|
|
413
|
+
* `absence` leaf is a tuple missing from the database. It appears at the
|
|
414
|
+
* root when the fact asked about was never derived, and under a
|
|
415
|
+
* derivation where a negated literal relied on it being missing, as
|
|
416
|
+
* evaluation saw it at the time. `truncated` marks where the depth cap
|
|
417
|
+
* or the cycle guard stopped the walk.
|
|
364
418
|
*/
|
|
365
419
|
declare function proofOf(db: Database, relation: string, tuple: Tuple, options?: ProofOptions): Proof;
|
|
366
420
|
|
|
@@ -431,8 +485,8 @@ declare class Database {
|
|
|
431
485
|
private relation;
|
|
432
486
|
/**
|
|
433
487
|
* The key this relation gives a tuple, whether or not the fact is
|
|
434
|
-
* present. Evaluation keys its ledger of derived facts by it
|
|
435
|
-
*
|
|
488
|
+
* present. Evaluation keys its ledger of derived facts by it. Nothing
|
|
489
|
+
* outside this module accepts a key.
|
|
436
490
|
*/
|
|
437
491
|
keyFor(relationName: string, tuple: Tuple): FactKey;
|
|
438
492
|
/**
|
|
@@ -467,6 +521,12 @@ declare class Database {
|
|
|
467
521
|
* time. Callers rarely need it.
|
|
468
522
|
*/
|
|
469
523
|
retract(relationName: string, tuples: Iterable<Tuple>): number;
|
|
524
|
+
/**
|
|
525
|
+
* Remove every fact in a relation, and return how many there were. The
|
|
526
|
+
* database ends up as `retract` leaves it when handed every tuple, but
|
|
527
|
+
* nothing looks a tuple up to remove it.
|
|
528
|
+
*/
|
|
529
|
+
retractAll(relationName: string): number;
|
|
470
530
|
size(relationName: string): number;
|
|
471
531
|
relationNames(): string[];
|
|
472
532
|
}
|
|
@@ -524,25 +584,32 @@ interface Derivation {
|
|
|
524
584
|
rule: Rule;
|
|
525
585
|
body: readonly BodyMatch[];
|
|
526
586
|
}
|
|
587
|
+
/**
|
|
588
|
+
* The rules that can derive any of `relations`: the ones with such a
|
|
589
|
+
* head, and every rule behind a relation their bodies read, negated
|
|
590
|
+
* literals included. Evaluating only these derives the same tuples for
|
|
591
|
+
* `relations` as evaluating all of `rules`, and skips the relations
|
|
592
|
+
* nothing asked about.
|
|
593
|
+
*/
|
|
594
|
+
declare function rulesDeriving(rules: readonly Rule[], relations: readonly string[]): Rule[];
|
|
527
595
|
/**
|
|
528
596
|
* Assign each derived relation a stratum such that positive
|
|
529
597
|
* dependencies never decrease the stratum and negative dependencies
|
|
530
|
-
* strictly increase it
|
|
598
|
+
* strictly increase it, iterating to a fixpoint. A stratum above the
|
|
531
599
|
* relation count means there is a negation cycle, which cannot be
|
|
532
600
|
* stratified, so this throws.
|
|
533
601
|
*/
|
|
534
602
|
declare function stratify(rules: Rule[]): Rule[][];
|
|
535
603
|
/**
|
|
536
604
|
* How many rows the joins of one evaluation may read, and how many they
|
|
537
|
-
* have read so far.
|
|
538
|
-
*
|
|
539
|
-
*
|
|
605
|
+
* have read so far. The budget counts rows read rather than new tuples.
|
|
606
|
+
* A question that runs away keeps finding conclusions it already has, so
|
|
607
|
+
* the tuple count stays flat while the rows read keep growing.
|
|
540
608
|
*
|
|
541
|
-
* The caller builds one with `rowBudget` and reads `examined`
|
|
542
|
-
*
|
|
543
|
-
*
|
|
544
|
-
*
|
|
545
|
-
* compare either way.
|
|
609
|
+
* The caller builds one with `rowBudget` and reads `examined` after the
|
|
610
|
+
* call to learn what the question cost, whether or not it settled. An
|
|
611
|
+
* evaluation with no budget gets a limit of Infinity, so the hot loop
|
|
612
|
+
* does the same increment and the same compare either way.
|
|
546
613
|
*/
|
|
547
614
|
interface RowBudget {
|
|
548
615
|
examined: number;
|
|
@@ -576,31 +643,24 @@ declare class BudgetExhausted extends Error {
|
|
|
576
643
|
* database: a resumed run tags only what the new facts reach.
|
|
577
644
|
*
|
|
578
645
|
* With a `budget`, evaluation gives up once its joins have read that
|
|
579
|
-
* many rows and throws `BudgetExhausted`. Either way
|
|
580
|
-
* afterwards what the evaluation cost.
|
|
646
|
+
* many rows and throws `BudgetExhausted`. Either way, `budget.examined`
|
|
647
|
+
* afterwards gives what the evaluation cost.
|
|
581
648
|
*/
|
|
582
649
|
declare function evaluate<Tag = never>(db: Database, rules: Rule[], algebra?: TagAlgebra<Tag>, budget?: RowBudget): Database;
|
|
583
650
|
/**
|
|
584
|
-
* Empty these relations
|
|
585
|
-
* it got to.
|
|
586
|
-
*
|
|
587
|
-
* `retract` cannot do this. A fact leaving the database can take away a
|
|
588
|
-
* conclusion drawn anywhere, so it sends the next run back to the base
|
|
589
|
-
* facts. A caller here is saying something stronger than "these facts
|
|
590
|
-
* are gone": nothing outside `relations` was derived from them, so what
|
|
591
|
-
* is left is already the fixpoint and the next run has only the facts
|
|
592
|
-
* that arrive after this to work through.
|
|
651
|
+
* Empty these relations and let `rules` resume from its last fixpoint.
|
|
593
652
|
*
|
|
594
|
-
*
|
|
595
|
-
*
|
|
596
|
-
*
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
* contents, which are the answers a caller has already read.
|
|
653
|
+
* After `retract`, the next run starts again from the base facts, since
|
|
654
|
+
* a fact leaving the database can take away a conclusion drawn anywhere.
|
|
655
|
+
* A caller of this function promises more: nothing outside `relations`
|
|
656
|
+
* was derived from these facts. What is left is already the fixpoint,
|
|
657
|
+
* and the next run works through only the facts added after this.
|
|
600
658
|
*
|
|
601
|
-
*
|
|
602
|
-
*
|
|
659
|
+
* A demand-driven caller clears `deriveOnDemand`'s demand-driven
|
|
660
|
+
* relations between questions, and the complete ones keep the answers
|
|
661
|
+
* already read (DESIGN.md has an example). Any other rule set over the
|
|
662
|
+
* same database starts over, since it may have derived from these.
|
|
603
663
|
*/
|
|
604
664
|
declare function clearRelations(db: Database, rules: Rule[], relations: readonly string[]): void;
|
|
605
665
|
|
|
606
|
-
export { type AbandonedQuestion, type AddOutcome, type Atom, type BodyMatch, BudgetExhausted, type ConfidenceLevel, Database, type Demand, type Derivation, type EvaluationProfile, type Literal, type OnDemandRules, type Proof, type ProofDerived, type ProofOptions, type QuestionOutcome, type QuestionTally, type RowBudget, type Rule, type RuleCost, type TagAlgebra, type Term, type Tuple, Witness, type WitnessTag, chargeAbandoned, chargeQuestion, clearRelations, confidence, confidenceWith, constant, deriveOnDemand, evaluate, formatProfile, lit, notLit, profileEvaluation, profileEvaluationAsync, proofOf, rowBudget, rule, ruleLabel, stratify, tupleKey, tupleKeyParts, variable, witnesses };
|
|
666
|
+
export { type AbandonedQuestion, type AddOutcome, type Atom, type BodyMatch, BudgetExhausted, type ConfidenceLevel, Database, type Demand, type Derivation, type EvaluationProfile, type Literal, type OnDemandRules, type Proof, type ProofDerived, type ProofOptions, type QuestionOutcome, type QuestionTally, type RowBudget, type Rule, type RuleCost, type TagAlgebra, type Term, type Tuple, Witness, type WitnessTag, chargeAbandoned, chargeQuestion, clearRelations, confidence, confidenceWith, constant, deriveOnDemand, evaluate, formatProfile, lit, notLit, profileEvaluation, profileEvaluationAsync, proofOf, rowBudget, rule, ruleLabel, rulesDeriving, stratify, tupleKey, tupleKeyParts, variable, witnesses };
|