@suss/datalog 0.38.0 → 0.38.1
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.cjs +391 -20
- package/dist/index.d.cts +109 -1
- package/dist/index.d.ts +109 -1
- package/dist/index.js +390 -20
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -50,6 +50,108 @@ declare class FactIndex {
|
|
|
50
50
|
clear(): void;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* What an evaluation looked up, so a caller that cached its answer can
|
|
55
|
+
* tell later whether a fact that arrived since could change it.
|
|
56
|
+
*
|
|
57
|
+
* While `noteReads` runs, every lookup the join makes is noted as the
|
|
58
|
+
* relation, the column and the value, and a relation read whole is
|
|
59
|
+
* noted whole. At the end each lookup's facts are counted. The cached
|
|
60
|
+
* answer is still right when every count is the same and nothing was
|
|
61
|
+
* retracted from those relations. Without a retraction counts only
|
|
62
|
+
* grow, so the sum of the counts changes exactly when one of them does.
|
|
63
|
+
* DESIGN.md explains why a fact no lookup would find cannot change the
|
|
64
|
+
* answer, and what that asks of the rules.
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
/** @internal One relation's reads: the values looked up per column, unless something read it whole. */
|
|
68
|
+
interface RelationReads {
|
|
69
|
+
whole: boolean;
|
|
70
|
+
columns: (Set<Atom> | undefined)[];
|
|
71
|
+
}
|
|
72
|
+
/** @internal The join notes its reads here while `noteReads` runs. */
|
|
73
|
+
declare class ReadLog {
|
|
74
|
+
private readonly skipped;
|
|
75
|
+
/** Null for a relation the caller said to leave out. */
|
|
76
|
+
private readonly byRelation;
|
|
77
|
+
constructor(skipped: ReadonlySet<string>);
|
|
78
|
+
lookedUp(relation: string, column: number, value: Atom): void;
|
|
79
|
+
readWhole(relation: string): void;
|
|
80
|
+
/** Note everything an inner log noted, for an answer cached inside another. */
|
|
81
|
+
absorb(inner: ReadLog): void;
|
|
82
|
+
/**
|
|
83
|
+
* The reads with what each one finds in `db` now. One question looks
|
|
84
|
+
* up the same values in many columns, so each value is kept once, and
|
|
85
|
+
* each column keeps a bit per value saying whether it was looked up
|
|
86
|
+
* there.
|
|
87
|
+
*/
|
|
88
|
+
counted(db: Database): ReadsNoted;
|
|
89
|
+
/** @internal Null for a relation the caller said to leave out. */
|
|
90
|
+
readsOf(relation: string): RelationReads | null;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Group `g` is the column `columns[g]` of the relation at
|
|
94
|
+
* `names[groups[g]]`. Its bits start at word `g` times the words one
|
|
95
|
+
* group takes, and bit `i` says it looked up `values[i]`.
|
|
96
|
+
*/
|
|
97
|
+
interface Lookups {
|
|
98
|
+
names: readonly string[];
|
|
99
|
+
groups: readonly number[];
|
|
100
|
+
columns: readonly number[];
|
|
101
|
+
values: readonly Atom[];
|
|
102
|
+
bits: Uint32Array;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The lookups one evaluation made, and how many facts they found when
|
|
106
|
+
* it finished. The values are the atoms the facts already contain, so
|
|
107
|
+
* a value costs a reference, and each column it was looked up in costs
|
|
108
|
+
* a bit.
|
|
109
|
+
*/
|
|
110
|
+
declare class ReadsNoted {
|
|
111
|
+
private readonly skipped;
|
|
112
|
+
private readonly found;
|
|
113
|
+
private readonly names;
|
|
114
|
+
private readonly groups;
|
|
115
|
+
private readonly columns;
|
|
116
|
+
private readonly values;
|
|
117
|
+
private readonly bits;
|
|
118
|
+
private readonly words;
|
|
119
|
+
private readonly removed;
|
|
120
|
+
/** The facts in every relation read, when the counts last matched. */
|
|
121
|
+
private facts;
|
|
122
|
+
/** Each relation read, found by name the first time a check needs it. */
|
|
123
|
+
private readonly relations;
|
|
124
|
+
constructor(db: Database, skipped: ReadonlySet<string>, lookups: Lookups, found: number);
|
|
125
|
+
/**
|
|
126
|
+
* Whether every read would find what it found then. A fact left in a
|
|
127
|
+
* skipped relation would join a question asked now without anything
|
|
128
|
+
* here having looked it up, so one there makes the answer no. The
|
|
129
|
+
* counts are taken again only when a relation read has grown.
|
|
130
|
+
*/
|
|
131
|
+
unchangedIn(db: Database): boolean;
|
|
132
|
+
private relationAt;
|
|
133
|
+
private sumOver;
|
|
134
|
+
/**
|
|
135
|
+
* The facts the reads find, stopping once past `limit`. With nothing
|
|
136
|
+
* retracted every count only grows, so passing the old total already
|
|
137
|
+
* says that one of them grew.
|
|
138
|
+
*/
|
|
139
|
+
private countIn;
|
|
140
|
+
private countGroup;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Run `work` and note what the join and the database's readers look up
|
|
144
|
+
* in every relation outside `skipped`. A caller skips the
|
|
145
|
+
* relations it empties after every question, so that the work derives
|
|
146
|
+
* their facts again and the lookups behind them get noted. The reads
|
|
147
|
+
* are null when a skipped relation was not empty to begin with, since
|
|
148
|
+
* the work could then build on facts nothing here looked up.
|
|
149
|
+
*/
|
|
150
|
+
declare function noteReads<T>(db: Database, skipped: ReadonlySet<string>, work: () => T): {
|
|
151
|
+
value: T;
|
|
152
|
+
reads: ReadsNoted | null;
|
|
153
|
+
};
|
|
154
|
+
|
|
53
155
|
/**
|
|
54
156
|
* One relation's facts: which tuples are there, and an index per column
|
|
55
157
|
* so a join that has a value for a column does not scan them all.
|
|
@@ -78,6 +180,8 @@ interface Relation {
|
|
|
78
180
|
* kept up to date the same way as `columns`.
|
|
79
181
|
*/
|
|
80
182
|
combined: Map<number, CombinedIndex>;
|
|
183
|
+
/** How many facts have ever been retracted, which `ReadsNoted` checks before trusting a count. */
|
|
184
|
+
removed: number;
|
|
81
185
|
}
|
|
82
186
|
/**
|
|
83
187
|
* A tree of maps with one level per column in `columns`, lowest position
|
|
@@ -490,6 +594,8 @@ type AddOutcome = "added" | "improved" | "unchanged";
|
|
|
490
594
|
/** A set of facts per relation, with O(1) membership. */
|
|
491
595
|
declare class Database {
|
|
492
596
|
private readonly store;
|
|
597
|
+
/** @internal Where reads are noted while `noteReads` runs. */
|
|
598
|
+
readLog: ReadLog | null;
|
|
493
599
|
private relation;
|
|
494
600
|
/**
|
|
495
601
|
* The key this relation gives a tuple, whether or not the fact is
|
|
@@ -508,6 +614,8 @@ declare class Database {
|
|
|
508
614
|
/** The tag stored for this fact, or undefined when there is none. */
|
|
509
615
|
tagOf(relationName: string, tuple: Tuple): unknown;
|
|
510
616
|
has(relationName: string, tuple: Tuple): boolean;
|
|
617
|
+
/** A fact matching the whole tuple agrees on its first column, so that lookup is enough to note. */
|
|
618
|
+
private noteTupleRead;
|
|
511
619
|
facts(relationName: string): readonly Tuple[];
|
|
512
620
|
/**
|
|
513
621
|
* The facts with `value` at `column`. Once one of a literal's terms
|
|
@@ -671,4 +779,4 @@ declare function evaluate<Tag = never>(db: Database, rules: Rule[], algebra?: Ta
|
|
|
671
779
|
*/
|
|
672
780
|
declare function clearRelations(db: Database, rules: Rule[], relations: readonly string[]): void;
|
|
673
781
|
|
|
674
|
-
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 };
|
|
782
|
+
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, ReadsNoted, 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, noteReads, profileEvaluation, profileEvaluationAsync, proofOf, rowBudget, rule, ruleLabel, rulesDeriving, stratify, tupleKey, tupleKeyParts, variable, witnesses };
|
package/dist/index.d.ts
CHANGED
|
@@ -50,6 +50,108 @@ declare class FactIndex {
|
|
|
50
50
|
clear(): void;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* What an evaluation looked up, so a caller that cached its answer can
|
|
55
|
+
* tell later whether a fact that arrived since could change it.
|
|
56
|
+
*
|
|
57
|
+
* While `noteReads` runs, every lookup the join makes is noted as the
|
|
58
|
+
* relation, the column and the value, and a relation read whole is
|
|
59
|
+
* noted whole. At the end each lookup's facts are counted. The cached
|
|
60
|
+
* answer is still right when every count is the same and nothing was
|
|
61
|
+
* retracted from those relations. Without a retraction counts only
|
|
62
|
+
* grow, so the sum of the counts changes exactly when one of them does.
|
|
63
|
+
* DESIGN.md explains why a fact no lookup would find cannot change the
|
|
64
|
+
* answer, and what that asks of the rules.
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
/** @internal One relation's reads: the values looked up per column, unless something read it whole. */
|
|
68
|
+
interface RelationReads {
|
|
69
|
+
whole: boolean;
|
|
70
|
+
columns: (Set<Atom> | undefined)[];
|
|
71
|
+
}
|
|
72
|
+
/** @internal The join notes its reads here while `noteReads` runs. */
|
|
73
|
+
declare class ReadLog {
|
|
74
|
+
private readonly skipped;
|
|
75
|
+
/** Null for a relation the caller said to leave out. */
|
|
76
|
+
private readonly byRelation;
|
|
77
|
+
constructor(skipped: ReadonlySet<string>);
|
|
78
|
+
lookedUp(relation: string, column: number, value: Atom): void;
|
|
79
|
+
readWhole(relation: string): void;
|
|
80
|
+
/** Note everything an inner log noted, for an answer cached inside another. */
|
|
81
|
+
absorb(inner: ReadLog): void;
|
|
82
|
+
/**
|
|
83
|
+
* The reads with what each one finds in `db` now. One question looks
|
|
84
|
+
* up the same values in many columns, so each value is kept once, and
|
|
85
|
+
* each column keeps a bit per value saying whether it was looked up
|
|
86
|
+
* there.
|
|
87
|
+
*/
|
|
88
|
+
counted(db: Database): ReadsNoted;
|
|
89
|
+
/** @internal Null for a relation the caller said to leave out. */
|
|
90
|
+
readsOf(relation: string): RelationReads | null;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Group `g` is the column `columns[g]` of the relation at
|
|
94
|
+
* `names[groups[g]]`. Its bits start at word `g` times the words one
|
|
95
|
+
* group takes, and bit `i` says it looked up `values[i]`.
|
|
96
|
+
*/
|
|
97
|
+
interface Lookups {
|
|
98
|
+
names: readonly string[];
|
|
99
|
+
groups: readonly number[];
|
|
100
|
+
columns: readonly number[];
|
|
101
|
+
values: readonly Atom[];
|
|
102
|
+
bits: Uint32Array;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The lookups one evaluation made, and how many facts they found when
|
|
106
|
+
* it finished. The values are the atoms the facts already contain, so
|
|
107
|
+
* a value costs a reference, and each column it was looked up in costs
|
|
108
|
+
* a bit.
|
|
109
|
+
*/
|
|
110
|
+
declare class ReadsNoted {
|
|
111
|
+
private readonly skipped;
|
|
112
|
+
private readonly found;
|
|
113
|
+
private readonly names;
|
|
114
|
+
private readonly groups;
|
|
115
|
+
private readonly columns;
|
|
116
|
+
private readonly values;
|
|
117
|
+
private readonly bits;
|
|
118
|
+
private readonly words;
|
|
119
|
+
private readonly removed;
|
|
120
|
+
/** The facts in every relation read, when the counts last matched. */
|
|
121
|
+
private facts;
|
|
122
|
+
/** Each relation read, found by name the first time a check needs it. */
|
|
123
|
+
private readonly relations;
|
|
124
|
+
constructor(db: Database, skipped: ReadonlySet<string>, lookups: Lookups, found: number);
|
|
125
|
+
/**
|
|
126
|
+
* Whether every read would find what it found then. A fact left in a
|
|
127
|
+
* skipped relation would join a question asked now without anything
|
|
128
|
+
* here having looked it up, so one there makes the answer no. The
|
|
129
|
+
* counts are taken again only when a relation read has grown.
|
|
130
|
+
*/
|
|
131
|
+
unchangedIn(db: Database): boolean;
|
|
132
|
+
private relationAt;
|
|
133
|
+
private sumOver;
|
|
134
|
+
/**
|
|
135
|
+
* The facts the reads find, stopping once past `limit`. With nothing
|
|
136
|
+
* retracted every count only grows, so passing the old total already
|
|
137
|
+
* says that one of them grew.
|
|
138
|
+
*/
|
|
139
|
+
private countIn;
|
|
140
|
+
private countGroup;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Run `work` and note what the join and the database's readers look up
|
|
144
|
+
* in every relation outside `skipped`. A caller skips the
|
|
145
|
+
* relations it empties after every question, so that the work derives
|
|
146
|
+
* their facts again and the lookups behind them get noted. The reads
|
|
147
|
+
* are null when a skipped relation was not empty to begin with, since
|
|
148
|
+
* the work could then build on facts nothing here looked up.
|
|
149
|
+
*/
|
|
150
|
+
declare function noteReads<T>(db: Database, skipped: ReadonlySet<string>, work: () => T): {
|
|
151
|
+
value: T;
|
|
152
|
+
reads: ReadsNoted | null;
|
|
153
|
+
};
|
|
154
|
+
|
|
53
155
|
/**
|
|
54
156
|
* One relation's facts: which tuples are there, and an index per column
|
|
55
157
|
* so a join that has a value for a column does not scan them all.
|
|
@@ -78,6 +180,8 @@ interface Relation {
|
|
|
78
180
|
* kept up to date the same way as `columns`.
|
|
79
181
|
*/
|
|
80
182
|
combined: Map<number, CombinedIndex>;
|
|
183
|
+
/** How many facts have ever been retracted, which `ReadsNoted` checks before trusting a count. */
|
|
184
|
+
removed: number;
|
|
81
185
|
}
|
|
82
186
|
/**
|
|
83
187
|
* A tree of maps with one level per column in `columns`, lowest position
|
|
@@ -490,6 +594,8 @@ type AddOutcome = "added" | "improved" | "unchanged";
|
|
|
490
594
|
/** A set of facts per relation, with O(1) membership. */
|
|
491
595
|
declare class Database {
|
|
492
596
|
private readonly store;
|
|
597
|
+
/** @internal Where reads are noted while `noteReads` runs. */
|
|
598
|
+
readLog: ReadLog | null;
|
|
493
599
|
private relation;
|
|
494
600
|
/**
|
|
495
601
|
* The key this relation gives a tuple, whether or not the fact is
|
|
@@ -508,6 +614,8 @@ declare class Database {
|
|
|
508
614
|
/** The tag stored for this fact, or undefined when there is none. */
|
|
509
615
|
tagOf(relationName: string, tuple: Tuple): unknown;
|
|
510
616
|
has(relationName: string, tuple: Tuple): boolean;
|
|
617
|
+
/** A fact matching the whole tuple agrees on its first column, so that lookup is enough to note. */
|
|
618
|
+
private noteTupleRead;
|
|
511
619
|
facts(relationName: string): readonly Tuple[];
|
|
512
620
|
/**
|
|
513
621
|
* The facts with `value` at `column`. Once one of a literal's terms
|
|
@@ -671,4 +779,4 @@ declare function evaluate<Tag = never>(db: Database, rules: Rule[], algebra?: Ta
|
|
|
671
779
|
*/
|
|
672
780
|
declare function clearRelations(db: Database, rules: Rule[], relations: readonly string[]): void;
|
|
673
781
|
|
|
674
|
-
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 };
|
|
782
|
+
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, ReadsNoted, 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, noteReads, profileEvaluation, profileEvaluationAsync, proofOf, rowBudget, rule, ruleLabel, rulesDeriving, stratify, tupleKey, tupleKeyParts, variable, witnesses };
|