@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.cjs +237 -96
- package/dist/index.d.cts +89 -7
- package/dist/index.d.ts +89 -7
- package/dist/index.js +233 -96
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -27,6 +27,7 @@ function deriveOnDemand(rules, complete) {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
const derives = (relation) => rulesByHead.has(relation);
|
|
30
|
+
const labels = labelColumns(rules, derives);
|
|
30
31
|
const asked = /* @__PURE__ */ new Map();
|
|
31
32
|
const pending = [];
|
|
32
33
|
const want = (adorned) => {
|
|
@@ -48,7 +49,7 @@ function deriveOnDemand(rules, complete) {
|
|
|
48
49
|
while (pending.length > 0) {
|
|
49
50
|
const adorned = pending.pop();
|
|
50
51
|
for (const written of rulesByHead.get(adorned.relation) ?? []) {
|
|
51
|
-
const r = boundFirst(written, adorned.adornment);
|
|
52
|
+
const r = boundFirst(written, adorned.adornment, labels);
|
|
52
53
|
const adornments = bodyAdornments(r, adorned.adornment, derives);
|
|
53
54
|
r.body.forEach((literal, at) => {
|
|
54
55
|
const adornment = adornments[at];
|
|
@@ -91,7 +92,7 @@ function deriveOnDemand(rules, complete) {
|
|
|
91
92
|
});
|
|
92
93
|
}
|
|
93
94
|
for (const written of rulesByHead.get(adorned.relation) ?? []) {
|
|
94
|
-
const r = boundFirst(written, adorned.adornment);
|
|
95
|
+
const r = boundFirst(written, adorned.adornment, labels);
|
|
95
96
|
const guard = anyBound(adorned.adornment) ? [
|
|
96
97
|
positiveLiteral(
|
|
97
98
|
demandOn(adorned.relation, adorned.adornment),
|
|
@@ -132,7 +133,30 @@ function deriveOnDemand(rules, complete) {
|
|
|
132
133
|
return { rules: rewritten, demandDriven: [...demandDriven], demands };
|
|
133
134
|
}
|
|
134
135
|
var sameLiteral = (premises, conclusion) => premises.length === 1 && JSON.stringify(premises[0]) === JSON.stringify(conclusion);
|
|
135
|
-
function
|
|
136
|
+
function labelColumns(rules, derives) {
|
|
137
|
+
const labels = /* @__PURE__ */ new Map();
|
|
138
|
+
const note = ({
|
|
139
|
+
relation,
|
|
140
|
+
terms
|
|
141
|
+
}) => {
|
|
142
|
+
if (!derives(relation)) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
terms.forEach((term, column) => {
|
|
146
|
+
if (term.type === "constant") {
|
|
147
|
+
const columns = labels.get(relation) ?? /* @__PURE__ */ new Set();
|
|
148
|
+
columns.add(column);
|
|
149
|
+
labels.set(relation, columns);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
for (const r of rules) {
|
|
154
|
+
note(r.head);
|
|
155
|
+
r.body.forEach(note);
|
|
156
|
+
}
|
|
157
|
+
return labels;
|
|
158
|
+
}
|
|
159
|
+
function boundFirst(r, headAdornment, labels) {
|
|
136
160
|
const bound = /* @__PURE__ */ new Set();
|
|
137
161
|
r.head.terms.forEach((term, column) => {
|
|
138
162
|
if (headAdornment[column] && term.type === "variable") {
|
|
@@ -140,7 +164,7 @@ function boundFirst(r, headAdornment) {
|
|
|
140
164
|
}
|
|
141
165
|
});
|
|
142
166
|
const boundColumns = (literal) => literal.terms.filter(
|
|
143
|
-
(term) => term.type === "constant" || bound.has(term.name)
|
|
167
|
+
(term, column) => !labels.get(literal.relation)?.has(column) && (term.type === "constant" || bound.has(term.name))
|
|
144
168
|
).length;
|
|
145
169
|
const remaining = [...r.body];
|
|
146
170
|
const body = [];
|
|
@@ -185,21 +209,43 @@ function bodyAdornments(r, headAdornment, derives) {
|
|
|
185
209
|
var open = [];
|
|
186
210
|
var isProfiling = () => open.length > 0;
|
|
187
211
|
var ruleKey = (head, body) => `${head} <- ${body.join(", ")}`;
|
|
188
|
-
function addCost(into, key, head, body, ms, derived) {
|
|
212
|
+
function addCost(into, key, head, body, ms, derived, examined) {
|
|
189
213
|
const existing = into.get(key);
|
|
190
214
|
if (existing === void 0) {
|
|
191
|
-
into.set(key, { head, body, ms, derived, attempts: 1 });
|
|
215
|
+
into.set(key, { head, body, ms, derived, examined, attempts: 1 });
|
|
192
216
|
return;
|
|
193
217
|
}
|
|
194
218
|
existing.ms += ms;
|
|
195
219
|
existing.derived += derived;
|
|
220
|
+
existing.examined += examined;
|
|
196
221
|
existing.attempts += 1;
|
|
197
222
|
}
|
|
198
|
-
function chargeRule(ruleSet, head, body, ms, derived) {
|
|
223
|
+
function chargeRule(ruleSet, head, body, ms, derived, examined) {
|
|
199
224
|
const key = ruleKey(head, body);
|
|
200
225
|
for (const collector of open) {
|
|
201
|
-
addCost(collector.rules, key, head, body, ms, derived);
|
|
202
|
-
addCost(
|
|
226
|
+
addCost(collector.rules, key, head, body, ms, derived, examined);
|
|
227
|
+
addCost(
|
|
228
|
+
setIn(collector, ruleSet).rules,
|
|
229
|
+
key,
|
|
230
|
+
head,
|
|
231
|
+
body,
|
|
232
|
+
ms,
|
|
233
|
+
derived,
|
|
234
|
+
examined
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function chargeAbandoned(question, examined) {
|
|
239
|
+
for (const collector of open) {
|
|
240
|
+
collector.abandoned.push({ question, examined });
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function chargeQuestion(outcome) {
|
|
244
|
+
for (const collector of open) {
|
|
245
|
+
collector.questions.asked += 1;
|
|
246
|
+
if (outcome !== void 0) {
|
|
247
|
+
collector.questions[outcome] += 1;
|
|
248
|
+
}
|
|
203
249
|
}
|
|
204
250
|
}
|
|
205
251
|
function chargeEvaluation(ruleSet) {
|
|
@@ -208,6 +254,13 @@ function chargeEvaluation(ruleSet) {
|
|
|
208
254
|
setIn(collector, ruleSet).evaluations += 1;
|
|
209
255
|
}
|
|
210
256
|
}
|
|
257
|
+
function chargeEvaluationRows(examined) {
|
|
258
|
+
for (const collector of open) {
|
|
259
|
+
if (examined > collector.largestEvaluation) {
|
|
260
|
+
collector.largestEvaluation = examined;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
211
264
|
function chargeRound(ruleSet) {
|
|
212
265
|
for (const collector of open) {
|
|
213
266
|
collector.rounds += 1;
|
|
@@ -267,7 +320,10 @@ function openScope() {
|
|
|
267
320
|
rules: /* @__PURE__ */ new Map(),
|
|
268
321
|
relations: /* @__PURE__ */ new Map(),
|
|
269
322
|
derivedRelations: /* @__PURE__ */ new Set(),
|
|
270
|
-
ruleSets: /* @__PURE__ */ new Map()
|
|
323
|
+
ruleSets: /* @__PURE__ */ new Map(),
|
|
324
|
+
abandoned: [],
|
|
325
|
+
largestEvaluation: 0,
|
|
326
|
+
questions: { asked: 0, abandoned: 0, skipped: 0 }
|
|
271
327
|
};
|
|
272
328
|
open.push(mine);
|
|
273
329
|
return mine;
|
|
@@ -285,6 +341,13 @@ var totalMs = (rules) => {
|
|
|
285
341
|
}
|
|
286
342
|
return ms;
|
|
287
343
|
};
|
|
344
|
+
var totalExamined = (rules) => {
|
|
345
|
+
let rows = 0;
|
|
346
|
+
for (const cost of rules) {
|
|
347
|
+
rows += cost.examined;
|
|
348
|
+
}
|
|
349
|
+
return rows;
|
|
350
|
+
};
|
|
288
351
|
var byCost = (a, b) => b.ms - a.ms;
|
|
289
352
|
function summarise(mine) {
|
|
290
353
|
return {
|
|
@@ -292,6 +355,10 @@ function summarise(mine) {
|
|
|
292
355
|
datalogMs: totalMs(mine.rules.values()),
|
|
293
356
|
rounds: mine.rounds,
|
|
294
357
|
evaluations: mine.evaluations,
|
|
358
|
+
examined: totalExamined(mine.rules.values()),
|
|
359
|
+
largestEvaluation: mine.largestEvaluation,
|
|
360
|
+
abandoned: [...mine.abandoned],
|
|
361
|
+
questions: { ...mine.questions },
|
|
295
362
|
relations: [...mine.relations].map(([relation, tuples]) => ({
|
|
296
363
|
relation,
|
|
297
364
|
tuples,
|
|
@@ -312,14 +379,26 @@ function ruleLines(rules, datalogMs) {
|
|
|
312
379
|
return rules.slice(0, 20).map((r) => {
|
|
313
380
|
const ms = `${r.ms.toFixed(0)}ms`.padStart(8);
|
|
314
381
|
const derived = String(r.derived).padStart(7);
|
|
315
|
-
|
|
382
|
+
const examined = String(r.examined).padStart(9);
|
|
383
|
+
return ` ${ms} ${share(r.ms, datalogMs)} ${derived} tuples ${examined} rows read ${r.attempts} attempts ${ruleKey(r.head, r.body)}`;
|
|
316
384
|
});
|
|
317
385
|
}
|
|
318
386
|
function formatProfile(profile) {
|
|
319
387
|
const lines = [];
|
|
320
388
|
lines.push(
|
|
321
|
-
`datalog: ${profile.datalogMs.toFixed(0)}ms (${share(profile.datalogMs, profile.wallMs).trim()} of ${profile.wallMs.toFixed(0)}ms wall), ${profile.evaluations} evaluations, ${profile.rounds} rounds`
|
|
389
|
+
`datalog: ${profile.datalogMs.toFixed(0)}ms (${share(profile.datalogMs, profile.wallMs).trim()} of ${profile.wallMs.toFixed(0)}ms wall), ${profile.evaluations} evaluations, ${profile.rounds} rounds, ${profile.examined} rows read (${profile.largestEvaluation} in the biggest evaluation)`
|
|
322
390
|
);
|
|
391
|
+
const { asked, abandoned, skipped } = profile.questions;
|
|
392
|
+
if (asked > 0) {
|
|
393
|
+
lines.push(
|
|
394
|
+
` ${asked} questions under a site: ${abandoned} given up part way, ${skipped} never put once the run's rows were spent`
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
for (const given of profile.abandoned) {
|
|
398
|
+
lines.push(
|
|
399
|
+
` gave up on ${given.question} after reading ${given.examined} rows`
|
|
400
|
+
);
|
|
401
|
+
}
|
|
323
402
|
const derivedTuples = profile.relations.filter((r) => r.derived).reduce((sum, r) => sum + r.tuples, 0);
|
|
324
403
|
lines.push(" tuples by relation (d = derived by a rule):");
|
|
325
404
|
for (const { relation, tuples, derived } of profile.relations.slice(0, 20)) {
|
|
@@ -733,93 +812,109 @@ function headTuple(head, bindings) {
|
|
|
733
812
|
return bound;
|
|
734
813
|
});
|
|
735
814
|
}
|
|
736
|
-
var
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
const known = byDelta.get(deltaIndex);
|
|
745
|
-
if (known !== void 0) {
|
|
746
|
-
return known;
|
|
747
|
-
}
|
|
748
|
-
const positives = r.body.map((literal, index) => literal.negated ? -1 : index).filter((index) => index !== -1);
|
|
749
|
-
const deltaAt = positives[deltaIndex] ?? -1;
|
|
750
|
-
const computed = { deltaAt, order: planBodyOrder(r, deltaAt) };
|
|
751
|
-
byDelta.set(deltaIndex, computed);
|
|
752
|
-
return computed;
|
|
815
|
+
var positiveLiterals = /* @__PURE__ */ new WeakMap();
|
|
816
|
+
function deltaLiteral(r, deltaIndex) {
|
|
817
|
+
let positives = positiveLiterals.get(r);
|
|
818
|
+
if (positives === void 0) {
|
|
819
|
+
positives = r.body.map((literal, index) => literal.negated ? -1 : index).filter((index) => index !== -1);
|
|
820
|
+
positiveLiterals.set(r, positives);
|
|
821
|
+
}
|
|
822
|
+
return positives[deltaIndex] ?? -1;
|
|
753
823
|
}
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
)
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
824
|
+
var allBound = (literal, bindings) => literal.terms.every(
|
|
825
|
+
(term) => term.type === "constant" || boundValue(bindings, term.name) !== void 0
|
|
826
|
+
);
|
|
827
|
+
var rowBudget = (limit) => ({
|
|
828
|
+
examined: 0,
|
|
829
|
+
limit
|
|
830
|
+
});
|
|
831
|
+
var OUT_OF_BUDGET = /* @__PURE__ */ Symbol("datalog row budget exhausted");
|
|
832
|
+
var BudgetExhausted = class extends Error {
|
|
833
|
+
constructor(ruleSet, examined) {
|
|
834
|
+
super(`gave up after reading ${examined} rows without settling`);
|
|
835
|
+
this.ruleSet = ruleSet;
|
|
836
|
+
this.examined = examined;
|
|
837
|
+
this.name = "BudgetExhausted";
|
|
838
|
+
}
|
|
839
|
+
ruleSet;
|
|
840
|
+
examined;
|
|
841
|
+
};
|
|
842
|
+
function evaluateRule(db, deltas, r, deltaIndex, budget) {
|
|
843
|
+
const results = [];
|
|
844
|
+
const body = r.body;
|
|
845
|
+
const deltaAt = deltaLiteral(r, deltaIndex);
|
|
846
|
+
const whole = 2 ** body.length - 1;
|
|
847
|
+
const walk2 = (index, source, taken, bindings) => {
|
|
848
|
+
const literal = body[index];
|
|
849
|
+
for (const tuple of source) {
|
|
850
|
+
budget.examined++;
|
|
851
|
+
if (budget.examined > budget.limit) {
|
|
852
|
+
throw OUT_OF_BUDGET;
|
|
853
|
+
}
|
|
854
|
+
const next = unify(literal, tuple, bindings);
|
|
855
|
+
if (next !== NO_MATCH) {
|
|
856
|
+
step(taken | 1 << index, next);
|
|
857
|
+
}
|
|
786
858
|
}
|
|
787
|
-
return firstWhere(() => true);
|
|
788
859
|
};
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
take(nextLiteral());
|
|
792
|
-
}
|
|
793
|
-
return order;
|
|
794
|
-
}
|
|
795
|
-
function evaluateRule(db, deltas, r, deltaIndex) {
|
|
796
|
-
const results = [];
|
|
797
|
-
const { deltaAt, order } = bodyPlan(r, deltaIndex);
|
|
798
|
-
const step = (orderIndex, bindings) => {
|
|
799
|
-
if (orderIndex === order.length) {
|
|
860
|
+
const step = (taken, bindings) => {
|
|
861
|
+
if (taken === whole) {
|
|
800
862
|
results.push(headTuple(r.head, bindings));
|
|
801
863
|
return;
|
|
802
864
|
}
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
if (
|
|
807
|
-
|
|
865
|
+
let pick = -1;
|
|
866
|
+
let narrowest = null;
|
|
867
|
+
for (let index = 0; index < body.length; index++) {
|
|
868
|
+
if (taken & 1 << index) {
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
871
|
+
const literal = body[index];
|
|
872
|
+
if (literal.negated) {
|
|
873
|
+
if (allBound(literal, bindings)) {
|
|
874
|
+
if (!db.has(literal.relation, groundNegated(literal, bindings))) {
|
|
875
|
+
step(taken | 1 << index, bindings);
|
|
876
|
+
}
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
881
|
+
const source = narrowedSource(db, literal, bindings);
|
|
882
|
+
if (source === null) {
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
if (source.length === 0) {
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
if (narrowest === null || source.length < narrowest.length) {
|
|
889
|
+
pick = index;
|
|
890
|
+
narrowest = source;
|
|
808
891
|
}
|
|
892
|
+
}
|
|
893
|
+
if (narrowest !== null) {
|
|
894
|
+
walk2(pick, narrowest, taken, bindings);
|
|
809
895
|
return;
|
|
810
896
|
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
897
|
+
for (let index = 0; index < body.length; index++) {
|
|
898
|
+
if (!(taken & 1 << index) && !body[index].negated) {
|
|
899
|
+
walk2(index, db.facts(body[index].relation), taken, bindings);
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
for (let index = 0; index < body.length; index++) {
|
|
904
|
+
if (!(taken & 1 << index)) {
|
|
905
|
+
groundNegated(body[index], bindings);
|
|
816
906
|
}
|
|
817
907
|
}
|
|
818
908
|
};
|
|
819
|
-
|
|
909
|
+
if (deltaAt === -1) {
|
|
910
|
+
step(0, null);
|
|
911
|
+
} else {
|
|
912
|
+
walk2(deltaAt, deltas.get(body[deltaAt].relation) ?? [], 0, null);
|
|
913
|
+
}
|
|
820
914
|
return results;
|
|
821
915
|
}
|
|
822
|
-
|
|
916
|
+
var boundSource = (db, literal, bindings) => narrowedSource(db, literal, bindings) ?? db.facts(literal.relation);
|
|
917
|
+
function narrowedSource(db, literal, bindings) {
|
|
823
918
|
let narrowest = null;
|
|
824
919
|
for (let column = 0; column < literal.terms.length; column++) {
|
|
825
920
|
const term = literal.terms[column];
|
|
@@ -835,10 +930,10 @@ function boundSource(db, literal, bindings) {
|
|
|
835
930
|
narrowest = bucket;
|
|
836
931
|
}
|
|
837
932
|
}
|
|
838
|
-
return narrowest
|
|
933
|
+
return narrowest;
|
|
839
934
|
}
|
|
840
935
|
var EMPTY_TAGS = [];
|
|
841
|
-
function evaluateRuleTagged(db, deltas, r, deltaIndex, algebra) {
|
|
936
|
+
function evaluateRuleTagged(db, deltas, r, deltaIndex, algebra, budget) {
|
|
842
937
|
const results = [];
|
|
843
938
|
const bodyTags = [];
|
|
844
939
|
const bodyMatches = [];
|
|
@@ -876,6 +971,7 @@ function evaluateRuleTagged(db, deltas, r, deltaIndex, algebra) {
|
|
|
876
971
|
}
|
|
877
972
|
const source = positiveIndex === deltaIndex ? deltas.get(literal.relation) ?? [] : boundSource(db, literal, bindings);
|
|
878
973
|
for (const tuple of source) {
|
|
974
|
+
budget.examined++;
|
|
879
975
|
const next = unify(literal, tuple, bindings);
|
|
880
976
|
if (next !== NO_MATCH) {
|
|
881
977
|
if (readsTags) {
|
|
@@ -959,15 +1055,18 @@ function currentMarks(db) {
|
|
|
959
1055
|
}
|
|
960
1056
|
return marks;
|
|
961
1057
|
}
|
|
962
|
-
function evaluate(db, rules, algebra) {
|
|
1058
|
+
function evaluate(db, rules, algebra, budget) {
|
|
963
1059
|
if (algebra !== void 0 && isDemandRewritten(rules)) {
|
|
964
1060
|
throw new Error(
|
|
965
1061
|
"cannot evaluate demand-rewritten rules with a tag algebra: tags would follow the demand-transformed rules rather than the ones the caller wrote"
|
|
966
1062
|
);
|
|
967
1063
|
}
|
|
1064
|
+
if (algebra !== void 0 && budget !== void 0) {
|
|
1065
|
+
throw new Error("cannot evaluate with both a tag algebra and a row budget");
|
|
1066
|
+
}
|
|
968
1067
|
deriving.set(db, (deriving.get(db) ?? 0) + 1);
|
|
969
1068
|
try {
|
|
970
|
-
return runRules(db, rules, algebra);
|
|
1069
|
+
return runRules(db, rules, algebra, budget);
|
|
971
1070
|
} finally {
|
|
972
1071
|
const depth = (deriving.get(db) ?? 1) - 1;
|
|
973
1072
|
if (depth === 0) {
|
|
@@ -1017,13 +1116,19 @@ function storedMergeFor(algebra) {
|
|
|
1017
1116
|
return stored === void 0 && merged === algebra.asserted ? void 0 : merged;
|
|
1018
1117
|
};
|
|
1019
1118
|
}
|
|
1020
|
-
function runRules(db, rules, algebra) {
|
|
1119
|
+
function runRules(db, rules, algebra, given) {
|
|
1021
1120
|
const {
|
|
1022
1121
|
signature,
|
|
1023
1122
|
name: ruleSetName,
|
|
1024
1123
|
derivedRelations,
|
|
1025
1124
|
strata
|
|
1026
1125
|
} = shapeOf(rules);
|
|
1126
|
+
const budget = given ?? {
|
|
1127
|
+
examined: 0,
|
|
1128
|
+
limit: Number.POSITIVE_INFINITY
|
|
1129
|
+
};
|
|
1130
|
+
const startedAt = budget.examined;
|
|
1131
|
+
const addedHere = given === void 0 ? null : [];
|
|
1027
1132
|
chargeEvaluation(ruleSetName);
|
|
1028
1133
|
const states = statesFor(db);
|
|
1029
1134
|
const state = states.get(signature) ?? {
|
|
@@ -1045,10 +1150,11 @@ function runRules(db, rules, algebra) {
|
|
|
1045
1150
|
derived.set(relation, ledger);
|
|
1046
1151
|
}
|
|
1047
1152
|
ledger.set(keyOf2(tuple), tuple);
|
|
1153
|
+
addedHere?.push([relation, tuple]);
|
|
1048
1154
|
};
|
|
1049
1155
|
const mergeStored = algebra === void 0 ? void 0 : storedMergeFor(algebra);
|
|
1050
1156
|
const marks = canResume(rules, state) ? state.marks : void 0;
|
|
1051
|
-
|
|
1157
|
+
const runStratum = (stratum) => {
|
|
1052
1158
|
let delta = /* @__PURE__ */ new Map();
|
|
1053
1159
|
const derivedHere = new Set(stratum.map((r) => r.head.relation));
|
|
1054
1160
|
const record = (relation, tuple) => {
|
|
@@ -1074,13 +1180,14 @@ function runRules(db, rules, algebra) {
|
|
|
1074
1180
|
seed,
|
|
1075
1181
|
r,
|
|
1076
1182
|
deltaIndex,
|
|
1077
|
-
algebra
|
|
1183
|
+
algebra,
|
|
1184
|
+
budget
|
|
1078
1185
|
)) {
|
|
1079
1186
|
recordTagged(r.head.relation, found.tuple, found.tag);
|
|
1080
1187
|
}
|
|
1081
1188
|
return;
|
|
1082
1189
|
}
|
|
1083
|
-
for (const tuple of evaluateRule(db, seed, r, deltaIndex)) {
|
|
1190
|
+
for (const tuple of evaluateRule(db, seed, r, deltaIndex, budget)) {
|
|
1084
1191
|
record(r.head.relation, tuple);
|
|
1085
1192
|
}
|
|
1086
1193
|
};
|
|
@@ -1089,15 +1196,17 @@ function runRules(db, rules, algebra) {
|
|
|
1089
1196
|
deriveInto(r, seed, deltaIndex);
|
|
1090
1197
|
return;
|
|
1091
1198
|
}
|
|
1092
|
-
const
|
|
1199
|
+
const startedAt2 = performance.now();
|
|
1093
1200
|
const before = db.size(r.head.relation);
|
|
1201
|
+
const readBefore = budget.examined;
|
|
1094
1202
|
deriveInto(r, seed, deltaIndex);
|
|
1095
1203
|
chargeRule(
|
|
1096
1204
|
ruleSetName,
|
|
1097
1205
|
r.head.relation,
|
|
1098
1206
|
r.body.map(literalName),
|
|
1099
|
-
performance.now() -
|
|
1100
|
-
db.size(r.head.relation) - before
|
|
1207
|
+
performance.now() - startedAt2,
|
|
1208
|
+
db.size(r.head.relation) - before,
|
|
1209
|
+
budget.examined - readBefore
|
|
1101
1210
|
);
|
|
1102
1211
|
};
|
|
1103
1212
|
const applyDelta = (seed, derivedOnly) => {
|
|
@@ -1142,7 +1251,24 @@ function runRules(db, rules, algebra) {
|
|
|
1142
1251
|
applyDelta(lastDelta, true);
|
|
1143
1252
|
chargeRound(ruleSetName);
|
|
1144
1253
|
}
|
|
1254
|
+
};
|
|
1255
|
+
const readHere = () => budget.examined - startedAt;
|
|
1256
|
+
try {
|
|
1257
|
+
for (const stratum of strata) {
|
|
1258
|
+
runStratum(stratum);
|
|
1259
|
+
}
|
|
1260
|
+
} catch (error) {
|
|
1261
|
+
if (error !== OUT_OF_BUDGET) {
|
|
1262
|
+
throw error;
|
|
1263
|
+
}
|
|
1264
|
+
for (const [relation, tuples] of byRelation(addedHere ?? [])) {
|
|
1265
|
+
db.retract(relation, tuples);
|
|
1266
|
+
}
|
|
1267
|
+
state.marks = null;
|
|
1268
|
+
chargeEvaluationRows(readHere());
|
|
1269
|
+
throw new BudgetExhausted(ruleSetName, readHere());
|
|
1145
1270
|
}
|
|
1271
|
+
chargeEvaluationRows(readHere());
|
|
1146
1272
|
state.marks = currentMarks(db);
|
|
1147
1273
|
if (isProfiling()) {
|
|
1148
1274
|
chargeRelationSizes(
|
|
@@ -1152,6 +1278,13 @@ function runRules(db, rules, algebra) {
|
|
|
1152
1278
|
}
|
|
1153
1279
|
return db;
|
|
1154
1280
|
}
|
|
1281
|
+
function byRelation(added) {
|
|
1282
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
1283
|
+
for (const [relation, tuple] of added) {
|
|
1284
|
+
addTo(buckets, relation, tuple);
|
|
1285
|
+
}
|
|
1286
|
+
return buckets;
|
|
1287
|
+
}
|
|
1155
1288
|
function addTo(buckets, relation, tuple) {
|
|
1156
1289
|
const bucket = buckets.get(relation);
|
|
1157
1290
|
if (bucket === void 0) {
|
|
@@ -1161,8 +1294,11 @@ function addTo(buckets, relation, tuple) {
|
|
|
1161
1294
|
}
|
|
1162
1295
|
}
|
|
1163
1296
|
export {
|
|
1297
|
+
BudgetExhausted,
|
|
1164
1298
|
Database,
|
|
1165
1299
|
Witness,
|
|
1300
|
+
chargeAbandoned,
|
|
1301
|
+
chargeQuestion,
|
|
1166
1302
|
clearRelations,
|
|
1167
1303
|
confidence,
|
|
1168
1304
|
confidenceWith,
|
|
@@ -1175,6 +1311,7 @@ export {
|
|
|
1175
1311
|
profileEvaluation,
|
|
1176
1312
|
profileEvaluationAsync,
|
|
1177
1313
|
proofOf,
|
|
1314
|
+
rowBudget,
|
|
1178
1315
|
rule,
|
|
1179
1316
|
ruleLabel,
|
|
1180
1317
|
stratify,
|
package/package.json
CHANGED