@suss/datalog 0.31.0 → 0.31.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.
package/dist/index.d.cts CHANGED
@@ -90,9 +90,32 @@ interface RuleCost {
90
90
  ms: number;
91
91
  /** Tuples this rule added that no earlier derivation had reached. */
92
92
  derived: number;
93
+ /**
94
+ * Rows the join read for this rule. A rule that examines a hundred
95
+ * thousand rows to derive a dozen tuples is doing a walk somebody
96
+ * meant to be a lookup, and `derived` alone never shows that.
97
+ */
98
+ examined: number;
93
99
  /** How many times the rule was evaluated, over all rounds and deltas. */
94
100
  attempts: number;
95
101
  }
102
+ /** A question the engine gave up on, and how much it had read by then. */
103
+ interface AbandonedQuestion {
104
+ /** What the caller was asking, in whatever words the caller uses. */
105
+ question: string;
106
+ examined: number;
107
+ }
108
+ /**
109
+ * How a caller that asks one question at a time got on. `skipped` are
110
+ * the ones it never put, because what it had already spent was gone.
111
+ */
112
+ interface QuestionTally {
113
+ asked: number;
114
+ abandoned: number;
115
+ skipped: number;
116
+ }
117
+ /** How one question ended, for the tally. */
118
+ type QuestionOutcome = keyof Omit<QuestionTally, "asked">;
96
119
  /**
97
120
  * How big a relation grew. `derived` separates the relations rules
98
121
  * concluded from the base facts an adapter emitted, because "derive fewer
@@ -130,6 +153,18 @@ interface EvaluationProfile {
130
153
  * corpus size is worth more attention than the rule table.
131
154
  */
132
155
  evaluations: number;
156
+ /** Rows every join read, summed over every rule set. */
157
+ examined: number;
158
+ /**
159
+ * The most rows any one `evaluate` call read. A caller that asks a
160
+ * question at a time sizes a per-question budget off this, where the
161
+ * total only says how many questions it asked.
162
+ */
163
+ largestEvaluation: number;
164
+ /** Every question abandoned on its budget, in the order they were given up. */
165
+ abandoned: AbandonedQuestion[];
166
+ /** How many questions a one-at-a-time caller asked, and how they went. */
167
+ questions: QuestionTally;
133
168
  /** Final tuple count per relation, largest first. */
134
169
  relations: RelationSize[];
135
170
  /** Per-rule cost across every rule set, most expensive first. */
@@ -137,6 +172,17 @@ interface EvaluationProfile {
137
172
  /** The same time, split by the rule set that spent it. */
138
173
  ruleSets: RuleSetCost[];
139
174
  }
175
+ /**
176
+ * Note that a question was given up on. A caller reading this in a
177
+ * profile is looking at the reason an answer came back empty, so the
178
+ * question is recorded in the caller's own words.
179
+ */
180
+ declare function chargeAbandoned(question: string, examined: number): void;
181
+ /**
182
+ * Note that a caller put one question. `outcome` says whether it was
183
+ * given up part way or never put at all; leave it out when it answered.
184
+ */
185
+ declare function chargeQuestion(outcome?: QuestionOutcome): void;
140
186
  /**
141
187
  * Run `fn` with evaluation profiling on, and hand back what it returned
142
188
  * alongside the numbers. Nested calls share the outer collector, so a
@@ -403,18 +449,54 @@ interface Derivation {
403
449
  * stratified, so this throws.
404
450
  */
405
451
  declare function stratify(rules: Rule[]): Rule[][];
452
+ /**
453
+ * How many rows the joins of one evaluation may read, and how many they
454
+ * have read so far. Counting rows rather than new tuples is what catches
455
+ * a question that walks: the walk finds the same conclusions over and
456
+ * over, so nothing new arrives while the reading runs away.
457
+ *
458
+ * The caller builds one with `rowBudget` and reads `examined` back after
459
+ * the call, which is how it learns what a question cost whether or not
460
+ * the question settled. An evaluation with no budget gets a limit of
461
+ * Infinity, so the hot loop does the same increment and the same
462
+ * compare either way.
463
+ */
464
+ interface RowBudget {
465
+ examined: number;
466
+ limit: number;
467
+ }
468
+ /** A budget for one evaluation, ready to hand to `evaluate`. */
469
+ declare const rowBudget: (limit: number) => RowBudget;
470
+ /**
471
+ * What `evaluate` throws when a row budget runs out.
472
+ *
473
+ * By the time this reaches a caller the database is back where it
474
+ * started: every fact that evaluation derived has been retracted, and
475
+ * the rule set will start over from the base facts next time rather
476
+ * than from a delta that was never worked through. The caller's own
477
+ * facts, the question it asked among them, are still there for it to
478
+ * take back.
479
+ */
480
+ declare class BudgetExhausted extends Error {
481
+ readonly ruleSet: string;
482
+ readonly examined: number;
483
+ constructor(ruleSet: string, examined: number);
484
+ }
406
485
  /**
407
486
  * Evaluate `rules` over `db` to fixpoint, adding derived facts in
408
487
  * place. Rules are stratified first; within a stratum, semi-naïve
409
- * iteration joins each rule against the last round's delta so work is
410
- * proportional to new facts, not all facts.
488
+ * iteration joins each rule against the last round's delta, so work
489
+ * is proportional to new facts rather than to all facts.
411
490
  *
412
491
  * With an `algebra`, every derivation also tags its conclusion (see
413
- * `TagAlgebra`). Supply the same algebra on every evaluation of a rule
414
- * set over a database: a resumed run derives only from the new facts,
415
- * so it tags only what those reach.
492
+ * `TagAlgebra`). Supply the same algebra every time over one
493
+ * database: a resumed run tags only what the new facts reach.
494
+ *
495
+ * With a `budget`, evaluation gives up once its joins have read that
496
+ * many rows and throws `BudgetExhausted`. Either way the budget says
497
+ * afterwards what the evaluation cost.
416
498
  */
