@hviana/sema 0.5.3 → 0.5.4

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.
Files changed (143) hide show
  1. package/AGENTS.md +6 -6
  2. package/HOW_IT_WORKS.md +16 -0
  3. package/jsr.json +1 -1
  4. package/package.json +1 -1
  5. package/src/mind/match.ts +81 -0
  6. package/src/mind/mechanisms/recall.ts +29 -3
  7. package/test/76-reference-binding.test.mjs +72 -0
  8. package/dist/example/demo.d.ts +0 -1
  9. package/dist/example/demo.js +0 -39
  10. package/dist/example/train_base.d.ts +0 -93
  11. package/dist/example/train_base.js +0 -2269
  12. package/dist/src/alphabet.d.ts +0 -7
  13. package/dist/src/alphabet.js +0 -33
  14. package/dist/src/alu/src/alu.d.ts +0 -185
  15. package/dist/src/alu/src/alu.js +0 -440
  16. package/dist/src/alu/src/expr.d.ts +0 -61
  17. package/dist/src/alu/src/expr.js +0 -318
  18. package/dist/src/alu/src/index.d.ts +0 -11
  19. package/dist/src/alu/src/index.js +0 -19
  20. package/dist/src/alu/src/kernel-arith.d.ts +0 -16
  21. package/dist/src/alu/src/kernel-arith.js +0 -264
  22. package/dist/src/alu/src/kernel-bits.d.ts +0 -19
  23. package/dist/src/alu/src/kernel-bits.js +0 -152
  24. package/dist/src/alu/src/kernel-logic.d.ts +0 -4
  25. package/dist/src/alu/src/kernel-logic.js +0 -60
  26. package/dist/src/alu/src/kernel-nd.d.ts +0 -3
  27. package/dist/src/alu/src/kernel-nd.js +0 -208
  28. package/dist/src/alu/src/kernel-numeric.d.ts +0 -54
  29. package/dist/src/alu/src/kernel-numeric.js +0 -366
  30. package/dist/src/alu/src/operation.d.ts +0 -168
  31. package/dist/src/alu/src/operation.js +0 -189
  32. package/dist/src/alu/src/parser.d.ts +0 -221
  33. package/dist/src/alu/src/parser.js +0 -577
  34. package/dist/src/alu/src/resonance.d.ts +0 -55
  35. package/dist/src/alu/src/resonance.js +0 -126
  36. package/dist/src/alu/src/text.d.ts +0 -31
  37. package/dist/src/alu/src/text.js +0 -73
  38. package/dist/src/alu/src/value.d.ts +0 -109
  39. package/dist/src/alu/src/value.js +0 -300
  40. package/dist/src/alu/test/alu.test.d.ts +0 -1
  41. package/dist/src/alu/test/alu.test.js +0 -764
  42. package/dist/src/bytes.d.ts +0 -14
  43. package/dist/src/bytes.js +0 -59
  44. package/dist/src/canon.d.ts +0 -45
  45. package/dist/src/canon.js +0 -85
  46. package/dist/src/config.d.ts +0 -111
  47. package/dist/src/config.js +0 -91
  48. package/dist/src/derive/src/deduction.d.ts +0 -136
  49. package/dist/src/derive/src/deduction.js +0 -159
  50. package/dist/src/derive/src/index.d.ts +0 -8
  51. package/dist/src/derive/src/index.js +0 -11
  52. package/dist/src/derive/src/priority-queue.d.ts +0 -20
  53. package/dist/src/derive/src/priority-queue.js +0 -73
  54. package/dist/src/derive/src/rewrite.d.ts +0 -56
  55. package/dist/src/derive/src/rewrite.js +0 -100
  56. package/dist/src/derive/src/trie.d.ts +0 -90
  57. package/dist/src/derive/src/trie.js +0 -217
  58. package/dist/src/derive/test/derive.test.d.ts +0 -1
  59. package/dist/src/derive/test/derive.test.js +0 -122
  60. package/dist/src/extension.d.ts +0 -37
  61. package/dist/src/extension.js +0 -7
  62. package/dist/src/geometry.d.ts +0 -233
  63. package/dist/src/geometry.js +0 -1089
  64. package/dist/src/index.d.ts +0 -17
  65. package/dist/src/index.js +0 -19
  66. package/dist/src/ingest-cache.d.ts +0 -41
  67. package/dist/src/ingest-cache.js +0 -165
  68. package/dist/src/meter.d.ts +0 -176
  69. package/dist/src/meter.js +0 -274
  70. package/dist/src/mind/articulation.d.ts +0 -6
  71. package/dist/src/mind/articulation.js +0 -99
  72. package/dist/src/mind/attention.d.ts +0 -430
  73. package/dist/src/mind/attention.js +0 -2418
  74. package/dist/src/mind/bridge.d.ts +0 -37
  75. package/dist/src/mind/bridge.js +0 -907
  76. package/dist/src/mind/canonical.d.ts +0 -34
  77. package/dist/src/mind/canonical.js +0 -93
  78. package/dist/src/mind/graph-search.d.ts +0 -294
  79. package/dist/src/mind/graph-search.js +0 -996
  80. package/dist/src/mind/index.d.ts +0 -9
  81. package/dist/src/mind/index.js +0 -5
  82. package/dist/src/mind/junction.d.ts +0 -137
  83. package/dist/src/mind/junction.js +0 -358
  84. package/dist/src/mind/learning.d.ts +0 -75
  85. package/dist/src/mind/learning.js +0 -265
  86. package/dist/src/mind/match.d.ts +0 -351
  87. package/dist/src/mind/match.js +0 -959
  88. package/dist/src/mind/mechanisms/alu.d.ts +0 -4
  89. package/dist/src/mind/mechanisms/alu.js +0 -36
  90. package/dist/src/mind/mechanisms/cast.d.ts +0 -89
  91. package/dist/src/mind/mechanisms/cast.js +0 -1186
  92. package/dist/src/mind/mechanisms/confluence.d.ts +0 -24
  93. package/dist/src/mind/mechanisms/confluence.js +0 -271
  94. package/dist/src/mind/mechanisms/cover.d.ts +0 -6
  95. package/dist/src/mind/mechanisms/cover.js +0 -232
  96. package/dist/src/mind/mechanisms/extraction.d.ts +0 -33
  97. package/dist/src/mind/mechanisms/extraction.js +0 -327
  98. package/dist/src/mind/mechanisms/prefix-completion.d.ts +0 -22
  99. package/dist/src/mind/mechanisms/prefix-completion.js +0 -243
  100. package/dist/src/mind/mechanisms/recall.d.ts +0 -16
  101. package/dist/src/mind/mechanisms/recall.js +0 -460
  102. package/dist/src/mind/mechanisms/reference.d.ts +0 -6
  103. package/dist/src/mind/mechanisms/reference.js +0 -296
  104. package/dist/src/mind/mind.d.ts +0 -386
  105. package/dist/src/mind/mind.js +0 -754
  106. package/dist/src/mind/pipeline-mechanism.d.ts +0 -238
  107. package/dist/src/mind/pipeline-mechanism.js +0 -622
  108. package/dist/src/mind/pipeline.d.ts +0 -49
  109. package/dist/src/mind/pipeline.js +0 -343
  110. package/dist/src/mind/primitives.d.ts +0 -85
  111. package/dist/src/mind/primitives.js +0 -333
  112. package/dist/src/mind/rationale.d.ts +0 -139
  113. package/dist/src/mind/rationale.js +0 -163
  114. package/dist/src/mind/reasoning.d.ts +0 -42
  115. package/dist/src/mind/reasoning.js +0 -305
  116. package/dist/src/mind/recognition.d.ts +0 -20
  117. package/dist/src/mind/recognition.js +0 -696
  118. package/dist/src/mind/resonance.d.ts +0 -35
  119. package/dist/src/mind/resonance.js +0 -365
  120. package/dist/src/mind/trace.d.ts +0 -15
  121. package/dist/src/mind/trace.js +0 -73
  122. package/dist/src/mind/traverse.d.ts +0 -196
  123. package/dist/src/mind/traverse.js +0 -806
  124. package/dist/src/mind/types.d.ts +0 -418
  125. package/dist/src/mind/types.js +0 -179
  126. package/dist/src/rabitq-ivf/src/database.d.ts +0 -113
  127. package/dist/src/rabitq-ivf/src/database.js +0 -201
  128. package/dist/src/rabitq-ivf/src/index.d.ts +0 -7
  129. package/dist/src/rabitq-ivf/src/index.js +0 -4
  130. package/dist/src/rabitq-ivf/src/ivf.d.ts +0 -200
  131. package/dist/src/rabitq-ivf/src/ivf.js +0 -1165
  132. package/dist/src/rabitq-ivf/src/prng.d.ts +0 -19
  133. package/dist/src/rabitq-ivf/src/prng.js +0 -36
  134. package/dist/src/rabitq-ivf/src/rabitq.d.ts +0 -95
  135. package/dist/src/rabitq-ivf/src/rabitq.js +0 -313
  136. package/dist/src/sema.d.ts +0 -41
  137. package/dist/src/sema.js +0 -77
  138. package/dist/src/store-sqlite.d.ts +0 -184
  139. package/dist/src/store-sqlite.js +0 -942
  140. package/dist/src/store.d.ts +0 -762
  141. package/dist/src/store.js +0 -1823
  142. package/dist/src/vec.d.ts +0 -31
  143. package/dist/src/vec.js +0 -109
