@hviana/sema 0.9.0 → 0.9.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/AGENTS.md +7 -7
- package/dist/src/alu/src/index.d.ts +1 -1
- package/dist/src/alu/src/index.js +1 -1
- package/dist/src/alu/src/parser.js +2 -6
- package/dist/src/alu/src/resonance.d.ts +13 -0
- package/dist/src/alu/src/resonance.js +41 -0
- package/dist/src/alu/test/alu.test.js +39 -0
- package/dist/src/bytes.d.ts +6 -2
- package/dist/src/bytes.js +10 -4
- package/dist/src/canon.js +44 -0
- package/dist/src/geometry.d.ts +19 -1
- package/dist/src/geometry.js +125 -141
- package/dist/src/meter.d.ts +33 -0
- package/dist/src/meter.js +34 -1
- package/dist/src/mind/articulation.js +14 -1
- package/dist/src/mind/attention.d.ts +12 -0
- package/dist/src/mind/attention.js +44 -16
- package/dist/src/mind/bridge.js +3 -3
- package/dist/src/mind/derivation.d.ts +40 -0
- package/dist/src/mind/derivation.js +34 -0
- package/dist/src/mind/evidence.d.ts +24 -0
- package/dist/src/mind/evidence.js +90 -0
- package/dist/src/mind/graph-search.d.ts +89 -15
- package/dist/src/mind/graph-search.js +345 -174
- package/dist/src/mind/learning.js +1 -1
- package/dist/src/mind/mechanisms/cover.d.ts +19 -3
- package/dist/src/mind/mechanisms/cover.js +142 -61
- package/dist/src/mind/mechanisms/recall.js +10 -3
- package/dist/src/mind/mind.d.ts +6 -0
- package/dist/src/mind/mind.js +5 -2
- package/dist/src/mind/pipeline.d.ts +5 -1
- package/dist/src/mind/pipeline.js +220 -90
- package/dist/src/mind/primitives.d.ts +25 -5
- package/dist/src/mind/primitives.js +107 -44
- package/dist/src/mind/reasoning.d.ts +18 -4
- package/dist/src/mind/reasoning.js +487 -328
- package/dist/src/mind/recognition.js +29 -13
- package/dist/src/mind/resonance.js +1 -11
- package/dist/src/mind/traverse.d.ts +45 -5
- package/dist/src/mind/traverse.js +285 -8
- package/dist/src/mind/types.d.ts +16 -1
- package/dist/src/store-sqlite.d.ts +25 -0
- package/dist/src/store-sqlite.js +89 -1
- package/dist/src/store.d.ts +48 -4
- package/dist/src/store.js +86 -6
- package/docs/INDEX.md +20 -19
- package/docs/INVARIANTS.md +17 -16
- package/docs/architecture/bounded-reads.md +1 -1
- package/docs/architecture/caches.md +5 -4
- package/docs/architecture/closure.md +45 -5
- package/docs/architecture/cost-model.md +16 -0
- package/docs/architecture/evidence.md +113 -0
- package/docs/architecture/exact-vs-approximate.md +10 -9
- package/docs/architecture/factored-machinery.md +14 -13
- package/docs/architecture/fold-contract.md +51 -1
- package/docs/architecture/mechanism-market.md +21 -0
- package/docs/architecture/memoization.md +3 -3
- package/docs/architecture/meter.md +2 -1
- package/docs/architecture/saturation.md +12 -0
- package/docs/architecture/store.md +25 -2
- package/docs/failures/tempting-but-wrong.md +13 -2
- package/docs/harness/gates.md +12 -10
- package/docs/mechanisms/cover.md +23 -6
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/alu/README.md +10 -2
- package/src/alu/src/index.ts +1 -0
- package/src/alu/src/parser.ts +6 -6
- package/src/alu/src/resonance.ts +42 -0
- package/src/alu/test/alu.test.ts +40 -0
- package/src/bytes.ts +13 -3
- package/src/canon.ts +40 -0
- package/src/geometry.ts +183 -154
- package/src/meter.ts +34 -1
- package/src/mind/articulation.ts +14 -2
- package/src/mind/attention.ts +47 -25
- package/src/mind/bridge.ts +3 -3
- package/src/mind/derivation.ts +77 -0
- package/src/mind/evidence.ts +107 -0
- package/src/mind/graph-search.ts +449 -221
- package/src/mind/learning.ts +1 -7
- package/src/mind/match.ts +1 -2
- package/src/mind/mechanisms/cast.ts +1 -2
- package/src/mind/mechanisms/cover.ts +207 -87
- package/src/mind/mechanisms/extraction.ts +1 -2
- package/src/mind/mechanisms/prefix-completion.ts +1 -1
- package/src/mind/mechanisms/recall.ts +17 -5
- package/src/mind/mechanisms/reference.ts +1 -1
- package/src/mind/mind.ts +9 -30
- package/src/mind/pipeline.ts +263 -104
- package/src/mind/primitives.ts +119 -43
- package/src/mind/reasoning.ts +611 -419
- package/src/mind/recognition.ts +24 -9
- package/src/mind/resonance.ts +2 -16
- package/src/mind/trace.ts +1 -1
- package/src/mind/traverse.ts +321 -8
- package/src/mind/types.ts +15 -11
- package/src/store-sqlite.ts +92 -1
- package/src/store.ts +113 -7
- package/test/105-derive-through-reports-its-refusal.test.mjs +8 -5
- package/test/106-the-join-fires.test.mjs +21 -0
- package/test/111-the-cover-assembly-is-counted.test.mjs +8 -5
- package/test/128-the-leads-somewhere-pair-agrees.test.mjs +18 -12
- package/test/136-the-two-named-limits.test.mjs +3 -2
- package/test/137-the-law-lives-once-and-below.test.mjs +21 -0
- package/test/148-exact-shortcuts-agree.test.mjs +188 -0
- package/test/149-the-closure-engine.test.mjs +138 -0
- package/test/150-the-join-is-output-sensitive.test.mjs +66 -0
- package/test/151-the-cover-pays-for-what-it-reaches.test.mjs +142 -0
- package/test/152-the-read-side-names-as-the-write-side.test.mjs +146 -0
- package/test/153-a-cheaper-bound-is-looked-at-first.test.mjs +155 -0
- package/test/154-the-question-names-the-step.test.mjs +281 -0
- package/test/24-generalization.test.mjs +32 -0
- package/test/36-bloom.test.mjs +53 -0
- package/test/37-cluster-dispersion-fusion.test.mjs +75 -0
- package/test/48-recognise-turn-connective.test.mjs +3 -2
- package/test/55-cost-meter.test.mjs +4 -4
- package/test/90-connector-read-cap.test.mjs +7 -7
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
import { isChunk } from "../sema.js";
|
|
10
10
|
import { lightestDerivation, } from "../derive/src/index.js";
|
|
11
11
|
import { composeStructuralGist, consensusFloor, dominates, estimatorNoise, } from "../geometry.js";
|
|
12
|
-
import { foldTree, gistOf,
|
|
12
|
+
import { foldTree, gistOf, perceive, read, resolve } from "./primitives.js";
|
|
13
13
|
import { recognise } from "./recognition.js";
|
|
14
14
|
import { leafIdRun } from "./canonical.js";
|
|
15
15
|
import { atomIsHub, corpusN, edgeAncestors, hubBound, sharedReachMemo, } from "./traverse.js";
|
|
16
16
|
import { cachedRead, junctionContainersFrom, junctionSeeds, junctionSynonyms, loadJunctionSynonymSides, walkCache, } from "./junction.js";
|
|
17
|
-
import { indexOf } from "../bytes.js";
|
|
17
|
+
import { indexOf, latin1 } from "../bytes.js";
|
|
18
18
|
import { restates } from "./derivation.js";
|
|
19
19
|
import { rItem, rNode, traceDerivation } from "./trace.js";
|
|
20
20
|
function newTraceDraft(perceivedCount) {
|
|
@@ -74,7 +74,7 @@ export async function climbAttentionAll(ctx, query, k, mode = "inverse") {
|
|
|
74
74
|
// Content-keyed memo — works for both single-turn respond() and multi-turn
|
|
75
75
|
// respondTurn().
|
|
76
76
|
if (ctx.climbMemo) {
|
|
77
|
-
const contentKey =
|
|
77
|
+
const contentKey = latin1(query);
|
|
78
78
|
const modeKey = `${k}:${mode}`;
|
|
79
79
|
let byRead = ctx.climbMemo.get(contentKey);
|
|
80
80
|
if (byRead === undefined) {
|
|
@@ -1125,7 +1125,7 @@ export function poolVotes(ctx, regionVotes, sat, N, td) {
|
|
|
1125
1125
|
* Strict `<` (not `<=`): verified against gap 3.1's own "gender equality"
|
|
1126
1126
|
* root, whose two genuine clusters sit EXACTLY W bytes apart — `<= W`
|
|
1127
1127
|
* would wrongly merge them into one and break that pinned requirement. */
|
|
1128
|
-
function countClusters(spans, W) {
|
|
1128
|
+
export function countClusters(spans, W) {
|
|
1129
1129
|
if (spans.length === 0)
|
|
1130
1130
|
return 0;
|
|
1131
1131
|
const sorted = [...spans].sort((a, b) => a[0] - b[0]);
|
|
@@ -1521,19 +1521,47 @@ export function canonicalChunkId(ctx, regionBytes, N, reachMemo) {
|
|
|
1521
1521
|
if (len < 2)
|
|
1522
1522
|
return flatId;
|
|
1523
1523
|
// Within one window, the widest reach is still the right CANONICAL
|
|
1524
|
-
// identity — a chunk's anchor should be its most general stable form.
|
|
1525
|
-
|
|
1526
|
-
|
|
1524
|
+
// identity — a chunk's anchor should be its most general stable form. A
|
|
1525
|
+
// SATURATED reach is the widest there is: its count is wherever the climb
|
|
1526
|
+
// happened to stop deciding, so an unsaturated prefix never out-widens it
|
|
1527
|
+
// by comparing against that count. (It used to: on test/24's gap-3.1
|
|
1528
|
+
// fixture the region `ace and ` had its saturated window replaced by the
|
|
1529
|
+
// unsaturated 2-byte prefix `e `, and that sub-window anchor voted.)
|
|
1530
|
+
//
|
|
1531
|
+
// So a window's anchor is its SHORTEST saturated form when any saturates,
|
|
1532
|
+
// and its widest-reaching form otherwise — and the forms are climbed
|
|
1533
|
+
// shortest first, stopping at the first that saturates: the forms longer
|
|
1534
|
+
// than it cannot change the anchor. Measured over 8 queries on the
|
|
1535
|
+
// 31.7M-node store: 339 of 347 windows saturate, 312 already at their
|
|
1536
|
+
// 2-byte prefix, while the full window and the 3-byte prefix — climbed
|
|
1537
|
+
// first, to √N contexts each — were 89% of the visits.
|
|
1538
|
+
const forms = [flatId];
|
|
1527
1539
|
for (let k2 = 1; k2 < len; k2++) {
|
|
1528
|
-
const
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1540
|
+
const shortId = ctx.store.findBranch(ids.slice(0, len - k2));
|
|
1541
|
+
if (shortId !== null)
|
|
1542
|
+
forms.push(shortId);
|
|
1543
|
+
}
|
|
1544
|
+
const reaches = new Array(forms.length);
|
|
1545
|
+
let bestId = null;
|
|
1546
|
+
let bestReach = null;
|
|
1547
|
+
for (let f = forms.length - 1; f >= 0; f--) {
|
|
1548
|
+
const r = edgeAncestors(ctx, forms[f], N, reachMemo);
|
|
1549
|
+
if (r.saturated) {
|
|
1550
|
+
bestId = forms[f];
|
|
1551
|
+
bestReach = r;
|
|
1552
|
+
break;
|
|
1553
|
+
}
|
|
1554
|
+
reaches[f] = r;
|
|
1555
|
+
}
|
|
1556
|
+
if (bestId === null || bestReach === null) {
|
|
1557
|
+
// None saturates: the widest reach, the longer form on a tie.
|
|
1558
|
+
bestId = forms[0];
|
|
1559
|
+
bestReach = reaches[0];
|
|
1560
|
+
for (let f = 1; f < forms.length; f++) {
|
|
1561
|
+
if (reaches[f].contextsReached > bestReach.contextsReached) {
|
|
1562
|
+
bestId = forms[f];
|
|
1563
|
+
bestReach = reaches[f];
|
|
1564
|
+
}
|
|
1537
1565
|
}
|
|
1538
1566
|
}
|
|
1539
1567
|
if (fallback === null)
|
package/dist/src/mind/bridge.js
CHANGED
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
import { cosine } from "../vec.js";
|
|
93
93
|
import { conceptThreshold, dominates, significanceBar } from "../geometry.js";
|
|
94
94
|
import { bytesEqual, indexOf } from "../bytes.js";
|
|
95
|
-
import {
|
|
95
|
+
import { exactNode, perceive, read } from "./primitives.js";
|
|
96
96
|
import { chainReach, leafIdRun } from "./canonical.js";
|
|
97
97
|
import { allWindowsAreScaffolding, corpusN, edgeAncestors, hubBound, sharedReachMemo, } from "./traverse.js";
|
|
98
98
|
import { rItem, rNode } from "./trace.js";
|
|
@@ -438,7 +438,7 @@ async function bridgeImpl(ctx, query, proposed) {
|
|
|
438
438
|
continue;
|
|
439
439
|
let use = sid;
|
|
440
440
|
if (!ctx.store.hasNext(use)) {
|
|
441
|
-
const folded =
|
|
441
|
+
const folded = exactNode(ctx, tb);
|
|
442
442
|
if (folded === null || folded === sid || !ctx.store.hasNext(folded)) {
|
|
443
443
|
continue;
|
|
444
444
|
}
|
|
@@ -457,7 +457,7 @@ async function bridgeImpl(ctx, query, proposed) {
|
|
|
457
457
|
// be a FLAT content twin whose continuation edge lives on the
|
|
458
458
|
// fold-shaped deposit node with the same bytes — the same twin split
|
|
459
459
|
// canonResolve bridges by re-folding (primitives.ts) — but the re-fold
|
|
460
|
-
// (
|
|
460
|
+
// (an identity fold of the candidate's bytes) is paid only for proposals
|
|
461
461
|
// that could align at all: alignment can only seed at a picked anchor
|
|
462
462
|
// window occurring literally in the candidate (measured: unconditional
|
|
463
463
|
// re-folds multiplied the refusal-path latency several-fold).
|
|
@@ -204,3 +204,43 @@ export declare function closure(d: DerivationState, query: Uint8Array, W: number
|
|
|
204
204
|
onTaken?: (before: DerivationState, after: DerivationState, witnesses: ReadonlyArray<Witness>) => void,
|
|
205
205
|
/** Called when the law refused the continuation the layer offered. */
|
|
206
206
|
onRefused?: (at: DerivationState) => void): Promise<DerivationState>;
|
|
207
|
+
/** A LAYER of the closure — a named producer of continuations for one state.
|
|
208
|
+
* The layer OFFERS (its {@link Offer} is the whole of what it decides); the
|
|
209
|
+
* law admits; the engine sequences. The hooks are the layer's own
|
|
210
|
+
* instrumentation, called by the engine at the moments the law defines. */
|
|
211
|
+
export interface ClosureLayer {
|
|
212
|
+
/** The layer's name — its rationale scope and its meter phase. */
|
|
213
|
+
readonly name: string;
|
|
214
|
+
/** Whether the layer has anything to offer this state at all. A layer that
|
|
215
|
+
* does not engage is not ENTERED: no offer is asked and no work is charged
|
|
216
|
+
* to it. Omitted: it always engages. */
|
|
217
|
+
readonly engages?: (d: DerivationState) => boolean;
|
|
218
|
+
/** The layer's continuations, one at a time ({@link Offer}'s contract). */
|
|
219
|
+
readonly offer: Offer;
|
|
220
|
+
/** Called for each step the law admits, with the state before and after. */
|
|
221
|
+
readonly onTaken?: (before: DerivationState, after: DerivationState, witnesses: ReadonlyArray<Witness>) => void;
|
|
222
|
+
/** Called when the law refused the continuation the layer offered. */
|
|
223
|
+
readonly onRefused?: (at: DerivationState) => void;
|
|
224
|
+
/** Called once the layer's walk has ended — the layer offered nothing more,
|
|
225
|
+
* or the law refused — with the state it began from and the one it reached. */
|
|
226
|
+
readonly onEnd?: (from: DerivationState, to: DerivationState) => void;
|
|
227
|
+
}
|
|
228
|
+
/** THE CLOSURE ENGINE — close a derivation under the law, layer by layer.
|
|
229
|
+
*
|
|
230
|
+
* Each layer is walked to its own end ({@link closure}: the layer offers, the
|
|
231
|
+
* law admits or refuses), and the state it reaches is the state the next layer
|
|
232
|
+
* is offered against. Layers are PHASES, in the order given, never revisited:
|
|
233
|
+
* a later layer composes what an earlier one produced, and nothing it adds is
|
|
234
|
+
* re-offered to the earlier one.
|
|
235
|
+
*
|
|
236
|
+
* THE LAW'S FIRST CLAUSE IS APPLIED HERE, ONCE. A FIXED state admits no
|
|
237
|
+
* transition (`admissible` refuses it before anything else), so no layer is
|
|
238
|
+
* entered at all — the decision a caller used to spell as "skip the walk for a
|
|
239
|
+
* complete grounding, and skip the fusion too" is the law's, not the caller's,
|
|
240
|
+
* and it is taken before any layer pays for an offer it could never have
|
|
241
|
+
* taken.
|
|
242
|
+
*
|
|
243
|
+
* `enter` wraps each entered layer's walk — the caller's instrumentation (a
|
|
244
|
+
* meter phase), which this module cannot import: it reads nothing but the
|
|
245
|
+
* state and the layers. */
|
|
246
|
+
export declare function closeOver(d: DerivationState, query: Uint8Array, W: number, layers: ReadonlyArray<ClosureLayer>, enter?: (name: string, walk: () => Promise<DerivationState>) => Promise<DerivationState>): Promise<DerivationState>;
|
|
@@ -334,3 +334,37 @@ onRefused) {
|
|
|
334
334
|
d = next;
|
|
335
335
|
}
|
|
336
336
|
}
|
|
337
|
+
/** THE CLOSURE ENGINE — close a derivation under the law, layer by layer.
|
|
338
|
+
*
|
|
339
|
+
* Each layer is walked to its own end ({@link closure}: the layer offers, the
|
|
340
|
+
* law admits or refuses), and the state it reaches is the state the next layer
|
|
341
|
+
* is offered against. Layers are PHASES, in the order given, never revisited:
|
|
342
|
+
* a later layer composes what an earlier one produced, and nothing it adds is
|
|
343
|
+
* re-offered to the earlier one.
|
|
344
|
+
*
|
|
345
|
+
* THE LAW'S FIRST CLAUSE IS APPLIED HERE, ONCE. A FIXED state admits no
|
|
346
|
+
* transition (`admissible` refuses it before anything else), so no layer is
|
|
347
|
+
* entered at all — the decision a caller used to spell as "skip the walk for a
|
|
348
|
+
* complete grounding, and skip the fusion too" is the law's, not the caller's,
|
|
349
|
+
* and it is taken before any layer pays for an offer it could never have
|
|
350
|
+
* taken.
|
|
351
|
+
*
|
|
352
|
+
* `enter` wraps each entered layer's walk — the caller's instrumentation (a
|
|
353
|
+
* meter phase), which this module cannot import: it reads nothing but the
|
|
354
|
+
* state and the layers. */
|
|
355
|
+
export async function closeOver(d, query, W, layers, enter) {
|
|
356
|
+
for (const layer of layers) {
|
|
357
|
+
if (d.fixed)
|
|
358
|
+
return d;
|
|
359
|
+
if (layer.engages !== undefined && !layer.engages(d))
|
|
360
|
+
continue;
|
|
361
|
+
const from = d;
|
|
362
|
+
const walk = async () => {
|
|
363
|
+
const to = await closure(from, query, W, layer.offer, layer.onTaken, layer.onRefused);
|
|
364
|
+
layer.onEnd?.(from, to);
|
|
365
|
+
return to;
|
|
366
|
+
};
|
|
367
|
+
d = enter === undefined ? await walk() : await enter(layer.name, walk);
|
|
368
|
+
}
|
|
369
|
+
return d;
|
|
370
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Where each W-window of one source first occurs. Built once per source and
|
|
2
|
+
* reused across every form asked against it. */
|
|
3
|
+
export type WindowIndex = Map<string, number>;
|
|
4
|
+
export declare function windowIndex(bytes: Uint8Array, W: number): WindowIndex;
|
|
5
|
+
/** The windows of `index` (over `bytes`) that `spoken` does not hold — what
|
|
6
|
+
* of the question no product of the derivation has restated yet. The keys
|
|
7
|
+
* keep their positions in `bytes`, so a witnessing span still names the
|
|
8
|
+
* asker's bytes. */
|
|
9
|
+
export declare function unspoken(index: WindowIndex, spoken: ReadonlyArray<WindowIndex>): WindowIndex;
|
|
10
|
+
/** How a form is witnessed by a list of sources. */
|
|
11
|
+
export interface Witnessing {
|
|
12
|
+
/** Every byte of the form lies in a window some source holds. */
|
|
13
|
+
complete: boolean;
|
|
14
|
+
/** The windows SOURCE 0 alone supplied — windows no later source holds — as
|
|
15
|
+
* merged spans of source 0. With the question as source 0, this is what the
|
|
16
|
+
* asker said about the form that the derivation did not already have. */
|
|
17
|
+
spans: Array<[number, number]>;
|
|
18
|
+
/** Bytes of source 0 inside `spans`. */
|
|
19
|
+
bytes: number;
|
|
20
|
+
}
|
|
21
|
+
/** Witness `form` against `sources` (their window indexes, same order). A
|
|
22
|
+
* window is credited to the LAST source holding it — so source 0 is credited
|
|
23
|
+
* only with what nothing else at hand supplies. */
|
|
24
|
+
export declare function witness(form: Uint8Array, indexes: ReadonlyArray<WindowIndex>, W: number): Witnessing;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// evidence.ts — what of a stored form the material at hand WITNESSES.
|
|
2
|
+
//
|
|
3
|
+
// A stored form is WITNESSED by some material when every one of its bytes lies
|
|
4
|
+
// inside a W-window that occurs literally in that material — in any order, at
|
|
5
|
+
// any place. It is the order-free reading of correspondence (`alignRuns` is
|
|
6
|
+
// its run-producing sibling, `junctionContainersFrom(…, unordered)` its
|
|
7
|
+
// container-finding one): a learnt form is evidenced by bytes that hold all of
|
|
8
|
+
// its windows, whichever way the asker happened to arrange them.
|
|
9
|
+
//
|
|
10
|
+
// The material is a LIST of sources because what a derivation has at hand is
|
|
11
|
+
// more than the question: the node it stands on is material too. A trained
|
|
12
|
+
// question `Edmond T. Gréville place of death` is in neither the asker's
|
|
13
|
+
// `Where was the place of death of the director of film Beat Girl?` nor the
|
|
14
|
+
// fact `The director of Beat Girl is Edmond T. Gréville.` the first hop reached
|
|
15
|
+
// — and both together hold every byte of it. The witness records which source
|
|
16
|
+
// held each window, so a consumer can tell the question's share (what the asker
|
|
17
|
+
// SAID about the form) from what the derivation itself brought.
|
|
18
|
+
//
|
|
19
|
+
// Exact, deterministic and linear: one window index per source, one probe per
|
|
20
|
+
// window of the form. Below one window a byte agreement is chance (the floor
|
|
21
|
+
// identityBar, attestedQ and the site test all draw), so a form shorter than W
|
|
22
|
+
// is never witnessed.
|
|
23
|
+
//
|
|
24
|
+
// Layering: bytes only — importable from traverse.ts and everything above it.
|
|
25
|
+
import { latin1 } from "../bytes.js";
|
|
26
|
+
export function windowIndex(bytes, W) {
|
|
27
|
+
const index = new Map();
|
|
28
|
+
for (let o = 0; o + W <= bytes.length; o++) {
|
|
29
|
+
const key = latin1(bytes.subarray(o, o + W));
|
|
30
|
+
if (!index.has(key))
|
|
31
|
+
index.set(key, o);
|
|
32
|
+
}
|
|
33
|
+
return index;
|
|
34
|
+
}
|
|
35
|
+
/** The windows of `index` (over `bytes`) that `spoken` does not hold — what
|
|
36
|
+
* of the question no product of the derivation has restated yet. The keys
|
|
37
|
+
* keep their positions in `bytes`, so a witnessing span still names the
|
|
38
|
+
* asker's bytes. */
|
|
39
|
+
export function unspoken(index, spoken) {
|
|
40
|
+
const out = new Map();
|
|
41
|
+
for (const [key, at] of index) {
|
|
42
|
+
if (!spoken.some((s) => s.has(key)))
|
|
43
|
+
out.set(key, at);
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
/** Witness `form` against `sources` (their window indexes, same order). A
|
|
48
|
+
* window is credited to the LAST source holding it — so source 0 is credited
|
|
49
|
+
* only with what nothing else at hand supplies. */
|
|
50
|
+
export function witness(form, indexes, W) {
|
|
51
|
+
const none = { complete: false, spans: [], bytes: 0 };
|
|
52
|
+
if (form.length < W || indexes.length === 0)
|
|
53
|
+
return none;
|
|
54
|
+
const covered = new Uint8Array(form.length);
|
|
55
|
+
const own = [];
|
|
56
|
+
for (let o = 0; o + W <= form.length; o++) {
|
|
57
|
+
const key = latin1(form.subarray(o, o + W));
|
|
58
|
+
let at = -1;
|
|
59
|
+
let from = -1;
|
|
60
|
+
for (let s = indexes.length - 1; s >= 0; s--) {
|
|
61
|
+
const p = indexes[s].get(key);
|
|
62
|
+
if (p !== undefined) {
|
|
63
|
+
at = p;
|
|
64
|
+
from = s;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (from < 0)
|
|
69
|
+
continue;
|
|
70
|
+
covered.fill(1, o, o + W);
|
|
71
|
+
if (from === 0)
|
|
72
|
+
own.push([at, at + W]);
|
|
73
|
+
}
|
|
74
|
+
for (let i = 0; i < form.length; i++)
|
|
75
|
+
if (!covered[i])
|
|
76
|
+
return none;
|
|
77
|
+
own.sort((a, b) => a[0] - b[0]);
|
|
78
|
+
const spans = [];
|
|
79
|
+
for (const [s, e] of own) {
|
|
80
|
+
const last = spans[spans.length - 1];
|
|
81
|
+
if (last !== undefined && s <= last[1])
|
|
82
|
+
last[1] = Math.max(last[1], e);
|
|
83
|
+
else
|
|
84
|
+
spans.push([s, e]);
|
|
85
|
+
}
|
|
86
|
+
let bytes = 0;
|
|
87
|
+
for (const [s, e] of spans)
|
|
88
|
+
bytes += e - s;
|
|
89
|
+
return { complete: true, spans, bytes };
|
|
90
|
+
}
|
|
@@ -46,6 +46,11 @@ export type GItem = {
|
|
|
46
46
|
* consolidated, more-explanatory reading wins over leaving the parts split.
|
|
47
47
|
* See {@link GraphSearch.fuse} and {@link GraphSearch.formRules}. */
|
|
48
48
|
rcmp?: boolean;
|
|
49
|
+
/** Set on a concept hop's ASKING form: the edge-less form `node` whose halo
|
|
50
|
+
* target the caller has not granted yet, held at the hop's own cost and
|
|
51
|
+
* span. Popping it asks for the target; it leads nowhere itself, so it
|
|
52
|
+
* is never part of a final derivation (see {@link Licence}). */
|
|
53
|
+
ask?: boolean;
|
|
49
54
|
} | {
|
|
50
55
|
kind: "out";
|
|
51
56
|
i: number;
|
|
@@ -145,6 +150,45 @@ export interface DerivationItem {
|
|
|
145
150
|
* conclusion shape (the rules carry no label, so this classifies by structure,
|
|
146
151
|
* the single place that maps rule geometry to a name). */
|
|
147
152
|
export type DerivationMove = "axiom" | "follow-edge" | "concept-hop" | "voice" | "ground" | "splice-connector" | "split" | "fuse" | "recompose" | "derive-through" | "bridge" | "pool-vote" | "step";
|
|
153
|
+
/** THE ASYNC PREMISES A COVER USES, RESOLVED ONLY WHERE THE SEARCH REACHES THEM.
|
|
154
|
+
*
|
|
155
|
+
* Two of the cover's rules need data the synchronous search cannot fetch — a
|
|
156
|
+
* connector splice (a bridge between two answers) and a concept hop (an
|
|
157
|
+
* edge-less form's halo sibling) — and both used to be resolved for EVERY
|
|
158
|
+
* candidate before the search ran. A rule fires only from popped premises,
|
|
159
|
+
* and the agenda pops in cost order up to the goal, so most of that work fed
|
|
160
|
+
* rules the search never reached: measured on the 31.7M-node store, a 262-byte
|
|
161
|
+
* query recognised whole paid 23 bridges (143,520 junction pops, 7.3 s of its
|
|
162
|
+
* 7.9 s) for sub-forms whose rewrites cost a STEP each and so never left the
|
|
163
|
+
* agenda before the one-STEP goal — and then 1.2 s of halo reads for concept
|
|
164
|
+
* hops priced at CONCEPT, ten times that goal.
|
|
165
|
+
*
|
|
166
|
+
* So the caller OFFERS what it can resolve; where the search reaches an offered
|
|
167
|
+
* key it uses a GRANTED value and ASKS for an ungranted one — a connector when
|
|
168
|
+
* its splice's premises meet, a concept target when the hop's conclusion, held
|
|
169
|
+
* as an asking form at the hop's own cost, is popped. A run that asked
|
|
170
|
+
* nothing is the run every key resolved in advance would have made: the pops
|
|
171
|
+
* are decided only by the rules fired from popped items, a granted key fires
|
|
172
|
+
* the very rule it fired before, and an asking form that was never popped fired
|
|
173
|
+
* none. So its cover is final. A run that asked is PROVISIONAL: the caller
|
|
174
|
+
* grants the asked keys (the async reads the search cannot run) and covers
|
|
175
|
+
* again; every round grants at least one offered key, so the rounds end. */
|
|
176
|
+
export interface Licence<K, V> {
|
|
177
|
+
/** Every key the caller can resolve. */
|
|
178
|
+
readonly offered: ReadonlySet<K>;
|
|
179
|
+
/** The keys resolved so far: their value, or null (nothing learnt). */
|
|
180
|
+
readonly granted: ReadonlyMap<K, V | null>;
|
|
181
|
+
/** Filled by the search: the offered keys it reached ungranted. */
|
|
182
|
+
readonly asked: Set<K>;
|
|
183
|
+
}
|
|
184
|
+
/** Connectors, keyed `L,R` by answer-node ids, valued by the learnt glue. */
|
|
185
|
+
export type ConnectorLicence = Licence<string, Uint8Array>;
|
|
186
|
+
/** Concept hops, keyed by an edge-less form's node, valued by the node its
|
|
187
|
+
* halo sibling's edge leads to. */
|
|
188
|
+
export type ConceptLicence = Licence<number, number>;
|
|
189
|
+
/** Nothing offered: a cover with no async premises (a nested completion,
|
|
190
|
+
* articulation's revoicing). */
|
|
191
|
+
export declare function noLicence<K, V>(): Licence<K, V>;
|
|
148
192
|
/** The lightest-derivation search over the Sema graph. One instance binds the
|
|
149
193
|
* store, `maxGroup` (the fusible span ceiling), and the canonical
|
|
150
194
|
* {@link resolve} callback; {@link cover} then solves one query. */
|
|
@@ -162,13 +206,14 @@ export declare class GraphSearch {
|
|
|
162
206
|
* recursive completion), and chooseNext (distributional-evidence edge
|
|
163
207
|
* disambiguation when a recognised form has multiple continuations). */
|
|
164
208
|
host: GraphSearchHost);
|
|
165
|
-
/** The
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
209
|
+
/** The hub bound √N (bounded-reads.md) — the ONE fan-out cap, stated here
|
|
210
|
+
* rather than imported from `traverse.ts` because this module is
|
|
211
|
+
* deliberately host-based (it holds a bare Store, never a MindContext).
|
|
212
|
+
* That is the same write/read-side duplication convention canonical.ts's
|
|
213
|
+
* header documents: if the formula changes it must change in BOTH places.
|
|
214
|
+
* It is stated ONCE per side, though — the expression used to be spelled
|
|
215
|
+
* out at three call sites here, one of them inside a per-item rules
|
|
216
|
+
* generator, and they had already drifted on the `Math.max(2, …)` floor. */
|
|
172
217
|
private hubBound;
|
|
173
218
|
/** Explore the Sema graph for the lightest cover of the query and return its
|
|
174
219
|
* chosen spans left-to-right — WITH the derivation's total weight (the g
|
|
@@ -185,8 +230,10 @@ export declare class GraphSearch {
|
|
|
185
230
|
*
|
|
186
231
|
* Any learnt connector between two rewrites is spliced IN by the in-search
|
|
187
232
|
* connector rule (see {@link outRules}), so the returned spans already carry
|
|
188
|
-
* it — there is no post-pass.
|
|
189
|
-
|
|
233
|
+
* it — there is no post-pass. With a {@link ConnectorLicence}, a cover
|
|
234
|
+
* returned while `connectors.asked` is non-empty is PROVISIONAL: grant the
|
|
235
|
+
* asked pairs and cover again. */
|
|
236
|
+
cover(queryLen: number, sites: ReadonlyArray<Site>, concepts: ConceptLicence, leaves: ReadonlyArray<Leaf>, splits: ReadonlySet<number>, substitutions?: ReadonlyMap<number, Uint8Array>, connectors?: ConnectorLicence, computedResults?: ReadonlyArray<ComputedResult>,
|
|
190
237
|
/** When given, receives each solved span's lightest derivation — the full
|
|
191
238
|
* adapted A*LD proof tree as classified {@link DerivationStep}s — for the
|
|
192
239
|
* TOP cover AND every nested completion the sink is threaded into (see
|
|
@@ -262,11 +309,12 @@ export declare class GraphSearch {
|
|
|
262
309
|
private bridgeRule;
|
|
263
310
|
/** The connector-SPLICE rule for an oriented (l, r) pair, or null when the
|
|
264
311
|
* pair does not qualify — the ONE body behind {@link outRules}' two
|
|
265
|
-
* mirror loops (this-as-left over
|
|
266
|
-
* over
|
|
312
|
+
* mirror loops (this-as-left over offered right partners, this-as-right
|
|
313
|
+
* over offered left partners). Fires only when both sides are
|
|
267
314
|
* recognised, r starts at or after l ends, and the gap between them is
|
|
268
315
|
* empty or wholly recognised — never across the asker's own literal
|
|
269
|
-
* separator.
|
|
316
|
+
* separator — and the pair's connector is granted and learnt. A qualifying
|
|
317
|
+
* pair not yet granted is ASKED for (see {@link ConnectorLicence}). */
|
|
270
318
|
private trySplice;
|
|
271
319
|
/** form(i,j,node,via): follow the graph out of `node`, or (in articulation)
|
|
272
320
|
* emit its substitute voice directly. */
|
|
@@ -307,6 +355,14 @@ export declare class GraphSearch {
|
|
|
307
355
|
* outs of a long query re-cover each distinct node at most once); reset at the
|
|
308
356
|
* top of {@link cover}. */
|
|
309
357
|
private recompleteMemo;
|
|
358
|
+
/** {@link entityProposals}, per fact BYTES — a pure function of them while
|
|
359
|
+
* the store is read-only (one response), so a fact the chart reaches as
|
|
360
|
+
* several items (cover/fix variants, nested completions) is scanned once.
|
|
361
|
+
* Reset at the top of {@link cover}, like {@link recompleteMemo}. */
|
|
362
|
+
private entityMemo;
|
|
363
|
+
/** The joins licensed under one cover's {@link Licence}, kept across its
|
|
364
|
+
* re-covers (see {@link solve}). */
|
|
365
|
+
private readonly joinsKept;
|
|
310
366
|
/** The derivation sink of the TOP cover, threaded into every nested
|
|
311
367
|
* completion so a produced form's own recompositions are reported in the
|
|
312
368
|
* same trace instead of vanishing after the first layer. Undefined when
|
|
@@ -321,6 +377,21 @@ export declare class GraphSearch {
|
|
|
321
377
|
* most once per cover. A Set, not a flag, because it states WHICH node is
|
|
322
378
|
* open — the invariant a reader needs to check the guard. */
|
|
323
379
|
private recompleteOpen;
|
|
380
|
+
/** The ENTITIES a produced fact's bytes contain that lead somewhere — the
|
|
381
|
+
* candidates a join may travel through. The forms the fact CONTAINS, by the
|
|
382
|
+
* same recogniser the query went through (so the evidence standard is the
|
|
383
|
+
* query's), plus — because the recognition of a STORED WHOLE returns the
|
|
384
|
+
* whole and stops (measured: one site, the fact's own node, for `The
|
|
385
|
+
* director of Eva is Gustaf Molander.`) — the longest canonically-resolving
|
|
386
|
+
* form at each offset. A byte atom is never a subject. The scan runs only
|
|
387
|
+
* for a FORM (≥ W: per letter it measured 20-26 s in test/99), and each probe
|
|
388
|
+
* is a `canonResolve` whose exact tier decides a miss by its segment probe,
|
|
389
|
+
* without a fold. The admission predicate is the store's `leadsSomewhere`
|
|
390
|
+
* (edge or halo); the host lends its memoised form when it can.
|
|
391
|
+
*
|
|
392
|
+
* The SOURCE of each proposal travels with it, so a refusal names which path
|
|
393
|
+
* proposed the candidate. Memoised per fact bytes ({@link entityMemo}). */
|
|
394
|
+
private entityProposals;
|
|
324
395
|
/** DERIVE-THROUGH — a RULE this module's DeductionSystem was missing. The
|
|
325
396
|
* A*LD library is untouched: this is one more `premises → conclusion + cost`
|
|
326
397
|
* rule in the system {@link buildSearch} hands to {@link lightestDerivation},
|
|
@@ -335,7 +406,10 @@ export declare class GraphSearch {
|
|
|
335
406
|
* fact reached through it. On the ladder it is one STEP: a direct edge,
|
|
336
407
|
* exactly as following a literal continuation is. Deterministic and
|
|
337
408
|
* point-probed (`resolve` + `nextFirst`, no scan), so it adds no read that
|
|
338
|
-
* grows with the corpus.
|
|
409
|
+
* grows with the corpus. Asked ONCE per fact a lightest derivation stood on
|
|
410
|
+
* (the join license, {@link solve}) — never per fact the exploration merely
|
|
411
|
+
* reached — so the number of facts it prices is the answer's, not the
|
|
412
|
+
* corpus's. The move is visible in the rationale as its own act
|
|
339
413
|
* (`classifyMove` reports `derive-through`), distinct from the
|
|
340
414
|
* byte-concatenating `fuse`/`splice` — and named `derive-through` rather than
|
|
341
415
|
* `join` so it cannot be read as the confluence mechanism's `Provenance`. */
|
|
@@ -343,8 +417,8 @@ export declare class GraphSearch {
|
|
|
343
417
|
/** out(i,j,bytes,…): index it for the binary rules, then offer splicing a
|
|
344
418
|
* learnt connector (the in-search bridge), splitting (at a sub-leaf form
|
|
345
419
|
* boundary), bridging (cover(i) ∧ this → cover(j)), fusing with an adjacent
|
|
346
|
-
* finalised out, and — for a
|
|
347
|
-
* with the query's tail ({@link deriveThrough}). */
|
|
420
|
+
* finalised out, and — for a LICENSED fact — JOINING the entity it contains
|
|
421
|
+
* with the query's tail ({@link deriveThrough}, granted in {@link solve}). */
|
|
348
422
|
private outRules;
|
|
349
423
|
/** Whether the query span [from, to) is wholly covered by RECOGNISED outs —
|
|
350
424
|
* the test that lets a connector jump across INTERIOR answers (an N-ary whole)
|