@hviana/sema 0.1.9 → 0.2.1
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 +148 -0
- package/dist/src/geometry.js +511 -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 +72 -0
- package/dist/src/mind/attention.js +898 -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 +270 -0
- package/dist/src/mind/graph-search.js +927 -0
- package/dist/src/mind/index.d.ts +5 -0
- package/dist/src/mind/index.js +5 -0
- package/dist/src/mind/junction.d.ts +95 -0
- package/dist/src/mind/junction.js +262 -0
- package/dist/src/mind/learning.d.ts +47 -0
- package/dist/src/mind/learning.js +233 -0
- package/dist/src/mind/match.d.ts +113 -0
- package/dist/src/mind/match.js +446 -0
- package/dist/src/mind/mechanisms/alu.d.ts +4 -0
- package/dist/src/mind/mechanisms/alu.js +29 -0
- package/dist/src/mind/mechanisms/cast.d.ts +35 -0
- package/dist/src/mind/mechanisms/cast.js +461 -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 +179 -0
- package/dist/src/mind/mechanisms/extraction.d.ts +67 -0
- package/dist/src/mind/mechanisms/extraction.js +342 -0
- package/dist/src/mind/mechanisms/recall.d.ts +13 -0
- package/dist/src/mind/mechanisms/recall.js +235 -0
- package/dist/src/mind/mind.d.ts +273 -0
- package/dist/src/mind/mind.js +598 -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 +185 -0
- package/dist/src/mind/primitives.d.ts +63 -0
- package/dist/src/mind/primitives.js +251 -0
- package/dist/src/mind/rationale.d.ts +134 -0
- package/dist/src/mind/rationale.js +162 -0
- package/dist/src/mind/reasoning.d.ts +15 -0
- package/dist/src/mind/reasoning.js +162 -0
- package/dist/src/mind/recognition.d.ts +20 -0
- package/dist/src/mind/recognition.js +246 -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 +488 -0
- package/dist/src/mind/types.d.ts +186 -0
- package/dist/src/mind/types.js +84 -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 +654 -0
- package/dist/src/store.js +1612 -0
- package/dist/src/vec.d.ts +31 -0
- package/dist/src/vec.js +109 -0
- package/package.json +1 -1
- package/src/mind/graph-search.ts +128 -46
- package/src/mind/mind.ts +37 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// reasoning.ts — multi-hop reasoning + multi-topic fusion (Section 4 of the mind).
|
|
2
|
+
//
|
|
3
|
+
// reason — extend an answer forward across facts (multi-hop)
|
|
4
|
+
// fuseAttention — fuse independent points of attention (multi-topic)
|
|
5
|
+
import { rItem, rNode } from "./trace.js";
|
|
6
|
+
import { bytesEqual, indexOf } from "../bytes.js";
|
|
7
|
+
import { resolve } from "./primitives.js";
|
|
8
|
+
import { corpusN } from "./traverse.js";
|
|
9
|
+
import { follow, haloSiblings, project } from "./match.js";
|
|
10
|
+
import { joinWithBridge, pivotInto } from "./resonance.js";
|
|
11
|
+
/** Extend a grounded answer forward across facts (multi-hop reasoning).
|
|
12
|
+
* Pivots on the longest unconsumed learnt context each answer contains,
|
|
13
|
+
* then follows the pivot's continuation to the next fact. Repeats up
|
|
14
|
+
* to `cfg.recallQueryK` hops. `preConsumed` carries node ids already
|
|
15
|
+
* spoken for by the grounding stage (cover/extract/CAST). `pre` is the
|
|
16
|
+
* response's shared pre-computation — the post-grounding stages read the
|
|
17
|
+
* same container the mechanisms did. */
|
|
18
|
+
export async function reason(ctx, query, answer, preConsumed, pre) {
|
|
19
|
+
// Echo guard: a query that is ITSELF a learnt continuation (some context's
|
|
20
|
+
// answer) is being asked back at the system — hopping forward from it would
|
|
21
|
+
// chain through the very fact that produced it and echo the conversation
|
|
22
|
+
// back. The grounded answer alone is the honest read-out. Deliberately a
|
|
23
|
+
// broad structural gate; pinned by test/31-audit.
|
|
24
|
+
const qId = pre.queryResolved;
|
|
25
|
+
if (qId !== null && ctx.store.prevCount(qId) > 0)
|
|
26
|
+
return answer;
|
|
27
|
+
const consumed = new Set();
|
|
28
|
+
// Consume a node and its neighbours for pivot-cycle prevention — CAPPED at
|
|
29
|
+
// the hub bound, via the store's LIMITed edge reads: a common continuation's
|
|
30
|
+
// reverse fan-in (and a hub context's forward fan-out) is corpus-sized, and
|
|
31
|
+
// no per-hop operation may grow with the corpus. The cap follows the one
|
|
32
|
+
// convention every fan-out decision uses (first √N in the relation's own
|
|
33
|
+
// read order); a pivot suppressed only by a beyond-cap neighbour may now
|
|
34
|
+
// fire — the same visibility trade chooseNext documents.
|
|
35
|
+
const hubBound = Math.ceil(Math.sqrt(corpusN(ctx)));
|
|
36
|
+
const consumeNode = (id) => {
|
|
37
|
+
if (id === null)
|
|
38
|
+
return;
|
|
39
|
+
consumed.add(id);
|
|
40
|
+
for (const p of ctx.store.prevFirst(id, hubBound))
|
|
41
|
+
consumed.add(p);
|
|
42
|
+
};
|
|
43
|
+
const consumeAll = (id) => {
|
|
44
|
+
if (id === null)
|
|
45
|
+
return;
|
|
46
|
+
consumeNode(id);
|
|
47
|
+
for (const n of ctx.store.nextFirst(id, hubBound))
|
|
48
|
+
consumed.add(n);
|
|
49
|
+
};
|
|
50
|
+
// Pre-consume whatever the grounding stage already spoke for. The halo
|
|
51
|
+
// sweep is one ANN query per node — cap it at haloQueryK sweeps (cover
|
|
52
|
+
// grounding can pre-consume one node per recognised site, O(query length));
|
|
53
|
+
// nodes past the cap are still consumed directly, they just skip the
|
|
54
|
+
// synonym expansion.
|
|
55
|
+
let haloSweeps = 0;
|
|
56
|
+
for (const id of preConsumed) {
|
|
57
|
+
consumeNode(id);
|
|
58
|
+
if (haloSweeps >= ctx.cfg.haloQueryK)
|
|
59
|
+
continue;
|
|
60
|
+
const h = ctx.store.halo(id);
|
|
61
|
+
if (!h)
|
|
62
|
+
continue;
|
|
63
|
+
haloSweeps++;
|
|
64
|
+
for (const sib of await haloSiblings(ctx, id, h))
|
|
65
|
+
consumeNode(sib.id);
|
|
66
|
+
}
|
|
67
|
+
let cur = answer;
|
|
68
|
+
const qv = pre.guide; // the response-wide guide IS the query's gist
|
|
69
|
+
let t;
|
|
70
|
+
const startedFrom = answer;
|
|
71
|
+
for (let hop = 0; hop < ctx.cfg.recallQueryK; hop++) {
|
|
72
|
+
const curId = resolve(ctx, cur);
|
|
73
|
+
consumeNode(curId);
|
|
74
|
+
// Forward-absorb: follow only UNCONSUMED continuations. The gate below
|
|
75
|
+
// checks an unconsumed edge EXISTS, but follow()'s chooseNext knows
|
|
76
|
+
// nothing of `consumed` and may still walk to a consumed fixpoint —
|
|
77
|
+
// absorbing it would repeat content the grounding stage already spoke
|
|
78
|
+
// for, so a consumed fixpoint falls through to the pivot step instead.
|
|
79
|
+
if (curId !== null &&
|
|
80
|
+
ctx.store.nextFirst(curId, hubBound).some((n) => !consumed.has(n))) {
|
|
81
|
+
const fwd = await follow(ctx, curId, qv);
|
|
82
|
+
const fwdId = fwd !== null ? resolve(ctx, fwd) : null;
|
|
83
|
+
if (fwd !== null && !bytesEqual(fwd, cur) &&
|
|
84
|
+
(fwdId === null || !consumed.has(fwdId))) {
|
|
85
|
+
consumeAll(curId);
|
|
86
|
+
t ??= ctx.trace?.enter("reason", [
|
|
87
|
+
rItem(startedFrom, "grounded"),
|
|
88
|
+
]);
|
|
89
|
+
ctx.trace?.step("absorbForward", [rItem(cur, "answer", curId)], [rItem(fwd, "answer", resolve(ctx, fwd) ?? undefined)], "the answer is itself a learnt fact — follow its continuation to the fixpoint");
|
|
90
|
+
cur = fwd;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Pivot: find the longest unconsumed learnt context the answer contains.
|
|
95
|
+
consumeAll(curId);
|
|
96
|
+
const pivot = await pivotInto(ctx, cur, consumed);
|
|
97
|
+
if (pivot === null)
|
|
98
|
+
break;
|
|
99
|
+
const fc = await follow(ctx, pivot, qv);
|
|
100
|
+
consumeAll(pivot);
|
|
101
|
+
if (fc === null || bytesEqual(fc, cur))
|
|
102
|
+
break;
|
|
103
|
+
t ??= ctx.trace?.enter("reason", [rItem(startedFrom, "grounded")]);
|
|
104
|
+
ctx.trace?.step("pivotStep", [rItem(cur, "answer"), rNode(ctx, pivot, "pivot")], [rItem(fc, "answer", resolve(ctx, fc) ?? undefined)], "pivot on the shared span this answer contains, then step forward across that fact");
|
|
105
|
+
cur = fc;
|
|
106
|
+
}
|
|
107
|
+
t?.done([rItem(cur, "answer", resolve(ctx, cur) ?? undefined)], "the multi-hop chain's fixpoint");
|
|
108
|
+
return cur;
|
|
109
|
+
}
|
|
110
|
+
/** Fuse independent points of attention into one answer (multi-topic).
|
|
111
|
+
* When the consensus climb finds more than one dominant point, each
|
|
112
|
+
* independent point grounds its own answer; they are bridged together
|
|
113
|
+
* by any learnt connector the graph holds between them. */
|
|
114
|
+
export async function fuseAttention(ctx, query, primary, pre) {
|
|
115
|
+
// When the answer is structurally drawn from the query itself
|
|
116
|
+
// (extraction), it already spans all the query's pieces — fusion
|
|
117
|
+
// would only add noise from unrelated stored contexts. The gate is
|
|
118
|
+
// STRICT containment (resolved node in the query's tree, or a contiguous
|
|
119
|
+
// byte run): the old sparse-subsequence test was trivially satisfied by
|
|
120
|
+
// short answers over long queries, silently starving multi-topic queries
|
|
121
|
+
// of fusion.
|
|
122
|
+
if (containsSpan(ctx, query, primary))
|
|
123
|
+
return primary;
|
|
124
|
+
// The committed points of attention ARE the shared climb's roots (same
|
|
125
|
+
// query, same k, same DF mode) — read them from Precomputed instead of
|
|
126
|
+
// re-climbing, so even a traced response pays for the climb once.
|
|
127
|
+
const forest = (await pre.attention()).roots;
|
|
128
|
+
if (forest.length <= 1)
|
|
129
|
+
return primary;
|
|
130
|
+
const pieces = [
|
|
131
|
+
{ start: forest[0].start, bytes: primary },
|
|
132
|
+
];
|
|
133
|
+
const qv = pre.guide; // once, not per root
|
|
134
|
+
const t = ctx.trace?.enter("fuseAttention", [
|
|
135
|
+
rItem(primary, "primary"),
|
|
136
|
+
...forest.slice(1).map((r) => rNode(ctx, r.anchor, "point", r.vote)),
|
|
137
|
+
]);
|
|
138
|
+
for (const root of forest.slice(1)) {
|
|
139
|
+
const g = await project(ctx, root.anchor, qv);
|
|
140
|
+
if (g === null || g.length === 0)
|
|
141
|
+
continue;
|
|
142
|
+
if (pieces.some((p) => indexOf(p.bytes, g, 0) >= 0))
|
|
143
|
+
continue;
|
|
144
|
+
pieces.push({ start: root.start, bytes: g });
|
|
145
|
+
}
|
|
146
|
+
if (pieces.length === 1) {
|
|
147
|
+
t?.done([rItem(primary, "answer")], "no further independent point grounded");
|
|
148
|
+
return primary;
|
|
149
|
+
}
|
|
150
|
+
pieces.sort((a, b) => a.start - b.start);
|
|
151
|
+
let out = pieces[0].bytes;
|
|
152
|
+
for (let i = 1; i < pieces.length; i++) {
|
|
153
|
+
// An approximate-resonance miss (or a genuinely unlearnt junction) joins
|
|
154
|
+
// the pieces bare — joinWithBridge surfaces it as a bridgeMiss step.
|
|
155
|
+
out = await joinWithBridge(ctx, out, pieces[i].bytes);
|
|
156
|
+
}
|
|
157
|
+
t?.done([rItem(out, "answer", resolve(ctx, out) ?? undefined)], `fused ${pieces.length} independent points of attention into one answer`);
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
// (resonance.js is already a static dependency above — `bridge` — so the old
|
|
161
|
+
// dynamic import of pivotInto guarded against a cycle that does not exist.)
|
|
162
|
+
import { containsSpan } from "./mechanisms/extraction.js";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { MindContext, Recognition, Segment } from "./types.js";
|
|
2
|
+
/** Decompose a byte stream into every stored form that leads somewhere
|
|
3
|
+
* (has a continuation edge or a halo). Two complementary readings:
|
|
4
|
+
*
|
|
5
|
+
* • structural — walk the query's own perceived tree, naming each subtree
|
|
6
|
+
* by findLeaf at the leaves and findBranch above. Catches every form
|
|
7
|
+
* aligned to the query's segmentation.
|
|
8
|
+
*
|
|
9
|
+
* • canonical — re-derive the store's segmentation directly: at each byte,
|
|
10
|
+
* the longest known leaf, chained into flat branches. Names forms the
|
|
11
|
+
* query's own cut cannot, and records sub-leaf boundaries as `splits`.
|
|
12
|
+
*
|
|
13
|
+
* Both O(n · maxGroup) bounded O(1) probes — never a scan of the corpus. */
|
|
14
|
+
export declare function recognise(ctx: MindContext, bytes: Uint8Array): Recognition;
|
|
15
|
+
/** Segment bytes using the geometry's own groupings — leaf-parent
|
|
16
|
+
* nodes from the perceived tree, with consecutive bare leaves merged
|
|
17
|
+
* into one segment. Each segment's gist is perceived from its bytes
|
|
18
|
+
* IN ISOLATION, so the same content has the same gist regardless of
|
|
19
|
+
* where it appears. */
|
|
20
|
+
export declare function segment(ctx: MindContext, bytes: Uint8Array): Segment[];
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// recognition.ts — Section 2 of the mind:
|
|
2
|
+
// Address + Read over byte streams — decompose a query into its known forms.
|
|
3
|
+
//
|
|
4
|
+
// recognise — structural + canonical decomposition into every stored form
|
|
5
|
+
// that leads somewhere (has a continuation edge or a halo).
|
|
6
|
+
// segment — leaf-parent segmentation using the geometry's own groupings.
|
|
7
|
+
import { rItem } from "./trace.js";
|
|
8
|
+
import { canonResolve, foldTree, gistOf, latin1Key, perceive, resolve, } from "./primitives.js";
|
|
9
|
+
import { atomIsHub, corpusN, leadsSomewhere } from "./traverse.js";
|
|
10
|
+
import { chainReach, leafIdAt } from "./canonical.js";
|
|
11
|
+
import { isChunk } from "../sema.js";
|
|
12
|
+
/** Decompose a byte stream into every stored form that leads somewhere
|
|
13
|
+
* (has a continuation edge or a halo). Two complementary readings:
|
|
14
|
+
*
|
|
15
|
+
* • structural — walk the query's own perceived tree, naming each subtree
|
|
16
|
+
* by findLeaf at the leaves and findBranch above. Catches every form
|
|
17
|
+
* aligned to the query's segmentation.
|
|
18
|
+
*
|
|
19
|
+
* • canonical — re-derive the store's segmentation directly: at each byte,
|
|
20
|
+
* the longest known leaf, chained into flat branches. Names forms the
|
|
21
|
+
* query's own cut cannot, and records sub-leaf boundaries as `splits`.
|
|
22
|
+
*
|
|
23
|
+
* Both O(n · maxGroup) bounded O(1) probes — never a scan of the corpus. */
|
|
24
|
+
export function recognise(ctx, bytes) {
|
|
25
|
+
// Content-keyed memo — works for both single-turn respond() and multi-turn
|
|
26
|
+
// respondTurn() (where the map persists across calls). Skipped while
|
|
27
|
+
// tracing so every call still emits its rationale step.
|
|
28
|
+
if (ctx.recogniseMemo && !ctx.trace) {
|
|
29
|
+
const key = latin1Key(bytes);
|
|
30
|
+
const hit = ctx.recogniseMemo.get(key);
|
|
31
|
+
if (hit !== undefined)
|
|
32
|
+
return hit;
|
|
33
|
+
const fresh = recogniseImpl(ctx, bytes);
|
|
34
|
+
ctx.recogniseMemo.set(key, fresh);
|
|
35
|
+
return fresh;
|
|
36
|
+
}
|
|
37
|
+
return recogniseImpl(ctx, bytes);
|
|
38
|
+
}
|
|
39
|
+
function recogniseImpl(ctx, bytes) {
|
|
40
|
+
const store = ctx.store;
|
|
41
|
+
const sites = [];
|
|
42
|
+
const leaves = [];
|
|
43
|
+
const splits = new Set();
|
|
44
|
+
if (bytes.length === 0)
|
|
45
|
+
return { sites, leaves, splits };
|
|
46
|
+
// Span-resolve memo for THIS call: the structural pass (sub-runs inside
|
|
47
|
+
// leaf-parents) and the canonical pass (leaf-id chains) probe overlapping
|
|
48
|
+
// spans, and each resolve() is a full fold of the sub-span (fresh subarray
|
|
49
|
+
// objects — the per-response perceive memo cannot see them). Keyed
|
|
50
|
+
// numerically by (start, end); resolve is pure and the store is read-only
|
|
51
|
+
// here, so a hit is exact.
|
|
52
|
+
const spanIds = new Map();
|
|
53
|
+
const resolveSpan = (start, end) => {
|
|
54
|
+
const key = start * (bytes.length + 1) + end;
|
|
55
|
+
let id = spanIds.get(key);
|
|
56
|
+
if (id === undefined) {
|
|
57
|
+
id = resolve(ctx, bytes.subarray(start, end));
|
|
58
|
+
spanIds.set(key, id);
|
|
59
|
+
}
|
|
60
|
+
return id;
|
|
61
|
+
};
|
|
62
|
+
// Byte atoms (implicit negative-id single-byte leaves) are admitted as
|
|
63
|
+
// recognised sites only while atoms can still DISCRIMINATE at this corpus
|
|
64
|
+
// scale (see {@link atomIsHub}). On a small store a single-letter fact
|
|
65
|
+
// ("a" → "A") is genuine learnt content and its site is essential; on a
|
|
66
|
+
// large one every letter of every query would otherwise become a
|
|
67
|
+
// "recognised form" — the bridge then finds junction connectors between
|
|
68
|
+
// bare letters, cover follows edges hanging off them, and pure noise
|
|
69
|
+
// ("qq8f3kz9…") grounds to an arbitrary learnt sentence instead of
|
|
70
|
+
// silence. Atoms stay available as leaves (PASS-carried literals) and
|
|
71
|
+
// through exact tier-0 resolution regardless.
|
|
72
|
+
const atomsAreHubs = atomIsHub(ctx, corpusN(ctx));
|
|
73
|
+
const emit = (start, end, id) => {
|
|
74
|
+
if (id < 0 && atomsAreHubs)
|
|
75
|
+
return;
|
|
76
|
+
if (leadsSomewhere(ctx, id)) {
|
|
77
|
+
sites.push({ start, end, payload: id });
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
// ── structural: the query's own perceived tree ──────────────────────
|
|
81
|
+
const starts = new Set();
|
|
82
|
+
starts.add(0);
|
|
83
|
+
foldTree(ctx, perceive(ctx, bytes), 0, (n, start, end, node) => {
|
|
84
|
+
if (n.kids === null) {
|
|
85
|
+
leaves.push({ start, end, bytes: n.leaf ?? new Uint8Array(0), node });
|
|
86
|
+
}
|
|
87
|
+
if (node !== null)
|
|
88
|
+
emit(start, end, node);
|
|
89
|
+
// Canonical fallback: a subtree whose exact content-addressed lookup
|
|
90
|
+
// missed may still be a stored form under the response's equivalence
|
|
91
|
+
// (case, width, whitespace — whatever the injected canonicalizer says).
|
|
92
|
+
// O(subtree bytes) per miss, memoised per response; a no-op when no
|
|
93
|
+
// canonicalizer was injected or the store has no canon index.
|
|
94
|
+
else if (end - start >= 2) {
|
|
95
|
+
const cid = canonResolve(ctx, bytes.subarray(start, end));
|
|
96
|
+
if (cid !== null)
|
|
97
|
+
emit(start, end, cid);
|
|
98
|
+
}
|
|
99
|
+
if (isChunk(n)) {
|
|
100
|
+
starts.add(start);
|
|
101
|
+
// Try every sub-span within this leaf-parent.
|
|
102
|
+
const leafOffsets = [];
|
|
103
|
+
let off = start;
|
|
104
|
+
for (const k of n.kids) {
|
|
105
|
+
leafOffsets.push(off);
|
|
106
|
+
off += k.leaf?.length ?? 0;
|
|
107
|
+
}
|
|
108
|
+
for (let i = 0; i < n.kids.length; i++) {
|
|
109
|
+
const subIds = [];
|
|
110
|
+
for (let j = i; j < n.kids.length; j++) {
|
|
111
|
+
const kj = n.kids[j];
|
|
112
|
+
if (kj.kids !== null || !kj.leaf)
|
|
113
|
+
break;
|
|
114
|
+
const lid = store.findLeaf(kj.leaf);
|
|
115
|
+
if (lid === null)
|
|
116
|
+
break;
|
|
117
|
+
subIds.push(lid);
|
|
118
|
+
const branch = store.findBranch(subIds);
|
|
119
|
+
if (branch === null)
|
|
120
|
+
continue;
|
|
121
|
+
const subEnd = leafOffsets[j] + (kj.leaf?.length ?? 0);
|
|
122
|
+
const resolved = resolveSpan(leafOffsets[i], subEnd);
|
|
123
|
+
if (resolved !== null)
|
|
124
|
+
emit(leafOffsets[i], subEnd, resolved);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
// ── canonical: longest-known-leaf re-segmentation ──────────────────
|
|
130
|
+
const W = ctx.space.maxGroup;
|
|
131
|
+
const singleLeaf = new Array(bytes.length).fill(null);
|
|
132
|
+
for (let p = 0; p < bytes.length; p++) {
|
|
133
|
+
const id = leafIdAt(ctx, bytes, p);
|
|
134
|
+
if (id !== null)
|
|
135
|
+
singleLeaf[p] = { id, end: p + 1 };
|
|
136
|
+
}
|
|
137
|
+
const leafFrom = (p) => {
|
|
138
|
+
if (p >= bytes.length)
|
|
139
|
+
return null;
|
|
140
|
+
return singleLeaf[p];
|
|
141
|
+
};
|
|
142
|
+
const chunkEnd = new Uint32Array(bytes.length);
|
|
143
|
+
const sorted = [...starts].sort((a, b) => a - b);
|
|
144
|
+
for (let si = 0; si < sorted.length; si++) {
|
|
145
|
+
const chunkStart = sorted[si];
|
|
146
|
+
const chunkLimit = si + 1 < sorted.length ? sorted[si + 1] : bytes.length;
|
|
147
|
+
for (let p = chunkStart; p < chunkLimit; p++) {
|
|
148
|
+
chunkEnd[p] = chunkLimit;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const tryChain = (p, maxIds) => {
|
|
152
|
+
const first = leafFrom(p);
|
|
153
|
+
if (!first)
|
|
154
|
+
return;
|
|
155
|
+
emit(p, first.end, first.id);
|
|
156
|
+
const ids = [first.id];
|
|
157
|
+
let pos = first.end;
|
|
158
|
+
let prevId = null;
|
|
159
|
+
for (let depth = 1; pos < bytes.length && ids.length <= maxIds; depth++) {
|
|
160
|
+
const nx = leafFrom(pos);
|
|
161
|
+
if (!nx)
|
|
162
|
+
break;
|
|
163
|
+
ids.push(nx.id);
|
|
164
|
+
pos = nx.end;
|
|
165
|
+
if (store.findBranch(ids) === null)
|
|
166
|
+
continue;
|
|
167
|
+
const id = resolveSpan(p, pos);
|
|
168
|
+
if (id === null || id === prevId)
|
|
169
|
+
continue;
|
|
170
|
+
prevId = id;
|
|
171
|
+
emit(p, pos, id);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
for (let p = 0; p < bytes.length; p++) {
|
|
175
|
+
if (starts.has(p)) {
|
|
176
|
+
tryChain(p, chainReach(W)); // boundary start — full reach
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
const limit = chunkEnd[p] + W;
|
|
180
|
+
tryChain(p, Math.min(limit - p, chainReach(W)));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// ── splits: a form boundary that does not fall on a leaf edge ────────
|
|
184
|
+
const leafEdges = new Set([bytes.length]);
|
|
185
|
+
for (const lf of leaves)
|
|
186
|
+
leafEdges.add(lf.start);
|
|
187
|
+
for (const s of sites) {
|
|
188
|
+
if (!leafEdges.has(s.start))
|
|
189
|
+
splits.add(s.start);
|
|
190
|
+
if (!leafEdges.has(s.end))
|
|
191
|
+
splits.add(s.end);
|
|
192
|
+
}
|
|
193
|
+
ctx.trace?.step("recognise", [rItem(bytes, "query")], sites.map((s) => rItem(bytes.subarray(s.start, s.end), "form", s.payload, [
|
|
194
|
+
s.start,
|
|
195
|
+
s.end,
|
|
196
|
+
])), `decompose the query into ${sites.length} learnt form(s) that lead somewhere` +
|
|
197
|
+
` (over ${leaves.length} perceived leaves)`);
|
|
198
|
+
return { sites, leaves, splits };
|
|
199
|
+
}
|
|
200
|
+
/** Segment bytes using the geometry's own groupings — leaf-parent
|
|
201
|
+
* nodes from the perceived tree, with consecutive bare leaves merged
|
|
202
|
+
* into one segment. Each segment's gist is perceived from its bytes
|
|
203
|
+
* IN ISOLATION, so the same content has the same gist regardless of
|
|
204
|
+
* where it appears. */
|
|
205
|
+
export function segment(ctx, bytes) {
|
|
206
|
+
const tree = perceive(ctx, bytes);
|
|
207
|
+
const out = [];
|
|
208
|
+
let pendingStart = -1;
|
|
209
|
+
let pendingEnd = -1;
|
|
210
|
+
const flush = () => {
|
|
211
|
+
if (pendingStart >= 0 && pendingEnd > pendingStart) {
|
|
212
|
+
out.push({
|
|
213
|
+
start: pendingStart,
|
|
214
|
+
end: pendingEnd,
|
|
215
|
+
v: gistOf(ctx, bytes.subarray(pendingStart, pendingEnd)),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
pendingStart = -1;
|
|
219
|
+
pendingEnd = -1;
|
|
220
|
+
};
|
|
221
|
+
const walk = (n, start) => {
|
|
222
|
+
if (n.kids === null) {
|
|
223
|
+
const end = start + (n.leaf?.length ?? 0);
|
|
224
|
+
if (pendingStart < 0)
|
|
225
|
+
pendingStart = start;
|
|
226
|
+
pendingEnd = end;
|
|
227
|
+
return end;
|
|
228
|
+
}
|
|
229
|
+
if (isChunk(n)) {
|
|
230
|
+
flush();
|
|
231
|
+
let end = start;
|
|
232
|
+
for (const c of n.kids)
|
|
233
|
+
end += c.leaf?.length ?? 0;
|
|
234
|
+
out.push({ start, end, v: gistOf(ctx, bytes.subarray(start, end)) });
|
|
235
|
+
return end;
|
|
236
|
+
}
|
|
237
|
+
flush();
|
|
238
|
+
let pos = start;
|
|
239
|
+
for (const c of n.kids)
|
|
240
|
+
pos = walk(c, pos);
|
|
241
|
+
return pos;
|
|
242
|
+
};
|
|
243
|
+
walk(tree, 0);
|
|
244
|
+
flush();
|
|
245
|
+
return out;
|
|
246
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { MindContext } from "./types.js";
|
|
2
|
+
/** The connector that belongs BETWEEN two adjacent results — the graded
|
|
3
|
+
* junction ladder described in the module note above. Returns null when
|
|
4
|
+
* the graph holds no evidence that the two ever ran together. */
|
|
5
|
+
export declare function bridge(ctx: MindContext, left: Uint8Array, right: Uint8Array, interiorAllowance?: number): Promise<Uint8Array | null>;
|
|
6
|
+
/** Join two spans with the learnt connector between them, when one exists —
|
|
7
|
+
* the composition step every out-of-search assembly (multi-topic fusion,
|
|
8
|
+
* CAST's substitution and comparison) shares. A miss joins the pieces BARE
|
|
9
|
+
* and is never silent: it emits the same `bridgeMiss` trace step everywhere,
|
|
10
|
+
* so a degraded join is visible in the rationale regardless of which
|
|
11
|
+
* mechanism paid it. (The in-search connector splice in graph-search.ts is
|
|
12
|
+
* the same concept inside the deduction, where the join is a costed rule.) */
|
|
13
|
+
export declare function joinWithBridge(ctx: MindContext, left: Uint8Array, right: Uint8Array): Promise<Uint8Array>;
|
|
14
|
+
/** The pivot a produced answer bridges through: the longest UNCONSUMED learnt
|
|
15
|
+
* CONTEXT (a node bearing a continuation edge) whose bytes `answer` literally
|
|
16
|
+
* contains. Candidates are gathered by resonating the answer's sub-regions
|
|
17
|
+
* (breadth-first, leaves skipped, probes capped by branch count), then
|
|
18
|
+
* confirmed by exact byte containment — a near-resonance alone never hops. */
|
|
19
|
+
export declare function pivotInto(ctx: MindContext, answer: Uint8Array, consumed: ReadonlySet<number>): Promise<number | null>;
|
|
20
|
+
export declare function meaningOf(ctx: MindContext, bytes: Uint8Array, anchors: ReadonlyArray<{
|
|
21
|
+
name: string;
|
|
22
|
+
form: Uint8Array;
|
|
23
|
+
}>): Promise<string | null>;
|
|
Binary file
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MindContext } from "./types.js";
|
|
2
|
+
import type { DerivationStep } from "./graph-search.js";
|
|
3
|
+
import type { RationaleItem } from "./rationale.js";
|
|
4
|
+
export declare function rItem(bytes: Uint8Array, role?: string, node?: number, span?: [number, number]): RationaleItem;
|
|
5
|
+
export declare function rNode(ctx: MindContext, id: number, role?: string, score?: number): RationaleItem;
|
|
6
|
+
export declare function rDeriv(ctx: MindContext, it: DerivationStep["conclusion"], role?: string): RationaleItem;
|
|
7
|
+
/** The standard FALL-THROUGH closer every self-gating mechanism ends with:
|
|
8
|
+
* close the open scope with no outputs and the reason, and return null so
|
|
9
|
+
* the caller can `return fail("…")` in one expression. `t` is the scope an
|
|
10
|
+
* enclosing `ctx.trace?.enter(...)` returned (undefined when not tracing). */
|
|
11
|
+
export declare function traceFail(t: {
|
|
12
|
+
done(outputs: RationaleItem[], note?: string): void;
|
|
13
|
+
} | undefined): (note: string) => null;
|
|
14
|
+
export declare const MOVE_NOTE: Record<string, string>;
|
|
15
|
+
export declare function traceDerivation(ctx: MindContext, steps: ReadonlyArray<DerivationStep>): void;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// trace.ts — trace instrumentation + persistence (Section 9 of the mind).
|
|
2
|
+
//
|
|
3
|
+
// rItem, rNode, rDeriv — build RationaleItems from bytes/nodes/derivations
|
|
4
|
+
// traceDerivation — trace a full derivation proof tree
|
|
5
|
+
// MOVE_NOTE — human-readable names for each derivation move
|
|
6
|
+
import { read } from "./primitives.js";
|
|
7
|
+
import { decodeText } from "./rationale.js";
|
|
8
|
+
export function rItem(bytes, role, node, span) {
|
|
9
|
+
return {
|
|
10
|
+
text: decodeText(bytes),
|
|
11
|
+
role,
|
|
12
|
+
node: node ?? undefined,
|
|
13
|
+
span,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function rNode(ctx, id, role, score) {
|
|
17
|
+
return {
|
|
18
|
+
text: decodeText(read(ctx, id)),
|
|
19
|
+
node: id,
|
|
20
|
+
role,
|
|
21
|
+
score,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function rDeriv(ctx, it, role) {
|
|
25
|
+
const text = it.bytes
|
|
26
|
+
? decodeText(it.bytes)
|
|
27
|
+
: it.node !== undefined
|
|
28
|
+
? decodeText(read(ctx, it.node))
|
|
29
|
+
: it.kind === "cover"
|
|
30
|
+
? `cover@${it.span[0]}`
|
|
31
|
+
: `[${it.span[0]},${it.span[1]})`;
|
|
32
|
+
return { text, role: role ?? it.kind, node: it.node, span: it.span };
|
|
33
|
+
}
|
|
34
|
+
/** The standard FALL-THROUGH closer every self-gating mechanism ends with:
|
|
35
|
+
* close the open scope with no outputs and the reason, and return null so
|
|
36
|
+
* the caller can `return fail("…")` in one expression. `t` is the scope an
|
|
37
|
+
* enclosing `ctx.trace?.enter(...)` returned (undefined when not tracing). */
|
|
38
|
+
export function traceFail(t) {
|
|
39
|
+
return (note) => {
|
|
40
|
+
t?.done([], note);
|
|
41
|
+
return null;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export const MOVE_NOTE = {
|
|
45
|
+
"follow-edge": "follow a learned continuation edge — 'what follows what'",
|
|
46
|
+
"concept-hop": "jump a concept (halo) link — a synonym's edge",
|
|
47
|
+
"voice": "emit the asker's own wording for this form (articulation)",
|
|
48
|
+
"ground": "a chain reached its terminal answer",
|
|
49
|
+
"splice-connector": "splice a learnt connector between two rewrites",
|
|
50
|
+
"split": "cut a span at a sub-leaf form boundary so a form can be reached",
|
|
51
|
+
"fuse": "fuse adjacent fragments toward a deeper learned form",
|
|
52
|
+
"recompose": "recompose fused parts into a learned whole that leads on",
|
|
53
|
+
"bridge": "advance the cover frontier across this span",
|
|
54
|
+
"pool-vote": "pool independent regions' evidence for a shared anchor (sum, not shortest path)",
|
|
55
|
+
"axiom": "a seed: a perceived leaf, recognised form, or computed result",
|
|
56
|
+
"step": "a derivation step",
|
|
57
|
+
};
|
|
58
|
+
export function traceDerivation(ctx, steps) {
|
|
59
|
+
const t = ctx.trace;
|
|
60
|
+
if (!t)
|
|
61
|
+
return;
|
|
62
|
+
const indexOfOrder = new Map();
|
|
63
|
+
for (const s of steps) {
|
|
64
|
+
const note = MOVE_NOTE[s.move] ?? s.move;
|
|
65
|
+
const deps = s.producers
|
|
66
|
+
.map((o) => indexOfOrder.get(o))
|
|
67
|
+
.filter((x) => x !== undefined);
|
|
68
|
+
const premises = s.premises.map((p) => rDeriv(ctx, p));
|
|
69
|
+
const conclusion = [rDeriv(ctx, s.conclusion)];
|
|
70
|
+
const index = t.step(s.move, premises, conclusion, s.cost > 0 ? `${note} (cost ${s.cost})` : note, deps.length > 0 ? deps : undefined);
|
|
71
|
+
indexOfOrder.set(s.order, index);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Vec } from "../vec.js";
|
|
2
|
+
import type { AncestorReach, MindContext } from "./types.js";
|
|
3
|
+
/** Climb the structural DAG from a node to its edge-bearing ancestor contexts.
|
|
4
|
+
* Ascent stops at hub nodes (parents > √N) — their reach is non-discriminative.
|
|
5
|
+
* When the start node has no structural parents, climbs from containment parents
|
|
6
|
+
* (sub-span flat branches inheriting their chunks' context).
|
|
7
|
+
*
|
|
8
|
+
* `memo`, when given, caches whole climbs by start id for the duration of ONE
|
|
9
|
+
* query (the store is read-only while a query is in flight, so a climb is a
|
|
10
|
+
* pure function of the id). The consensus pipeline climbs the SAME anchors
|
|
11
|
+
* repeatedly — regions sharing a chunk, and canonicalChunkId probing each
|
|
12
|
+
* chunk's prefixes — so without the memo every repeat re-pays the full
|
|
13
|
+
* fan-out reads. */
|
|
14
|
+
export declare function edgeAncestors(ctx: MindContext, id: number, contextCount: number, memo?: Map<number, AncestorReach>): AncestorReach;
|
|
15
|
+
/** Convenience: forward edges of a node. */
|
|
16
|
+
export declare function nextOf(ctx: MindContext, id: number): number[];
|
|
17
|
+
/** Convenience: reverse edges of a node. */
|
|
18
|
+
export declare function prevOf(ctx: MindContext, id: number): number[];
|
|
19
|
+
/** The uniform-expectation floor on a byte atom's corpus commonality: N
|
|
20
|
+
* learnt contexts, each at least one perception chunk of up to W of the 256
|
|
21
|
+
* possible byte values, contain a given atom in ≥ N·W/256 contexts on
|
|
22
|
+
* average. An atom's TRUE containment is unmeasurable (atoms carry no
|
|
23
|
+
* kid/contain links by construction), so this floor is the honest stand-in:
|
|
24
|
+
* derived entirely from the corpus scale N, the perception window W, and
|
|
25
|
+
* the alphabet size — never tuned. */
|
|
26
|
+
export declare function atomReach(ctx: MindContext, contextCount: number): number;
|
|
27
|
+
/** Whether a byte atom is a hub at this corpus scale — its commonality floor
|
|
28
|
+
* {@link atomReach} exceeds the hub bound √N. Below it (small stores) an
|
|
29
|
+
* atom votes and is recognised exactly as any stored form; above it the
|
|
30
|
+
* alphabet is scaffolding everywhere and abstains. */
|
|
31
|
+
export declare function atomIsHub(ctx: MindContext, contextCount: number): boolean;
|
|
32
|
+
/** Whether a node LEADS SOMEWHERE — it bears a continuation edge or a halo.
|
|
33
|
+
* The admission predicate recognition filters sites with (HOW_IT_WORKS
|
|
34
|
+
* §15.3): a form that leads nowhere contributes nothing to any derivation.
|
|
35
|
+
* Runs once per candidate span on the recognition hot path — `hasNext` is
|
|
36
|
+
* cached per response (the same flat-branch ids are probed across prefix
|
|
37
|
+
* variants by canonicalChunkId). `hasHalo` is not cached: it's a single
|
|
38
|
+
* indexed point probe per candidate, and the candidates that reach this
|
|
39
|
+
* check have already been filtered by hasNext above in edgeAncestors. */
|
|
40
|
+
export declare function leadsSomewhere(ctx: MindContext, id: number): boolean;
|
|
41
|
+
/** The structural IDF read of ONE node: how many distinct learnt contexts
|
|
42
|
+
* its containment/edge climb reaches, or Infinity when it reaches none or
|
|
43
|
+
* saturates (no usable identity evidence). The number every
|
|
44
|
+
* discriminative-vs-scaffolding decision derives from — paired with the
|
|
45
|
+
* half-dominance convention (geometry.dominates(reach, N)): content
|
|
46
|
+
* reaching a corpus MINORITY of contexts discriminates (an entity, a
|
|
47
|
+
* filler); content reaching a majority is frame scaffolding. */
|
|
48
|
+
export declare function reachOf(ctx: MindContext, id: number, contextCount: number, memo?: Map<number, AncestorReach>): number;
|
|
49
|
+
/** The corpus scale N — the count of DISTINCT learnt contexts, floored at 2
|
|
50
|
+
* so its derived readings (ln N in the consensus floor, √N in the hub bound)
|
|
51
|
+
* stay meaningful on a near-empty store. The one definition every consumer
|
|
52
|
+
* of "how big is this corpus?" reads. */
|
|
53
|
+
export declare function corpusN(ctx: MindContext): number;
|
|
54
|
+
/** The hub bound √N itself (≥ 2 always, since N is floored at 2) — for
|
|
55
|
+
* consumers that pass it to the store's LIMITed reads instead of capping a
|
|
56
|
+
* materialised list. {@link hubCap} is the list-side reading of the same
|
|
57
|
+
* convention. */
|
|
58
|
+
export declare function hubBound(ctx: MindContext): number;
|
|
59
|
+
/** Cap a candidate list at the hub bound √N (insertion order) — the ONE
|
|
60
|
+
* fan-out convention every walk and disambiguation uses (see HOW_IT_WORKS
|
|
61
|
+
* §8.6). A node connected to more than √N others is a hub whose individual
|
|
62
|
+
* connections carry ~no discriminative information; materialising or scoring
|
|
63
|
+
* them all would make single decisions scale with the corpus. */
|
|
64
|
+
export declare function hubCap<T>(ctx: MindContext, ids: readonly T[]): readonly T[];
|
|
65
|
+
/** Whether `descendant` lies within `ancestor`'s subtree — a structural DAG
|
|
66
|
+
* relation read off the hash-consed `kids` lists, by a bounded explicit-stack
|
|
67
|
+
* descent. Used by articulation to keep a voice from revoicing a fragment
|
|
68
|
+
* OF that voice. */
|
|
69
|
+
export declare function contains(ctx: MindContext, ancestor: number, descendant: number): boolean;
|
|
70
|
+
/** The best-scoring item by cosine against `query`, among items scoring at
|
|
71
|
+
* or above `threshold` — the shared arg-max every Pattern-A "which of these
|
|
72
|
+
* resonates best" decision reduces to. `strict` picks the tie-break a
|
|
73
|
+
* caller needs: `true` keeps the first-seen leader on a tie (`>`), the
|
|
74
|
+
* default lets a later equal score take it (`>=`). */
|
|
75
|
+
export declare function argmaxBy<T>(items: Iterable<T>, scoreOf: (item: T) => number, threshold: number, strict?: boolean): {
|
|
76
|
+
item: T;
|
|
77
|
+
score: number;
|
|
78
|
+
} | null;
|
|
79
|
+
export declare function argmaxCosine<T>(query: Vec, items: Iterable<T>, vecOf: (item: T) => Vec | null | undefined, threshold: number, strict?: boolean): {
|
|
80
|
+
item: T;
|
|
81
|
+
score: number;
|
|
82
|
+
} | null;
|
|
83
|
+
/** The guided-or-first continuation of a node, as answer-shaped bytes source:
|
|
84
|
+
* chooseNext under the response guide, falling back to the FIRST-inserted
|
|
85
|
+
* edge — the one no-guide convention chooseNext, project() and the search's
|
|
86
|
+
* formRules all share. undefined when the node has no continuation. */
|
|
87
|
+
export declare function guidedFirst(ctx: MindContext, id: number): number | undefined;
|
|
88
|
+
export declare function guidedNext(ctx: MindContext, node: number): number | undefined;
|
|
89
|
+
/** Disambiguate among a node's learnt continuations by distributional
|
|
90
|
+
* support. NOTE the `guide` contract: its VALUE is deliberately unused —
|
|
91
|
+
* only its PRESENCE gates disambiguation (a null guide means no query is in
|
|
92
|
+
* flight, so structural walkers keep plain first-edge behaviour). The
|
|
93
|
+
* gist-cosine of short answer candidates against a query guide is dominated
|
|
94
|
+
* by accidental byte-pattern correlations, not semantic relatedness, so the
|
|
95
|
+
* evidence consulted is structural: each candidate's reverse-edge support
|
|
96
|
+
* count (see below). Contrast {@link chooseAmong}, the REVERSE-direction
|
|
97
|
+
* disambiguator, whose candidates are whole learnt contexts — long enough
|
|
98
|
+
* that their perceived gists ARE semantically meaningful — and which
|
|
99
|
+
* therefore scores by guide cosine. The two directions consult different
|
|
100
|
+
* halves of the evidence on purpose. */
|
|
101
|
+
export declare function chooseNext(ctx: MindContext, id: number, guide?: Vec | null): number | undefined;
|
|
102
|
+
/** The perceived gist of a candidate node, through the session gist cache.
|
|
103
|
+
* Re-gisting a candidate is a full river fold of its bytes — the measured
|
|
104
|
+
* recall bottleneck (a hub context offers up to √N continuations, EACH
|
|
105
|
+
* re-perceived per pick). A node's bytes are immutable and perception is
|
|
106
|
+
* pure, so the cached gist is valid for the store's lifetime. Exported for
|
|
107
|
+
* every "score node ids against a guide" decision (chooseAmong here, the
|
|
108
|
+
* bridge's junction pick) so they share ONE cache and one convention. */
|
|
109
|
+
export declare function candidateGist(ctx: MindContext, c: number): Vec | null;
|
|
110
|
+
export declare function chooseAmong(ctx: MindContext, candidates: readonly number[], guide: Vec): {
|
|
111
|
+
id: number;
|
|
112
|
+
score: number;
|
|
113
|
+
};
|