@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
package/src/mind/primitives.ts
CHANGED
|
@@ -8,34 +8,20 @@ import { Sema } from "../sema.js";
|
|
|
8
8
|
import {
|
|
9
9
|
bytesToTree,
|
|
10
10
|
contentFoldIncremental,
|
|
11
|
+
contentIdentity,
|
|
11
12
|
Grid,
|
|
12
13
|
gridToTree,
|
|
13
14
|
hilbertBytes,
|
|
14
15
|
stackGrids,
|
|
15
16
|
} from "../geometry.js";
|
|
16
17
|
import { canonHash } from "../canon.js";
|
|
17
|
-
import { bytesEqual } from "../bytes.js";
|
|
18
|
+
import { bytesEqual, concatBytes, latin1 } from "../bytes.js";
|
|
18
19
|
import { ALL } from "./types.js";
|
|
19
20
|
import type { Input, MindContext } from "./types.js";
|
|
20
21
|
import type { ContentFold } from "../geometry.js";
|
|
21
22
|
|
|
22
23
|
// ── Address: bytes → node ──────────────────────────────────────────────
|
|
23
24
|
|
|
24
|
-
/** The content key of a byte span — one latin1 char per byte, an exact,
|
|
25
|
-
* collision-free encoding. Spans on the perception path are query-scale
|
|
26
|
-
* (windows, regions, candidate spans), so key construction is far cheaper
|
|
27
|
-
* than the river fold it deduplicates. */
|
|
28
|
-
export function latin1Key(bytes: Uint8Array): string {
|
|
29
|
-
// Batched String.fromCharCode — avoids the O(n²) cost of repeated += on
|
|
30
|
-
// potentially-large query spans, and stays well under the ~65536 arg limit.
|
|
31
|
-
const n = bytes.length;
|
|
32
|
-
let s = "";
|
|
33
|
-
for (let i = 0; i < n; i += 4096) {
|
|
34
|
-
s += String.fromCharCode(...bytes.subarray(i, Math.min(i + 4096, n)));
|
|
35
|
-
}
|
|
36
|
-
return s;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
25
|
/** The {@link perceive} memo key: the span's content PLUS the boundary set it
|
|
40
26
|
* was folded under. The tree is a function of BOTH — the same bytes fold
|
|
41
27
|
* plainly with no boundaries and into a left-nested stable-prefix shape with
|
|
@@ -50,7 +36,7 @@ export function perceiveKey(
|
|
|
50
36
|
bytes: Uint8Array,
|
|
51
37
|
boundaries?: readonly number[],
|
|
52
38
|
): string {
|
|
53
|
-
const k =
|
|
39
|
+
const k = latin1(bytes);
|
|
54
40
|
return boundaries === undefined || boundaries.length === 0
|
|
55
41
|
? k
|
|
56
42
|
: k + "\u0000" + boundaries.join(",");
|
|
@@ -154,7 +140,7 @@ export function perceiveDeposit(
|
|
|
154
140
|
.filter((L) => L >= 2 && L < bytes.length)
|
|
155
141
|
.sort((a, b) => b - a);
|
|
156
142
|
for (const L of lens) {
|
|
157
|
-
const hit = ctx._depositTrees.get(
|
|
143
|
+
const hit = ctx._depositTrees.get(latin1(bytes.subarray(0, L)));
|
|
158
144
|
if (hit !== undefined) {
|
|
159
145
|
prev = hit.content;
|
|
160
146
|
break;
|
|
@@ -173,7 +159,7 @@ export function perceiveDeposit(
|
|
|
173
159
|
ctx._depositLens.clear();
|
|
174
160
|
ctx._depositTrees.clear();
|
|
175
161
|
}
|
|
176
|
-
ctx._depositTrees.set(
|
|
162
|
+
ctx._depositTrees.set(latin1(bytes), { content: folded.fold });
|
|
177
163
|
ctx._depositLens.add(bytes.length);
|
|
178
164
|
}
|
|
179
165
|
return folded.tree;
|
|
@@ -252,12 +238,10 @@ export function foldTree(
|
|
|
252
238
|
return { end, node };
|
|
253
239
|
}
|
|
254
240
|
let pos = start;
|
|
255
|
-
|
|
256
|
-
const kids: number[] = [];
|
|
241
|
+
const kids: Array<number | null> = [];
|
|
257
242
|
for (const k of n.kids) {
|
|
258
243
|
const r = foldTree(ctx, k, pos, visit);
|
|
259
|
-
|
|
260
|
-
else if (known) kids.push(r.node);
|
|
244
|
+
kids.push(r.node);
|
|
261
245
|
pos = r.end;
|
|
262
246
|
}
|
|
263
247
|
// Same store-probe elision as the leaf case: a cached entry already names
|
|
@@ -265,11 +249,10 @@ export function foldTree(
|
|
|
265
249
|
// id need not be re-derived. Using it also keeps a warm walk's ids
|
|
266
250
|
// bit-identical to a cold walk's rather than re-deriving them from children
|
|
267
251
|
// that may themselves have come from cache.
|
|
268
|
-
const
|
|
269
|
-
? cached.id
|
|
270
|
-
:
|
|
271
|
-
|
|
272
|
-
: null;
|
|
252
|
+
const named = cached !== undefined
|
|
253
|
+
? { id: cached.id, byBytes: false }
|
|
254
|
+
: branchNaming(ctx, kids, treeBytes(n));
|
|
255
|
+
const node = named.id;
|
|
273
256
|
visit?.(n, start, pos, node);
|
|
274
257
|
if (node !== null && ctx._resolvedSubtrees) {
|
|
275
258
|
ctx._resolvedSubtrees.set(n, { id: node, len: pos - start });
|
|
@@ -277,15 +260,113 @@ export function foldTree(
|
|
|
277
260
|
return { end: pos, node };
|
|
278
261
|
}
|
|
279
262
|
|
|
263
|
+
/** A perceived subtree's bytes, its leaves in order. */
|
|
264
|
+
function treeBytes(n: Sema): Uint8Array {
|
|
265
|
+
const parts: Uint8Array[] = [];
|
|
266
|
+
const walk = (x: Sema): void => {
|
|
267
|
+
if (x.kids === null) parts.push(x.leaf ?? new Uint8Array(0));
|
|
268
|
+
else for (const k of x.kids) walk(k);
|
|
269
|
+
};
|
|
270
|
+
walk(n);
|
|
271
|
+
return concatBytes(parts);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** The EXACT content-addressed node of a byte stream — `foldTree(perceive)`,
|
|
275
|
+
* read for identity alone.
|
|
276
|
+
*
|
|
277
|
+
* A fold names a branch only when every child is named, so identity needs the
|
|
278
|
+
* fold's SHAPE and the store's answer per node, never its vectors:
|
|
279
|
+
* {@link contentIdentity} walks the same shape (geometry.ts — one grouping
|
|
280
|
+
* rule, two algebras) and asks the store bottom-up, building no D-dimensional
|
|
281
|
+
* gist and leaving nothing in the perception memo. Each node is named the
|
|
282
|
+
* way the store's write side names it ({@link branchNaming}). `test/148` pins
|
|
283
|
+
* the agreement with the full fold over random and corpus spans. */
|
|
284
|
+
export function exactNode(ctx: MindContext, bytes: Uint8Array): number | null {
|
|
285
|
+
return exactNaming(ctx, bytes).id;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** {@link exactNode}, with whether the span's own name was found only through
|
|
289
|
+
* its BYTES — its children named no branch, and the flat node over the same
|
|
290
|
+
* bytes did ({@link branchNaming}). That is where the exact lookup used to
|
|
291
|
+
* MISS, so it is where {@link resolve} still asks the canonical class: the
|
|
292
|
+
* class may hold the learnt member that leads somewhere, which a flat index
|
|
293
|
+
* entry need not (measured: `tonight` named an edge-less window and
|
|
294
|
+
* pre-empted the case-folded `Tonight` whose edge a composition stood on).
|
|
295
|
+
* Recognition's probes reach it through `resolve`; asking it again for the
|
|
296
|
+
* perceived tree's own byte-named groups changed none of 116 real queries and
|
|
297
|
+
* no test, so it is not asked there. */
|
|
298
|
+
export function exactNaming(
|
|
299
|
+
ctx: MindContext,
|
|
300
|
+
bytes: Uint8Array,
|
|
301
|
+
): { id: number | null; byBytes: boolean } {
|
|
302
|
+
if (bytes.length === 0) {
|
|
303
|
+
return { id: foldTree(ctx, perceive(ctx, bytes), 0).node, byBytes: false };
|
|
304
|
+
}
|
|
305
|
+
if (ctx.meter) ctx.meter.identityBytes += bytes.length;
|
|
306
|
+
let byBytes = false;
|
|
307
|
+
const id = contentIdentity(
|
|
308
|
+
ctx.space,
|
|
309
|
+
ctx.alphabet,
|
|
310
|
+
bytes,
|
|
311
|
+
(from, to) =>
|
|
312
|
+
to - from === 1
|
|
313
|
+
? ctx.store.findLeaf(bytes.subarray(from, to))
|
|
314
|
+
: flatNode(ctx, bytes.subarray(from, to)),
|
|
315
|
+
(kids, from, to) => {
|
|
316
|
+
const named = branchNaming(ctx, kids, bytes.subarray(from, to));
|
|
317
|
+
if (from === 0 && to === bytes.length) byBytes = named.byBytes;
|
|
318
|
+
return named.id;
|
|
319
|
+
},
|
|
320
|
+
);
|
|
321
|
+
return { id, byBytes };
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** The flat node over a span's single-byte atoms — the node every deposit
|
|
325
|
+
* interns for its whole input and for each canonical window (learning.ts
|
|
326
|
+
* `deposit`, `indexSubSpans`). The store's negative filter refuses most
|
|
327
|
+
* misses without a lookup. */
|
|
328
|
+
function flatNode(ctx: MindContext, span: Uint8Array): number | null {
|
|
329
|
+
const store = ctx.store;
|
|
330
|
+
return store.findFlatBranch
|
|
331
|
+
? store.findFlatBranch(span)
|
|
332
|
+
: store.findBranch(Array.from(span, (b) => -(b + 1)));
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** THE READ SIDE NAMES A BRANCH EXACTLY AS THE WRITE SIDE DID. `intern`
|
|
336
|
+
* (store.ts) names a branch by its children; when they name none, it looks up
|
|
337
|
+
* the flat node over the same bytes and REUSES it (step 1b, "same bytes, same
|
|
338
|
+
* node") — so a deposit whose fold grouped `ver` + `!` was stored with the
|
|
339
|
+
* window `ver!` as that child. Reading by the children alone could never
|
|
340
|
+
* name such a deposit again: measured on the 31.7M-node store, 8 of 80 stored
|
|
341
|
+
* dialogue turns asked verbatim resolved to nothing (a 25-byte turn, a final
|
|
342
|
+
* `?` or `!`, …) and fell to the composition path. Same order as the write
|
|
343
|
+
* side: the children first, the bytes when they name nothing — and an unnamed
|
|
344
|
+
* child does not settle it, since the write side minted that child and still
|
|
345
|
+
* reached step 1b. */
|
|
346
|
+
function branchNaming(
|
|
347
|
+
ctx: MindContext,
|
|
348
|
+
kids: ReadonlyArray<number | null>,
|
|
349
|
+
span: Uint8Array,
|
|
350
|
+
): { id: number | null; byBytes: boolean } {
|
|
351
|
+
if (kids.every((k) => k !== null)) {
|
|
352
|
+
const id = ctx.store.findBranch(kids as number[]);
|
|
353
|
+
if (id !== null) return { id, byBytes: false };
|
|
354
|
+
}
|
|
355
|
+
if (kids.length < 2) return { id: null, byBytes: false };
|
|
356
|
+
const id = flatNode(ctx, span);
|
|
357
|
+
if (id !== null && ctx.meter) ctx.meter.flatBranchNames++;
|
|
358
|
+
return { id, byBytes: id !== null };
|
|
359
|
+
}
|
|
360
|
+
|
|
280
361
|
/** The canonical node id of a byte span: perceive it in isolation — the way
|
|
281
362
|
* training did — and recover its root bottom-up. Returns null if any part is
|
|
282
363
|
* unknown. */
|
|
283
364
|
export function resolve(ctx: MindContext, bytes: Uint8Array): number | null {
|
|
284
365
|
if (bytes.length === 0) return null;
|
|
285
366
|
if (ctx.meter) ctx.meter.resolves++;
|
|
286
|
-
const exact =
|
|
287
|
-
if (exact !== null) return exact;
|
|
288
|
-
return canonResolve(ctx, bytes);
|
|
367
|
+
const { id: exact, byBytes } = exactNaming(ctx, bytes);
|
|
368
|
+
if (exact !== null && !byBytes) return exact;
|
|
369
|
+
return canonResolve(ctx, bytes) ?? exact;
|
|
289
370
|
}
|
|
290
371
|
|
|
291
372
|
/** Equivalence-class resolution: when the exact content-addressed lookup
|
|
@@ -305,7 +386,7 @@ export function canonResolve(
|
|
|
305
386
|
if (canon === null || !store.canonFind) return null;
|
|
306
387
|
if (bytes.length < 2) return null;
|
|
307
388
|
const memo = ctx.canonMemo;
|
|
308
|
-
const memoKey = memo ?
|
|
389
|
+
const memoKey = memo ? latin1(bytes) : "";
|
|
309
390
|
if (memo) {
|
|
310
391
|
const hit = memo.get(memoKey);
|
|
311
392
|
if (hit !== undefined) return hit;
|
|
@@ -320,7 +401,7 @@ export function canonResolve(
|
|
|
320
401
|
// skips identity rows) — the exact content-addressed lookup of the
|
|
321
402
|
// canonical bytes finds it directly.
|
|
322
403
|
if (key.length !== bytes.length || !bytesEqual(key, bytes)) {
|
|
323
|
-
const direct =
|
|
404
|
+
const direct = exactNode(ctx, key);
|
|
324
405
|
if (direct !== null) return set(direct);
|
|
325
406
|
}
|
|
326
407
|
if (ctx.meter) ctx.meter.canonLookups++;
|
|
@@ -336,17 +417,12 @@ export function canonResolve(
|
|
|
336
417
|
// resolved for these bytes is their FOLD — the deposit-shaped node that
|
|
337
418
|
// carries the edges and halos. Re-folding the candidate's bytes lands
|
|
338
419
|
// on exactly the node the canonical-case query would have found.
|
|
339
|
-
const folded =
|
|
420
|
+
const folded = exactNode(ctx, bytesOf);
|
|
340
421
|
const use = folded ?? id;
|
|
341
|
-
// THE ADMISSION PREDICATE,
|
|
342
|
-
//
|
|
343
|
-
//
|
|
344
|
-
|
|
345
|
-
// bar and this site would rank a node as leading on evidence the law
|
|
346
|
-
// refuses. Calling `leadsSomewhere` here is not possible — `traverse.ts`
|
|
347
|
-
// imports THIS file, so it would be a cycle — which is why the pair is
|
|
348
|
-
// spelled out rather than named.
|
|
349
|
-
const leads = store.hasNext(use) || store.hasHalo(use);
|
|
422
|
+
// THE ADMISSION PREDICATE, asked of the store that owns it (edge or halo,
|
|
423
|
+
// the halo tier carrying the mass bar). Asking `haloMass(use) > 0` instead
|
|
424
|
+
// would agree only while `minHaloMass <= 1`.
|
|
425
|
+
const leads = store.leadsSomewhere(use);
|
|
350
426
|
if (
|
|
351
427
|
best === null || (leads && !bestLeads) ||
|
|
352
428
|
(leads === bestLeads && use < best)
|