@hviana/sema 0.2.6 → 0.2.8
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/example/demo.d.ts +1 -0
- package/dist/example/demo.js +39 -0
- package/dist/example/train_base.d.ts +87 -0
- package/dist/example/train_base.js +2252 -0
- package/dist/src/alphabet.d.ts +7 -0
- package/dist/src/alphabet.js +33 -0
- package/dist/src/alu/src/alu.d.ts +185 -0
- package/dist/src/alu/src/alu.js +440 -0
- package/dist/src/alu/src/expr.d.ts +61 -0
- package/dist/src/alu/src/expr.js +318 -0
- package/dist/src/alu/src/index.d.ts +11 -0
- package/dist/src/alu/src/index.js +19 -0
- package/dist/src/alu/src/kernel-arith.d.ts +16 -0
- package/dist/src/alu/src/kernel-arith.js +264 -0
- package/dist/src/alu/src/kernel-bits.d.ts +19 -0
- package/dist/src/alu/src/kernel-bits.js +152 -0
- package/dist/src/alu/src/kernel-logic.d.ts +4 -0
- package/dist/src/alu/src/kernel-logic.js +60 -0
- package/dist/src/alu/src/kernel-nd.d.ts +3 -0
- package/dist/src/alu/src/kernel-nd.js +208 -0
- package/dist/src/alu/src/kernel-numeric.d.ts +54 -0
- package/dist/src/alu/src/kernel-numeric.js +366 -0
- package/dist/src/alu/src/operation.d.ts +168 -0
- package/dist/src/alu/src/operation.js +189 -0
- package/dist/src/alu/src/parser.d.ts +221 -0
- package/dist/src/alu/src/parser.js +577 -0
- package/dist/src/alu/src/resonance.d.ts +55 -0
- package/dist/src/alu/src/resonance.js +126 -0
- package/dist/src/alu/src/text.d.ts +31 -0
- package/dist/src/alu/src/text.js +73 -0
- package/dist/src/alu/src/value.d.ts +109 -0
- package/dist/src/alu/src/value.js +300 -0
- package/dist/src/alu/test/alu.test.d.ts +1 -0
- package/dist/src/alu/test/alu.test.js +764 -0
- package/dist/src/bytes.d.ts +14 -0
- package/dist/src/bytes.js +59 -0
- package/dist/src/canon.d.ts +26 -0
- package/dist/src/canon.js +57 -0
- package/dist/src/config.d.ts +111 -0
- package/dist/src/config.js +91 -0
- package/dist/src/derive/src/deduction.d.ts +125 -0
- package/dist/src/derive/src/deduction.js +155 -0
- package/dist/src/derive/src/index.d.ts +7 -0
- package/dist/src/derive/src/index.js +11 -0
- package/dist/src/derive/src/priority-queue.d.ts +20 -0
- package/dist/src/derive/src/priority-queue.js +73 -0
- package/dist/src/derive/src/rewrite.d.ts +56 -0
- package/dist/src/derive/src/rewrite.js +100 -0
- package/dist/src/derive/src/trie.d.ts +90 -0
- package/dist/src/derive/src/trie.js +217 -0
- package/dist/src/derive/test/derive.test.d.ts +1 -0
- package/dist/src/derive/test/derive.test.js +122 -0
- package/dist/src/extension.d.ts +37 -0
- package/dist/src/extension.js +7 -0
- package/dist/src/geometry.d.ts +202 -0
- package/dist/src/geometry.js +575 -0
- package/dist/src/index.d.ts +16 -0
- package/dist/src/index.js +18 -0
- package/dist/src/ingest-cache.d.ts +41 -0
- package/dist/src/ingest-cache.js +161 -0
- package/dist/src/mind/articulation.d.ts +6 -0
- package/dist/src/mind/articulation.js +99 -0
- package/dist/src/mind/attention.d.ts +405 -0
- package/dist/src/mind/attention.js +1829 -0
- package/dist/src/mind/bridge.d.ts +30 -0
- package/dist/src/mind/bridge.js +569 -0
- package/dist/src/mind/canonical.d.ts +29 -0
- package/dist/src/mind/canonical.js +88 -0
- package/dist/src/mind/graph-search.d.ts +285 -0
- package/dist/src/mind/graph-search.js +956 -0
- package/dist/src/mind/index.d.ts +7 -0
- package/dist/src/mind/index.js +5 -0
- package/dist/src/mind/junction.d.ts +124 -0
- package/dist/src/mind/junction.js +317 -0
- package/dist/src/mind/learning.d.ts +47 -0
- package/dist/src/mind/learning.js +248 -0
- package/dist/src/mind/match.d.ts +126 -0
- package/dist/src/mind/match.js +441 -0
- package/dist/src/mind/mechanisms/alu.d.ts +4 -0
- package/dist/src/mind/mechanisms/alu.js +36 -0
- package/dist/src/mind/mechanisms/cast.d.ts +89 -0
- package/dist/src/mind/mechanisms/cast.js +716 -0
- package/dist/src/mind/mechanisms/confluence.d.ts +24 -0
- package/dist/src/mind/mechanisms/confluence.js +225 -0
- package/dist/src/mind/mechanisms/cover.d.ts +6 -0
- package/dist/src/mind/mechanisms/cover.js +203 -0
- package/dist/src/mind/mechanisms/extraction.d.ts +67 -0
- package/dist/src/mind/mechanisms/extraction.js +387 -0
- package/dist/src/mind/mechanisms/recall.d.ts +13 -0
- package/dist/src/mind/mechanisms/recall.js +325 -0
- package/dist/src/mind/mind.d.ts +276 -0
- package/dist/src/mind/mind.js +572 -0
- package/dist/src/mind/pipeline-mechanism.d.ts +142 -0
- package/dist/src/mind/pipeline-mechanism.js +213 -0
- package/dist/src/mind/pipeline.d.ts +20 -0
- package/dist/src/mind/pipeline.js +217 -0
- package/dist/src/mind/primitives.d.ts +70 -0
- package/dist/src/mind/primitives.js +287 -0
- package/dist/src/mind/rationale.d.ts +139 -0
- package/dist/src/mind/rationale.js +163 -0
- package/dist/src/mind/reasoning.d.ts +34 -0
- package/dist/src/mind/reasoning.js +240 -0
- package/dist/src/mind/recognition.d.ts +20 -0
- package/dist/src/mind/recognition.js +390 -0
- package/dist/src/mind/resonance.d.ts +23 -0
- package/dist/src/mind/resonance.js +0 -0
- package/dist/src/mind/trace.d.ts +15 -0
- package/dist/src/mind/trace.js +73 -0
- package/dist/src/mind/traverse.d.ts +113 -0
- package/dist/src/mind/traverse.js +568 -0
- package/dist/src/mind/types.d.ts +289 -0
- package/dist/src/mind/types.js +130 -0
- package/dist/src/rabitq-ivf/src/database.d.ts +113 -0
- package/dist/src/rabitq-ivf/src/database.js +201 -0
- package/dist/src/rabitq-ivf/src/index.d.ts +7 -0
- package/dist/src/rabitq-ivf/src/index.js +4 -0
- package/dist/src/rabitq-ivf/src/ivf.d.ts +200 -0
- package/dist/src/rabitq-ivf/src/ivf.js +1165 -0
- package/dist/src/rabitq-ivf/src/prng.d.ts +19 -0
- package/dist/src/rabitq-ivf/src/prng.js +36 -0
- package/dist/src/rabitq-ivf/src/rabitq.d.ts +95 -0
- package/dist/src/rabitq-ivf/src/rabitq.js +283 -0
- package/dist/src/sema.d.ts +31 -0
- package/dist/src/sema.js +63 -0
- package/dist/src/store-sqlite.d.ts +184 -0
- package/dist/src/store-sqlite.js +942 -0
- package/dist/src/store.d.ts +663 -0
- package/dist/src/store.js +1618 -0
- package/dist/src/vec.d.ts +31 -0
- package/dist/src/vec.js +109 -0
- package/package.json +1 -1
- package/src/mind/attention.ts +178 -63
- package/test/51-structural-resonance-ladder.test.mjs +6 -3
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
// match.ts — the ONE elementary operation behind every generalising mechanism:
|
|
2
|
+
// MATCH a learned structure against bytes, then PROJECT along a learned
|
|
3
|
+
// relation, gated by a derived threshold.
|
|
4
|
+
//
|
|
5
|
+
// Every grounding/generalisation mechanism in the mind is a configuration of
|
|
6
|
+
// this single (matcher, direction, gate) operation:
|
|
7
|
+
//
|
|
8
|
+
// mechanism matcher direction gate
|
|
9
|
+
// ─────────────────── ────────────────────────────── ───────────── ────────────────
|
|
10
|
+
// cover follow-edge exact (content-addressed) forward —
|
|
11
|
+
// concept hop halo sibling forward conceptThreshold
|
|
12
|
+
// recall tier 0–1 identity / whole-query gist fwd/reverse identityBar
|
|
13
|
+
// skill extraction locate() ladder (exact→halo→ read-out per-step gates
|
|
14
|
+
// gist) on the exemplar's frames
|
|
15
|
+
// CAST substitution alignGraded() (graded ladder: insert frame shapes
|
|
16
|
+
// literal W-grams → halo sites)
|
|
17
|
+
// CAST comparison analogyStrength() (halo, juxtapose significanceBar
|
|
18
|
+
// direct or mutual-sibling)
|
|
19
|
+
// multi-hop pivot byte containment forward —
|
|
20
|
+
// articulation halo sibling substitute conceptThreshold
|
|
21
|
+
//
|
|
22
|
+
// This module holds the shared vocabulary those configurations are built
|
|
23
|
+
// from — the MATCHERS (locate, alignRuns, alignGraded, analogyStrength) and
|
|
24
|
+
// the PROJECTIONS (follow, conceptHop, reverseContext, project) — so each
|
|
25
|
+
// mechanism file states only its configuration, never its own copy of the
|
|
26
|
+
// machinery. The gates all live in geometry.ts (derived, never tuned).
|
|
27
|
+
import { cosine } from "../vec.js";
|
|
28
|
+
import { conceptThreshold, identityBar, significanceBar } from "../geometry.js";
|
|
29
|
+
import { indexOf } from "../bytes.js";
|
|
30
|
+
import { leafIdRun } from "./canonical.js";
|
|
31
|
+
import { gistOf, read, resolve } from "./primitives.js";
|
|
32
|
+
import { argmaxCosine, chooseAmong, chooseNext, guidedFirst, hubBound, hubCap, } from "./traverse.js";
|
|
33
|
+
import { recognise, segment } from "./recognition.js";
|
|
34
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
35
|
+
// MATCHERS — locating learned structure in/against bytes, by graded strictness
|
|
36
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
37
|
+
/** The graded LOCATE ladder: find `needle` in `haystack` starting at
|
|
38
|
+
* `fromPos`, strictest matcher first, relaxing only when the stricter one
|
|
39
|
+
* fails. This is the read-out matcher skill extraction locates exemplar
|
|
40
|
+
* frames with.
|
|
41
|
+
*
|
|
42
|
+
* 1. exact — literal byte match (the fast path).
|
|
43
|
+
* 2. halo — the needle's distributional role matches a recognised query
|
|
44
|
+
* form (gate: conceptThreshold).
|
|
45
|
+
* 3. gist — the needle's perceived gist matches a query segment
|
|
46
|
+
* (gate: identityBar — scale-aware).
|
|
47
|
+
*
|
|
48
|
+
* Returns the absolute byte position, or −1. */
|
|
49
|
+
export function locate(ctx, haystack, needle, fromPos, sites) {
|
|
50
|
+
// 1. Exact match — fast, preserves backward compatibility.
|
|
51
|
+
const exact = indexOf(haystack.subarray(fromPos), needle, 0);
|
|
52
|
+
if (exact >= 0)
|
|
53
|
+
return fromPos + exact;
|
|
54
|
+
// 2. Halo-based: the frame bytes' distributional role matches a query form.
|
|
55
|
+
if (sites && sites.length > 0) {
|
|
56
|
+
const frameId = resolve(ctx, needle);
|
|
57
|
+
if (frameId !== null) {
|
|
58
|
+
const frameHalo = ctx.store.halo(frameId);
|
|
59
|
+
if (frameHalo) {
|
|
60
|
+
const bestSite = bestHaloMate(ctx, frameHalo, sites.filter((s) => s.start >= fromPos), (s) => ctx.store.halo(s.payload));
|
|
61
|
+
if (bestSite !== null)
|
|
62
|
+
return bestSite.item.start;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// 3. Gist resonance: the frame's perceived gist against query segments.
|
|
67
|
+
const frameGist = gistOf(ctx, needle);
|
|
68
|
+
const segments = segment(ctx, haystack.subarray(fromPos));
|
|
69
|
+
// The gist tier claims the WHOLE needle appears as a segment — an
|
|
70
|
+
// identity claim over `needle.length` bytes, so its bar is the
|
|
71
|
+
// scale-aware {@link identityBar} (one river window of tolerated foreign
|
|
72
|
+
// bytes), not the fixed estimator floor. For quantum-sized frames the
|
|
73
|
+
// two coincide; for long needles the fixed bar accepted segments that
|
|
74
|
+
// differed by whole windows.
|
|
75
|
+
const bestSeg = argmaxCosine(frameGist, segments, (s) => s.v, identityBar(ctx.store.D, ctx.space.maxGroup, needle.length), true);
|
|
76
|
+
if (bestSeg !== null)
|
|
77
|
+
return fromPos + bestSeg.item.start;
|
|
78
|
+
return -1;
|
|
79
|
+
}
|
|
80
|
+
/** The ALIGNED matcher: maximal literal matching runs between `query` and
|
|
81
|
+
* `ct` (a learned context's bytes), by seed-and-extend over
|
|
82
|
+
* `space.maxGroup`-sized n-gram seeds. Where locate() finds ONE position of
|
|
83
|
+
* a short frame, this finds EVERY run two whole structures share — the
|
|
84
|
+
* matcher CAST detects a woven query with. Returns non-overlapping runs
|
|
85
|
+
* sorted by query position. */
|
|
86
|
+
export function alignRuns(ctx, query, ct) {
|
|
87
|
+
const quantum = Math.min(ctx.space.maxGroup, ct.length);
|
|
88
|
+
if (quantum < 1 || query.length < quantum)
|
|
89
|
+
return [];
|
|
90
|
+
const gram = (b, at) => {
|
|
91
|
+
let s = "";
|
|
92
|
+
for (let i = 0; i < quantum; i++)
|
|
93
|
+
s += String.fromCharCode(b[at + i]);
|
|
94
|
+
return s;
|
|
95
|
+
};
|
|
96
|
+
const seeds = new Map();
|
|
97
|
+
for (let i = 0; i + quantum <= query.length; i++) {
|
|
98
|
+
const k2 = gram(query, i);
|
|
99
|
+
const bucket = seeds.get(k2);
|
|
100
|
+
if (bucket === undefined)
|
|
101
|
+
seeds.set(k2, [i]);
|
|
102
|
+
else
|
|
103
|
+
bucket.push(i);
|
|
104
|
+
}
|
|
105
|
+
const found = [];
|
|
106
|
+
for (let j = 0; j + quantum <= ct.length; j++) {
|
|
107
|
+
const bucket = seeds.get(gram(ct, j));
|
|
108
|
+
if (bucket === undefined)
|
|
109
|
+
continue;
|
|
110
|
+
for (const i of bucket) {
|
|
111
|
+
if (i > 0 && j > 0 && query[i - 1] === ct[j - 1])
|
|
112
|
+
continue;
|
|
113
|
+
let len = quantum;
|
|
114
|
+
while (i + len < query.length && j + len < ct.length &&
|
|
115
|
+
query[i + len] === ct[j + len])
|
|
116
|
+
len++;
|
|
117
|
+
found.push({ qs: i, qe: i + len, cs: j, len });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
found.sort((a, b) => b.len - a.len);
|
|
121
|
+
const runs = [];
|
|
122
|
+
for (const r of found) {
|
|
123
|
+
const clash = runs.some((o) => (r.qs < o.qe && o.qs < r.qe) ||
|
|
124
|
+
(r.cs < o.cs + (o.qe - o.qs) && o.cs < r.cs + r.len));
|
|
125
|
+
if (!clash)
|
|
126
|
+
runs.push({ qs: r.qs, qe: r.qe, cs: r.cs });
|
|
127
|
+
}
|
|
128
|
+
return runs.sort((a, b) => a.qs - b.qs);
|
|
129
|
+
}
|
|
130
|
+
/** The GRADED alignment matcher: extends literal W-gram alignment
|
|
131
|
+
* ({@link alignRuns}) with halo-matched recognised sites in query regions
|
|
132
|
+
* that have no literal coverage. Same ladder as {@link locate}: literal
|
|
133
|
+
* first, then distributional role (halo-matched sites, gate:
|
|
134
|
+
* conceptThreshold, enforced by {@link bestHaloMate}). Returns weighted
|
|
135
|
+
* runs sorted by query position.
|
|
136
|
+
*
|
|
137
|
+
* `querySites` are the pre-computed recognition sites for the query
|
|
138
|
+
* (optional — when absent, only literal alignment fires and graded degrades
|
|
139
|
+
* to the original behaviour). Context sites are recognised internally. */
|
|
140
|
+
export function alignGraded(ctx, query, contextBytes, querySites) {
|
|
141
|
+
const lit = alignRuns(ctx, query, contextBytes);
|
|
142
|
+
const out = lit.map((r) => ({ ...r, weight: 1 }));
|
|
143
|
+
if (!querySites || querySites.length === 0)
|
|
144
|
+
return out;
|
|
145
|
+
// Mark query positions ALREADY covered by literal runs — halo fills gaps.
|
|
146
|
+
// If literal coverage is already complete, skip the halo step entirely
|
|
147
|
+
// (recognise is O(|ctx|·W) — wasted when every byte is accounted for).
|
|
148
|
+
const covered = new Uint8Array(query.length);
|
|
149
|
+
let gaps = false;
|
|
150
|
+
for (const r of lit) {
|
|
151
|
+
for (let i = r.qs; i < r.qe; i++)
|
|
152
|
+
covered[i] = 1;
|
|
153
|
+
}
|
|
154
|
+
for (let i = 0; i < query.length; i++) {
|
|
155
|
+
if (!covered[i]) {
|
|
156
|
+
gaps = true;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (!gaps)
|
|
161
|
+
return out;
|
|
162
|
+
// Recognise sites in the exemplar context — structural positions for halo
|
|
163
|
+
// matching. (Circular import with recognition.ts is safe: recognise() is
|
|
164
|
+
// called lazily, never at module load — the same pattern `segment` uses.)
|
|
165
|
+
const ctxSites = recognise(ctx, contextBytes).sites;
|
|
166
|
+
if (ctxSites.length === 0)
|
|
167
|
+
return out;
|
|
168
|
+
// Context sites with halos, hoisted: the same set serves every query site.
|
|
169
|
+
const ctxCands = ctxSites.filter((cs) => ctx.store.hasHalo(cs.payload));
|
|
170
|
+
if (ctxCands.length === 0)
|
|
171
|
+
return out;
|
|
172
|
+
// Candidate halos, also hoisted (lazily, first query site that needs them):
|
|
173
|
+
// bestHaloMate consults every candidate's halo PER QUERY SITE, and sites
|
|
174
|
+
// share the candidate set — without this memo the same few dozen halos were
|
|
175
|
+
// re-fetched thousands of times per response. Distinct payloads can repeat
|
|
176
|
+
// across sites, hence the map by payload id.
|
|
177
|
+
const ctxHalos = new Map();
|
|
178
|
+
const ctxHaloOf = (cs) => {
|
|
179
|
+
let h = ctxHalos.get(cs.payload);
|
|
180
|
+
if (h === undefined) {
|
|
181
|
+
h = ctx.store.halo(cs.payload);
|
|
182
|
+
ctxHalos.set(cs.payload, h);
|
|
183
|
+
}
|
|
184
|
+
return h;
|
|
185
|
+
};
|
|
186
|
+
for (const qs of querySites) {
|
|
187
|
+
// Only sites that overlap UNCOVERED query regions add new evidence.
|
|
188
|
+
let touchesGap = false;
|
|
189
|
+
for (let i = qs.start; i < qs.end; i++) {
|
|
190
|
+
if (!covered[i]) {
|
|
191
|
+
touchesGap = true;
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (!touchesGap)
|
|
196
|
+
continue;
|
|
197
|
+
const qHalo = ctx.store.halo(qs.payload);
|
|
198
|
+
if (!qHalo)
|
|
199
|
+
continue;
|
|
200
|
+
// bestHaloMate already gates at conceptThreshold — no second check needed.
|
|
201
|
+
const match = bestHaloMate(ctx, qHalo, ctxCands, ctxHaloOf);
|
|
202
|
+
if (match === null)
|
|
203
|
+
continue;
|
|
204
|
+
out.push({
|
|
205
|
+
qs: qs.start,
|
|
206
|
+
qe: qs.end,
|
|
207
|
+
cs: match.item.start,
|
|
208
|
+
weight: match.score,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
out.sort((a, b) => a.qs - b.qs);
|
|
212
|
+
return out;
|
|
213
|
+
}
|
|
214
|
+
/** The IN-LIST halo matcher: the best halo-mate for `halo` among EXPLICIT
|
|
215
|
+
* candidates, above the concept threshold — the list counterpart of
|
|
216
|
+
* {@link haloSiblings}, which asks the halo INDEX for candidates instead.
|
|
217
|
+
* Behind locate()'s halo step and articulation's voice matching; a third
|
|
218
|
+
* "best halo among these" decision must come here, not inline. */
|
|
219
|
+
export function bestHaloMate(ctx, halo, items, haloOf) {
|
|
220
|
+
return argmaxCosine(halo, items, haloOf, conceptThreshold(ctx.store.D));
|
|
221
|
+
}
|
|
222
|
+
/** The HALO-SIBLING matcher: the nodes that keep the same distributional
|
|
223
|
+
* company as `id`, nearest first — `resonateHalo` filtered to exclude the
|
|
224
|
+
* node itself and everything below `bar` (default: the concept threshold).
|
|
225
|
+
* `halo`, when the caller has already read the node's halo row, is reused
|
|
226
|
+
* instead of refetched (one read per relation). Returns [] for a node with
|
|
227
|
+
* no halo. The one sibling enumeration behind the concept hop, the
|
|
228
|
+
* reasoning stage's synonym expansion, and the analogy matcher below. */
|
|
229
|
+
const haloSiblingMemo = new WeakMap();
|
|
230
|
+
export async function haloSiblings(ctx, id, halo, bar = conceptThreshold(ctx.store.D)) {
|
|
231
|
+
// Per-response memo for the DEFAULT-ARGUMENT reading (the one the concept
|
|
232
|
+
// hop, the bridge's synonym tier, and reasoning's synonym expansion all
|
|
233
|
+
// use): the same node's siblings are asked for repeatedly within one
|
|
234
|
+
// response (bridge pairs share sides), each a full halo-ANN query, and the
|
|
235
|
+
// store is read-only while a response is in flight. Keyed by the response
|
|
236
|
+
// lifecycle object (ctx.climbMemo — fresh per respond, nulled after).
|
|
237
|
+
// Calls with an explicit halo or bar (analogyStrength's gated reading)
|
|
238
|
+
// bypass the memo — their filter differs.
|
|
239
|
+
const memoable = halo === undefined &&
|
|
240
|
+
bar === conceptThreshold(ctx.store.D) && ctx.climbMemo !== null;
|
|
241
|
+
let memo;
|
|
242
|
+
if (memoable) {
|
|
243
|
+
memo = haloSiblingMemo.get(ctx.climbMemo);
|
|
244
|
+
if (memo === undefined) {
|
|
245
|
+
haloSiblingMemo.set(ctx.climbMemo, memo = new Map());
|
|
246
|
+
}
|
|
247
|
+
const hit = memo.get(id);
|
|
248
|
+
if (hit !== undefined)
|
|
249
|
+
return hit;
|
|
250
|
+
}
|
|
251
|
+
const h = halo ?? ctx.store.halo(id);
|
|
252
|
+
const out = h
|
|
253
|
+
? (await ctx.store.resonateHalo(h, ctx.cfg.haloQueryK))
|
|
254
|
+
.filter((sib) => sib.id !== id && sib.score >= bar)
|
|
255
|
+
: [];
|
|
256
|
+
if (memo !== undefined)
|
|
257
|
+
memo.set(id, out);
|
|
258
|
+
return out;
|
|
259
|
+
}
|
|
260
|
+
export async function analogyStrength(ctx, a, b) {
|
|
261
|
+
const ha = ctx.store.halo(a);
|
|
262
|
+
const hb = ctx.store.halo(b);
|
|
263
|
+
if (ha && hb) {
|
|
264
|
+
const bar = significanceBar(ctx.store.D);
|
|
265
|
+
const direct = cosine(ha, hb);
|
|
266
|
+
if (direct >= bar)
|
|
267
|
+
return { score: direct, halo: true };
|
|
268
|
+
const sibsA = await haloSiblings(ctx, a, ha, bar);
|
|
269
|
+
const sibsB = await haloSiblings(ctx, b, hb, bar);
|
|
270
|
+
let best = 0;
|
|
271
|
+
for (const x of sibsA) {
|
|
272
|
+
if (x.id === b)
|
|
273
|
+
continue;
|
|
274
|
+
const y = sibsB.find((s) => s.id === x.id);
|
|
275
|
+
if (y !== undefined) {
|
|
276
|
+
best = Math.max(best, Math.min(x.score, y.score));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (best > 0)
|
|
280
|
+
return { score: best, halo: true };
|
|
281
|
+
}
|
|
282
|
+
return { score: sharedFrameStrength(ctx, a, b), halo: false };
|
|
283
|
+
}
|
|
284
|
+
/** The STRUCTURAL analogy tier: two nodes are analogs when their byte
|
|
285
|
+
* streams share a LEARNT frame — a content-addressed flat form of at least
|
|
286
|
+
* one full river window (W bytes, the perception quantum) that occurs in
|
|
287
|
+
* BOTH. This is what "playing the same role" means structurally: "Ice is
|
|
288
|
+
* cold" and "Steel is hard" share the learnt " is " frame even though they
|
|
289
|
+
* keep disjoint distributional company. Halos measure company by IDENTITY
|
|
290
|
+
* (company signatures — see sema.ts), so unrelated-company analogs must be
|
|
291
|
+
* validated by the frame itself, not by content leaking through halo
|
|
292
|
+
* vectors. Strength is the shared learnt coverage of the SHORTER side —
|
|
293
|
+
* a fraction, comparable to the cosine tiers above. Derived: the window
|
|
294
|
+
* is maxGroup, the same quantum differsByOneWindow and canonicalChunkId
|
|
295
|
+
* measure by; no tuned constants. */
|
|
296
|
+
export function sharedFrameStrength(ctx, a, b) {
|
|
297
|
+
const W = ctx.space.maxGroup;
|
|
298
|
+
const A = read(ctx, a);
|
|
299
|
+
const B = read(ctx, b);
|
|
300
|
+
if (A.length < W || B.length < W)
|
|
301
|
+
return 0;
|
|
302
|
+
// Mark every byte of the shorter side covered by a learnt W-window that
|
|
303
|
+
// also occurs in the longer side.
|
|
304
|
+
const [s, l] = A.length <= B.length ? [A, B] : [B, A];
|
|
305
|
+
const covered = new Uint8Array(s.length);
|
|
306
|
+
for (let off = 0; off + W <= s.length; off++) {
|
|
307
|
+
const win = s.subarray(off, off + W);
|
|
308
|
+
// Learnt: the window resolves as a content-addressed flat form.
|
|
309
|
+
const ids = leafIdRun(ctx, s, off, off + W);
|
|
310
|
+
if (ids === null || ctx.store.findBranch(ids) === null)
|
|
311
|
+
continue;
|
|
312
|
+
if (indexOf(l, win, 0) < 0)
|
|
313
|
+
continue;
|
|
314
|
+
covered.fill(1, off, off + W);
|
|
315
|
+
}
|
|
316
|
+
let n = 0;
|
|
317
|
+
for (let i = 0; i < s.length; i++)
|
|
318
|
+
n += covered[i];
|
|
319
|
+
return n >= W ? n / s.length : 0;
|
|
320
|
+
}
|
|
321
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
322
|
+
// PROJECTIONS — what a matched node is projected ALONG (the direction)
|
|
323
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
324
|
+
/** FORWARD through a synonym: the continuation an edge-less node borrows from
|
|
325
|
+
* a concept (halo) sibling — resonate the node's halo, take the first
|
|
326
|
+
* sibling above the concept threshold that itself has a direct edge. */
|
|
327
|
+
export async function conceptHop(ctx, id) {
|
|
328
|
+
for (const sib of await haloSiblings(ctx, id)) {
|
|
329
|
+
const hop = guidedFirst(ctx, sib.id);
|
|
330
|
+
if (hop !== undefined)
|
|
331
|
+
return hop;
|
|
332
|
+
}
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
/** FORWARD projection: follow continuation edges from a node to its fixpoint.
|
|
336
|
+
* The first hop may cross a concept (halo) link — a synonym. The rest
|
|
337
|
+
* follow direct edges. Convergence is intrinsic: the seen set guards
|
|
338
|
+
* against cycles. `guide` disambiguates multi-continuation nodes by
|
|
339
|
+
* resonance. */
|
|
340
|
+
export async function follow(ctx, id, guide) {
|
|
341
|
+
const seen = new Set([id]);
|
|
342
|
+
// First hop: a direct edge, else a concept sibling's edge (the synonym).
|
|
343
|
+
let next = chooseNext(ctx, id, guide);
|
|
344
|
+
if (next === undefined) {
|
|
345
|
+
const hop = await conceptHop(ctx, id);
|
|
346
|
+
if (hop === null)
|
|
347
|
+
return null;
|
|
348
|
+
next = hop;
|
|
349
|
+
}
|
|
350
|
+
// Direct successors to the fixpoint. Only the FIXPOINT's bytes are
|
|
351
|
+
// returned, so the walk tracks node ids and reads bytes exactly once at
|
|
352
|
+
// the end — a K-hop chain used to pay K full reconstructions and discard
|
|
353
|
+
// K−1 of them.
|
|
354
|
+
while (!seen.has(next)) {
|
|
355
|
+
seen.add(next);
|
|
356
|
+
const fwd = chooseNext(ctx, next, guide);
|
|
357
|
+
if (fwd === undefined || seen.has(fwd))
|
|
358
|
+
break;
|
|
359
|
+
next = fwd;
|
|
360
|
+
}
|
|
361
|
+
return read(ctx, next);
|
|
362
|
+
}
|
|
363
|
+
/** REVERSE projection: the context a learnt continuation follows, voiced as
|
|
364
|
+
* bytes. A common continuation ("Yes.") follows MANY contexts; with a
|
|
365
|
+
* `guide` the context whose gist resonates with the query wins (seat
|
|
366
|
+
* symmetry) — without one, the most-corroborated context wins (poured halo
|
|
367
|
+
* MASS, the direct measure of how many episodes established it), falling
|
|
368
|
+
* back to first-learnt on equal mass. Among many predecessors RECIPROCAL
|
|
369
|
+
* ones (mutual edges) are preferred when any exist (RC5). Callers that
|
|
370
|
+
* HAVE a query gist must pass it, or they silently change disambiguation
|
|
371
|
+
* regime.
|
|
372
|
+
*
|
|
373
|
+
* `rev`, when the caller has already materialised prevOf (one read per
|
|
374
|
+
* relation — a hub's reverse fan-in is corpus-sized), is reused instead of
|
|
375
|
+
* refetched. Returns null when there is no predecessor or the picked
|
|
376
|
+
* context reads empty (a zero-length context is no grounding: an empty
|
|
377
|
+
* Uint8Array is truthy, and returning it would flow a hollow "answer"
|
|
378
|
+
* onward). */
|
|
379
|
+
export function reverseContext(ctx, id, guide, rev) {
|
|
380
|
+
// CAPPED default read: only the first √N predecessors are ever candidates
|
|
381
|
+
// (hubCap below / in chooseAmong), so only they are read. hubBound ≥ 2
|
|
382
|
+
// keeps the single-predecessor shortcut exact.
|
|
383
|
+
const candidates = rev ?? ctx.store.prevFirst(id, hubBound(ctx));
|
|
384
|
+
if (candidates.length === 0)
|
|
385
|
+
return null;
|
|
386
|
+
// RECIPROCAL PREFERENCE: among many predecessors, one that `id` also
|
|
387
|
+
// continues TO (cand → id AND id → cand both learnt) is a mutually
|
|
388
|
+
// established pairing — the strongest structural evidence a predecessor
|
|
389
|
+
// can carry (bidirectional training deposits both directions of a genuine
|
|
390
|
+
// pair). A bare predecessor is one episode's adjacency; guide-resonance
|
|
391
|
+
// over bare predecessors favours whichever stored document merely
|
|
392
|
+
// CONTAINS the query's bytes (the linear fold's cosine is byte overlap —
|
|
393
|
+
// the observed "merci → unrelated French document" failure). One capped
|
|
394
|
+
// forward read decides; when no reciprocal exists, behaviour is unchanged
|
|
395
|
+
// — bare predecessors ARE the honest answer for a shared deposited
|
|
396
|
+
// continuation (two questions → one answer; audited by 31-audit C1), and
|
|
397
|
+
// this arm serves every mechanism's reverse projection, so abstaining
|
|
398
|
+
// here starves far more than the one containment failure it would fix.
|
|
399
|
+
let pool = candidates;
|
|
400
|
+
if (candidates.length > 1) {
|
|
401
|
+
const fwd = new Set(ctx.store.nextFirst(id, hubBound(ctx)));
|
|
402
|
+
if (fwd.size > 0) {
|
|
403
|
+
const mutual = candidates.filter((c) => fwd.has(c));
|
|
404
|
+
if (mutual.length > 0)
|
|
405
|
+
pool = mutual;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
const pick = pool.length === 1
|
|
409
|
+
? pool[0]
|
|
410
|
+
: guide
|
|
411
|
+
? chooseAmong(ctx, pool, guide).id
|
|
412
|
+
: pickByMass(ctx, pool);
|
|
413
|
+
const g = read(ctx, pick);
|
|
414
|
+
return g.length > 0 ? g : null;
|
|
415
|
+
}
|
|
416
|
+
/** The most-corroborated candidate by poured halo mass (first-seen wins a
|
|
417
|
+
* tie). Capped at √N candidates by insertion order — the same hub bound
|
|
418
|
+
* every fan-out walk uses. */
|
|
419
|
+
function pickByMass(ctx, ids) {
|
|
420
|
+
const capped = hubCap(ctx, ids);
|
|
421
|
+
let best = capped[0];
|
|
422
|
+
let bestMass = ctx.store.haloMass(best);
|
|
423
|
+
for (let i = 1; i < capped.length; i++) {
|
|
424
|
+
const mass = ctx.store.haloMass(capped[i]);
|
|
425
|
+
if (mass > bestMass) {
|
|
426
|
+
best = capped[i];
|
|
427
|
+
bestMass = mass;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return best;
|
|
431
|
+
}
|
|
432
|
+
/** THE projection: ground a matched node to answer bytes — FORWARD to its
|
|
433
|
+
* continuation fixpoint (which may cross a concept hop), else REVERSE to
|
|
434
|
+
* the context it follows. This is the direction ladder every mechanism's
|
|
435
|
+
* final grounding step reduces to. */
|
|
436
|
+
export async function project(ctx, id, guide) {
|
|
437
|
+
const fc = await follow(ctx, id, guide);
|
|
438
|
+
if (fc)
|
|
439
|
+
return fc;
|
|
440
|
+
return reverseContext(ctx, id, guide);
|
|
441
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// mechanisms/alu.ts — the ALU wrapped as an ordinary PipelineMechanism.
|
|
2
|
+
//
|
|
3
|
+
// The ALU is a self-contained sublibrary (src/alu) that knows nothing about
|
|
4
|
+
// the pipeline; this adapter is the whole coupling. Its `parse` populates
|
|
5
|
+
// `pre.computed` before the grounding loop; the cover mechanism handles
|
|
6
|
+
// masking (see mechanisms/cover.ts). The ALU's own trace steps
|
|
7
|
+
// (`evalComputation`) are emitted inside its `parse()`. A user extension
|
|
8
|
+
// joins the same way — see MindOptions.mechanismFactories.
|
|
9
|
+
import { STEP } from "../graph-search.js";
|
|
10
|
+
import { unexplainedLabel } from "../rationale.js";
|
|
11
|
+
/** Wrap the ALU as a {@link PipelineMechanism}. */
|
|
12
|
+
export function aluToMechanism(alu) {
|
|
13
|
+
return {
|
|
14
|
+
name: "alu",
|
|
15
|
+
// Not a cover derivation: cover.ts composes an answer by walking
|
|
16
|
+
// recognised query STRUCTURE; the ALU evaluates a recognised expression
|
|
17
|
+
// to its authoritative result and hands the bytes back untouched. It
|
|
18
|
+
// shares cover's near-zero floor (computation always wins, masked into
|
|
19
|
+
// cover's own search — see mechanisms/cover.ts), but the candidate this
|
|
20
|
+
// produces is not one of cover's derivations, so it carries its own
|
|
21
|
+
// honest label, the same way extract/cast/recall each carry theirs.
|
|
22
|
+
provenance: "alu",
|
|
23
|
+
parse: (query) => alu.parse(query),
|
|
24
|
+
async floor(_ctx, _query, pre, _worthRunning) {
|
|
25
|
+
return pre.computed.length > 0 ? 0 : null;
|
|
26
|
+
},
|
|
27
|
+
async run(_ctx, query, pre) {
|
|
28
|
+
return pre.computed.map((u) => ({
|
|
29
|
+
bytes: u.bytes,
|
|
30
|
+
accounted: [[u.i, u.j]],
|
|
31
|
+
moves: STEP,
|
|
32
|
+
unexplained: unexplainedLabel(query, [[u.i, u.j]]),
|
|
33
|
+
}));
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { MindContext } from "../types.js";
|
|
2
|
+
import type { Vec } from "../../vec.js";
|
|
3
|
+
/** A CAST answer plus its elementary evidence for think's grounding decider:
|
|
4
|
+
* `accounted` — the query spans the weave's aligned runs explain; `moves` —
|
|
5
|
+
* the ladder cost of the acts the taken branch performed (STEP per
|
|
6
|
+
* projection, CONCEPT for the halo-mediated analogy gate). */
|
|
7
|
+
export interface CastResult {
|
|
8
|
+
bytes: Uint8Array;
|
|
9
|
+
used: ReadonlySet<number>;
|
|
10
|
+
accounted: Array<[number, number]>;
|
|
11
|
+
moves: number;
|
|
12
|
+
/** A human-readable label for the query bytes this schema left
|
|
13
|
+
* unexplained — purely diagnostic, never priced (see the module's
|
|
14
|
+
* Task 2 note in pipeline.ts's Candidate interface). */
|
|
15
|
+
unexplained: string;
|
|
16
|
+
}
|
|
17
|
+
/** The seat that establishes a node's role in an analogical comparison:
|
|
18
|
+
* the REVERSE context (what leads to it) when a predecessor genuinely
|
|
19
|
+
* ESTABLISHES id — introduces or describes it by name — else the FORWARD
|
|
20
|
+
* continuation (what it leads to), else `fallback`.
|
|
21
|
+
*
|
|
22
|
+
* An earlier version gated this purely on `prevCount(id) > 0`: any
|
|
23
|
+
* predecessor at all was treated as proof of a genuine named ENTITY
|
|
24
|
+
* (seat it by what established it), while no predecessor meant a bare
|
|
25
|
+
* learnt CONTEXT (seat it by what it leads to, since voicing it verbatim
|
|
26
|
+
* would answer a question with a question). That test measured the wrong
|
|
27
|
+
* thing — a broad sample of this store's own question-shaped nodes showed
|
|
28
|
+
* the large majority (≈71%) have at least one predecessor, most of them a
|
|
29
|
+
* handful of generic, high-fan-out sentences that recur as an INCIDENTAL
|
|
30
|
+
* neighbour to dozens of otherwise-unrelated destinations (a SmolSent-
|
|
31
|
+
* style sentence-adjacency artifact, never naming or describing what
|
|
32
|
+
* follows). Traced live: "What is the capital of France?" — whose own
|
|
33
|
+
* forward edge unambiguously resolves to "The capital of France is
|
|
34
|
+
* Paris." — has exactly one such incidental predecessor ("Create an
|
|
35
|
+
* example of a types of questions a GPT model can answer.?"), wrongly
|
|
36
|
+
* read as disqualifying proof of "genuine entity."
|
|
37
|
+
*
|
|
38
|
+
* A plain forward-first swap (matching {@link project}'s universal
|
|
39
|
+
* priority) over-corrected: test/29's C2/C3 pin that a genuine entity
|
|
40
|
+
* analog (e.g. "Leonardo da Vinci", established by "The Mona Lisa was
|
|
41
|
+
* painted by Leonardo da Vinci.") must be seated by that establishing
|
|
42
|
+
* sentence, NOT by its own biography fact — voicing the bio leaks exactly
|
|
43
|
+
* what a comparison must keep out, and loses the embedded "Mona Lisa"
|
|
44
|
+
* term C3 relies on for a further hop.
|
|
45
|
+
*
|
|
46
|
+
* The distinguishing signal is content-addressed, not a count: a genuine
|
|
47
|
+
* establishing predecessor's bytes CONTAIN id's own bytes — it names or
|
|
48
|
+
* describes id ("...painted by Leonardo da Vinci." contains "Leonardo da
|
|
49
|
+
* Vinci"). An incidental adjacency predecessor never does — it merely
|
|
50
|
+
* preceded id in some unrelated document without ever mentioning it. No
|
|
51
|
+
* new tuned constant: containment is the same primitive `restatesQuery`
|
|
52
|
+
* and `dominates`-style checks already use throughout this codebase.
|
|
53
|
+
*
|
|
54
|
+
* `allowForward` (default true) gates the FORWARD branch specifically —
|
|
55
|
+
* see the call sites below: the DOMINANT is what the query is actually
|
|
56
|
+
* ASKING, so completing it forward is the whole point; an ANALOG is only
|
|
57
|
+
* being CITED for comparison; the query never asked about IT, so chasing
|
|
58
|
+
* its own further continuation drifts onto whatever coincidentally
|
|
59
|
+
* follows it in the corpus. Traced live: the analog "What is the capital
|
|
60
|
+
* of Japan?\nTokyo is the capital of Japan." is ALREADY a complete,
|
|
61
|
+
* self-answering unit (prevCount 0, so no establishing predecessor
|
|
62
|
+
* either) — its sole forward edge is "And what is the capital of the
|
|
63
|
+
* Moon?", an unrelated quiz question sharing nothing but corpus
|
|
64
|
+
* adjacency. With forward disallowed, an analog like this falls through
|
|
65
|
+
* to `fallback` — its own bytes, exactly the complete fact that made it a
|
|
66
|
+
* genuine analog in the first place. See
|
|
67
|
+
* test/41-seatofnode-direction.test.mjs and
|
|
68
|
+
* test/43-cast-analog-seat.test.mjs. */
|
|
69
|
+
export declare function seatOfNode(ctx: MindContext, id: number, guide: Vec | null | undefined, fallback: Uint8Array, allowForward?: boolean): Promise<Uint8Array>;
|
|
70
|
+
/** CAST's own entry gates, checked once here and reused by
|
|
71
|
+
/** The main CAST entry point. Given a query and its pre-computed pre.rec.sites,
|
|
72
|
+
* determine whether the query weaves together multiple independent learnt
|
|
73
|
+
* structures (by graded alignment — literal first, then halo-matched pre.rec.sites).
|
|
74
|
+
* If so, attempt substitution, redirection, AND analogical comparison —
|
|
75
|
+
* each schema is tried independently and every one that fires yields its
|
|
76
|
+
* OWN candidate; think's grounding decider (which already compares weights
|
|
77
|
+
* across mechanisms) picks among them, so CAST no longer needs an internal
|
|
78
|
+
* priority order.
|
|
79
|
+
*
|
|
80
|
+
* `climb`, when given, is {@link castFloor}'s own climb result — reused
|
|
81
|
+
* instead of re-running climbAttentionAll (see the note on {@link
|
|
82
|
+
* CastFloor}). Its gates (`query.length`, `edgeSourceCount`,
|
|
83
|
+
* `ranked.length < 2`) MUST stay in sync with castFloor's — one is the
|
|
84
|
+
* other's admissible lower bound, checked before this runs.
|
|
85
|
+
*
|
|
86
|
+
* Returns the array of {@link CastResult}s that fired (possibly empty). */
|
|
87
|
+
export declare function counterfactualTransfer(ctx: MindContext, query: Uint8Array, pre: Precomputed): Promise<CastResult[]>;
|
|
88
|
+
import type { PipelineMechanism, Precomputed } from "../pipeline-mechanism.js";
|
|
89
|
+
export declare const castMechanism: PipelineMechanism;
|