@hviana/sema 0.2.9 → 0.4.0
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 +71 -5
- package/dist/src/derive/src/deduction.d.ts +12 -1
- package/dist/src/derive/src/deduction.js +5 -1
- package/dist/src/derive/src/index.d.ts +1 -0
- package/dist/src/geometry.d.ts +3 -30
- package/dist/src/geometry.js +330 -82
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/meter.d.ts +171 -0
- package/dist/src/meter.js +269 -0
- package/dist/src/mind/attention.d.ts +5 -4
- package/dist/src/mind/attention.js +254 -23
- package/dist/src/mind/bridge.d.ts +10 -1
- package/dist/src/mind/bridge.js +179 -10
- package/dist/src/mind/canonical.d.ts +6 -1
- package/dist/src/mind/canonical.js +6 -1
- package/dist/src/mind/graph-search.d.ts +9 -0
- package/dist/src/mind/graph-search.js +59 -19
- package/dist/src/mind/index.d.ts +2 -0
- package/dist/src/mind/junction.d.ts +10 -0
- package/dist/src/mind/junction.js +14 -0
- package/dist/src/mind/learning.d.ts +32 -4
- package/dist/src/mind/learning.js +26 -4
- package/dist/src/mind/match.d.ts +40 -0
- package/dist/src/mind/match.js +125 -1
- package/dist/src/mind/mechanisms/cast.js +63 -6
- package/dist/src/mind/mechanisms/extraction.d.ts +0 -34
- package/dist/src/mind/mechanisms/extraction.js +1 -88
- package/dist/src/mind/mechanisms/recall.d.ts +3 -0
- package/dist/src/mind/mechanisms/recall.js +77 -14
- package/dist/src/mind/mind.d.ts +59 -5
- package/dist/src/mind/mind.js +115 -93
- package/dist/src/mind/pipeline-mechanism.d.ts +33 -3
- package/dist/src/mind/pipeline-mechanism.js +179 -10
- package/dist/src/mind/pipeline.d.ts +29 -0
- package/dist/src/mind/pipeline.js +79 -21
- package/dist/src/mind/primitives.d.ts +11 -15
- package/dist/src/mind/primitives.js +47 -28
- package/dist/src/mind/reasoning.d.ts +7 -1
- package/dist/src/mind/reasoning.js +40 -8
- package/dist/src/mind/recognition.js +93 -20
- package/dist/src/mind/traverse.d.ts +11 -0
- package/dist/src/mind/traverse.js +88 -7
- package/dist/src/mind/types.d.ts +39 -5
- package/dist/src/store.d.ts +15 -0
- package/dist/src/store.js +91 -6
- package/package.json +1 -1
- package/src/derive/src/deduction.ts +15 -0
- package/src/derive/src/index.ts +1 -0
- package/src/geometry.ts +350 -122
- package/src/index.ts +1 -0
- package/src/meter.ts +333 -0
- package/src/mind/attention.ts +276 -31
- package/src/mind/bridge.ts +187 -10
- package/src/mind/canonical.ts +6 -1
- package/src/mind/graph-search.ts +60 -21
- package/src/mind/index.ts +6 -0
- package/src/mind/junction.ts +12 -0
- package/src/mind/learning.ts +46 -5
- package/src/mind/match.ts +146 -1
- package/src/mind/mechanisms/cast.ts +62 -6
- package/src/mind/mechanisms/extraction.ts +2 -103
- package/src/mind/mechanisms/recall.ts +84 -17
- package/src/mind/mind.ts +144 -99
- package/src/mind/pipeline-mechanism.ts +203 -13
- package/src/mind/pipeline.ts +144 -36
- package/src/mind/primitives.ts +49 -33
- package/src/mind/reasoning.ts +39 -7
- package/src/mind/recognition.ts +89 -19
- package/src/mind/traverse.ts +89 -8
- package/src/mind/types.ts +39 -5
- package/src/store.ts +75 -6
- package/test/14-scaling.test.mjs +17 -7
- package/test/31-audit.test.mjs +4 -1
- package/test/33-multi-candidate.test.mjs +13 -1
- package/test/36-already-answered-fusion.test.mjs +10 -3
- package/test/46-recognise-multibyte-edge.test.mjs +3 -3
- package/test/53-cross-region-probe-instrumentation.test.mjs +36 -6
- package/test/54-evidence-k-instrumentation.test.mjs +175 -0
- package/test/55-cost-meter.test.mjs +284 -0
- package/test/56-bridge-identity-admission.test.mjs +209 -0
- package/test/57-fusion-order.test.mjs +104 -0
- package/test/58-subquantum-sites.test.mjs +112 -0
- package/test/59-fold-invariance.test.mjs +226 -0
package/dist/src/store.js
CHANGED
|
@@ -516,6 +516,8 @@ export class AbstractStore {
|
|
|
516
516
|
}
|
|
517
517
|
// ── DAG traversal ──────────────────────────────────────────────────────
|
|
518
518
|
get(id) {
|
|
519
|
+
if (this.meter)
|
|
520
|
+
this.meter.nodeRecords++;
|
|
519
521
|
// Byte leaves are implicit — fabricate from the id.
|
|
520
522
|
if (id < 0) {
|
|
521
523
|
return { id, leaf: new Uint8Array([-(id + 1)]), kids: null };
|
|
@@ -536,7 +538,20 @@ export class AbstractStore {
|
|
|
536
538
|
* outlive their records — the read degrades safely to empty bytes, this
|
|
537
539
|
* counter is what keeps that degradation observable. */
|
|
538
540
|
danglingReads = 0;
|
|
541
|
+
/** {@link Store.meter} — the per-response work accumulator, or null when
|
|
542
|
+
* nothing is profiling. Every read below bumps it through `?.`, so an
|
|
543
|
+
* unprofiled store pays one null check per read and allocates nothing. */
|
|
544
|
+
meter = null;
|
|
539
545
|
bytes(id) {
|
|
546
|
+
if (this.meter) {
|
|
547
|
+
this.meter.byteReads++;
|
|
548
|
+
// Charged BEFORE the fast path returns, and from the cache entry when
|
|
549
|
+
// there is one: the volume a caller pulled through is the cost signal
|
|
550
|
+
// (an unbounded read is unbounded whether or not it was cached).
|
|
551
|
+
const c = this._bytesCache.get(id);
|
|
552
|
+
if (c)
|
|
553
|
+
this.meter.bytesRead += c.length;
|
|
554
|
+
}
|
|
540
555
|
// Fast path.
|
|
541
556
|
const hit = this._bytesCache.get(id);
|
|
542
557
|
if (hit)
|
|
@@ -583,7 +598,10 @@ export class AbstractStore {
|
|
|
583
598
|
const out = concat(kids.map((k) => cache.get(k)));
|
|
584
599
|
cache.set(nid, out);
|
|
585
600
|
}
|
|
586
|
-
|
|
601
|
+
const out = cache.get(id) ?? _ZERO;
|
|
602
|
+
if (this.meter)
|
|
603
|
+
this.meter.bytesRead += out.length;
|
|
604
|
+
return out;
|
|
587
605
|
}
|
|
588
606
|
/** First `maxLen` bytes of a node. Walks only the leftmost branch,
|
|
589
607
|
* stopping at `maxLen` — so a 1 MB document root costs the same as a
|
|
@@ -593,8 +611,6 @@ export class AbstractStore {
|
|
|
593
611
|
* may be shared with the byte cache and with other callers — treat them
|
|
594
612
|
* as read-only. Mutating one would corrupt every subsequent read. */
|
|
595
613
|
bytesPrefix(id, maxLen) {
|
|
596
|
-
if (maxLen <= 0)
|
|
597
|
-
return _ZERO;
|
|
598
614
|
// A FULL read (the ALL sentinel) routes through bytes(), whose
|
|
599
615
|
// reconstruction enters the byte-budget cache. Without this, the mind's
|
|
600
616
|
// read() — which always passes ALL — re-walked the DAG and re-concatenated
|
|
@@ -602,6 +618,23 @@ export class AbstractStore {
|
|
|
602
618
|
// exists precisely for those reconstructions.
|
|
603
619
|
if (maxLen >= 0x7fffffff)
|
|
604
620
|
return this.bytes(id);
|
|
621
|
+
// METERING BOUNDARY: this public entry point is charged ONCE per logical
|
|
622
|
+
// read; the walk below recurses through `_prefix`, which is not charged.
|
|
623
|
+
// Counting the recursion instead made a single read of an N-byte branch
|
|
624
|
+
// report as N reads (one per node descended), so `byteReads` measured
|
|
625
|
+
// tree size rather than read requests — it read as ~1 byte per read.
|
|
626
|
+
if (this.meter)
|
|
627
|
+
this.meter.byteReads++;
|
|
628
|
+
const out = this._prefix(id, maxLen);
|
|
629
|
+
if (this.meter)
|
|
630
|
+
this.meter.bytesRead += out.length;
|
|
631
|
+
return out;
|
|
632
|
+
}
|
|
633
|
+
/** {@link bytesPrefix}'s recursive body — uncharged; see the metering
|
|
634
|
+
* boundary note there. */
|
|
635
|
+
_prefix(id, maxLen) {
|
|
636
|
+
if (maxLen <= 0)
|
|
637
|
+
return _ZERO;
|
|
605
638
|
// Full-cache hit: bytes() already reconstructed the whole node.
|
|
606
639
|
const full = this._bytesCache.get(id);
|
|
607
640
|
if (full)
|
|
@@ -625,13 +658,15 @@ export class AbstractStore {
|
|
|
625
658
|
for (const k of kids) {
|
|
626
659
|
if (got >= maxLen)
|
|
627
660
|
break;
|
|
628
|
-
const child = this.
|
|
661
|
+
const child = this._prefix(k, maxLen - got);
|
|
629
662
|
parts.push(child);
|
|
630
663
|
got += child.length;
|
|
631
664
|
}
|
|
632
665
|
return concat(parts);
|
|
633
666
|
}
|
|
634
667
|
contentLen(id, cap = Infinity) {
|
|
668
|
+
if (this.meter)
|
|
669
|
+
this.meter.lenReads++;
|
|
635
670
|
if (id < 0)
|
|
636
671
|
return 1; // implicit single-byte leaf
|
|
637
672
|
const hit = this._lenCache.get(id);
|
|
@@ -661,6 +696,8 @@ export class AbstractStore {
|
|
|
661
696
|
}
|
|
662
697
|
// ── Content-addressed lookup ───────────────────────────────────────────
|
|
663
698
|
findLeaf(bytes) {
|
|
699
|
+
if (this.meter)
|
|
700
|
+
this.meter.leafLookups++;
|
|
664
701
|
if (bytes.length === 1)
|
|
665
702
|
return -(bytes[0] + 1);
|
|
666
703
|
const key = keyOf(bytes);
|
|
@@ -673,6 +710,8 @@ export class AbstractStore {
|
|
|
673
710
|
return id;
|
|
674
711
|
}
|
|
675
712
|
findBranch(kids) {
|
|
713
|
+
if (this.meter)
|
|
714
|
+
this.meter.branchLookups++;
|
|
676
715
|
const key = kids.join(",");
|
|
677
716
|
const cached = this._branchKey.get(key);
|
|
678
717
|
if (cached !== undefined)
|
|
@@ -693,15 +732,23 @@ export class AbstractStore {
|
|
|
693
732
|
}
|
|
694
733
|
// ── Structural parents ─────────────────────────────────────────────────
|
|
695
734
|
parents(id) {
|
|
735
|
+
if (this.meter)
|
|
736
|
+
this.meter.parentReads++;
|
|
696
737
|
return this._dbGetParents(id);
|
|
697
738
|
}
|
|
698
739
|
parentsFirst(id, limit) {
|
|
740
|
+
if (this.meter)
|
|
741
|
+
this.meter.parentReads++;
|
|
699
742
|
return this._dbGetParentsFirst(id, limit);
|
|
700
743
|
}
|
|
701
744
|
hasParents(id) {
|
|
745
|
+
if (this.meter)
|
|
746
|
+
this.meter.parentProbes++;
|
|
702
747
|
return this._dbGetParentsFirst(id, 1).length > 0;
|
|
703
748
|
}
|
|
704
749
|
chainRun(id) {
|
|
750
|
+
if (this.meter)
|
|
751
|
+
this.meter.chainRuns++;
|
|
705
752
|
const hit = this._chainMemo.get(id);
|
|
706
753
|
if (hit !== undefined)
|
|
707
754
|
return hit;
|
|
@@ -736,12 +783,16 @@ export class AbstractStore {
|
|
|
736
783
|
set.add(parent);
|
|
737
784
|
}
|
|
738
785
|
hasContainers(child) {
|
|
786
|
+
if (this.meter)
|
|
787
|
+
this.meter.containerProbes++;
|
|
739
788
|
if (this._dbContainExists(child))
|
|
740
789
|
return true;
|
|
741
790
|
const buf = this._containBuf.get(child);
|
|
742
791
|
return buf !== undefined && buf.size > 0;
|
|
743
792
|
}
|
|
744
793
|
containersSlice(child, offset, limit) {
|
|
794
|
+
if (this.meter)
|
|
795
|
+
this.meter.containerReads++;
|
|
745
796
|
const out = this._dbGetContainParentsSlice(child, offset, limit);
|
|
746
797
|
if (out.length >= limit)
|
|
747
798
|
return out;
|
|
@@ -765,6 +816,8 @@ export class AbstractStore {
|
|
|
765
816
|
return out;
|
|
766
817
|
}
|
|
767
818
|
containers(child) {
|
|
819
|
+
if (this.meter)
|
|
820
|
+
this.meter.containerReads++;
|
|
768
821
|
const stored = this._dbGetContainParents(child);
|
|
769
822
|
const buf = this._containBuf.get(child);
|
|
770
823
|
if (stored.length === 0)
|
|
@@ -1152,9 +1205,14 @@ export class AbstractStore {
|
|
|
1152
1205
|
const cache = this._resonateCache;
|
|
1153
1206
|
if (cache) {
|
|
1154
1207
|
const hit = cache.get(rk);
|
|
1155
|
-
if (hit !== undefined)
|
|
1208
|
+
if (hit !== undefined) {
|
|
1209
|
+
if (this.meter)
|
|
1210
|
+
this.meter.annCacheHits++;
|
|
1156
1211
|
return hit;
|
|
1212
|
+
}
|
|
1157
1213
|
}
|
|
1214
|
+
if (this.meter)
|
|
1215
|
+
this.meter.annQueries++;
|
|
1158
1216
|
const clusters = this._vecContentClusterCount();
|
|
1159
1217
|
const results = this._vecContentQuery(normalize(copy(v)), k * this.overfetch,
|
|
1160
1218
|
// Exhaustive: probe every cluster (ef ≥ 4·clusters guarantees the
|
|
@@ -1169,6 +1227,10 @@ export class AbstractStore {
|
|
|
1169
1227
|
if (out.length >= k)
|
|
1170
1228
|
break;
|
|
1171
1229
|
}
|
|
1230
|
+
// Vectors the index actually scored for THIS descent — the counter that
|
|
1231
|
+
// exposes a query whose ANN cost is growing with the corpus.
|
|
1232
|
+
if (this.meter)
|
|
1233
|
+
this.meter.annVectorReads += this._vecContentLastReads();
|
|
1172
1234
|
const rc = this._resonateCache ??= new Map();
|
|
1173
1235
|
if (rc.size >= RESONATE_CACHE_MAX)
|
|
1174
1236
|
rc.clear();
|
|
@@ -1390,24 +1452,36 @@ export class AbstractStore {
|
|
|
1390
1452
|
this._dbInsertEdge(from, to);
|
|
1391
1453
|
}
|
|
1392
1454
|
next(id) {
|
|
1455
|
+
if (this.meter)
|
|
1456
|
+
this.meter.edgeReads++;
|
|
1393
1457
|
return this._dbGetNextEdges(id);
|
|
1394
1458
|
}
|
|
1395
1459
|
/** {@link Store.hasNext} — one indexed point probe, never a range read. */
|
|
1396
1460
|
hasNext(id) {
|
|
1461
|
+
if (this.meter)
|
|
1462
|
+
this.meter.edgeProbes++;
|
|
1397
1463
|
return this._dbEdgeSrcExists(id);
|
|
1398
1464
|
}
|
|
1399
1465
|
prev(id) {
|
|
1466
|
+
if (this.meter)
|
|
1467
|
+
this.meter.prevReads++;
|
|
1400
1468
|
return this._dbGetPrevEdges(id);
|
|
1401
1469
|
}
|
|
1402
1470
|
nextFirst(id, limit) {
|
|
1471
|
+
if (this.meter)
|
|
1472
|
+
this.meter.edgeReads++;
|
|
1403
1473
|
return this._dbGetNextEdgesFirst(id, limit);
|
|
1404
1474
|
}
|
|
1405
1475
|
prevFirst(id, limit) {
|
|
1476
|
+
if (this.meter)
|
|
1477
|
+
this.meter.prevReads++;
|
|
1406
1478
|
return this._dbGetPrevEdgesFirst(id, limit);
|
|
1407
1479
|
}
|
|
1408
1480
|
/** {@link Store.prevCount}. Subclasses with an indexed reverse-edge count
|
|
1409
1481
|
* should override; this default materialises (correct, not optimal). */
|
|
1410
1482
|
prevCount(id) {
|
|
1483
|
+
if (this.meter)
|
|
1484
|
+
this.meter.prevProbes++;
|
|
1411
1485
|
return this._dbGetPrevEdges(id).length;
|
|
1412
1486
|
}
|
|
1413
1487
|
edgeSourceCount() {
|
|
@@ -1422,10 +1496,14 @@ export class AbstractStore {
|
|
|
1422
1496
|
}
|
|
1423
1497
|
// ── Halos ──────────────────────────────────────────────────────────────
|
|
1424
1498
|
haloMass(id) {
|
|
1499
|
+
if (this.meter)
|
|
1500
|
+
this.meter.haloProbes++;
|
|
1425
1501
|
const r = this._dbGetHalo(id);
|
|
1426
1502
|
return r ? r.mass : 0;
|
|
1427
1503
|
}
|
|
1428
1504
|
halo(id) {
|
|
1505
|
+
if (this.meter)
|
|
1506
|
+
this.meter.haloReads++;
|
|
1429
1507
|
const cached = this._haloNorm.get(id);
|
|
1430
1508
|
if (cached !== undefined)
|
|
1431
1509
|
return copy(cached);
|
|
@@ -1440,6 +1518,8 @@ export class AbstractStore {
|
|
|
1440
1518
|
/** {@link Store.hasHalo} — MUST mirror {@link halo}'s null condition
|
|
1441
1519
|
* exactly (row present AND mass ≥ minHaloMass), minus the decode. */
|
|
1442
1520
|
hasHalo(id) {
|
|
1521
|
+
if (this.meter)
|
|
1522
|
+
this.meter.haloProbes++;
|
|
1443
1523
|
const r = this._dbGetHalo(id);
|
|
1444
1524
|
return r !== null && r.mass >= this.minHaloMass;
|
|
1445
1525
|
}
|
|
@@ -1473,9 +1553,14 @@ export class AbstractStore {
|
|
|
1473
1553
|
const cache = this._resonateHaloCache;
|
|
1474
1554
|
if (cache) {
|
|
1475
1555
|
const hit = cache.get(rk);
|
|
1476
|
-
if (hit !== undefined)
|
|
1556
|
+
if (hit !== undefined) {
|
|
1557
|
+
if (this.meter)
|
|
1558
|
+
this.meter.annCacheHits++;
|
|
1477
1559
|
return hit;
|
|
1560
|
+
}
|
|
1478
1561
|
}
|
|
1562
|
+
if (this.meter)
|
|
1563
|
+
this.meter.haloQueries++;
|
|
1479
1564
|
const results = this._vecHaloQuery(normalize(copy(v)), k * this.overfetch, this.efFor(this._vecHaloClusterCount()));
|
|
1480
1565
|
const out = [];
|
|
1481
1566
|
for (const r of results) {
|
package/package.json
CHANGED
|
@@ -128,12 +128,25 @@ export interface Derivation<I> {
|
|
|
128
128
|
premises: Array<Derivation<I>>;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
/** Optional search-effort read-out, filled in place while the search runs.
|
|
132
|
+
* A plain structural record so the sublibrary stays firewalled (it imports
|
|
133
|
+
* nothing from Sema, and Sema copies these into its own accounting). Purely
|
|
134
|
+
* observational: the search never reads it back. */
|
|
135
|
+
export interface SearchStats {
|
|
136
|
+
/** Agenda entries popped, stale lazy-deletion entries included — the chart
|
|
137
|
+
* work actually done. */
|
|
138
|
+
pops: number;
|
|
139
|
+
/** Agenda entries pushed by a successful relaxation. */
|
|
140
|
+
pushes: number;
|
|
141
|
+
}
|
|
142
|
+
|
|
131
143
|
/**
|
|
132
144
|
* Find a lightest derivation of a goal item, or `null` if none exists.
|
|
133
145
|
* `cost` on the returned root is the total derivation cost.
|
|
134
146
|
*/
|
|
135
147
|
export function lightestDerivation<I>(
|
|
136
148
|
system: DeductionSystem<I>,
|
|
149
|
+
stats?: SearchStats,
|
|
137
150
|
): Derivation<I> | null {
|
|
138
151
|
const g = new Map<string, number>(); // best known cost per item
|
|
139
152
|
const proof = new Map<string, Rule<I> | null>(); // producing rule per item
|
|
@@ -177,6 +190,7 @@ export function lightestDerivation<I>(
|
|
|
177
190
|
g.set(key, cost);
|
|
178
191
|
proof.set(key, rule);
|
|
179
192
|
items.set(key, item);
|
|
193
|
+
if (stats) stats.pushes++;
|
|
180
194
|
agenda.push(cost + h(item, key), { key, g: cost });
|
|
181
195
|
}
|
|
182
196
|
};
|
|
@@ -185,6 +199,7 @@ export function lightestDerivation<I>(
|
|
|
185
199
|
|
|
186
200
|
while (agenda.size > 0) {
|
|
187
201
|
const { value } = agenda.pop() as { value: { key: string; g: number } };
|
|
202
|
+
if (stats) stats.pops++;
|
|
188
203
|
const key = value.key;
|
|
189
204
|
// Lazy deletion: an entry is stale if a cheaper derivation has since been
|
|
190
205
|
// recorded for the same item.
|
package/src/derive/src/index.ts
CHANGED