feat(8.0): u64 BigInt graph provider contract — punch list a-d,g,h
GraphIndexProvider now speaks BigInt at the boundary (D.2 mirror): - getNeighbors/getVerbIdsBySource/getVerbIdsByTarget take entity ints and return entity/verb ints as bigint[] - new REQUIRED verbIntsToIds(bigint[]) batch reverse resolver (L.7 identity-fingerprint design — verb ids are UUIDs by contract, so the provider-side interning is losslessly reversible) - addVerb(verb, sourceInt, targetInt) returns the interned verb int; removeVerb(verbId) joins the contract Coordinator (brainy.ts) owns ALL UUID <-> int conversion: getOrAssign on writes, getInt on reads (unmapped UUID -> empty result without calling the provider), getUuid / verbIntsToIds on returns, plus a bounded ~100k-entry insertion-order warm cache for verb-int -> verb-id pairs fed by addVerb returns and resolver results. GraphVerb gains derived sourceInt/targetInt (populated at add time, never persisted). findConnectedSubtype gains a native fast path that routes single-type single-subtype outgoing BFS through the provider when available. JS GraphAdjacencyIndex satisfies the contract while staying string/u32-keyed internally: entity ints resolve through the shared entity-id mapper (threaded in by the coordinator on init/fork/checkout), verb ints come from an in-process append-only interning map re-derived from storage on rebuild/cold-start. ColumnStoreProvider widens the same way: addEntity/ removeEntity take bigint, sortTopK/filteredSortTopK return bigint[]. relate() now rejects a caller-supplied id with a teaching error — verb ids are brainy-generated UUIDs by contract in 8.0 (previously a passed id was silently ignored). No Roaring64 provider-boundary decode site exists yet; the JS-internal column store stays Roaring32 and the Treemap decoder lands with the first consumer of provider-returned filter buffers. Public brain API unchanged. 1413 tests green (+10 new BigInt contract tests).
This commit is contained in:
parent
62f6472fa0
commit
2427bb7960
17 changed files with 1164 additions and 328 deletions
|
|
@ -15,6 +15,7 @@
|
|||
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest'
|
||||
import { Brainy } from '../../src/brainy.js'
|
||||
import { GraphAdjacencyIndex } from '../../src/graph/graphAdjacencyIndex.js'
|
||||
import { EntityIdMapper } from '../../src/utils/entityIdMapper.js'
|
||||
import { MemoryStorage } from '../../src/storage/adapters/memoryStorage.js'
|
||||
import { performance } from 'perf_hooks'
|
||||
|
||||
|
|
@ -112,8 +113,18 @@ describe('🧠 Graph Scale Performance Benchmarks', () => {
|
|||
let brain: Brainy
|
||||
let graphIndex: GraphAdjacencyIndex
|
||||
let storage: MemoryStorage
|
||||
let idMapper: EntityIdMapper
|
||||
const scale = getTestScale()
|
||||
|
||||
/** Resolve a UUID to its entity int for the 8.0 BigInt boundary. */
|
||||
const entityInt = (uuid: string): bigint => BigInt(idMapper.getOrAssign(uuid))
|
||||
|
||||
/** Map returned entity ints back to UUIDs. */
|
||||
const intsToUuids = (ints: bigint[]): string[] =>
|
||||
ints
|
||||
.map((i) => idMapper.getUuid(Number(i)))
|
||||
.filter((u): u is string => u !== undefined)
|
||||
|
||||
// Performance tracking
|
||||
const lookupStats = new PerformanceStats()
|
||||
const updateStats = new PerformanceStats()
|
||||
|
|
@ -130,10 +141,13 @@ describe('🧠 Graph Scale Performance Benchmarks', () => {
|
|||
storage = new MemoryStorage()
|
||||
await storage.init()
|
||||
|
||||
idMapper = new EntityIdMapper({ storage })
|
||||
await idMapper.init()
|
||||
|
||||
graphIndex = new GraphAdjacencyIndex(storage, {
|
||||
maxIndexSize: scale.nodes,
|
||||
autoOptimize: true
|
||||
})
|
||||
}, idMapper)
|
||||
|
||||
// Initialize Brainy for unified testing
|
||||
brain = new Brainy({ requireSubtype: false,
|
||||
|
|
@ -226,7 +240,7 @@ describe('🧠 Graph Scale Performance Benchmarks', () => {
|
|||
|
||||
// Warm up the index
|
||||
await graphIndex.rebuild()
|
||||
await graphIndex.getNeighbors('node-100') // Warm up
|
||||
await graphIndex.getNeighbors(entityInt('node-100')) // Warm up
|
||||
|
||||
// Test random lookups
|
||||
const testIterations = Math.min(1000, scale.nodes / 10)
|
||||
|
|
@ -236,7 +250,7 @@ describe('🧠 Graph Scale Performance Benchmarks', () => {
|
|||
|
||||
for (const nodeId of sampleNodes) {
|
||||
const startTime = performance.now()
|
||||
const neighbors = await graphIndex.getNeighbors(nodeId)
|
||||
const neighbors = await graphIndex.getNeighbors(entityInt(nodeId))
|
||||
const elapsed = performance.now() - startTime
|
||||
|
||||
lookupStats.addSample(elapsed)
|
||||
|
|
@ -408,9 +422,9 @@ describe('🧠 Graph Scale Performance Benchmarks', () => {
|
|||
visited.add(id)
|
||||
nodesTraversed++
|
||||
|
||||
// Get neighbors
|
||||
const neighbors = await graphIndex.getNeighbors(id, 'out')
|
||||
for (const neighbor of neighbors) {
|
||||
// Get neighbors (BigInt boundary: ints out, mapped back to UUIDs)
|
||||
const neighborInts = await graphIndex.getNeighbors(entityInt(id), { direction: 'out' })
|
||||
for (const neighbor of intsToUuids(neighborInts)) {
|
||||
if (!visited.has(neighbor)) {
|
||||
queue.push({ id: neighbor, depth: depth + 1 })
|
||||
}
|
||||
|
|
@ -687,7 +701,7 @@ describe('🧠 Graph Scale Performance Benchmarks', () => {
|
|||
|
||||
switch (operationType) {
|
||||
case 0: // Neighbor lookup
|
||||
operations.push(graphIndex.getNeighbors(`node-${Math.floor(Math.random() * scale.nodes)}`))
|
||||
operations.push(graphIndex.getNeighbors(entityInt(`node-${Math.floor(Math.random() * scale.nodes)}`)))
|
||||
break
|
||||
case 1: // Unified find
|
||||
operations.push(brain.find({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue