@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/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 conclusion is only as sure as its weakest premise, so a rule
81
- * firing takes the minimum level across its body. A fact reached two
82
- * ways keeps the better level, so a second derivation merges with
83
- * maximum. Both are idempotent: ten medium steps come out medium, and
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"`: the caller read
92
- * it directly from its source. To say less, assert the fact with a
93
- * level: `db.add(relation, tuple, "medium")`. A matched negation also
94
- * counts as `"high"`, because negation here is exact over the
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` are
185
- * the ones it never put, because what it had already spent was gone.
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, which is the engine's own cost. */
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
- * Note that a caller put one question. `outcome` says whether it was
258
- * given up part way or never put at all; leave it out when it answered.
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 behaves exactly as it does
300
- * untagged. First-wins means a proof, not the shortest proof; the
301
- * algebra interface already allows a different merge. `proofOf` walks
302
- * the stored witnesses into a tree when somebody asks, the way
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, at the root
360
- * when the asked-about fact was never derived, and under a derivation
361
- * where a negated literal relied on it being missing, reported as
362
- * evaluation saw it rather than re-checked now. `truncated` is where
363
- * the depth cap or the cycle guard stopped the walk.
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; a
435
- * caller outside this module has nowhere to spend one.
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. Iterates to fixpoint. A stratum above the
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. Counting rows rather than new tuples is what catches
538
- * a question that walks: the walk finds the same conclusions over and
539
- * over, so nothing new arrives while the reading runs away.
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` back after
542
- * the call, which is how it learns what a question cost whether or not
543
- * the question settled. An evaluation with no budget gets a limit of
544
- * Infinity, so the hot loop does the same increment and the same
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 the budget says
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, and leave `rules` able to carry on from where
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
- * That is what a demand-driven rule set gives a caller between
595
- * questions. Every relation `deriveOnDemand` restricts is derived under
596
- * a demand fact, so clearing the demand together with everything under
597
- * it costs one question's worth of derivation rather than every
598
- * question asked so far. The relations listed as complete keep their
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
- * Any other rule set over the same database does start over, since a
602
- * relation it derived from may be one of these.
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 conclusion is only as sure as its weakest premise, so a rule
81
- * firing takes the minimum level across its body. A fact reached two
82
- * ways keeps the better level, so a second derivation merges with
83
- * maximum. Both are idempotent: ten medium steps come out medium, and
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"`: the caller read
92
- * it directly from its source. To say less, assert the fact with a
93
- * level: `db.add(relation, tuple, "medium")`. A matched negation also
94
- * counts as `"high"`, because negation here is exact over the
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` are
185
- * the ones it never put, because what it had already spent was gone.
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, which is the engine's own cost. */
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
- * Note that a caller put one question. `outcome` says whether it was
258
- * given up part way or never put at all; leave it out when it answered.
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 behaves exactly as it does
300
- * untagged. First-wins means a proof, not the shortest proof; the
301
- * algebra interface already allows a different merge. `proofOf` walks
302
- * the stored witnesses into a tree when somebody asks, the way
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, at the root
360
- * when the asked-about fact was never derived, and under a derivation
361
- * where a negated literal relied on it being missing, reported as
362
- * evaluation saw it rather than re-checked now. `truncated` is where
363
- * the depth cap or the cycle guard stopped the walk.
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; a
435
- * caller outside this module has nowhere to spend one.
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. Iterates to fixpoint. A stratum above the
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. Counting rows rather than new tuples is what catches
538
- * a question that walks: the walk finds the same conclusions over and
539
- * over, so nothing new arrives while the reading runs away.
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` back after
542
- * the call, which is how it learns what a question cost whether or not
543
- * the question settled. An evaluation with no budget gets a limit of
544
- * Infinity, so the hot loop does the same increment and the same
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 the budget says
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, and leave `rules` able to carry on from where
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
- * That is what a demand-driven rule set gives a caller between
595
- * questions. Every relation `deriveOnDemand` restricts is derived under
596
- * a demand fact, so clearing the demand together with everything under
597
- * it costs one question's worth of derivation rather than every
598
- * question asked so far. The relations listed as complete keep their
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
- * Any other rule set over the same database does start over, since a
602
- * relation it derived from may be one of these.
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 };