@suss/datalog 0.9.0 → 0.11.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 +124 -0
- package/dist/index.cjs +395 -156
- package/dist/index.d.cts +192 -5
- package/dist/index.d.ts +192 -5
- package/dist/index.js +388 -155
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Confidence levels as a tag algebra: how sure the run is of each
|
|
3
|
+
* derived fact, given how sure it is of the facts and rules behind it.
|
|
4
|
+
*
|
|
5
|
+
* A conclusion is only as sure as its weakest premise, so a rule
|
|
6
|
+
* firing takes the minimum level across its body. A fact reached two
|
|
7
|
+
* ways keeps the better level, so a second derivation merges with
|
|
8
|
+
* maximum. Both are idempotent: ten medium steps come out medium, and
|
|
9
|
+
* that is the intended reading rather than a loss of information.
|
|
10
|
+
*
|
|
11
|
+
* A rule can be a heuristic itself, and then its conclusions should
|
|
12
|
+
* not outrank it however sure its premises are. `confidenceWith`
|
|
13
|
+
* takes a level per rule and folds it into the minimum. The plain
|
|
14
|
+
* `confidence` algebra treats every rule as exact.
|
|
15
|
+
*
|
|
16
|
+
* An asserted fact without a tag counts as `"high"`: the caller read
|
|
17
|
+
* it directly from its source. To say less, assert the fact with a
|
|
18
|
+
* level: `db.add(relation, tuple, "medium")`. A matched negation also
|
|
19
|
+
* counts as `"high"`, because negation here is exact over the
|
|
20
|
+
* database as computed, not a guess about the world.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/** How sure the run is of one fact. The order is high > medium > low. */
|
|
24
|
+
type ConfidenceLevel = "high" | "medium" | "low";
|
|
25
|
+
/**
|
|
26
|
+
* The confidence algebra with a level per rule. Rules the callback
|
|
27
|
+
* returns `undefined` for count as exact, so a rule set with one
|
|
28
|
+
* heuristic only has to speak for that one.
|
|
29
|
+
*/
|
|
30
|
+
declare function confidenceWith(levelOf: (rule: Rule) => ConfidenceLevel | undefined): TagAlgebra<ConfidenceLevel>;
|
|
31
|
+
/** The confidence algebra with every rule counted as exact. */
|
|
32
|
+
declare const confidence: TagAlgebra<ConfidenceLevel>;
|
|
33
|
+
|
|
1
34
|
/** A rewritten rule set, and which of its relations demand restricts. */
|
|
2
35
|
interface OnDemandRules {
|
|
3
36
|
/** The rewritten rules, ready to evaluate. */
|
|
@@ -116,6 +149,81 @@ declare const tupleKey: (parts: readonly Atom[]) => string;
|
|
|
116
149
|
*/
|
|
117
150
|
declare function tupleKeyParts(key: string): string[];
|
|
118
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Witnesses: one derivation kept per derived fact, and proofs rebuilt
|
|
154
|
+
* on demand by walking those derivations backward.
|
|
155
|
+
*
|
|
156
|
+
* Evaluate under the `witnesses` algebra and every derived fact stores
|
|
157
|
+
* a `Witness`: the rule that fired and one entry per body literal. The
|
|
158
|
+
* merge keeps whatever is already stored, so a fact derived nine ways
|
|
159
|
+
* keeps its first witness and the fixpoint behaves exactly as it does
|
|
160
|
+
* untagged. First-wins means a proof, not the shortest proof; the
|
|
161
|
+
* algebra interface already allows a different merge. `proofOf` walks
|
|
162
|
+
* the stored witnesses into a tree when somebody asks, the way
|
|
163
|
+
* Soufflé's provenance mode does, and never re-runs a rule.
|
|
164
|
+
*/
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The tag the `witnesses` algebra stores on a derived fact. A class,
|
|
168
|
+
* so `proofOf` can tell a witness from another algebra's tag by
|
|
169
|
+
* `instanceof` and treat anything else as an untagged leaf.
|
|
170
|
+
*/
|
|
171
|
+
declare class Witness {
|
|
172
|
+
readonly rule: Rule;
|
|
173
|
+
readonly body: readonly BodyMatch[];
|
|
174
|
+
constructor(rule: Rule, body: readonly BodyMatch[]);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* What a fact's tag can be under `witnesses`. The marks stand in for a
|
|
178
|
+
* caller-asserted fact and a matched negation inside the algebra;
|
|
179
|
+
* neither is ever stored, so `tagOf` only ever returns a `Witness`.
|
|
180
|
+
*/
|
|
181
|
+
type WitnessTag = Witness | "asserted" | "absent";
|
|
182
|
+
/**
|
|
183
|
+
* The algebra that keeps one witness per derived fact. Merge returns
|
|
184
|
+
* the stored tag, so no fact ever re-enters the delta and evaluation
|
|
185
|
+
* costs only the bookkeeping.
|
|
186
|
+
*/
|
|
187
|
+
declare const witnesses: TagAlgebra<WitnessTag>;
|
|
188
|
+
/** A reconstructed derivation tree for one fact. */
|
|
189
|
+
type Proof = ProofDerived | {
|
|
190
|
+
kind: "fact";
|
|
191
|
+
relation: string;
|
|
192
|
+
tuple: Tuple;
|
|
193
|
+
} | {
|
|
194
|
+
kind: "absence";
|
|
195
|
+
relation: string;
|
|
196
|
+
tuple: Tuple;
|
|
197
|
+
} | {
|
|
198
|
+
kind: "truncated";
|
|
199
|
+
relation: string;
|
|
200
|
+
tuple: Tuple;
|
|
201
|
+
reason: "depth" | "cycle";
|
|
202
|
+
};
|
|
203
|
+
interface ProofDerived {
|
|
204
|
+
kind: "derived";
|
|
205
|
+
relation: string;
|
|
206
|
+
tuple: Tuple;
|
|
207
|
+
rule: Rule;
|
|
208
|
+
/** One proof per body literal, in rule-body order. */
|
|
209
|
+
premises: readonly Proof[];
|
|
210
|
+
}
|
|
211
|
+
interface ProofOptions {
|
|
212
|
+
/** How many derived nodes deep the walk goes before truncating. */
|
|
213
|
+
maxDepth?: number;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Rebuild the proof of one fact from stored witnesses, without
|
|
217
|
+
* re-running any evaluation. A `fact` leaf has no witness: the caller
|
|
218
|
+
* asserted it, or it was derived without the `witnesses` algebra. An
|
|
219
|
+
* `absence` leaf is a tuple missing from the database, at the root
|
|
220
|
+
* when the asked-about fact was never derived, and under a derivation
|
|
221
|
+
* where a negated literal relied on it being missing, reported as
|
|
222
|
+
* evaluation saw it rather than re-checked now. `truncated` is where
|
|
223
|
+
* the depth cap or the cycle guard stopped the walk.
|
|
224
|
+
*/
|
|
225
|
+
declare function proofOf(db: Database, relation: string, tuple: Tuple, options?: ProofOptions): Proof;
|
|
226
|
+
|
|
119
227
|
/**
|
|
120
228
|
* @suss/datalog, a small semi-naïve Datalog evaluator with stratified
|
|
121
229
|
* negation, and the rules engine behind suss's derived program facts:
|
|
@@ -163,14 +271,34 @@ interface Rule {
|
|
|
163
271
|
terms: Term[];
|
|
164
272
|
};
|
|
165
273
|
body: Literal[];
|
|
274
|
+
/** The name a proof shows for this rule. `ruleLabel` renders a default without it. */
|
|
275
|
+
name?: string;
|
|
166
276
|
}
|
|
167
|
-
declare const rule: (relation: string, terms: Term[], body: Literal[]) => Rule;
|
|
277
|
+
declare const rule: (relation: string, terms: Term[], body: Literal[], name?: string) => Rule;
|
|
278
|
+
/**
|
|
279
|
+
* The label a proof prints for a rule: the name the caller gave it, or
|
|
280
|
+
* the head and body relations, as in `path :- path, !blocked`. Two
|
|
281
|
+
* rules over the same relations get the same default label, which is
|
|
282
|
+
* why a witness keeps the `Rule` object itself and renders it only
|
|
283
|
+
* when asked.
|
|
284
|
+
*/
|
|
285
|
+
declare const ruleLabel: (r: Rule) => string;
|
|
286
|
+
/** What `add` did: a new fact, a better tag on an existing one, or nothing. */
|
|
287
|
+
type AddOutcome = "added" | "improved" | "unchanged";
|
|
168
288
|
/** A set of facts per relation, with O(1) membership. */
|
|
169
289
|
declare class Database {
|
|
170
290
|
private readonly store;
|
|
171
291
|
private relation;
|
|
172
|
-
/**
|
|
173
|
-
|
|
292
|
+
/**
|
|
293
|
+
* Add a fact, or merge a tag into one already present. A new fact
|
|
294
|
+
* stores `tag` when one is given. For a fact already present,
|
|
295
|
+
* `merge` gets the stored tag (undefined when there is none) and the
|
|
296
|
+
* incoming one; returning anything but the stored value stores the
|
|
297
|
+
* result and reports "improved".
|
|
298
|
+
*/
|
|
299
|
+
add(relationName: string, tuple: Tuple, tag?: unknown, merge?: (stored: unknown, incoming: unknown) => unknown): AddOutcome;
|
|
300
|
+
/** The tag stored for this fact, or undefined when there is none. */
|
|
301
|
+
tagOf(relationName: string, tuple: Tuple): unknown;
|
|
174
302
|
has(relationName: string, tuple: Tuple): boolean;
|
|
175
303
|
facts(relationName: string): readonly Tuple[];
|
|
176
304
|
/**
|
|
@@ -194,6 +322,60 @@ declare class Database {
|
|
|
194
322
|
size(relationName: string): number;
|
|
195
323
|
relationNames(): string[];
|
|
196
324
|
}
|
|
325
|
+
/**
|
|
326
|
+
* A provenance semiring (Green, Karvounarakis, Tannen 2007): every
|
|
327
|
+
* fact may have a tag, a rule firing combines its body facts' tags
|
|
328
|
+
* into a tag for its conclusion, and a conclusion reached a second
|
|
329
|
+
* time merges the new tag into the stored one. An algebra is an
|
|
330
|
+
* optional argument to `evaluate`; rules never mention tags, and
|
|
331
|
+
* without an algebra the evaluator never touches them.
|
|
332
|
+
*
|
|
333
|
+
* `merge` must return the stored tag itself when the new derivation
|
|
334
|
+
* does not improve on it; any other result is stored and the fact
|
|
335
|
+
* re-enters the delta. For recursive rule sets merge must be a
|
|
336
|
+
* bounded meet, or evaluation will not terminate; the engine does
|
|
337
|
+
* not check this. `undefined` is not a valid tag.
|
|
338
|
+
*/
|
|
339
|
+
interface TagAlgebra<Tag> {
|
|
340
|
+
/** What a fact nobody tagged contributes, i.e. every caller-asserted fact. */
|
|
341
|
+
asserted: Tag;
|
|
342
|
+
/** What a matched negated literal contributes: there is no fact to read. */
|
|
343
|
+
absent: Tag;
|
|
344
|
+
/** The head's tag from the body's, in rule-body order, once per derivation. */
|
|
345
|
+
combine(bodyTags: readonly Tag[], derivation: Derivation): Tag;
|
|
346
|
+
/**
|
|
347
|
+
* Set when `combine` never reads `bodyTags`, and the evaluator then
|
|
348
|
+
* skips looking each body fact's stored tag up. A witness is built
|
|
349
|
+
* from the derivation alone, and on a large sweep the lookups were
|
|
350
|
+
* three quarters of the tagged path's cost.
|
|
351
|
+
*/
|
|
352
|
+
ignoresBodyTags?: boolean;
|
|
353
|
+
/** The tag to store when a fact is derived again; see the merge contract above. */
|
|
354
|
+
merge(stored: Tag, incoming: Tag): Tag;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* How one body literal was satisfied: the fact a positive literal
|
|
358
|
+
* matched, or the grounded tuple that was not in the database when a
|
|
359
|
+
* negated literal checked, kept so a proof can point at the absence.
|
|
360
|
+
*/
|
|
361
|
+
type BodyMatch = {
|
|
362
|
+
kind: "fact";
|
|
363
|
+
relation: string;
|
|
364
|
+
tuple: Tuple;
|
|
365
|
+
} | {
|
|
366
|
+
kind: "absence";
|
|
367
|
+
relation: string;
|
|
368
|
+
tuple: Tuple;
|
|
369
|
+
};
|
|
370
|
+
/**
|
|
371
|
+
* One rule firing, handed to `combine` beside the body tags: the rule
|
|
372
|
+
* as the caller wrote it, and one `BodyMatch` per body literal in
|
|
373
|
+
* rule-body order. An algebra that only folds tags ignores it.
|
|
374
|
+
*/
|
|
375
|
+
interface Derivation {
|
|
376
|
+
rule: Rule;
|
|
377
|
+
body: readonly BodyMatch[];
|
|
378
|
+
}
|
|
197
379
|
/**
|
|
198
380
|
* Assign each derived relation a stratum such that positive
|
|
199
381
|
* dependencies never decrease the stratum and negative dependencies
|
|
@@ -207,8 +389,13 @@ declare function stratify(rules: Rule[]): Rule[][];
|
|
|
207
389
|
* place. Rules are stratified first; within a stratum, semi-naïve
|
|
208
390
|
* iteration joins each rule against the last round's delta so work is
|
|
209
391
|
* proportional to new facts, not all facts.
|
|
392
|
+
*
|
|
393
|
+
* With an `algebra`, every derivation also tags its conclusion (see
|
|
394
|
+
* `TagAlgebra`). Supply the same algebra on every evaluation of a rule
|
|
395
|
+
* set over a database: a resumed run derives only from the new facts,
|
|
396
|
+
* so it tags only what those reach.
|
|
210
397
|
*/
|
|
211
|
-
declare function evaluate(db: Database, rules: Rule[]): Database;
|
|
398
|
+
declare function evaluate<Tag = never>(db: Database, rules: Rule[], algebra?: TagAlgebra<Tag>): Database;
|
|
212
399
|
/**
|
|
213
400
|
* Empty these relations, and leave `rules` able to carry on from where
|
|
214
401
|
* it got to.
|
|
@@ -232,4 +419,4 @@ declare function evaluate(db: Database, rules: Rule[]): Database;
|
|
|
232
419
|
*/
|
|
233
420
|
declare function clearRelations(db: Database, rules: Rule[], relations: readonly string[]): void;
|
|
234
421
|
|
|
235
|
-
export { type Atom, Database, type EvaluationProfile, type Literal, type OnDemandRules, type Rule, type RuleCost, type Term, type Tuple, clearRelations, constant, deriveOnDemand, evaluate, formatProfile, lit, notLit, profileEvaluation, profileEvaluationAsync, rule, stratify, tupleKey, tupleKeyParts, variable };
|
|
422
|
+
export { type AddOutcome, type Atom, type BodyMatch, type ConfidenceLevel, Database, 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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Confidence levels as a tag algebra: how sure the run is of each
|
|
3
|
+
* derived fact, given how sure it is of the facts and rules behind it.
|
|
4
|
+
*
|
|
5
|
+
* A conclusion is only as sure as its weakest premise, so a rule
|
|
6
|
+
* firing takes the minimum level across its body. A fact reached two
|
|
7
|
+
* ways keeps the better level, so a second derivation merges with
|
|
8
|
+
* maximum. Both are idempotent: ten medium steps come out medium, and
|
|
9
|
+
* that is the intended reading rather than a loss of information.
|
|
10
|
+
*
|
|
11
|
+
* A rule can be a heuristic itself, and then its conclusions should
|
|
12
|
+
* not outrank it however sure its premises are. `confidenceWith`
|
|
13
|
+
* takes a level per rule and folds it into the minimum. The plain
|
|
14
|
+
* `confidence` algebra treats every rule as exact.
|
|
15
|
+
*
|
|
16
|
+
* An asserted fact without a tag counts as `"high"`: the caller read
|
|
17
|
+
* it directly from its source. To say less, assert the fact with a
|
|
18
|
+
* level: `db.add(relation, tuple, "medium")`. A matched negation also
|
|
19
|
+
* counts as `"high"`, because negation here is exact over the
|
|
20
|
+
* database as computed, not a guess about the world.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/** How sure the run is of one fact. The order is high > medium > low. */
|
|
24
|
+
type ConfidenceLevel = "high" | "medium" | "low";
|
|
25
|
+
/**
|
|
26
|
+
* The confidence algebra with a level per rule. Rules the callback
|
|
27
|
+
* returns `undefined` for count as exact, so a rule set with one
|
|
28
|
+
* heuristic only has to speak for that one.
|
|
29
|
+
*/
|
|
30
|
+
declare function confidenceWith(levelOf: (rule: Rule) => ConfidenceLevel | undefined): TagAlgebra<ConfidenceLevel>;
|
|
31
|
+
/** The confidence algebra with every rule counted as exact. */
|
|
32
|
+
declare const confidence: TagAlgebra<ConfidenceLevel>;
|
|
33
|
+
|
|
1
34
|
/** A rewritten rule set, and which of its relations demand restricts. */
|
|
2
35
|
interface OnDemandRules {
|
|
3
36
|
/** The rewritten rules, ready to evaluate. */
|
|
@@ -116,6 +149,81 @@ declare const tupleKey: (parts: readonly Atom[]) => string;
|
|
|
116
149
|
*/
|
|
117
150
|
declare function tupleKeyParts(key: string): string[];
|
|
118
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Witnesses: one derivation kept per derived fact, and proofs rebuilt
|
|
154
|
+
* on demand by walking those derivations backward.
|
|
155
|
+
*
|
|
156
|
+
* Evaluate under the `witnesses` algebra and every derived fact stores
|
|
157
|
+
* a `Witness`: the rule that fired and one entry per body literal. The
|
|
158
|
+
* merge keeps whatever is already stored, so a fact derived nine ways
|
|
159
|
+
* keeps its first witness and the fixpoint behaves exactly as it does
|
|
160
|
+
* untagged. First-wins means a proof, not the shortest proof; the
|
|
161
|
+
* algebra interface already allows a different merge. `proofOf` walks
|
|
162
|
+
* the stored witnesses into a tree when somebody asks, the way
|
|
163
|
+
* Soufflé's provenance mode does, and never re-runs a rule.
|
|
164
|
+
*/
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The tag the `witnesses` algebra stores on a derived fact. A class,
|
|
168
|
+
* so `proofOf` can tell a witness from another algebra's tag by
|
|
169
|
+
* `instanceof` and treat anything else as an untagged leaf.
|
|
170
|
+
*/
|
|
171
|
+
declare class Witness {
|
|
172
|
+
readonly rule: Rule;
|
|
173
|
+
readonly body: readonly BodyMatch[];
|
|
174
|
+
constructor(rule: Rule, body: readonly BodyMatch[]);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* What a fact's tag can be under `witnesses`. The marks stand in for a
|
|
178
|
+
* caller-asserted fact and a matched negation inside the algebra;
|
|
179
|
+
* neither is ever stored, so `tagOf` only ever returns a `Witness`.
|
|
180
|
+
*/
|
|
181
|
+
type WitnessTag = Witness | "asserted" | "absent";
|
|
182
|
+
/**
|
|
183
|
+
* The algebra that keeps one witness per derived fact. Merge returns
|
|
184
|
+
* the stored tag, so no fact ever re-enters the delta and evaluation
|
|
185
|
+
* costs only the bookkeeping.
|
|
186
|
+
*/
|
|
187
|
+
declare const witnesses: TagAlgebra<WitnessTag>;
|
|
188
|
+
/** A reconstructed derivation tree for one fact. */
|
|
189
|
+
type Proof = ProofDerived | {
|
|
190
|
+
kind: "fact";
|
|
191
|
+
relation: string;
|
|
192
|
+
tuple: Tuple;
|
|
193
|
+
} | {
|
|
194
|
+
kind: "absence";
|
|
195
|
+
relation: string;
|
|
196
|
+
tuple: Tuple;
|
|
197
|
+
} | {
|
|
198
|
+
kind: "truncated";
|
|
199
|
+
relation: string;
|
|
200
|
+
tuple: Tuple;
|
|
201
|
+
reason: "depth" | "cycle";
|
|
202
|
+
};
|
|
203
|
+
interface ProofDerived {
|
|
204
|
+
kind: "derived";
|
|
205
|
+
relation: string;
|
|
206
|
+
tuple: Tuple;
|
|
207
|
+
rule: Rule;
|
|
208
|
+
/** One proof per body literal, in rule-body order. */
|
|
209
|
+
premises: readonly Proof[];
|
|
210
|
+
}
|
|
211
|
+
interface ProofOptions {
|
|
212
|
+
/** How many derived nodes deep the walk goes before truncating. */
|
|
213
|
+
maxDepth?: number;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Rebuild the proof of one fact from stored witnesses, without
|
|
217
|
+
* re-running any evaluation. A `fact` leaf has no witness: the caller
|
|
218
|
+
* asserted it, or it was derived without the `witnesses` algebra. An
|
|
219
|
+
* `absence` leaf is a tuple missing from the database, at the root
|
|
220
|
+
* when the asked-about fact was never derived, and under a derivation
|
|
221
|
+
* where a negated literal relied on it being missing, reported as
|
|
222
|
+
* evaluation saw it rather than re-checked now. `truncated` is where
|
|
223
|
+
* the depth cap or the cycle guard stopped the walk.
|
|
224
|
+
*/
|
|
225
|
+
declare function proofOf(db: Database, relation: string, tuple: Tuple, options?: ProofOptions): Proof;
|
|
226
|
+
|
|
119
227
|
/**
|
|
120
228
|
* @suss/datalog, a small semi-naïve Datalog evaluator with stratified
|
|
121
229
|
* negation, and the rules engine behind suss's derived program facts:
|
|
@@ -163,14 +271,34 @@ interface Rule {
|
|
|
163
271
|
terms: Term[];
|
|
164
272
|
};
|
|
165
273
|
body: Literal[];
|
|
274
|
+
/** The name a proof shows for this rule. `ruleLabel` renders a default without it. */
|
|
275
|
+
name?: string;
|
|
166
276
|
}
|
|
167
|
-
declare const rule: (relation: string, terms: Term[], body: Literal[]) => Rule;
|
|
277
|
+
declare const rule: (relation: string, terms: Term[], body: Literal[], name?: string) => Rule;
|
|
278
|
+
/**
|
|
279
|
+
* The label a proof prints for a rule: the name the caller gave it, or
|
|
280
|
+
* the head and body relations, as in `path :- path, !blocked`. Two
|
|
281
|
+
* rules over the same relations get the same default label, which is
|
|
282
|
+
* why a witness keeps the `Rule` object itself and renders it only
|
|
283
|
+
* when asked.
|
|
284
|
+
*/
|
|
285
|
+
declare const ruleLabel: (r: Rule) => string;
|
|
286
|
+
/** What `add` did: a new fact, a better tag on an existing one, or nothing. */
|
|
287
|
+
type AddOutcome = "added" | "improved" | "unchanged";
|
|
168
288
|
/** A set of facts per relation, with O(1) membership. */
|
|
169
289
|
declare class Database {
|
|
170
290
|
private readonly store;
|
|
171
291
|
private relation;
|
|
172
|
-
/**
|
|
173
|
-
|
|
292
|
+
/**
|
|
293
|
+
* Add a fact, or merge a tag into one already present. A new fact
|
|
294
|
+
* stores `tag` when one is given. For a fact already present,
|
|
295
|
+
* `merge` gets the stored tag (undefined when there is none) and the
|
|
296
|
+
* incoming one; returning anything but the stored value stores the
|
|
297
|
+
* result and reports "improved".
|
|
298
|
+
*/
|
|
299
|
+
add(relationName: string, tuple: Tuple, tag?: unknown, merge?: (stored: unknown, incoming: unknown) => unknown): AddOutcome;
|
|
300
|
+
/** The tag stored for this fact, or undefined when there is none. */
|
|
301
|
+
tagOf(relationName: string, tuple: Tuple): unknown;
|
|
174
302
|
has(relationName: string, tuple: Tuple): boolean;
|
|
175
303
|
facts(relationName: string): readonly Tuple[];
|
|
176
304
|
/**
|
|
@@ -194,6 +322,60 @@ declare class Database {
|
|
|
194
322
|
size(relationName: string): number;
|
|
195
323
|
relationNames(): string[];
|
|
196
324
|
}
|
|
325
|
+
/**
|
|
326
|
+
* A provenance semiring (Green, Karvounarakis, Tannen 2007): every
|
|
327
|
+
* fact may have a tag, a rule firing combines its body facts' tags
|
|
328
|
+
* into a tag for its conclusion, and a conclusion reached a second
|
|
329
|
+
* time merges the new tag into the stored one. An algebra is an
|
|
330
|
+
* optional argument to `evaluate`; rules never mention tags, and
|
|
331
|
+
* without an algebra the evaluator never touches them.
|
|
332
|
+
*
|
|
333
|
+
* `merge` must return the stored tag itself when the new derivation
|
|
334
|
+
* does not improve on it; any other result is stored and the fact
|
|
335
|
+
* re-enters the delta. For recursive rule sets merge must be a
|
|
336
|
+
* bounded meet, or evaluation will not terminate; the engine does
|
|
337
|
+
* not check this. `undefined` is not a valid tag.
|
|
338
|
+
*/
|
|
339
|
+
interface TagAlgebra<Tag> {
|
|
340
|
+
/** What a fact nobody tagged contributes, i.e. every caller-asserted fact. */
|
|
341
|
+
asserted: Tag;
|
|
342
|
+
/** What a matched negated literal contributes: there is no fact to read. */
|
|
343
|
+
absent: Tag;
|
|
344
|
+
/** The head's tag from the body's, in rule-body order, once per derivation. */
|
|
345
|
+
combine(bodyTags: readonly Tag[], derivation: Derivation): Tag;
|
|
346
|
+
/**
|
|
347
|
+
* Set when `combine` never reads `bodyTags`, and the evaluator then
|
|
348
|
+
* skips looking each body fact's stored tag up. A witness is built
|
|
349
|
+
* from the derivation alone, and on a large sweep the lookups were
|
|
350
|
+
* three quarters of the tagged path's cost.
|
|
351
|
+
*/
|
|
352
|
+
ignoresBodyTags?: boolean;
|
|
353
|
+
/** The tag to store when a fact is derived again; see the merge contract above. */
|
|
354
|
+
merge(stored: Tag, incoming: Tag): Tag;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* How one body literal was satisfied: the fact a positive literal
|
|
358
|
+
* matched, or the grounded tuple that was not in the database when a
|
|
359
|
+
* negated literal checked, kept so a proof can point at the absence.
|
|
360
|
+
*/
|
|
361
|
+
type BodyMatch = {
|
|
362
|
+
kind: "fact";
|
|
363
|
+
relation: string;
|
|
364
|
+
tuple: Tuple;
|
|
365
|
+
} | {
|
|
366
|
+
kind: "absence";
|
|
367
|
+
relation: string;
|
|
368
|
+
tuple: Tuple;
|
|
369
|
+
};
|
|
370
|
+
/**
|
|
371
|
+
* One rule firing, handed to `combine` beside the body tags: the rule
|
|
372
|
+
* as the caller wrote it, and one `BodyMatch` per body literal in
|
|
373
|
+
* rule-body order. An algebra that only folds tags ignores it.
|
|
374
|
+
*/
|
|
375
|
+
interface Derivation {
|
|
376
|
+
rule: Rule;
|
|
377
|
+
body: readonly BodyMatch[];
|
|
378
|
+
}
|
|
197
379
|
/**
|
|
198
380
|
* Assign each derived relation a stratum such that positive
|
|
199
381
|
* dependencies never decrease the stratum and negative dependencies
|
|
@@ -207,8 +389,13 @@ declare function stratify(rules: Rule[]): Rule[][];
|
|
|
207
389
|
* place. Rules are stratified first; within a stratum, semi-naïve
|
|
208
390
|
* iteration joins each rule against the last round's delta so work is
|
|
209
391
|
* proportional to new facts, not all facts.
|
|
392
|
+
*
|
|
393
|
+
* With an `algebra`, every derivation also tags its conclusion (see
|
|
394
|
+
* `TagAlgebra`). Supply the same algebra on every evaluation of a rule
|
|
395
|
+
* set over a database: a resumed run derives only from the new facts,
|
|
396
|
+
* so it tags only what those reach.
|
|
210
397
|
*/
|
|
211
|
-
declare function evaluate(db: Database, rules: Rule[]): Database;
|
|
398
|
+
declare function evaluate<Tag = never>(db: Database, rules: Rule[], algebra?: TagAlgebra<Tag>): Database;
|
|
212
399
|
/**
|
|
213
400
|
* Empty these relations, and leave `rules` able to carry on from where
|
|
214
401
|
* it got to.
|
|
@@ -232,4 +419,4 @@ declare function evaluate(db: Database, rules: Rule[]): Database;
|
|
|
232
419
|
*/
|
|
233
420
|
declare function clearRelations(db: Database, rules: Rule[], relations: readonly string[]): void;
|
|
234
421
|
|
|
235
|
-
export { type Atom, Database, type EvaluationProfile, type Literal, type OnDemandRules, type Rule, type RuleCost, type Term, type Tuple, clearRelations, constant, deriveOnDemand, evaluate, formatProfile, lit, notLit, profileEvaluation, profileEvaluationAsync, rule, stratify, tupleKey, tupleKeyParts, variable };
|
|
422
|
+
export { type AddOutcome, type Atom, type BodyMatch, type ConfidenceLevel, Database, 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 };
|