@@ -1,159 +0,0 @@
1
- /**
2
- * Knuth's lightest-derivation algorithm with an A* outside bound.
3
- *
4
- * A *weighted deduction system* (equivalently an implicit AND-OR hypergraph) is
5
- * a set of items combined by inference rules
6
- *
7
- * premise₁ ∧ … ∧ premiseₖ --localCost--> conclusion
8
- *
9
- * where a derivation's cost is the sum of the local costs of the rules used.
10
- * {@link lightestDerivation} finds a minimum-cost derivation of a goal item.
11
- * The engine is the Dijkstra-like core of Knuth (1977) — an item's cost is
12
- * final the moment it is popped — extended with an admissible heuristic so that
13
- * partial derivations which cannot lead cheaply to the goal are never expanded
14
- * (A* parsing). It is completely generic: it knows nothing of what items are,
15
- * only how to canonicalise them, enumerate their rules, score them, and test
16
- * the goal.
17
- *
18
- * The four reductions the search relies on:
19
- * 1. **Canonical chart memoization** — items are keyed by {@link
20
- * DeductionSystem.key}; equivalent partial derivations collapse to one
21
- * chart entry, the cheapest.
22
- * 2. **Backward demand filtering** — {@link DeductionSystem.rules} only emits
23
- * rules whose conclusion can still reach the goal, so work unrelated to the
24
- * goal is never generated.
25
- * 3. **A* lower-bound pruning** — {@link DeductionSystem.heuristic} keeps the
26
- * agenda ordered by g + h, so only competitive items are expanded.
27
- * 4. **Lazy hyperedge generation** — rules (including bridges) are produced by
28
- * `rules` only when one of their premises is finalised, never up front.
29
- *
30
- * Correctness conditions (the caller must uphold these):
31
- * - Local costs are non-negative (more generally, monotone / superior).
32
- * - The heuristic never overestimates the remaining cost to a goal
33
- * (admissible) and is hyperedge-consistent:
34
- * h(conclusion) ≤ ruleCost + Σ h(premiseᵢ)
35
- * i.e. relaxing a rule cannot decrease f. The default heuristic (0) is
36
- * trivially consistent and turns the search into plain Knuth/Dijkstra.
37
- * - {@link DeductionSystem.key} preserves every part of an item that can
38
- * affect how it later combines (its "boundary signature"); anything the key
39
- * drops is asserted to be irrelevant to future composition.
40
- */
41
- import { MinHeap } from "./priority-queue.js";
42
- /**
43
- * Find a lightest derivation of a goal item, or `null` if none exists.
44
- * `cost` on the returned root is the total derivation cost.
45
- */
46
- export function lightestDerivation(system, stats) {
47
- const g = new Map(); // best known cost per item
48
- const proof = new Map(); // producing rule per item
49
- const items = new Map(); // key → the item it stands for
50
- const hCache = new Map();
51
- const agenda = new MinHeap();
52
- const heuristic = system.heuristic;
53
- const h = (item, key) => {
54
- if (!heuristic)
55
- return 0;
56
- let v = hCache.get(key);
57
- if (v === undefined) {
58
- v = heuristic(item);
59
- hCache.set(key, v);
60
- }
61
- return v;
62
- };
63
- const costOf = (item) => g.get(system.key(item)) ?? Infinity;
64
- const relax = (item, cost, rule) => {
65
- const key = system.key(item);
66
- if (rule?.combine === "sum" && system.pool) {
67
- // Evidence pooling: accumulate this firing rather than compete for the
68
- // cheapest — see {@link Rule.combine}. The premises are already
69
- // finalised (the caller only relaxes a rule once every premise's cost
70
- // is known), so their derivations can be read back immediately.
71
- const premises = rule.premises.map((p) => reconstruct(p, system, g, proof));
72
- const prior = system.pool.get(key);
73
- system.pool.set(key, {
74
- item,
75
- cost: (prior?.cost ?? 0) + cost,
76
- contributions: [...(prior?.contributions ?? []), { rule, premises }],
77
- });
78
- return;
79
- }
80
- const current = g.get(key);
81
- if (current === undefined || cost < current) {
82
- g.set(key, cost);
83
- proof.set(key, rule);
84
- items.set(key, item);
85
- if (stats)
86
- stats.pushes++;
87
- agenda.push(cost + h(item, key), { key, g: cost });
88
- }
89
- };
90
- for (const { item, cost } of system.axioms())
91
- relax(item, cost, null);
92
- while (agenda.size > 0) {
93
- const { value } = agenda.pop();
94
- if (stats)
95
- stats.pops++;
96
- const key = value.key;
97
- // Lazy deletion: an entry is stale if a cheaper derivation has since been
98
- // recorded for the same item.
99
- if (value.g !== g.get(key))
100
- continue;
101
- const item = items.get(key);
102
- if (system.isGoal(item)) {
103
- return reconstruct(item, system, g, proof);
104
- }
105
- for (const rule of system.rules(item, costOf)) {
106
- let sum = rule.cost;
107
- let ready = true;
108
- for (const p of rule.premises) {
109
- const pc = g.get(system.key(p));
110
- if (pc === undefined) {
111
- ready = false;
112
- break;
113
- }
114
- sum += pc;
115
- }
116
- if (ready)
117
- relax(rule.conclusion, sum, rule);
118
- }
119
- }
120
- return null;
121
- }
122
- function reconstruct(item, system, g, proof) {
123
- // Iterative post-order over the derivation hypergraph. In the rewrite
124
- // search every rule has one premise, so the derivation is a chain whose
125
- // length equals the number of frontier edges — which, with long inputs,
126
- // can exceed the call stack. Multi-premise rules (the test-suite bridge
127
- // case) are handled by the same explicit stack.
128
- const done = new Map();
129
- const stack = [item];
130
- while (stack.length > 0) {
131
- const cur = stack[stack.length - 1]; // peek
132
- const key = system.key(cur);
133
- if (done.has(key)) {
134
- stack.pop();
135
- continue;
136
- }
137
- const rule = proof.get(key) ?? null;
138
- const premises = rule?.premises ?? [];
139
- // Push any unresolved premises (rightmost first → leftmost resolves first).
140
- let pending = false;
141
- for (let i = premises.length - 1; i >= 0; i--) {
142
- if (!done.has(system.key(premises[i]))) {
143
- stack.push(premises[i]);
144
- pending = true;
145
- }
146
- }
147
- if (!pending) {
148
- stack.pop(); // this item
149
- const kids = premises.map((p) => done.get(system.key(p)));
150
- done.set(key, {
151
- item: cur,
152
- cost: g.get(key),
153
- rule,
154
- premises: kids,
155
- });
156
- }
157
- }
158
- return done.get(system.key(item));
159
- }
@@ -1,8 +0,0 @@
1
- export { lightestDerivation } from "./deduction.js";
2
- export type { SearchStats } from "./deduction.js";
3
- export type { DeductionSystem, Derivation, PooledConclusion, PooledContribution, Rule, } from "./deduction.js";
4
- export { coverSequence } from "./rewrite.js";
5
- export type { CandidateSpan, Cover } from "./rewrite.js";
6
- export { Trie } from "./trie.js";
7
- export type { Match } from "./trie.js";
8
- export { MinHeap } from "./priority-queue.js";
@@ -1,11 +0,0 @@
1
- // derive — A* lightest-derivation rewrite search.
2
- //
3
- // A small, self-contained library for finding minimum-cost derivations in a
4
- // weighted deduction system (an implicit AND-OR hypergraph), with on-demand
5
- // (lazy) rule generation and an admissible A* outside bound. It has no
6
- // dependency on the rest of the codebase and is reusable for any
7
- // symbolic-rewriting mechanism. See ./../README.md for the design.
8
- export { lightestDerivation } from "./deduction.js";
9
- export { coverSequence } from "./rewrite.js";
10
- export { Trie } from "./trie.js";
11
- export { MinHeap } from "./priority-queue.js";
@@ -1,20 +0,0 @@
1
- /**
2
- * Binary min-heap keyed by a numeric priority, carrying an arbitrary payload.
3
- *
4
- * It is the agenda of the lightest-derivation search, where the priority is the
5
- * estimate f = g + h. Stale entries are tolerated by the consumer (lazy
6
- * deletion), so there is no decrease-key: when an item's cost improves it is
7
- * simply pushed again, and the older, higher-priority copy is recognised as
8
- * stale and discarded when it surfaces. Parallel arrays (rather than an array
9
- * of objects) keep the hot path allocation-free.
10
- */
11
- export declare class MinHeap<T> {
12
- private readonly keys;
13
- private readonly vals;
14
- get size(): number;
15
- push(priority: number, value: T): void;
16
- pop(): {
17
- priority: number;
18
- value: T;
19
- } | undefined;
20
- }
@@ -1,73 +0,0 @@
1
- /**
2
- * Binary min-heap keyed by a numeric priority, carrying an arbitrary payload.
3
- *
4
- * It is the agenda of the lightest-derivation search, where the priority is the
5
- * estimate f = g + h. Stale entries are tolerated by the consumer (lazy
6
- * deletion), so there is no decrease-key: when an item's cost improves it is
7
- * simply pushed again, and the older, higher-priority copy is recognised as
8
- * stale and discarded when it surfaces. Parallel arrays (rather than an array
9
- * of objects) keep the hot path allocation-free.
10
- */
11
- export class MinHeap {
12
- keys = [];
13
- vals = [];
14
- get size() {
15
- return this.keys.length;
16
- }
17
- push(priority, value) {
18
- const keys = this.keys;
19
- const vals = this.vals;
20
- let i = keys.length;
21
- keys.push(priority);
22
- vals.push(value);
23
- while (i > 0) {
24
- const parent = (i - 1) >> 1;
25
- if (keys[i] < keys[parent]) {
26
- const tk = keys[i];
27
- keys[i] = keys[parent];
28
- keys[parent] = tk;
29
- const tv = vals[i];
30
- vals[i] = vals[parent];
31
- vals[parent] = tv;
32
- i = parent;
33
- }
34
- else
35
- break;
36
- }
37
- }
38
- pop() {
39
- const keys = this.keys;
40
- const vals = this.vals;
41
- const n = keys.length;
42
- if (n === 0)
43
- return undefined;
44
- const top = { priority: keys[0], value: vals[0] };
45
- const lastKey = keys.pop();
46
- const lastVal = vals.pop();
47
- const m = keys.length;
48
- if (m > 0) {
49
- keys[0] = lastKey;
50
- vals[0] = lastVal;
51
- let i = 0;
52
- for (;;) {
53
- const left = 2 * i + 1;
54
- const right = left + 1;
55
- let best = i;
56
- if (left < m && keys[left] < keys[best])
57
- best = left;
58
- if (right < m && keys[right] < keys[best])
59
- best = right;
60
- if (best === i)
61
- break;
62
- const tk = keys[i];
63
- keys[i] = keys[best];
64
- keys[best] = tk;
65
- const tv = vals[i];
66
- vals[i] = vals[best];
67
- vals[best] = tv;
68
- i = best;
69
- }
70
- }
71
- return top;
72
- }
73
- }
@@ -1,56 +0,0 @@
1
- /**
2
- * Sequence segmentation on the lightest-derivation engine.
3
- *
4
- * The only structure here is the **frontier**: the items are the positions
5
- * `0…length` of a sequence, and a derivation is a path through them. That is
6
- * what keeps the search linear in the input rather than quadratic — there is no
7
- * enumeration of span *pairs* and no chart of O(n²) sub-sequences.
8
- *
9
- * Candidate spans are produced **on demand**: when the search finalises a
10
- * position it asks a {@link Trie} which learned forms begin there. The trie walk
11
- * stops exactly where the data's patterns stop — a position with nothing learned
12
- * dead-ends immediately, a position inside a long form walks precisely that far.
13
- * Nothing is scanned ahead of demand and no length bound is imposed; the
14
- * structure of what was learned is the bound.
15
- *
16
- * {@link coverSequence} is the segmentation primitive: the lightest set of
17
- * non-overlapping spans covering a sequence. It is the principled,
18
- * corpus-independent replacement for "scan the whole stream with an automaton,
19
- * then greedily keep the longest non-overlapping matches" — same linear cost,
20
- * but the *optimal* cover and only the work the goal demands.
21
- */
22
- /** A candidate span over a sequence, carrying caller payload. */
23
- export interface CandidateSpan<P> {
24
- /** Start offset (inclusive). */
25
- start: number;
26
- /** End offset (exclusive); must be > start. */
27
- end: number;
28
- /** Relative cost of using this span (default 1). Lower wins ties. */
29
- weight?: number;
30
- /** Caller data, returned on the chosen spans. */
31
- payload: P;
32
- }
33
- export interface Cover<P> {
34
- /** Chosen non-overlapping spans, left to right. */
35
- spans: Array<CandidateSpan<P>>;
36
- /** Symbols covered by the chosen spans. */
37
- covered: number;
38
- /** Symbols left uncovered. */
39
- uncovered: number;
40
- }
41
- /**
42
- * The lightest non-overlapping cover of `[0, length)` drawn from `candidates`.
43
- *
44
- * Primary objective: cover the most symbols (fewest left uncovered). Secondary:
45
- * least total span weight — with the default unit weight this prefers fewer,
46
- * longer spans, the optimal analogue of greedy longest-match, but it can return
47
- * a pair of shorter spans when together they cover more than one long one (which
48
- * greedy cannot). Modelled as a shortest path over frontier positions: from a
49
- * position you either leave one symbol uncovered (costly) or take a candidate
50
- * that starts there (cheap), reaching `length`.
51
- *
52
- * Cost: O((length + |candidates|) · log length) — the frontier has `length + 1`
53
- * items, each finalised once, with one "skip" edge plus the candidates that
54
- * start there. No quadratic span structure.
55
- */
56
- export declare function coverSequence<P>(length: number, candidates: ReadonlyArray<CandidateSpan<P>>): Cover<P>;
@@ -1,100 +0,0 @@
1
- /**
2
- * Sequence segmentation on the lightest-derivation engine.
3
- *
4
- * The only structure here is the **frontier**: the items are the positions
5
- * `0…length` of a sequence, and a derivation is a path through them. That is
6
- * what keeps the search linear in the input rather than quadratic — there is no
7
- * enumeration of span *pairs* and no chart of O(n²) sub-sequences.
8
- *
9
- * Candidate spans are produced **on demand**: when the search finalises a
10
- * position it asks a {@link Trie} which learned forms begin there. The trie walk
11
- * stops exactly where the data's patterns stop — a position with nothing learned
12
- * dead-ends immediately, a position inside a long form walks precisely that far.
13
- * Nothing is scanned ahead of demand and no length bound is imposed; the
14
- * structure of what was learned is the bound.
15
- *
16
- * {@link coverSequence} is the segmentation primitive: the lightest set of
17
- * non-overlapping spans covering a sequence. It is the principled,
18
- * corpus-independent replacement for "scan the whole stream with an automaton,
19
- * then greedily keep the longest non-overlapping matches" — same linear cost,
20
- * but the *optimal* cover and only the work the goal demands.
21
- */
22
- import { lightestDerivation } from "./deduction.js";
23
- /**
24
- * The lightest non-overlapping cover of `[0, length)` drawn from `candidates`.
25
- *
26
- * Primary objective: cover the most symbols (fewest left uncovered). Secondary:
27
- * least total span weight — with the default unit weight this prefers fewer,
28
- * longer spans, the optimal analogue of greedy longest-match, but it can return
29
- * a pair of shorter spans when together they cover more than one long one (which
30
- * greedy cannot). Modelled as a shortest path over frontier positions: from a
31
- * position you either leave one symbol uncovered (costly) or take a candidate
32
- * that starts there (cheap), reaching `length`.
33
- *
34
- * Cost: O((length + |candidates|) · log length) — the frontier has `length + 1`
35
- * items, each finalised once, with one "skip" edge plus the candidates that
36
- * start there. No quadratic span structure.
37
- */
38
- export function coverSequence(length, candidates) {
39
- if (length <= 0)
40
- return { spans: [], covered: 0, uncovered: 0 };
41
- // Candidates indexed by where they start, and by their exact (start,end) edge
42
- // for reconstruction. Both are O(|candidates|) to build and to read.
43
- const byStart = Array.from({ length }, () => []);
44
- const byEdge = new Map();
45
- let maxWeight = 1;
46
- for (const c of candidates) {
47
- if (c.end <= c.start || c.start < 0 || c.end > length)
48
- continue;
49
- const w = c.weight ?? 1;
50
- if (w > maxWeight)
51
- maxWeight = w;
52
- byStart[c.start].push(c);
53
- const edge = c.start * (length + 1) + c.end;
54
- const prev = byEdge.get(edge);
55
- if (!prev || w < (prev.weight ?? 1))
56
- byEdge.set(edge, c);
57
- }
58
- // One uncovered symbol must outweigh any sum of span weights, so coverage is
59
- // strictly the primary objective and weight only breaks ties.
60
- const skipCost = maxWeight * length + 1;
61
- const system = {
62
- key: (p) => "" + p,
63
- axioms: () => [{ item: 0, cost: 0 }],
64
- isGoal: (p) => p === length,
65
- // No nonzero admissible bound is available here: a single candidate can
66
- // cover the whole remainder for unit cost, so any per-symbol estimate would
67
- // overestimate. With h = 0 this is exact Knuth/Dijkstra over the frontier —
68
- // optimal, and linear in the positions. (The A* outside bound is for systems
69
- // whose remaining cost can be genuinely lower-bounded; see the engine.)
70
- *rules(p) {
71
- if (p >= length)
72
- return;
73
- yield { premises: [p], conclusion: p + 1, cost: skipCost }; // leave uncovered
74
- for (const c of byStart[p]) {
75
- yield { premises: [p], conclusion: c.end, cost: c.weight ?? 1 };
76
- }
77
- },
78
- };
79
- const best = lightestDerivation(system);
80
- if (!best)
81
- return { spans: [], covered: 0, uncovered: length };
82
- // Walk the frontier chain back to the axiom; an edge p→q is a chosen span iff
83
- // a candidate spans exactly [p, q) (a covering edge is always cheaper than the
84
- // skips it replaces, so it appears on the optimal path wherever it is used).
85
- const spans = [];
86
- let node = best;
87
- while (node && node.rule) {
88
- const to = node.item;
89
- const from = node.premises[0].item;
90
- const span = byEdge.get(from * (length + 1) + to);
91
- if (span)
92
- spans.push(span);
93
- node = node.premises[0];
94
- }
95
- spans.reverse();
96
- let covered = 0;
97
- for (const s of spans)
98
- covered += s.end - s.start;
99
- return { spans, covered, uncovered: length - covered };
100
- }
@@ -1,90 +0,0 @@
1
- /**
2
- * Forward prefix trie over integer symbols — the lazy site matcher.
3
- *
4
- * This is the matching primitive the lightest-derivation search consults *on
5
- * demand*: given a position in a sequence, {@link Trie.matchesAt} walks forward
6
- * from the root and reports every stored pattern that begins there, in
7
- * O(longest matching pattern). Nothing is scanned that the search never asks
8
- * about — there is no global automaton, no failure links, no precomputed match
9
- * table. That is the whole point: rewrite sites are *materialised only when
10
- * demanded* (the "lazy hyperedge generation" of the rewrite search), so the
11
- * matcher itself stays trivial and the search decides what to look at.
12
- *
13
- * It is fully generic and self-contained: symbols are non-negative integers
14
- * (bytes 0–255, Unicode code points, opcodes, …); patterns are any
15
- * `ArrayLike<number>` (`Uint8Array` or `number[]`); each pattern carries an
16
- * arbitrary `payload` returned on every match. The trie also exposes a tiny
17
- * cursor API ({@link Trie.root}, {@link Trie.step}, {@link Trie.terminal}) so a
18
- * caller can extend a partial match symbol-by-symbol — e.g. to ask "could this
19
- * span still grow into a known form?" while composing.
20
- *
21
- * ## Memory
22
- *
23
- * Most states (99.7 % in typical use) have exactly one outgoing transition.
24
- * Storing a full `Map` per state would cost ~88 bytes each. Instead, a singleton
25
- * transition is packed inline into two typed arrays — `_nxt` (Int32Array, 4
26
- * bytes) and `_sym` (Uint8Array, 1 byte) — for ~5 bytes per state, a 17×
27
- * reduction. Only the rare multi-transition state (~0.3 %) allocates a `Map`,
28
- * kept in a sparse `_multi` table keyed by state number.
29
- */
30
- /** One pattern occurrence found by the trie. */
31
- export interface Match<P> {
32
- /** Start offset in the searched sequence (inclusive). */
33
- start: number;
34
- /** End offset (exclusive); `end - start === length`. */
35
- end: number;
36
- /** Length of the matched pattern. */
37
- length: number;
38
- /** Pattern id, assigned in insertion order (0-based). */
39
- id: number;
40
- /** Payload registered with the pattern. */
41
- payload: P;
42
- }
43
- export declare class Trie<P = undefined> {
44
- private _nxt;
45
- private _sym;
46
- private _multi;
47
- private _end;
48
- private readonly _lens;
49
- private readonly _vals;
50
- private _len;
51
- constructor();
52
- /** The root state, for cursor walks. */
53
- get root(): number;
54
- /** Number of distinct patterns stored. */
55
- get size(): number;
56
- /** Allocate a fresh state, growing the typed arrays by a fixed increment
57
- * when full. No power-of-2 doubling — the transient double-memory spike
58
- * during growth is bounded to the increment. */
59
- private _state;
60
- /** Follow symbol `c` from state `s`. Returns the next state, or -1. */
61
- private _follow;
62
- /** Follow one symbol from `state`; returns the next state or -1 if none. */
63
- step(state: number, symbol: number): number;
64
- /** The pattern ending exactly at `state`, or null. */
65
- terminal(state: number): {
66
- id: number;
67
- payload: P;
68
- } | null;
69
- /** Length of the pattern with this id. */
70
- lengthOf(id: number): number;
71
- /**
72
- * Insert a pattern, returning its id. Inserting the same symbol-sequence
73
- * twice returns the first id and keeps the first payload (patterns are keyed
74
- * by content). Empty patterns are ignored and return -1.
75
- */
76
- insert(pattern: ArrayLike<number>, payload: P): number;
77
- /**
78
- * Every stored pattern that begins exactly at `pos` in `seq`, shortest first.
79
- * Walks forward from the root in O(longest match); reports nothing about any
80
- * other position. This is the on-demand probe the search uses.
81
- */
82
- matchesAt(seq: ArrayLike<number>, pos: number): Array<Match<P>>;
83
- /**
84
- * Every occurrence of every pattern anywhere in `seq` (eager form, the union
85
- * of {@link matchesAt} over all start positions). O(seq · longest pattern),
86
- * independent of how many patterns are stored. Use {@link matchesAt} when the
87
- * search only needs the sites at a particular position.
88
- */
89
- scan(seq: ArrayLike<number>): Array<Match<P>>;
90
- }