417
- declare function evaluate<Tag = never>(db: Database, rules: Rule[], algebra?: TagAlgebra<Tag>): Database;
499
+ declare function evaluate<Tag = never>(db: Database, rules: Rule[], algebra?: TagAlgebra<Tag>, budget?: RowBudget): Database;
418
500
  /**
419
501
  * Empty these relations, and leave `rules` able to carry on from where
420
502
  * it got to.
@@ -438,4 +520,4 @@ declare function evaluate<Tag = never>(db: Database, rules: Rule[], algebra?: Ta
438
520
  */
439
521
  declare function clearRelations(db: Database, rules: Rule[], relations: readonly string[]): void;
440
522
 
441
- export { type AddOutcome, type Atom, type BodyMatch, type ConfidenceLevel, Database, type Demand, type Derivation, type EvaluationProfile, type Literal, type OnDemandRules, type Proof, type ProofDerived, type ProofOptions, type Rule, type RuleCost, type TagAlgebra, type Term, type Tuple, Witness, type WitnessTag, clearRelations, confidence, confidenceWith, constant, deriveOnDemand, evaluate, formatProfile, lit, notLit, profileEvaluation, profileEvaluationAsync, proofOf, rule, ruleLabel, stratify, tupleKey, tupleKeyParts, variable, witnesses };
523
+ 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 };
package/dist/index.d.ts CHANGED
@@ -90,9 +90,32 @@ interface RuleCost {
90
90
  ms: number;
91
91
  /** Tuples this rule added that no earlier derivation had reached. */
92
92
  derived: number;
93
+ /**
94
+ * Rows the join read for this rule. A rule that examines a hundred
95
+ * thousand rows to derive a dozen tuples is doing a walk somebody
96
+ * meant to be a lookup, and `derived` alone never shows that.
97
+ */
98
+ examined: number;
93
99
  /** How many times the rule was evaluated, over all rounds and deltas. */
94
100
  attempts: number;
95
101
  }
102
+ /** A question the engine gave up on, and how much it had read by then. */
103
+ interface AbandonedQuestion {
104
+ /** What the caller was asking, in whatever words the caller uses. */
105
+ question: string;
106
+ examined: number;
107
+ }
108
+ /**
109
+ * How a caller that asks one question at a time got on. `skipped` are
110
+ * the ones it never put, because what it had already spent was gone.
111
+ */
112
+ interface QuestionTally {
113
+ asked: number;
114
+ abandoned: number;
115
+ skipped: number;
116
+ }
117
+ /** How one question ended, for the tally. */
118
+ type QuestionOutcome = keyof Omit<QuestionTally, "asked">;
96
119
  /**
97
120
  * How big a relation grew. `derived` separates the relations rules
98
121
  * concluded from the base facts an adapter emitted, because "derive fewer
@@ -130,6 +153,18 @@ interface EvaluationProfile {
130
153
  * corpus size is worth more attention than the rule table.
131
154
  */
132
155
  evaluations: number;
156
+ /** Rows every join read, summed over every rule set. */
157
+ examined: number;
158
+ /**
159
+ * The most rows any one `evaluate` call read. A caller that asks a
160
+ * question at a time sizes a per-question budget off this, where the
161
+ * total only says how many questions it asked.
162
+ */
163
+ largestEvaluation: number;
164
+ /** Every question abandoned on its budget, in the order they were given up. */
165
+ abandoned: AbandonedQuestion[];
166
+ /** How many questions a one-at-a-time caller asked, and how they went. */
167
+ questions: QuestionTally;
133
168
  /** Final tuple count per relation, largest first. */
134
169
  relations: RelationSize[];
135
170
  /** Per-rule cost across every rule set, most expensive first. */
