@hviana/sema 0.1.5 → 0.1.6
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 +6 -5
- package/CITATION.cff +49 -0
- package/HOW_IT_WORKS.md +11 -12
- package/README.md +7 -5
- package/dist/example/demo.js +25 -25
- package/dist/example/train_base.d.ts +19 -21
- package/dist/example/train_base.js +1687 -1952
- package/dist/src/alphabet.d.ts +3 -3
- package/dist/src/alphabet.js +24 -27
- package/dist/src/alu/src/alu.d.ts +169 -194
- package/dist/src/alu/src/alu.js +374 -414
- package/dist/src/alu/src/expr.d.ts +36 -43
- package/dist/src/alu/src/expr.js +252 -278
- package/dist/src/alu/src/index.d.ts +9 -98
- package/dist/src/alu/src/index.js +10 -64
- package/dist/src/alu/src/kernel-arith.d.ts +1 -5
- package/dist/src/alu/src/kernel-arith.js +195 -285
- package/dist/src/alu/src/kernel-bits.d.ts +1 -5
- package/dist/src/alu/src/kernel-bits.js +81 -115
- package/dist/src/alu/src/kernel-logic.js +33 -63
- package/dist/src/alu/src/kernel-nd.js +155 -195
- package/dist/src/alu/src/kernel-numeric.d.ts +16 -80
- package/dist/src/alu/src/kernel-numeric.js +259 -331
- package/dist/src/alu/src/operation.d.ts +112 -132
- package/dist/src/alu/src/operation.js +149 -163
- package/dist/src/alu/src/parser.d.ts +168 -175
- package/dist/src/alu/src/parser.js +382 -421
- package/dist/src/alu/src/resonance.d.ts +16 -25
- package/dist/src/alu/src/resonance.js +49 -56
- package/dist/src/alu/src/text.d.ts +3 -7
- package/dist/src/alu/src/text.js +31 -37
- package/dist/src/alu/src/value.d.ts +14 -14
- package/dist/src/alu/src/value.js +150 -160
- package/dist/src/alu/test/alu.test.js +543 -656
- package/dist/src/bytes.d.ts +1 -5
- package/dist/src/bytes.js +33 -40
- package/dist/src/config.d.ts +99 -102
- package/dist/src/config.js +78 -83
- package/dist/src/derive/src/deduction.d.ts +60 -62
- package/dist/src/derive/src/deduction.js +103 -109
- package/dist/src/derive/src/index.d.ts +1 -7
- package/dist/src/derive/src/priority-queue.d.ts +8 -8
- package/dist/src/derive/src/priority-queue.js +57 -61
- package/dist/src/derive/src/rewrite.d.ts +15 -18
- package/dist/src/derive/src/rewrite.js +59 -67
- package/dist/src/derive/src/trie.d.ts +56 -56
- package/dist/src/derive/src/trie.js +175 -179
- package/dist/src/derive/test/derive.test.js +96 -100
- package/dist/src/extension.d.ts +13 -16
- package/dist/src/geometry.d.ts +17 -42
- package/dist/src/geometry.js +235 -266
- package/dist/src/index.d.ts +2 -17
- package/dist/src/index.js +2 -12
- package/dist/src/ingest-cache.d.ts +25 -30
- package/dist/src/ingest-cache.js +108 -127
- package/dist/src/mind/articulation.d.ts +1 -5
- package/dist/src/mind/articulation.js +77 -112
- package/dist/src/mind/attention.d.ts +40 -108
- package/dist/src/mind/attention.js +749 -871
- package/dist/src/mind/canonical.d.ts +4 -19
- package/dist/src/mind/canonical.js +27 -31
- package/dist/src/mind/graph-search.d.ts +201 -225
- package/dist/src/mind/graph-search.js +742 -821
- package/dist/src/mind/index.d.ts +2 -10
- package/dist/src/mind/junction.d.ts +31 -58
- package/dist/src/mind/junction.js +172 -237
- package/dist/src/mind/learning.d.ts +16 -52
- package/dist/src/mind/learning.js +143 -165
- package/dist/src/mind/match.d.ts +20 -69
- package/dist/src/mind/match.js +259 -318
- package/dist/src/mind/mechanisms/alu.js +16 -16
- package/dist/src/mind/mechanisms/cast.d.ts +9 -13
- package/dist/src/mind/mechanisms/cast.js +363 -456
- package/dist/src/mind/mechanisms/confluence.d.ts +8 -12
- package/dist/src/mind/mechanisms/confluence.js +152 -183
- package/dist/src/mind/mechanisms/cover.d.ts +2 -8
- package/dist/src/mind/mechanisms/cover.js +148 -210
- package/dist/src/mind/mechanisms/extraction.d.ts +8 -34
- package/dist/src/mind/mechanisms/extraction.js +234 -288
- package/dist/src/mind/mechanisms/recall.d.ts +6 -10
- package/dist/src/mind/mechanisms/recall.js +126 -185
- package/dist/src/mind/mind.d.ts +129 -154
- package/dist/src/mind/mind.js +258 -288
- package/dist/src/mind/pipeline-mechanism.d.ts +112 -124
- package/dist/src/mind/pipeline-mechanism.js +161 -172
- package/dist/src/mind/pipeline.d.ts +4 -14
- package/dist/src/mind/pipeline.js +125 -189
- package/dist/src/mind/primitives.d.ts +8 -32
- package/dist/src/mind/primitives.js +99 -117
- package/dist/src/mind/rationale.d.ts +86 -98
- package/dist/src/mind/rationale.js +113 -121
- package/dist/src/mind/reasoning.d.ts +2 -13
- package/dist/src/mind/reasoning.js +129 -166
- package/dist/src/mind/recognition.d.ts +1 -4
- package/dist/src/mind/recognition.js +183 -208
- package/dist/src/mind/resonance.d.ts +5 -22
- package/dist/src/mind/resonance.js +0 -0
- package/dist/src/mind/trace.d.ts +6 -25
- package/dist/src/mind/trace.js +50 -58
- package/dist/src/mind/traverse.d.ts +16 -58
- package/dist/src/mind/traverse.js +304 -357
- package/dist/src/mind/types.d.ts +120 -127
- package/dist/src/mind/types.js +60 -69
- 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-hnsw → rabitq-ivf}/src/index.js +1 -3
- 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 +9 -13
- package/dist/src/sema.js +26 -40
- package/dist/src/store-sqlite.d.ts +169 -166
- package/dist/src/store-sqlite.js +762 -662
- package/dist/src/store.d.ts +586 -630
- package/dist/src/store.js +1423 -1581
- package/dist/src/vec.d.ts +5 -9
- package/dist/src/vec.js +61 -73
- package/example/train_base.ts +13 -10
- package/package.json +1 -1
- package/src/alu/README.md +1 -1
- package/src/alu/src/index.ts +1 -1
- package/src/config.ts +19 -27
- package/src/index.ts +6 -11
- package/src/rabitq-ivf/README.md +56 -0
- package/src/rabitq-ivf/src/database.ts +276 -0
- package/src/{rabitq-hnsw → rabitq-ivf}/src/index.ts +2 -5
- package/src/rabitq-ivf/src/ivf.ts +1330 -0
- package/src/{rabitq-hnsw → rabitq-ivf}/src/prng.ts +1 -1
- package/src/store-sqlite.ts +196 -9
- package/src/store.ts +8 -32
- package/test/08-storage.test.mjs +3 -3
- package/test/14-scaling.test.mjs +2 -2
- package/test/35-ivf.test.mjs +263 -0
- package/test/36-bloom.test.mjs +123 -0
- package/dist/src/rabitq-hnsw/src/database.d.ts +0 -202
- package/dist/src/rabitq-hnsw/src/database.js +0 -405
- package/dist/src/rabitq-hnsw/src/heap.d.ts +0 -22
- package/dist/src/rabitq-hnsw/src/heap.js +0 -94
- package/dist/src/rabitq-hnsw/src/hnsw.d.ts +0 -125
- package/dist/src/rabitq-hnsw/src/hnsw.js +0 -500
- package/dist/src/rabitq-hnsw/src/index.d.ts +0 -15
- package/dist/src/rabitq-hnsw/src/prng.d.ts +0 -19
- package/dist/src/rabitq-hnsw/src/prng.js +0 -38
- package/dist/src/rabitq-hnsw/src/rabitq.d.ts +0 -95
- package/dist/src/rabitq-hnsw/src/rabitq.js +0 -299
- package/dist/src/rabitq-hnsw/src/store.d.ts +0 -162
- package/dist/src/rabitq-hnsw/src/store.js +0 -916
- package/dist/src/rabitq-hnsw/test/hnsw.test.d.ts +0 -1
- package/dist/src/rabitq-hnsw/test/hnsw.test.js +0 -1197
- package/src/rabitq-hnsw/README.md +0 -303
- package/src/rabitq-hnsw/src/database.ts +0 -492
- package/src/rabitq-hnsw/src/heap.ts +0 -90
- package/src/rabitq-hnsw/src/hnsw.ts +0 -514
- package/src/rabitq-hnsw/src/store.ts +0 -994
- package/src/rabitq-hnsw/test/hnsw.test.ts +0 -1213
- /package/src/{rabitq-hnsw → rabitq-ivf}/src/rabitq.ts +0 -0
package/dist/src/mind/match.js
CHANGED
|
@@ -29,14 +29,7 @@ import { conceptThreshold, identityBar, significanceBar } from "../geometry.js";
|
|
|
29
29
|
import { indexOf } from "../bytes.js";
|
|
30
30
|
import { leafIdRun } from "./canonical.js";
|
|
31
31
|
import { gistOf, read, resolve } from "./primitives.js";
|
|
32
|
-
import {
|
|
33
|
-
argmaxCosine,
|
|
34
|
-
chooseAmong,
|
|
35
|
-
chooseNext,
|
|
36
|
-
guidedFirst,
|
|
37
|
-
hubBound,
|
|
38
|
-
hubCap,
|
|
39
|
-
} from "./traverse.js";
|
|
32
|
+
import { argmaxCosine, chooseAmong, chooseNext, guidedFirst, hubBound, hubCap, } from "./traverse.js";
|
|
40
33
|
import { recognise, segment } from "./recognition.js";
|
|
41
34
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
42
35
|
// MATCHERS — locating learned structure in/against bytes, by graded strictness
|
|
@@ -54,49 +47,35 @@ import { recognise, segment } from "./recognition.js";
|
|
|
54
47
|
*
|
|
55
48
|
* Returns the absolute byte position, or −1. */
|
|
56
49
|
export function locate(ctx, haystack, needle, fromPos, sites) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
sites.filter((s) => s.start >= fromPos),
|
|
72
|
-
(s) => ctx.store.halo(s.payload),
|
|
73
|
-
);
|
|
74
|
-
if (bestSite !== null) {
|
|
75
|
-
return bestSite.item.start;
|
|
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
|
+
}
|
|
76
64
|
}
|
|
77
|
-
}
|
|
78
65
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
(s) => s.v,
|
|
93
|
-
identityBar(ctx.store.D, ctx.space.maxGroup, needle.length),
|
|
94
|
-
true,
|
|
95
|
-
);
|
|
96
|
-
if (bestSeg !== null) {
|
|
97
|
-
return fromPos + bestSeg.item.start;
|
|
98
|
-
}
|
|
99
|
-
return -1;
|
|
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;
|
|
100
79
|
}
|
|
101
80
|
/** The ALIGNED matcher: maximal literal matching runs between `query` and
|
|
102
81
|
* `ct` (a learned context's bytes), by seed-and-extend over
|
|
@@ -105,59 +84,48 @@ export function locate(ctx, haystack, needle, fromPos, sites) {
|
|
|
105
84
|
* matcher CAST detects a woven query with. Returns non-overlapping runs
|
|
106
85
|
* sorted by query position. */
|
|
107
86
|
export function alignRuns(ctx, query, ct) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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);
|
|
116
104
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const bucket = seeds.get(gram(ct, j));
|
|
132
|
-
if (bucket === undefined) {
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
for (const i of bucket) {
|
|
136
|
-
if (i > 0 && j > 0 && query[i - 1] === ct[j - 1]) {
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
let len = quantum;
|
|
140
|
-
while (
|
|
141
|
-
i + len < query.length && j + len < ct.length &&
|
|
142
|
-
query[i + len] === ct[j + len]
|
|
143
|
-
) {
|
|
144
|
-
len++;
|
|
145
|
-
}
|
|
146
|
-
found.push({ qs: i, qe: i + len, cs: j, len });
|
|
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
|
+
}
|
|
147
119
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
);
|
|
156
|
-
if (!clash) {
|
|
157
|
-
runs.push({ qs: r.qs, qe: r.qe, cs: r.cs });
|
|
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 });
|
|
158
127
|
}
|
|
159
|
-
|
|
160
|
-
return runs.sort((a, b) => a.qs - b.qs);
|
|
128
|
+
return runs.sort((a, b) => a.qs - b.qs);
|
|
161
129
|
}
|
|
162
130
|
/** The GRADED alignment matcher: extends literal W-gram alignment
|
|
163
131
|
* ({@link alignRuns}) with halo-matched recognised sites in query regions
|
|
@@ -170,86 +138,78 @@ export function alignRuns(ctx, query, ct) {
|
|
|
170
138
|
* (optional — when absent, only literal alignment fires and graded degrades
|
|
171
139
|
* to the original behaviour). Context sites are recognised internally. */
|
|
172
140
|
export function alignGraded(ctx, query, contextBytes, querySites) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
covered[i] = 1;
|
|
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;
|
|
186
153
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
if (!gaps) {
|
|
195
|
-
return out;
|
|
196
|
-
}
|
|
197
|
-
// Recognise sites in the exemplar context — structural positions for halo
|
|
198
|
-
// matching. (Circular import with recognition.ts is safe: recognise() is
|
|
199
|
-
// called lazily, never at module load — the same pattern `segment` uses.)
|
|
200
|
-
const ctxSites = recognise(ctx, contextBytes).sites;
|
|
201
|
-
if (ctxSites.length === 0) {
|
|
202
|
-
return out;
|
|
203
|
-
}
|
|
204
|
-
// Context sites with halos, hoisted: the same set serves every query site.
|
|
205
|
-
const ctxCands = ctxSites.filter((cs) => ctx.store.hasHalo(cs.payload));
|
|
206
|
-
if (ctxCands.length === 0) {
|
|
207
|
-
return out;
|
|
208
|
-
}
|
|
209
|
-
// Candidate halos, also hoisted (lazily, first query site that needs them):
|
|
210
|
-
// bestHaloMate consults every candidate's halo PER QUERY SITE, and sites
|
|
211
|
-
// share the candidate set — without this memo the same few dozen halos were
|
|
212
|
-
// re-fetched thousands of times per response. Distinct payloads can repeat
|
|
213
|
-
// across sites, hence the map by payload id.
|
|
214
|
-
const ctxHalos = new Map();
|
|
215
|
-
const ctxHaloOf = (cs) => {
|
|
216
|
-
let h = ctxHalos.get(cs.payload);
|
|
217
|
-
if (h === undefined) {
|
|
218
|
-
h = ctx.store.halo(cs.payload);
|
|
219
|
-
ctxHalos.set(cs.payload, h);
|
|
220
|
-
}
|
|
221
|
-
return h;
|
|
222
|
-
};
|
|
223
|
-
for (const qs of querySites) {
|
|
224
|
-
// Only sites that overlap UNCOVERED query regions add new evidence.
|
|
225
|
-
let touchesGap = false;
|
|
226
|
-
for (let i = qs.start; i < qs.end; i++) {
|
|
227
|
-
if (!covered[i]) {
|
|
228
|
-
touchesGap = true;
|
|
229
|
-
break;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
if (!touchesGap) {
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
const qHalo = ctx.store.halo(qs.payload);
|
|
236
|
-
if (!qHalo) {
|
|
237
|
-
continue;
|
|
154
|
+
for (let i = 0; i < query.length; i++) {
|
|
155
|
+
if (!covered[i]) {
|
|
156
|
+
gaps = true;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
238
159
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
+
});
|
|
243
210
|
}
|
|
244
|
-
out.
|
|
245
|
-
|
|
246
|
-
qe: qs.end,
|
|
247
|
-
cs: match.item.start,
|
|
248
|
-
weight: match.score,
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
out.sort((a, b) => a.qs - b.qs);
|
|
252
|
-
return out;
|
|
211
|
+
out.sort((a, b) => a.qs - b.qs);
|
|
212
|
+
return out;
|
|
253
213
|
}
|
|
254
214
|
/** The IN-LIST halo matcher: the best halo-mate for `halo` among EXPLICIT
|
|
255
215
|
* candidates, above the concept threshold — the list counterpart of
|
|
@@ -257,7 +217,7 @@ export function alignGraded(ctx, query, contextBytes, querySites) {
|
|
|
257
217
|
* Behind locate()'s halo step and articulation's voice matching; a third
|
|
258
218
|
* "best halo among these" decision must come here, not inline. */
|
|
259
219
|
export function bestHaloMate(ctx, halo, items, haloOf) {
|
|
260
|
-
|
|
220
|
+
return argmaxCosine(halo, items, haloOf, conceptThreshold(ctx.store.D));
|
|
261
221
|
}
|
|
262
222
|
/** The HALO-SIBLING matcher: the nodes that keep the same distributional
|
|
263
223
|
* company as `id`, nearest first — `resonateHalo` filtered to exclude the
|
|
@@ -267,42 +227,35 @@ export function bestHaloMate(ctx, halo, items, haloOf) {
|
|
|
267
227
|
* no halo. The one sibling enumeration behind the concept hop, the
|
|
268
228
|
* reasoning stage's synonym expansion, and the analogy matcher below. */
|
|
269
229
|
const haloSiblingMemo = new WeakMap();
|
|
270
|
-
export async function haloSiblings(
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
haloSiblingMemo.set(ctx.climbMemo, memo = new Map());
|
|
291
|
-
}
|
|
292
|
-
const hit = memo.get(id);
|
|
293
|
-
if (hit !== undefined) {
|
|
294
|
-
return hit;
|
|
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;
|
|
295
250
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
return out;
|
|
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;
|
|
306
259
|
}
|
|
307
260
|
/** The DISTRIBUTIONAL matcher between two nodes: mutual-nearest-neighbour
|
|
308
261
|
* strength, not a pick. Returns the direct halo cosine, or failing that the
|
|
@@ -310,31 +263,28 @@ export async function haloSiblings(
|
|
|
310
263
|
* that the SHARED-FRAME strength (below) — the gate CAST's comparison
|
|
311
264
|
* schema validates genuine analogs with (bar: significanceBar). */
|
|
312
265
|
export async function analogyStrength(ctx, a, b) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
if (best > 0) {
|
|
334
|
-
return best;
|
|
266
|
+
const ha = ctx.store.halo(a);
|
|
267
|
+
const hb = ctx.store.halo(b);
|
|
268
|
+
if (ha && hb) {
|
|
269
|
+
const bar = significanceBar(ctx.store.D);
|
|
270
|
+
const direct = cosine(ha, hb);
|
|
271
|
+
if (direct >= bar)
|
|
272
|
+
return direct;
|
|
273
|
+
const sibsA = await haloSiblings(ctx, a, ha, bar);
|
|
274
|
+
const sibsB = await haloSiblings(ctx, b, hb, bar);
|
|
275
|
+
let best = 0;
|
|
276
|
+
for (const x of sibsA) {
|
|
277
|
+
if (x.id === b)
|
|
278
|
+
continue;
|
|
279
|
+
const y = sibsB.find((s) => s.id === x.id);
|
|
280
|
+
if (y !== undefined) {
|
|
281
|
+
best = Math.max(best, Math.min(x.score, y.score));
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (best > 0)
|
|
285
|
+
return best;
|
|
335
286
|
}
|
|
336
|
-
|
|
337
|
-
return sharedFrameStrength(ctx, a, b);
|
|
287
|
+
return sharedFrameStrength(ctx, a, b);
|
|
338
288
|
}
|
|
339
289
|
/** The STRUCTURAL analogy tier: two nodes are analogs when their byte
|
|
340
290
|
* streams share a LEARNT frame — a content-addressed flat form of at least
|
|
@@ -349,33 +299,29 @@ export async function analogyStrength(ctx, a, b) {
|
|
|
349
299
|
* is maxGroup, the same quantum differsByOneWindow and canonicalChunkId
|
|
350
300
|
* measure by; no tuned constants. */
|
|
351
301
|
export function sharedFrameStrength(ctx, a, b) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
continue;
|
|
302
|
+
const W = ctx.space.maxGroup;
|
|
303
|
+
const A = read(ctx, a);
|
|
304
|
+
const B = read(ctx, b);
|
|
305
|
+
if (A.length < W || B.length < W)
|
|
306
|
+
return 0;
|
|
307
|
+
// Mark every byte of the shorter side covered by a learnt W-window that
|
|
308
|
+
// also occurs in the longer side.
|
|
309
|
+
const [s, l] = A.length <= B.length ? [A, B] : [B, A];
|
|
310
|
+
const covered = new Uint8Array(s.length);
|
|
311
|
+
for (let off = 0; off + W <= s.length; off++) {
|
|
312
|
+
const win = s.subarray(off, off + W);
|
|
313
|
+
// Learnt: the window resolves as a content-addressed flat form.
|
|
314
|
+
const ids = leafIdRun(ctx, s, off, off + W);
|
|
315
|
+
if (ids === null || ctx.store.findBranch(ids) === null)
|
|
316
|
+
continue;
|
|
317
|
+
if (indexOf(l, win, 0) < 0)
|
|
318
|
+
continue;
|
|
319
|
+
covered.fill(1, off, off + W);
|
|
371
320
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
n += covered[i];
|
|
377
|
-
}
|
|
378
|
-
return n >= W ? n / s.length : 0;
|
|
321
|
+
let n = 0;
|
|
322
|
+
for (let i = 0; i < s.length; i++)
|
|
323
|
+
n += covered[i];
|
|
324
|
+
return n >= W ? n / s.length : 0;
|
|
379
325
|
}
|
|
380
326
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
381
327
|
// PROJECTIONS — what a matched node is projected ALONG (the direction)
|
|
@@ -384,13 +330,12 @@ export function sharedFrameStrength(ctx, a, b) {
|
|
|
384
330
|
* a concept (halo) sibling — resonate the node's halo, take the first
|
|
385
331
|
* sibling above the concept threshold that itself has a direct edge. */
|
|
386
332
|
export async function conceptHop(ctx, id) {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
333
|
+
for (const sib of await haloSiblings(ctx, id)) {
|
|
334
|
+
const hop = guidedFirst(ctx, sib.id);
|
|
335
|
+
if (hop !== undefined)
|
|
336
|
+
return hop;
|
|
391
337
|
}
|
|
392
|
-
|
|
393
|
-
return null;
|
|
338
|
+
return null;
|
|
394
339
|
}
|
|
395
340
|
/** FORWARD projection: follow continuation edges from a node to its fixpoint.
|
|
396
341
|
* The first hop may cross a concept (halo) link — a synonym. The rest
|
|
@@ -398,29 +343,27 @@ export async function conceptHop(ctx, id) {
|
|
|
398
343
|
* against cycles. `guide` disambiguates multi-continuation nodes by
|
|
399
344
|
* resonance. */
|
|
400
345
|
export async function follow(ctx, id, guide) {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
346
|
+
const seen = new Set([id]);
|
|
347
|
+
// First hop: a direct edge, else a concept sibling's edge (the synonym).
|
|
348
|
+
let next = chooseNext(ctx, id, guide);
|
|
349
|
+
if (next === undefined) {
|
|
350
|
+
const hop = await conceptHop(ctx, id);
|
|
351
|
+
if (hop === null)
|
|
352
|
+
return null;
|
|
353
|
+
next = hop;
|
|
408
354
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
break;
|
|
355
|
+
// Direct successors to the fixpoint. Only the FIXPOINT's bytes are
|
|
356
|
+
// returned, so the walk tracks node ids and reads bytes exactly once at
|
|
357
|
+
// the end — a K-hop chain used to pay K full reconstructions and discard
|
|
358
|
+
// K−1 of them.
|
|
359
|
+
while (!seen.has(next)) {
|
|
360
|
+
seen.add(next);
|
|
361
|
+
const fwd = chooseNext(ctx, next, guide);
|
|
362
|
+
if (fwd === undefined || seen.has(fwd))
|
|
363
|
+
break;
|
|
364
|
+
next = fwd;
|
|
420
365
|
}
|
|
421
|
-
|
|
422
|
-
}
|
|
423
|
-
return read(ctx, next);
|
|
366
|
+
return read(ctx, next);
|
|
424
367
|
}
|
|
425
368
|
/** REVERSE projection: the context a learnt continuation follows, voiced as
|
|
426
369
|
* bytes. A common continuation ("Yes.") follows MANY contexts; with a
|
|
@@ -437,45 +380,43 @@ export async function follow(ctx, id, guide) {
|
|
|
437
380
|
* Uint8Array is truthy, and returning it would flow a hollow "answer"
|
|
438
381
|
* onward). */
|
|
439
382
|
export function reverseContext(ctx, id, guide, rev) {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
return g.length > 0 ? g : null;
|
|
383
|
+
// CAPPED default read: only the first √N predecessors are ever candidates
|
|
384
|
+
// (hubCap below / in chooseAmong), so only they are read. hubBound ≥ 2
|
|
385
|
+
// keeps the single-predecessor shortcut exact.
|
|
386
|
+
const candidates = rev ?? ctx.store.prevFirst(id, hubBound(ctx));
|
|
387
|
+
if (candidates.length === 0)
|
|
388
|
+
return null;
|
|
389
|
+
const pick = candidates.length === 1
|
|
390
|
+
? candidates[0]
|
|
391
|
+
: guide
|
|
392
|
+
? chooseAmong(ctx, candidates, guide).id
|
|
393
|
+
: pickByMass(ctx, candidates);
|
|
394
|
+
const g = read(ctx, pick);
|
|
395
|
+
return g.length > 0 ? g : null;
|
|
454
396
|
}
|
|
455
397
|
/** The most-corroborated candidate by poured halo mass (first-seen wins a
|
|
456
398
|
* tie). Capped at √N candidates by insertion order — the same hub bound
|
|
457
399
|
* every fan-out walk uses. */
|
|
458
400
|
function pickByMass(ctx, ids) {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
401
|
+
const capped = hubCap(ctx, ids);
|
|
402
|
+
let best = capped[0];
|
|
403
|
+
let bestMass = ctx.store.haloMass(best);
|
|
404
|
+
for (let i = 1; i < capped.length; i++) {
|
|
405
|
+
const mass = ctx.store.haloMass(capped[i]);
|
|
406
|
+
if (mass > bestMass) {
|
|
407
|
+
best = capped[i];
|
|
408
|
+
bestMass = mass;
|
|
409
|
+
}
|
|
467
410
|
}
|
|
468
|
-
|
|
469
|
-
return best;
|
|
411
|
+
return best;
|
|
470
412
|
}
|
|
471
413
|
/** THE projection: ground a matched node to answer bytes — FORWARD to its
|
|
472
414
|
* continuation fixpoint (which may cross a concept hop), else REVERSE to
|
|
473
415
|
* the context it follows. This is the direction ladder every mechanism's
|
|
474
416
|
* final grounding step reduces to. */
|
|
475
417
|
export async function project(ctx, id, guide) {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
return reverseContext(ctx, id, guide);
|
|
418
|
+
const fc = await follow(ctx, id, guide);
|
|
419
|
+
if (fc)
|
|
420
|
+
return fc;
|
|
421
|
+
return reverseContext(ctx, id, guide);
|
|
481
422
|
}
|