@@ -137,6 +172,17 @@ interface EvaluationProfile {
137
172
  /** The same time, split by the rule set that spent it. */
138
173
  ruleSets: RuleSetCost[];
139
174
  }
175
+ /**
176
+ * Note that a question was given up on. A caller reading this in a
177
+ * profile is looking at the reason an answer came back empty, so the
178
+ * question is recorded in the caller's own words.
179
+ */
180
+ declare function chargeAbandoned(question: string, examined: number): void;
181
+ /**
182
+ * Note that a caller put one question. `outcome` says whether it was
183
+ * given up part way or never put at all; leave it out when it answered.
184
+ */
185
+ declare function chargeQuestion(outcome?: QuestionOutcome): void;
140
186
  /**
141
187
  * Run `fn` with evaluation profiling on, and hand back what it returned
142
188
  * alongside the numbers. Nested calls share the outer collector, so a
@@ -403,18 +449,54 @@ interface Derivation {
403
449
  * stratified, so this throws.
404
450
  */
405
451
  declare function stratify(rules: Rule[]): Rule[][];
452
+ /**
453
+ * How many rows the joins of one evaluation may read, and how many they
454
+ * have read so far. Counting rows rather than new tuples is what catches
455
+ * a question that walks: the walk finds the same conclusions over and
456
+ * over, so nothing new arrives while the reading runs away.
457
+ *
458
+ * The caller builds one with `rowBudget` and reads `examined` back after
459
+ * the call, which is how it learns what a question cost whether or not
460
+ * the question settled. An evaluation with no budget gets a limit of
461
+ * Infinity, so the hot loop does the same increment and the same
462
+ * compare either way.
463
+ */
464
+ interface RowBudget {
465
+ examined: number;
466
+ limit: number;
467
+ }
468
+ /** A budget for one evaluation, ready to hand to `evaluate`. */
469
+ declare const rowBudget: (limit: number) => RowBudget;
470
+ /**
471
+ * What `evaluate` throws when a row budget runs out.
472
+ *
473
+ * By the time this reaches a caller the database is back where it
474
+ * started: every fact that evaluation derived has been retracted, and
475
+ * the rule set will start over from the base facts next time rather
476
+ * than from a delta that was never worked through. The caller's own
477
+ * facts, the question it asked among them, are still there for it to
478
+ * take back.
479
+ */
480
+ declare class BudgetExhausted extends Error {
481
+ readonly ruleSet: string;
482
+ readonly examined: number;
483
+ constructor(ruleSet: string, examined: number);
484
+ }
406
485
  /**
407
486
  * Evaluate `rules` over `db` to fixpoint, adding derived facts in
408
487
  * place. Rules are stratified first; within a stratum, semi-naïve
409
- * iteration joins each rule against the last round's delta so work is
410
- * proportional to new facts, not all facts.
488
+ * iteration joins each rule against the last round's delta, so work
489
+ * is proportional to new facts rather than to all facts.
411
490
  *
412
491
  * With an `algebra`, every derivation also tags its conclusion (see
413
- * `TagAlgebra`). Supply the same algebra on every evaluation of a rule
414
- * set over a database: a resumed run derives only from the new facts,
415
- * so it tags only what those reach.
492
+ * `TagAlgebra`). Supply the same algebra every time over one
493
+ * database: a resumed run tags only what the new facts reach.
494
+ *
495
+ * With a `budget`, evaluation gives up once its joins have read that
496
+ * many rows and throws `BudgetExhausted`. Either way the budget says
497
+ * afterwards what the evaluation cost.
416
498
  */
417
- declare function evaluate<Tag = never>(db: Database, rules: Rule[], algebra?: TagAlgebra<Tag>): Database;
499
+ declare function evaluate<Tag = never>(db: Database, rules: Rule[], algebra?: TagAlgebra<Tag>, budget?: RowBudget): Database;
418
500
  /**
419
501
  * Empty these relations, and leave `rules` able to carry on from where
420
502
  * it got to.
@@ -438,4 +520,4 @@ declare function evaluate<Tag = never>(db: Database, rules: Rule[], algebra?: Ta
438
520
  */
439
521
  declare function clearRelations(db: Database, rules: Rule[], relations: readonly string[]): void;
440
522
 
441
- export { type AddOutcome, type Atom, type BodyMatch, type ConfidenceLevel, Database, type Demand, type Derivation, type EvaluationProfile, type Literal, type OnDemandRules, type Proof, type ProofDerived, type ProofOptions, type Rule, type RuleCost, type TagAlgebra, type Term, type Tuple, Witness, type WitnessTag, clearRelations, confidence, confidenceWith, constant, deriveOnDemand, evaluate, formatProfile, lit, notLit, profileEvaluation, profileEvaluationAsync, proofOf, rule, ruleLabel, stratify, tupleKey, tupleKeyParts, variable, witnesses };
523
+ 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 };