@delali/narsil 0.2.4-next.51 → 0.2.4-next.52
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/README.md +27 -25
- package/dist/{capabilities-BWm4IITU.d.ts → capabilities-CuTSXvbN.d.ts} +2 -2
- package/dist/{chunk-BEIK3EOZ.mjs → chunk-2FO7KZ6K.mjs} +5468 -3837
- package/dist/{chunk-XE6XR2OE.mjs → chunk-3GWP2TO6.mjs} +1 -1
- package/dist/{chunk-KMMJMDUA.mjs → chunk-3L4U6EJQ.mjs} +15 -10
- package/dist/{chunk-MHE7YWAU.mjs → chunk-72AXTIBW.mjs} +2 -2
- package/dist/{chunk-CV5H7XL7.mjs → chunk-EY2ULUMT.mjs} +30 -7
- package/dist/chunk-IFBKLBGB.mjs +7 -0
- package/dist/{chunk-WYJXSLER.mjs → chunk-POWOTUKJ.mjs} +7 -1
- package/dist/chunk-T62YOSGS.mjs +4280 -0
- package/dist/chunk-U36IDJN7.mjs +366 -0
- package/dist/{chunk-ND34I3SV.mjs → chunk-ULJ36FK4.mjs} +3502 -2383
- package/dist/{chunk-RP23PWMV.mjs → chunk-WFKIGEBY.mjs} +3 -3
- package/dist/{chunk-CMVG2UEF.mjs → chunk-WM7IS45M.mjs} +55 -517
- package/dist/chunk-Y3L5MDRV.mjs +96 -0
- package/dist/{chunk-ZHUQKZBE.mjs → chunk-YPYULM5E.mjs} +20 -4
- package/dist/chunk-ZJ4CSAB2.mjs +13 -0
- package/dist/chunk-ZT5BRDFW.mjs +513 -0
- package/dist/{client-B_X6_AbT.d.ts → client-BMJWVxOP.d.ts} +3 -3
- package/dist/client.d.ts +8 -8
- package/dist/{config-OON7IJ_B.d.ts → config-BcYZkdMw.d.ts} +2 -2
- package/dist/{config-Czh2YNDi.d.ts → config-qMLpi5T4.d.ts} +2 -2
- package/dist/direct-executor-UJC36S2D.mjs +12 -0
- package/dist/distribution/coordinator/etcd.d.ts +2 -2
- package/dist/distribution/coordinator/in-memory.d.ts +2 -2
- package/dist/distribution.d.ts +8 -8
- package/dist/distribution.mjs +30 -15
- package/dist/{engine-9PtFY50q.d.ts → engine-zhms1qId.d.ts} +2 -2
- package/dist/index.browser.mjs +12845 -7766
- package/dist/index.d.ts +9 -21
- package/dist/index.mjs +13 -12
- package/dist/{memory-Bzd5xcEW.d.ts → memory-OmgkX_Z2.d.ts} +11 -1
- package/dist/persistence/durability/checkpoint-worker.d.ts +138 -3
- package/dist/persistence/durability/checkpoint-worker.mjs +31 -17
- package/dist/react.d.ts +6 -6
- package/dist/{schema-BqRx9t4E.d.ts → schema-OSWaaZzx.d.ts} +30 -6
- package/dist/serialization/crc32-worker.mjs +2 -1
- package/dist/server.d.ts +10 -10
- package/dist/server.mjs +75 -34
- package/dist/simd-DW5AtJ6B.d.ts +41 -0
- package/dist/{tasks-DT7L1p6w.d.ts → tasks-DiF1c9v2.d.ts} +1 -1
- package/dist/{thread-PZLEO6JL.mjs → thread-TLCX43WV.mjs} +129 -41
- package/dist/{types-DXPxaXPO.d.ts → types-LEzMyjVX.d.ts} +1 -1
- package/dist/vector/search-worker.d.ts +292 -128
- package/dist/vector/search-worker.mjs +70 -54
- package/dist/workers/entry.mjs +10 -7
- package/package.json +52 -31
- package/dist/brute-force-DbHYCTc3.d.ts +0 -8
- package/dist/chunk-CBLS46IE.mjs +0 -10
- package/dist/chunk-PIWKZPP7.mjs +0 -64
- package/dist/chunk-PMBHH44R.mjs +0 -492
- package/dist/chunk-X6YZO4MQ.mjs +0 -1572
- package/dist/direct-executor-OREXU72C.mjs +0 -12
- package/dist/vector/hnsw-build-worker.d.ts +0 -105
- package/dist/vector/hnsw-build-worker.mjs +0 -123
|
@@ -0,0 +1,4280 @@
|
|
|
1
|
+
import { OSQ_STEPS, OSQ_GRID, OSQ_REFINE_ROUNDS, OSQ_REFINE_TOLERANCE, OSQ4_NARROW_DIMENSION_LIMIT, DEFAULT_OSQ4_NARROW_OVERSAMPLE, DEFAULT_OVERSAMPLE_BY_BITS, OSQ_QUERY_BITS, OSQ_LAMBDA } from './chunk-ZJ4CSAB2.mjs';
|
|
2
|
+
import { COMPACTION_TOMBSTONE_RATIO, COMPACTION_ABSOLUTE_THRESHOLD, MAX_LAYER_CAP, PLACEMENT_ROOM_REQUESTS, INITIAL_LIST_CAPACITY, DEFAULT_EF_SEARCH, INITIAL_UPPER_SLOTS } from './chunk-WFKIGEBY.mjs';
|
|
3
|
+
import { compareCodePoints } from './chunk-2GWVQWGA.mjs';
|
|
4
|
+
import { NarsilError, ErrorCodes } from './chunk-P4ELXVJQ.mjs';
|
|
5
|
+
import { existsSync, readSync, closeSync, openSync } from 'fs';
|
|
6
|
+
import { createRequire } from 'module';
|
|
7
|
+
import { join, dirname } from 'path';
|
|
8
|
+
|
|
9
|
+
// src/vector/constants.ts
|
|
10
|
+
var WASM_PAGE_BYTES = 65536;
|
|
11
|
+
var MAX_WASM_PAGES = 65536;
|
|
12
|
+
var VECTOR_STORE_INITIAL_CAPACITY = 16;
|
|
13
|
+
var VECTOR_BLOCK_INITIAL_SLOTS = 256;
|
|
14
|
+
var DISK_STORAGE_BLOCK_BYTES = 67108864;
|
|
15
|
+
var MAX_VECTOR_ORDINALS = 16777216;
|
|
16
|
+
var VECTOR_SCRATCH_SLOTS = 64;
|
|
17
|
+
var SCRATCH_SLOTS_PER_THREAD_POOL = 31;
|
|
18
|
+
var MAX_DOC_ID_TABLE_BYTES = 1073741824;
|
|
19
|
+
var MAX_WORKER_COPIES = 64;
|
|
20
|
+
var VECTOR_SEARCH_TIMEOUT_MS = 3e4;
|
|
21
|
+
var VECTOR_SEARCH_LOAD_TIMEOUT_MS = 3e5;
|
|
22
|
+
var VECTOR_INSERT_TIMEOUT_MS = 6e5;
|
|
23
|
+
var NATIVE_ATTACH_RETRY_MS = 1e3;
|
|
24
|
+
var LOCK_SPIN_ITERATIONS = 128;
|
|
25
|
+
var LOCK_WAIT_SLICE_MS = 20;
|
|
26
|
+
var GRAPH_LOCK_WAIT_SLICE_MS = 1;
|
|
27
|
+
var GRAPH_BUILD_DISPATCH_CHUNK = 64;
|
|
28
|
+
var GRAPH_BUILD_CHUNKS_IN_FLIGHT_PER_WORKER = 2;
|
|
29
|
+
|
|
30
|
+
// src/vector/simd-wasm-binary.ts
|
|
31
|
+
var SIMD_DISTANCE_WASM_BASE64 = "AGFzbQEAAAABIgVgA39/fwF9YAJ/fwF9YAN/f38Bf2ABewF/YAR/f39/AX8DDw4AAQAAAgIDAgIEAgQCAgUDAQABB6QBCgZtZW1vcnkCAAtkb3RfcHJvZHVjdAAACW1hZ25pdHVkZQABGnNxdWFyZWRfZXVjbGlkZWFuX2Rpc3RhbmNlAAISZXVjbGlkZWFuX2Rpc3RhbmNlAAMGZG90X3U4AAQJc3FkaXN0X3U4AAUMb3NxX2RvdF9iaXRzAAkRb3NxX2RvdF9iaXRzX3BhaXIACxNvc3FfZG90X25pYmJsZXNfNHg0AA0K2RQOygIEAX8EewF9BH8gAkFwcSEJIAJBfHEhCv0MAAAAAAAAAAAAAAAAAAAAACEEQQAhAwJAA0AgAyAJTw0BIAAgA0ECdGohCyABIANBAnRqIQwgBCAL/QAEACAM/QAEAP3mAf3kASEEIAUgC/0ABBAgDP0ABBD95gH95AEhBSAGIAv9AAQgIAz9AAQg/eYB/eQBIQYgByAL/QAEMCAM/QAEMP3mAf3kASEHIANBEGohAwwACwsCQANAIAMgCk8NASAEIAAgA0ECdGr9AAQAIAEgA0ECdGr9AAQA/eYB/eQBIQQgA0EEaiEDDAALCyAEIAX95AEgBiAH/eQB/eQBIQQgBP0fACAE/R8BkiAE/R8CIAT9HwOSkiEIAkADQCADIAJPDQEgCCAAIANBAnRqKgIAIAEgA0ECdGoqAgCUkiEIIANBAWohAwwACwsgCAupAQQBfwJ7AX0BfyABQXxxIQb9DAAAAAAAAAAAAAAAAAAAAAAhA0EAIQICQANAIAIgBk8NASAAIAJBAnRq/QAEACEEIAMgBCAE/eYB/eQBIQMgAkEEaiECDAALCyAD/R8AIAP9HwGSIAP9HwIgA/0fA5KSIQUCQANAIAIgAU8NASAFIAAgAkECdGoqAgAgACACQQJ0aioCAJSSIQUgAkEBaiECDAALCyAFkQvAAQUBfwJ7AX0BfwF9IAJBfHEhB/0MAAAAAAAAAAAAAAAAAAAAACEEQQAhAwJAA0AgAyAHTw0BIAAgA0ECdGr9AAQAIAEgA0ECdGr9AAQA/eUBIQUgBCAFIAX95gH95AEhBCADQQRqIQMMAAsLIAT9HwAgBP0fAZIgBP0fAiAE/R8DkpIhBgJAA0AgAyACTw0BIAAgA0ECdGoqAgAgASADQQJ0aioCAJMhCCAGIAggCJSSIQYgA0EBaiEDDAALCyAGCwsAIAAgASACEAKRC7QBAwF/A3sCfyACQXBxIQhBAP0RIQRBACEDAkADQCADIAhPDQEgACADav0ABAAhBSABIANq/QAEACEGIAQgBf2JASAG/YkB/boB/a4BIQQgBCAF/YoBIAb9igH9ugH9rgEhBCADQRBqIQMMAAsLIAT9GwAgBP0bAWogBP0bAiAE/RsDamohBwJAA0AgAyACTw0BIAcgACADai0AACABIANqLQAAbGohByADQQFqIQMMAAsLIAcLzQEDAX8FewN/IAJBcHEhC0EA/REhBEEAIQMCQANAIAMgC08NASAAIANq/QAEACEFIAEgA2r9AAQAIQYgBf2JASAG/YkB/ZEBIQcgBf2KASAG/YoB/ZEBIQggBCAHIAf9ugH9rgEhBCAEIAggCP26Af2uASEEIANBEGohAwwACwsgBP0bACAE/RsBaiAE/RsCIAT9GwNqaiEJAkADQCADIAJPDQEgACADai0AACABIANqLQAAayEKIAkgCiAKbGohCSADQQFqIQMMAAsLIAkLIQEBeyAA/X8hASAB/RsAIAH9GwFqIAH9GwIgAf0bA2pqC68CAwh/A3sBfyABIAJqIQggCCACaiEJIAkgAmohCiACQXBxIQUCQANAIAMgBU8NASADQYAQaiEGIAYgBUsEQCAFIQYLQQD9ECENA0AgACADav0ABAAhCyALIAogA2r9AAQA/U79YiEMIAwgDP1uIAsgCSADav0ABAD9Tv1i/W4hDCAMIAz9biALIAggA2r9AAQA/U79Yv1uIQwgDCAM/W4gCyABIANq/QAEAP1O/WL9biEMIA0gDP19/Y4BIQ0gA0EQaiEDIAMgBkkNAAsgByANEAZqIQcMAAsLAkADQCADIAJPDQEgACADai0AACEOQQAhBAJAA0AgBEEETw0BIAcgDiABIAIgBGxqIANqLQAAcWkgBHRqIQcgBEEBaiEEDAALCyADQQFqIQMMAAsLIAcLhwMDCH8IewF/IAEgAmohCCAIIAJqIQkgCSACaiEKQQP9DyERIAJBcHEhBQJAA0AgAyAFTw0BIANBgBBqIQYgBiAFSwRAIAUhBgtBAP0QIRIDQCAAIANq/QAEACELIAsgEf1OIQwgC0EC/W0gEf1OIQ0gC0EE/W0gEf1OIQ4gC0EG/W0hDyABIANq/QAEACEQIBIgDCAQ/Z4BIAwgEP2fAf2OAf2OASESIAggA2r9AAQAIRAgEiANIBD9ngEgDSAQ/Z8B/Y4B/Y4BIRIgCSADav0ABAAhECASIA4gEP2eASAOIBD9nwH9jgH9jgEhEiAKIANq/QAEACEQIBIgDyAQ/Z4BIA8gEP2fAf2OAf2OASESIANBEGohAyADIAZJDQALIAcgEhAGaiEHDAALCwJAA0AgAyACTw0BIAAgA2otAAAhE0EAIQQCQANAIARBBE8NASAHIBMgBEEBdHZBA3EgASACIARsaiADai0AAGxqIQcgBEEBaiEEDAALCyADQQFqIQMMAAsLIAcLGwAgA0ECRgR/IAAgASACEAgFIAAgASACEAcLC54BAgR/AXsgAkFwcSEEAkADQCADIARPDQEgA0GAEGohBSAFIARLBEAgBCEFC0EA/RAhBwNAIAcgACADav0ABAAgASADav0ABAD9Tv1i/X39jgEhByADQRBqIQMgAyAFSQ0ACyAGIAcQBmohBgwACwsCQANAIAMgAk8NASAGIAAgA2otAAAgASADai0AAHFpaiEGIANBAWohAwwACwsgBgsbACADQQJGBH8gACABIAIQDAUgACABIAIQCgsL2gIDBH8IewR/QdUAIRFB1QAhEkHVAP0PIQdBqgH9DyEIIAJBcHEhBAJAA0AgAyAETw0BIANB4ANqIQUgBSAESwRAIAQhBQtBAP0PIQ5BAP0PIQxBAP0PIQ0DQCAAIANq/QAEACEJIAEgA2r9AAQAIQogCSAK/U4hCyAOIAv9Yv1uIQ4gDCALIAj9Tv1i/W4hDCANIAkgCkEB/W0gB/1OIApBAf1rIAj9Tv1Q/U79Yv1uIQ0gA0EQaiEDIAMgBUkNAAsgBiAO/X0QBiAM/X0QBkEDbCAN/X0QBkEBdGpqaiEGDAALCwJAA0AgAyACTw0BIAAgA2otAAAhDyABIANqLQAAIRAgBiAPIBFxIBBxaSAPIBFxIBBBAXYgEnFxaUEBdGogD0EBdiAScSAQIBFxcWlBAXQgD0EBdiAScSAQQQF2IBJxcWlBAnRqamohBiADQQFqIQMMAAsLIAYL/AEDA38IewJ/IAJBcHEhBEEP/Q8hDEEA/REhDUEAIQMCQANAIAMgBE8NASAAIANq/QAEACEGIAEgA2r9AAQAIQcgBiAM/U4hCCAGQQT9bSEJIAcgDP1OIQogB0EE/W0hCyANIAggCv2eASAJIAv9ngH9jgH9fyAIIAr9nwEgCSAL/Z8B/Y4B/X/9rgH9rgEhDSADQRBqIQMMAAsLIA39GwAgDf0bAWogDf0bAiAN/RsDamohBQJAA0AgAyACTw0BIAAgA2otAAAhDyABIANqLQAAIQ4gBSAPQQ9xIA5BD3FsIA9BBHYgDkEEdmxqaiEFIANBAWohAwwACwsgBQs=";
|
|
32
|
+
var SIMD_DISTANCE_SHARED_MEMORY_WASM_BASE64 = "AGFzbQEAAAABIgVgA39/fwF9YAJ/fwF9YAN/f38Bf2ABewF/YAR/f39/AX8CEgEDZW52Bm1lbW9yeQIDAYCABAMPDgABAAACAgMCAgQCBAICB5sBCQtkb3RfcHJvZHVjdAAACW1hZ25pdHVkZQABGnNxdWFyZWRfZXVjbGlkZWFuX2Rpc3RhbmNlAAISZXVjbGlkZWFuX2Rpc3RhbmNlAAMGZG90X3U4AAQJc3FkaXN0X3U4AAUMb3NxX2RvdF9iaXRzAAkRb3NxX2RvdF9iaXRzX3BhaXIACxNvc3FfZG90X25pYmJsZXNfNHg0AA0K2RQOygIEAX8EewF9BH8gAkFwcSEJIAJBfHEhCv0MAAAAAAAAAAAAAAAAAAAAACEEQQAhAwJAA0AgAyAJTw0BIAAgA0ECdGohCyABIANBAnRqIQwgBCAL/QAEACAM/QAEAP3mAf3kASEEIAUgC/0ABBAgDP0ABBD95gH95AEhBSAGIAv9AAQgIAz9AAQg/eYB/eQBIQYgByAL/QAEMCAM/QAEMP3mAf3kASEHIANBEGohAwwACwsCQANAIAMgCk8NASAEIAAgA0ECdGr9AAQAIAEgA0ECdGr9AAQA/eYB/eQBIQQgA0EEaiEDDAALCyAEIAX95AEgBiAH/eQB/eQBIQQgBP0fACAE/R8BkiAE/R8CIAT9HwOSkiEIAkADQCADIAJPDQEgCCAAIANBAnRqKgIAIAEgA0ECdGoqAgCUkiEIIANBAWohAwwACwsgCAupAQQBfwJ7AX0BfyABQXxxIQb9DAAAAAAAAAAAAAAAAAAAAAAhA0EAIQICQANAIAIgBk8NASAAIAJBAnRq/QAEACEEIAMgBCAE/eYB/eQBIQMgAkEEaiECDAALCyAD/R8AIAP9HwGSIAP9HwIgA/0fA5KSIQUCQANAIAIgAU8NASAFIAAgAkECdGoqAgAgACACQQJ0aioCAJSSIQUgAkEBaiECDAALCyAFkQvAAQUBfwJ7AX0BfwF9IAJBfHEhB/0MAAAAAAAAAAAAAAAAAAAAACEEQQAhAwJAA0AgAyAHTw0BIAAgA0ECdGr9AAQAIAEgA0ECdGr9AAQA/eUBIQUgBCAFIAX95gH95AEhBCADQQRqIQMMAAsLIAT9HwAgBP0fAZIgBP0fAiAE/R8DkpIhBgJAA0AgAyACTw0BIAAgA0ECdGoqAgAgASADQQJ0aioCAJMhCCAGIAggCJSSIQYgA0EBaiEDDAALCyAGCwsAIAAgASACEAKRC7QBAwF/A3sCfyACQXBxIQhBAP0RIQRBACEDAkADQCADIAhPDQEgACADav0ABAAhBSABIANq/QAEACEGIAQgBf2JASAG/YkB/boB/a4BIQQgBCAF/YoBIAb9igH9ugH9rgEhBCADQRBqIQMMAAsLIAT9GwAgBP0bAWogBP0bAiAE/RsDamohBwJAA0AgAyACTw0BIAcgACADai0AACABIANqLQAAbGohByADQQFqIQMMAAsLIAcLzQEDAX8FewN/IAJBcHEhC0EA/REhBEEAIQMCQANAIAMgC08NASAAIANq/QAEACEFIAEgA2r9AAQAIQYgBf2JASAG/YkB/ZEBIQcgBf2KASAG/YoB/ZEBIQggBCAHIAf9ugH9rgEhBCAEIAggCP26Af2uASEEIANBEGohAwwACwsgBP0bACAE/RsBaiAE/RsCIAT9GwNqaiEJAkADQCADIAJPDQEgACADai0AACABIANqLQAAayEKIAkgCiAKbGohCSADQQFqIQMMAAsLIAkLIQEBeyAA/X8hASAB/RsAIAH9GwFqIAH9GwIgAf0bA2pqC68CAwh/A3sBfyABIAJqIQggCCACaiEJIAkgAmohCiACQXBxIQUCQANAIAMgBU8NASADQYAQaiEGIAYgBUsEQCAFIQYLQQD9ECENA0AgACADav0ABAAhCyALIAogA2r9AAQA/U79YiEMIAwgDP1uIAsgCSADav0ABAD9Tv1i/W4hDCAMIAz9biALIAggA2r9AAQA/U79Yv1uIQwgDCAM/W4gCyABIANq/QAEAP1O/WL9biEMIA0gDP19/Y4BIQ0gA0EQaiEDIAMgBkkNAAsgByANEAZqIQcMAAsLAkADQCADIAJPDQEgACADai0AACEOQQAhBAJAA0AgBEEETw0BIAcgDiABIAIgBGxqIANqLQAAcWkgBHRqIQcgBEEBaiEEDAALCyADQQFqIQMMAAsLIAcLhwMDCH8IewF/IAEgAmohCCAIIAJqIQkgCSACaiEKQQP9DyERIAJBcHEhBQJAA0AgAyAFTw0BIANBgBBqIQYgBiAFSwRAIAUhBgtBAP0QIRIDQCAAIANq/QAEACELIAsgEf1OIQwgC0EC/W0gEf1OIQ0gC0EE/W0gEf1OIQ4gC0EG/W0hDyABIANq/QAEACEQIBIgDCAQ/Z4BIAwgEP2fAf2OAf2OASESIAggA2r9AAQAIRAgEiANIBD9ngEgDSAQ/Z8B/Y4B/Y4BIRIgCSADav0ABAAhECASIA4gEP2eASAOIBD9nwH9jgH9jgEhEiAKIANq/QAEACEQIBIgDyAQ/Z4BIA8gEP2fAf2OAf2OASESIANBEGohAyADIAZJDQALIAcgEhAGaiEHDAALCwJAA0AgAyACTw0BIAAgA2otAAAhE0EAIQQCQANAIARBBE8NASAHIBMgBEEBdHZBA3EgASACIARsaiADai0AAGxqIQcgBEEBaiEEDAALCyADQQFqIQMMAAsLIAcLGwAgA0ECRgR/IAAgASACEAgFIAAgASACEAcLC54BAgR/AXsgAkFwcSEEAkADQCADIARPDQEgA0GAEGohBSAFIARLBEAgBCEFC0EA/RAhBwNAIAcgACADav0ABAAgASADav0ABAD9Tv1i/X39jgEhByADQRBqIQMgAyAFSQ0ACyAGIAcQBmohBgwACwsCQANAIAMgAk8NASAGIAAgA2otAAAgASADai0AAHFpaiEGIANBAWohAwwACwsgBgsbACADQQJGBH8gACABIAIQDAUgACABIAIQCgsL2gIDBH8IewR/QdUAIRFB1QAhEkHVAP0PIQdBqgH9DyEIIAJBcHEhBAJAA0AgAyAETw0BIANB4ANqIQUgBSAESwRAIAQhBQtBAP0PIQ5BAP0PIQxBAP0PIQ0DQCAAIANq/QAEACEJIAEgA2r9AAQAIQogCSAK/U4hCyAOIAv9Yv1uIQ4gDCALIAj9Tv1i/W4hDCANIAkgCkEB/W0gB/1OIApBAf1rIAj9Tv1Q/U79Yv1uIQ0gA0EQaiEDIAMgBUkNAAsgBiAO/X0QBiAM/X0QBkEDbCAN/X0QBkEBdGpqaiEGDAALCwJAA0AgAyACTw0BIAAgA2otAAAhDyABIANqLQAAIRAgBiAPIBFxIBBxaSAPIBFxIBBBAXYgEnFxaUEBdGogD0EBdiAScSAQIBFxcWlBAXQgD0EBdiAScSAQQQF2IBJxcWlBAnRqamohBiADQQFqIQMMAAsLIAYL/AEDA38IewJ/IAJBcHEhBEEP/Q8hDEEA/REhDUEAIQMCQANAIAMgBE8NASAAIANq/QAEACEGIAEgA2r9AAQAIQcgBiAM/U4hCCAGQQT9bSEJIAcgDP1OIQogB0EE/W0hCyANIAggCv2eASAJIAv9ngH9jgH9fyAIIAr9nwEgCSAL/Z8B/Y4B/X/9rgH9rgEhDSADQRBqIQMMAAsLIA39GwAgDf0bAWogDf0bAiAN/RsDamohBQJAA0AgAyACTw0BIAAgA2otAAAhDyABIANqLQAAIQ4gBSAPQQ9xIA5BD3FsIA9BBHYgDkEEdmxqaiEFIANBAWohAwwACwsgBQs=";
|
|
33
|
+
|
|
34
|
+
// src/vector/simd.ts
|
|
35
|
+
var wasmExports = null;
|
|
36
|
+
var f32View = null;
|
|
37
|
+
function decodeBase64(base64) {
|
|
38
|
+
if (typeof Buffer !== "undefined") {
|
|
39
|
+
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
40
|
+
}
|
|
41
|
+
const binary = atob(base64);
|
|
42
|
+
const bytes = new Uint8Array(binary.length);
|
|
43
|
+
for (let i = 0; i < binary.length; i++) {
|
|
44
|
+
bytes[i] = binary.charCodeAt(i);
|
|
45
|
+
}
|
|
46
|
+
return bytes;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const bytes = decodeBase64(SIMD_DISTANCE_WASM_BASE64);
|
|
50
|
+
const wasmBuffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
51
|
+
if (typeof WebAssembly !== "undefined" && typeof WebAssembly.validate === "function" && WebAssembly.validate(wasmBuffer)) {
|
|
52
|
+
const module = new WebAssembly.Module(wasmBuffer);
|
|
53
|
+
const instance = new WebAssembly.Instance(module);
|
|
54
|
+
wasmExports = instance.exports;
|
|
55
|
+
f32View = new Float32Array(wasmExports.memory.buffer);
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
wasmExports = null;
|
|
59
|
+
f32View = null;
|
|
60
|
+
}
|
|
61
|
+
var OFFSET_B_BYTES = 16384;
|
|
62
|
+
function ensureMemory(dim) {
|
|
63
|
+
if (!wasmExports || !f32View) return;
|
|
64
|
+
const needed = OFFSET_B_BYTES + dim * 4;
|
|
65
|
+
const currentSize = wasmExports.memory.buffer.byteLength;
|
|
66
|
+
if (needed > currentSize) {
|
|
67
|
+
const pagesNeeded = Math.ceil(needed / 65536);
|
|
68
|
+
const currentPages = currentSize / 65536;
|
|
69
|
+
wasmExports.memory.grow(pagesNeeded - currentPages);
|
|
70
|
+
f32View = new Float32Array(wasmExports.memory.buffer);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function getF32View() {
|
|
74
|
+
if (!f32View) {
|
|
75
|
+
throw new Error("SIMD memory is not initialized");
|
|
76
|
+
}
|
|
77
|
+
return f32View;
|
|
78
|
+
}
|
|
79
|
+
function copyOneVector(a) {
|
|
80
|
+
getF32View().set(a, 0);
|
|
81
|
+
}
|
|
82
|
+
function copyTwoVectors(a, b) {
|
|
83
|
+
const view = getF32View();
|
|
84
|
+
view.set(a, 0);
|
|
85
|
+
view.set(b, OFFSET_B_BYTES / 4);
|
|
86
|
+
}
|
|
87
|
+
function simdDotProduct(a, b) {
|
|
88
|
+
if (!wasmExports || !f32View) return null;
|
|
89
|
+
ensureMemory(a.length);
|
|
90
|
+
copyTwoVectors(a, b);
|
|
91
|
+
return wasmExports.dot_product(0, OFFSET_B_BYTES, a.length);
|
|
92
|
+
}
|
|
93
|
+
function simdMagnitude(a) {
|
|
94
|
+
if (!wasmExports || !f32View) return null;
|
|
95
|
+
ensureMemory(a.length);
|
|
96
|
+
copyOneVector(a);
|
|
97
|
+
return wasmExports.magnitude(0, a.length);
|
|
98
|
+
}
|
|
99
|
+
function simdSquaredEuclideanDistance(a, b) {
|
|
100
|
+
if (!wasmExports || !f32View) return null;
|
|
101
|
+
ensureMemory(a.length);
|
|
102
|
+
copyTwoVectors(a, b);
|
|
103
|
+
return wasmExports.squared_euclidean_distance(0, OFFSET_B_BYTES, a.length);
|
|
104
|
+
}
|
|
105
|
+
function isSimdAvailable() {
|
|
106
|
+
return wasmExports !== null;
|
|
107
|
+
}
|
|
108
|
+
var arenaModule;
|
|
109
|
+
function getArenaModule() {
|
|
110
|
+
if (arenaModule !== void 0) return arenaModule;
|
|
111
|
+
try {
|
|
112
|
+
const bytes = decodeBase64(SIMD_DISTANCE_WASM_BASE64);
|
|
113
|
+
const wasmBuffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
114
|
+
if (typeof WebAssembly === "undefined" || typeof WebAssembly.validate !== "function" || !WebAssembly.validate(wasmBuffer)) {
|
|
115
|
+
arenaModule = null;
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
arenaModule = new WebAssembly.Module(wasmBuffer);
|
|
119
|
+
} catch {
|
|
120
|
+
arenaModule = null;
|
|
121
|
+
}
|
|
122
|
+
return arenaModule;
|
|
123
|
+
}
|
|
124
|
+
function createArenaSimd() {
|
|
125
|
+
try {
|
|
126
|
+
const module = getArenaModule();
|
|
127
|
+
if (module === null) return null;
|
|
128
|
+
const instance = new WebAssembly.Instance(module);
|
|
129
|
+
const exports$1 = instance.exports;
|
|
130
|
+
if (typeof exports$1.dot_u8 !== "function" || typeof exports$1.sqdist_u8 !== "function" || typeof exports$1.osq_dot_nibbles_4x4 !== "function" || typeof exports$1.osq_dot_bits !== "function" || typeof exports$1.osq_dot_bits_pair !== "function" || typeof exports$1.dot_product !== "function" || typeof exports$1.magnitude !== "function" || typeof exports$1.squared_euclidean_distance !== "function" || !(exports$1.memory instanceof WebAssembly.Memory)) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
return exports$1;
|
|
134
|
+
} catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
var sharedMemoryModule;
|
|
139
|
+
function getSharedMemoryModule() {
|
|
140
|
+
if (sharedMemoryModule !== void 0) return sharedMemoryModule;
|
|
141
|
+
try {
|
|
142
|
+
const bytes = decodeBase64(SIMD_DISTANCE_SHARED_MEMORY_WASM_BASE64);
|
|
143
|
+
const wasmBuffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
144
|
+
if (typeof WebAssembly === "undefined" || typeof WebAssembly.validate !== "function" || !WebAssembly.validate(wasmBuffer)) {
|
|
145
|
+
sharedMemoryModule = null;
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
sharedMemoryModule = new WebAssembly.Module(wasmBuffer);
|
|
149
|
+
} catch {
|
|
150
|
+
sharedMemoryModule = null;
|
|
151
|
+
}
|
|
152
|
+
return sharedMemoryModule;
|
|
153
|
+
}
|
|
154
|
+
function createSharedArenaSimd(memory) {
|
|
155
|
+
try {
|
|
156
|
+
const module = getSharedMemoryModule();
|
|
157
|
+
if (module === null) return null;
|
|
158
|
+
const instance = new WebAssembly.Instance(module, { env: { memory } });
|
|
159
|
+
const exports$1 = instance.exports;
|
|
160
|
+
if (typeof exports$1.dot_u8 !== "function" || typeof exports$1.sqdist_u8 !== "function" || typeof exports$1.osq_dot_nibbles_4x4 !== "function" || typeof exports$1.osq_dot_bits !== "function" || typeof exports$1.osq_dot_bits_pair !== "function" || typeof exports$1.dot_product !== "function" || typeof exports$1.magnitude !== "function" || typeof exports$1.squared_euclidean_distance !== "function") {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
memory,
|
|
165
|
+
dot_product: exports$1.dot_product,
|
|
166
|
+
magnitude: exports$1.magnitude,
|
|
167
|
+
squared_euclidean_distance: exports$1.squared_euclidean_distance,
|
|
168
|
+
dot_u8: exports$1.dot_u8,
|
|
169
|
+
sqdist_u8: exports$1.sqdist_u8,
|
|
170
|
+
osq_dot_nibbles_4x4: exports$1.osq_dot_nibbles_4x4,
|
|
171
|
+
osq_dot_bits: exports$1.osq_dot_bits,
|
|
172
|
+
osq_dot_bits_pair: exports$1.osq_dot_bits_pair
|
|
173
|
+
};
|
|
174
|
+
} catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function arenaFloat32Distance(simd, byteA, byteB, dimension, metric, magnitudeA, magnitudeB) {
|
|
179
|
+
if (metric === "euclidean") {
|
|
180
|
+
return Math.sqrt(simd.squared_euclidean_distance(byteA, byteB, dimension));
|
|
181
|
+
}
|
|
182
|
+
const dot = simd.dot_product(byteA, byteB, dimension);
|
|
183
|
+
if (metric === "dotProduct") return -dot;
|
|
184
|
+
if (magnitudeA === 0 || magnitudeB === 0) return 1;
|
|
185
|
+
return 1 - dot / (magnitudeA * magnitudeB);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/vector/native/backend.ts
|
|
189
|
+
var nativeSearchCoreEnabled = true;
|
|
190
|
+
function nativeSearchCoreIsEnabled() {
|
|
191
|
+
return nativeSearchCoreEnabled;
|
|
192
|
+
}
|
|
193
|
+
function stopUsingTheNativeSearchCore(error) {
|
|
194
|
+
if (!nativeSearchCoreEnabled) return;
|
|
195
|
+
nativeSearchCoreEnabled = false;
|
|
196
|
+
console.warn(
|
|
197
|
+
"The native search core raised an error, so this thread searches through WebAssembly from now on:",
|
|
198
|
+
error instanceof Error ? error.message : String(error)
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
function noteFallback(operation) {
|
|
202
|
+
}
|
|
203
|
+
var NATIVE_CORE_ABI_VERSION = 2;
|
|
204
|
+
var BINARY_FILE_NAME = "narsil-core.node";
|
|
205
|
+
var MUSL_DIRECTORY = "musl";
|
|
206
|
+
var PLATFORMS_WITH_A_BINARY = /* @__PURE__ */ new Set([
|
|
207
|
+
"darwin-arm64",
|
|
208
|
+
"darwin-x64",
|
|
209
|
+
"linux-arm64",
|
|
210
|
+
"linux-x64",
|
|
211
|
+
"win32-arm64",
|
|
212
|
+
"win32-x64"
|
|
213
|
+
]);
|
|
214
|
+
var loaded;
|
|
215
|
+
function usesMuslLibc() {
|
|
216
|
+
try {
|
|
217
|
+
const report = process.report.getReport();
|
|
218
|
+
return report.header?.glibcVersionRuntime === void 0;
|
|
219
|
+
} catch {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function publishedBinaryPath(require2) {
|
|
224
|
+
const target = `${process.platform}-${process.arch}`;
|
|
225
|
+
if (!PLATFORMS_WITH_A_BINARY.has(target)) return null;
|
|
226
|
+
try {
|
|
227
|
+
const manifest = require2.resolve(`@delali/narsil-native-${target}/package.json`);
|
|
228
|
+
const segments = process.platform === "linux" && usesMuslLibc() ? [MUSL_DIRECTORY, BINARY_FILE_NAME] : [BINARY_FILE_NAME];
|
|
229
|
+
return join(dirname(manifest), ...segments);
|
|
230
|
+
} catch {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function namedBinaryPath() {
|
|
235
|
+
const named = process.env.NARSIL_NATIVE_CORE_PATH;
|
|
236
|
+
return named !== void 0 && named.length > 0 ? named : null;
|
|
237
|
+
}
|
|
238
|
+
function refuseToSearchWithoutTheCore(reason) {
|
|
239
|
+
throw new NarsilError(ErrorCodes.CONFIG_INVALID, `NARSIL_REQUIRE_NATIVE_CORE is 1, and ${reason}`, {
|
|
240
|
+
platform: `${process.platform}-${process.arch}`
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
function reportMissingCore(reason, named) {
|
|
244
|
+
if (process.env.NARSIL_REQUIRE_NATIVE_CORE === "1") refuseToSearchWithoutTheCore(reason);
|
|
245
|
+
if (named) console.warn(`Narsil searches through WebAssembly in this process, because ${reason}.`);
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
function loadedCore(path) {
|
|
249
|
+
const require2 = createRequire(import.meta.url);
|
|
250
|
+
const core = require2(path);
|
|
251
|
+
if (typeof core.abiVersion !== "function" || core.abiVersion() !== NATIVE_CORE_ABI_VERSION) return null;
|
|
252
|
+
return core;
|
|
253
|
+
}
|
|
254
|
+
function loadNativeCore() {
|
|
255
|
+
if (loaded !== void 0) return loaded;
|
|
256
|
+
loaded = null;
|
|
257
|
+
const named = namedBinaryPath();
|
|
258
|
+
if (process.env.NARSIL_SEARCH_BACKEND === "wasm") {
|
|
259
|
+
return reportMissingCore("NARSIL_SEARCH_BACKEND asks for the WebAssembly search", false);
|
|
260
|
+
}
|
|
261
|
+
const path = named ?? publishedBinaryPath(createRequire(import.meta.url));
|
|
262
|
+
if (path === null || !existsSync(path)) {
|
|
263
|
+
return reportMissingCore(`this platform has no native search core at ${path ?? "any known path"}`, named !== null);
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
loaded = loadedCore(path);
|
|
267
|
+
} catch (error) {
|
|
268
|
+
return reportMissingCore(
|
|
269
|
+
`loading the native search core at ${path} failed with "${error instanceof Error ? error.message : String(error)}"`,
|
|
270
|
+
named !== null
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
if (loaded === null) {
|
|
274
|
+
return reportMissingCore(`the native search core at ${path} reports another ABI version`, named !== null);
|
|
275
|
+
}
|
|
276
|
+
return loaded;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// src/vector/shared-buffers/growable.ts
|
|
280
|
+
function sharedMemoryAvailable() {
|
|
281
|
+
return typeof SharedArrayBuffer === "function";
|
|
282
|
+
}
|
|
283
|
+
function createGrowableBuffer(initialBytes, maxBytes) {
|
|
284
|
+
const initial = Math.min(initialBytes, maxBytes);
|
|
285
|
+
if (sharedMemoryAvailable()) {
|
|
286
|
+
return new SharedArrayBuffer(initial, { maxByteLength: maxBytes });
|
|
287
|
+
}
|
|
288
|
+
return new ArrayBuffer(initial, { maxByteLength: maxBytes });
|
|
289
|
+
}
|
|
290
|
+
function createFixedBuffer(bytes) {
|
|
291
|
+
return sharedMemoryAvailable() ? new SharedArrayBuffer(bytes) : new ArrayBuffer(bytes);
|
|
292
|
+
}
|
|
293
|
+
function nextGrowthTarget(have, needed, max) {
|
|
294
|
+
return Math.min(max, Math.max(needed, have * 2));
|
|
295
|
+
}
|
|
296
|
+
function growBufferTo(buffer, bytes) {
|
|
297
|
+
const held = buffer.byteLength;
|
|
298
|
+
if (held >= bytes) return;
|
|
299
|
+
resizeBuffer(buffer, bytes, nextGrowthTarget(held, bytes, buffer.maxByteLength));
|
|
300
|
+
}
|
|
301
|
+
function growBufferToExactly(buffer, bytes) {
|
|
302
|
+
if (buffer.byteLength >= bytes) return;
|
|
303
|
+
resizeBuffer(buffer, bytes, bytes);
|
|
304
|
+
}
|
|
305
|
+
function resizeBuffer(buffer, bytes, target) {
|
|
306
|
+
if (bytes > buffer.maxByteLength) {
|
|
307
|
+
throw new NarsilError(
|
|
308
|
+
ErrorCodes.PARTITION_CAPACITY_EXCEEDED,
|
|
309
|
+
`A vector structure needs ${bytes} bytes where it may hold at most ${buffer.maxByteLength}`,
|
|
310
|
+
{ bytes, maxBytes: buffer.maxByteLength }
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
if (buffer instanceof ArrayBuffer) {
|
|
314
|
+
buffer.resize(target);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
buffer.grow(target);
|
|
319
|
+
} catch (err) {
|
|
320
|
+
if (buffer.byteLength < bytes) throw err;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function fixedView(buffer, kind) {
|
|
324
|
+
return new kind(buffer, 0, Math.floor(buffer.byteLength / kind.BYTES_PER_ELEMENT));
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// src/vector/hnsw/handles.ts
|
|
328
|
+
var GRAPH_ENTRY_POINT = 0;
|
|
329
|
+
var GRAPH_TOP_LAYER = 1;
|
|
330
|
+
var GRAPH_M = 2;
|
|
331
|
+
var GRAPH_MMAX0 = 3;
|
|
332
|
+
var GRAPH_EF_CONSTRUCTION = 4;
|
|
333
|
+
var GRAPH_METRIC = 5;
|
|
334
|
+
var GRAPH_NODE_COUNT = 32;
|
|
335
|
+
var GRAPH_TOMBSTONE_COUNT = 33;
|
|
336
|
+
var GRAPH_UPPER_USED = 64;
|
|
337
|
+
var GRAPH_SLOTS = 65;
|
|
338
|
+
var GRAPH_UPPER_CAPACITY = 66;
|
|
339
|
+
var GRAPH_SLOT_CAPACITY = 67;
|
|
340
|
+
var GRAPH_LOCK = 96;
|
|
341
|
+
var GRAPH_WRITERS_WAITING = 97;
|
|
342
|
+
var GRAPH_ENTRY_LOCK = 128;
|
|
343
|
+
var GRAPH_HEADER_WORDS = 160;
|
|
344
|
+
var HELD_WRITE = 0;
|
|
345
|
+
var HELD_WRITE_VERSION = 1;
|
|
346
|
+
var HELD_GRAPH = 2;
|
|
347
|
+
var HELD_FENCE = 3;
|
|
348
|
+
var HELD_GRAPH_WAITING = 4;
|
|
349
|
+
var HELD_WORDS_PER_THREAD = 32;
|
|
350
|
+
var METRIC_CODES = ["cosine", "dotProduct", "euclidean"];
|
|
351
|
+
function perOrdinal(bytesPerOrdinal, initialOrdinals) {
|
|
352
|
+
return createGrowableBuffer(initialOrdinals * bytesPerOrdinal, MAX_VECTOR_ORDINALS * bytesPerOrdinal);
|
|
353
|
+
}
|
|
354
|
+
function createSharedGraphHandles(shape) {
|
|
355
|
+
const header = new Int32Array(createFixedBuffer(GRAPH_HEADER_WORDS * 4), 0, GRAPH_HEADER_WORDS);
|
|
356
|
+
header[GRAPH_ENTRY_POINT] = -1;
|
|
357
|
+
header[GRAPH_TOP_LAYER] = -1;
|
|
358
|
+
header[GRAPH_M] = shape.m;
|
|
359
|
+
header[GRAPH_MMAX0] = shape.mMax0;
|
|
360
|
+
header[GRAPH_EF_CONSTRUCTION] = shape.efConstruction;
|
|
361
|
+
header[GRAPH_METRIC] = Math.max(0, METRIC_CODES.indexOf(shape.metric));
|
|
362
|
+
const level0Stride = shape.mMax0 + 2;
|
|
363
|
+
const upperStride = shape.m + 2;
|
|
364
|
+
const heldWords = VECTOR_SCRATCH_SLOTS * HELD_WORDS_PER_THREAD;
|
|
365
|
+
const handles = {
|
|
366
|
+
header,
|
|
367
|
+
nodeLevels: perOrdinal(1, VECTOR_STORE_INITIAL_CAPACITY),
|
|
368
|
+
level0: perOrdinal(level0Stride * 4, VECTOR_STORE_INITIAL_CAPACITY),
|
|
369
|
+
upperBase: perOrdinal(4, VECTOR_STORE_INITIAL_CAPACITY),
|
|
370
|
+
upper: perOrdinal(upperStride * 4, INITIAL_UPPER_SLOTS),
|
|
371
|
+
locks: perOrdinal(4, VECTOR_STORE_INITIAL_CAPACITY),
|
|
372
|
+
tombstones: perOrdinal(1, VECTOR_STORE_INITIAL_CAPACITY),
|
|
373
|
+
heldLocks: new Int32Array(createFixedBuffer(heldWords * 4), 0, heldWords)
|
|
374
|
+
};
|
|
375
|
+
publishGraphCapacity(handles);
|
|
376
|
+
return handles;
|
|
377
|
+
}
|
|
378
|
+
function raiseHeaderWord(header, word, value) {
|
|
379
|
+
for (; ; ) {
|
|
380
|
+
const seen = Atomics.load(header, word);
|
|
381
|
+
if (seen >= value) return;
|
|
382
|
+
if (Atomics.compareExchange(header, word, seen, value) === seen) return;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
function publishGraphCapacity(handles) {
|
|
386
|
+
const level0Stride = handles.header[GRAPH_MMAX0] + 2;
|
|
387
|
+
const slotCapacity = Math.min(
|
|
388
|
+
handles.nodeLevels.byteLength,
|
|
389
|
+
Math.floor(handles.level0.byteLength / (level0Stride * 4)),
|
|
390
|
+
Math.floor(handles.upperBase.byteLength / 4),
|
|
391
|
+
Math.floor(handles.locks.byteLength / 4),
|
|
392
|
+
handles.tombstones.byteLength
|
|
393
|
+
);
|
|
394
|
+
raiseHeaderWord(handles.header, GRAPH_SLOT_CAPACITY, slotCapacity);
|
|
395
|
+
raiseHeaderWord(handles.header, GRAPH_UPPER_CAPACITY, Math.floor(handles.upper.byteLength / 4));
|
|
396
|
+
}
|
|
397
|
+
function graphShapeOf(header) {
|
|
398
|
+
return {
|
|
399
|
+
m: header[GRAPH_M],
|
|
400
|
+
mMax0: header[GRAPH_MMAX0],
|
|
401
|
+
efConstruction: header[GRAPH_EF_CONSTRUCTION],
|
|
402
|
+
metric: METRIC_CODES[header[GRAPH_METRIC]] ?? "cosine"
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// src/vector/vector-store/blocks.ts
|
|
407
|
+
var SCRATCH_ALIGNMENT_BYTES = 16;
|
|
408
|
+
var BLOCK_MAX_BYTES = MAX_WASM_PAGES * WASM_PAGE_BYTES;
|
|
409
|
+
function alignUp(bytes) {
|
|
410
|
+
return Math.ceil(bytes / SCRATCH_ALIGNMENT_BYTES) * SCRATCH_ALIGNMENT_BYTES;
|
|
411
|
+
}
|
|
412
|
+
function computeVectorBlockLayout(dimension, blockBytes2 = BLOCK_MAX_BYTES) {
|
|
413
|
+
const scratchStride = alignUp(dimension * 4);
|
|
414
|
+
const slotsOffset = alignUp(VECTOR_SCRATCH_SLOTS * scratchStride);
|
|
415
|
+
const slotStride = alignUp(dimension * 4);
|
|
416
|
+
return {
|
|
417
|
+
dimension,
|
|
418
|
+
scratchStride,
|
|
419
|
+
slotsOffset,
|
|
420
|
+
slotStride,
|
|
421
|
+
capacity: Math.max(1, Math.floor((Math.min(blockBytes2, BLOCK_MAX_BYTES) - slotsOffset) / slotStride))
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
function initialBlockBytes(layout) {
|
|
425
|
+
return layout.slotsOffset + VECTOR_BLOCK_INITIAL_SLOTS * layout.slotStride;
|
|
426
|
+
}
|
|
427
|
+
function pagesFor(bytes) {
|
|
428
|
+
return Math.ceil(bytes / WASM_PAGE_BYTES);
|
|
429
|
+
}
|
|
430
|
+
function createSharedMemory(pages) {
|
|
431
|
+
if (!sharedMemoryAvailable() || typeof WebAssembly === "undefined") return null;
|
|
432
|
+
try {
|
|
433
|
+
const memory = new WebAssembly.Memory({ initial: pages, maximum: MAX_WASM_PAGES, shared: true });
|
|
434
|
+
return createSharedArenaSimd(memory) === null ? null : memory;
|
|
435
|
+
} catch {
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function createVectorBlock(layout) {
|
|
440
|
+
const initialBytes = initialBlockBytes(layout);
|
|
441
|
+
const shared = createSharedMemory(pagesFor(initialBytes));
|
|
442
|
+
if (shared !== null) return { layout, storage: { kind: "shared-memory", memory: shared } };
|
|
443
|
+
const simd = createArenaSimd();
|
|
444
|
+
if (simd !== null) {
|
|
445
|
+
const missing = pagesFor(initialBytes) - simd.memory.buffer.byteLength / WASM_PAGE_BYTES;
|
|
446
|
+
if (missing > 0) simd.memory.grow(missing);
|
|
447
|
+
return { layout, storage: { kind: "private-memory", simd } };
|
|
448
|
+
}
|
|
449
|
+
return { layout, storage: { kind: "bytes", buffer: createGrowableBuffer(initialBytes, fullBlockBytes(layout)) } };
|
|
450
|
+
}
|
|
451
|
+
function fullBlockBytes(layout) {
|
|
452
|
+
return Math.min(BLOCK_MAX_BYTES, layout.slotsOffset + layout.capacity * layout.slotStride);
|
|
453
|
+
}
|
|
454
|
+
function growMemoryTo(memory, bytes, layout) {
|
|
455
|
+
const have = memory.buffer.byteLength;
|
|
456
|
+
if (have >= bytes) return;
|
|
457
|
+
memory.grow(pagesFor(nextGrowthTarget(have, bytes, fullBlockBytes(layout))) - have / WASM_PAGE_BYTES);
|
|
458
|
+
}
|
|
459
|
+
function ensureBlockSlots(handle, slots) {
|
|
460
|
+
const bytes = handle.layout.slotsOffset + Math.min(slots, handle.layout.capacity) * handle.layout.slotStride;
|
|
461
|
+
const storage = handle.storage;
|
|
462
|
+
if (storage.kind === "bytes") {
|
|
463
|
+
growBufferTo(storage.buffer, bytes);
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
growMemoryTo(storage.kind === "shared-memory" ? storage.memory : storage.simd.memory, bytes, handle.layout);
|
|
467
|
+
}
|
|
468
|
+
function blockBuffer(handle) {
|
|
469
|
+
const storage = handle.storage;
|
|
470
|
+
if (storage.kind === "bytes") return storage.buffer;
|
|
471
|
+
return storage.kind === "shared-memory" ? storage.memory.buffer : storage.simd.memory.buffer;
|
|
472
|
+
}
|
|
473
|
+
var NOTHING_STAGED = -1;
|
|
474
|
+
var QUERY_STAGED = -2;
|
|
475
|
+
function openVectorBlock(handle, threadSlot) {
|
|
476
|
+
const { layout, storage } = handle;
|
|
477
|
+
const simd = storage.kind === "shared-memory" ? createSharedArenaSimd(storage.memory) : storage.kind === "private-memory" ? storage.simd : null;
|
|
478
|
+
const hasScratch = simd !== null && threadSlot >= 0 && threadSlot < VECTOR_SCRATCH_SLOTS;
|
|
479
|
+
const scratchSlot = hasScratch ? threadSlot : 0;
|
|
480
|
+
let float32View = fixedView(blockBuffer(handle), Float32Array);
|
|
481
|
+
let byteView = fixedView(blockBuffer(handle), Uint8Array);
|
|
482
|
+
let dataView = new DataView(blockBuffer(handle));
|
|
483
|
+
return {
|
|
484
|
+
handle,
|
|
485
|
+
simd,
|
|
486
|
+
hasScratch,
|
|
487
|
+
scratchByteOffset: scratchSlot * layout.scratchStride,
|
|
488
|
+
stagedOrdinal: NOTHING_STAGED,
|
|
489
|
+
float32(neededLength) {
|
|
490
|
+
if (neededLength > float32View.length) float32View = fixedView(blockBuffer(handle), Float32Array);
|
|
491
|
+
return float32View;
|
|
492
|
+
},
|
|
493
|
+
bytes(neededLength) {
|
|
494
|
+
if (neededLength > byteView.length) byteView = fixedView(blockBuffer(handle), Uint8Array);
|
|
495
|
+
return byteView;
|
|
496
|
+
},
|
|
497
|
+
data(neededLength) {
|
|
498
|
+
if (neededLength > dataView.byteLength) dataView = new DataView(blockBuffer(handle));
|
|
499
|
+
return dataView;
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
function slotByteOffset(layout, localOrdinal) {
|
|
504
|
+
return layout.slotsOffset + localOrdinal * layout.slotStride;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// src/vector/osq/estimate.ts
|
|
508
|
+
var POPCOUNT = new Uint8Array(256);
|
|
509
|
+
for (let value = 0; value < 256; value++) {
|
|
510
|
+
let count = 0;
|
|
511
|
+
let rest = value;
|
|
512
|
+
while (rest !== 0) {
|
|
513
|
+
count += rest & 1;
|
|
514
|
+
rest >>= 1;
|
|
515
|
+
}
|
|
516
|
+
POPCOUNT[value] = count;
|
|
517
|
+
}
|
|
518
|
+
var LEVEL_BIT_MASK = { 1: 255, 2: 85 };
|
|
519
|
+
var STAGED_QUERY_PLANES = 4;
|
|
520
|
+
function osqNibbleProducts(a, aOffset, b, bOffset, bytes) {
|
|
521
|
+
let total = 0;
|
|
522
|
+
for (let n = 0; n < bytes; n++) {
|
|
523
|
+
const left = a[aOffset + n];
|
|
524
|
+
const right = b[bOffset + n];
|
|
525
|
+
total += (left & 15) * (right & 15) + (left >> 4) * (right >> 4);
|
|
526
|
+
}
|
|
527
|
+
return total;
|
|
528
|
+
}
|
|
529
|
+
function twoBitStagedProducts(document2, documentOffset, streams, streamsOffset, streamBytes) {
|
|
530
|
+
let total = 0;
|
|
531
|
+
for (let n = 0; n < streamBytes; n++) {
|
|
532
|
+
const packed = document2[documentOffset + n];
|
|
533
|
+
for (let k = 0; k < STAGED_QUERY_PLANES; k++) {
|
|
534
|
+
total += (packed >> 2 * k & 3) * streams[streamsOffset + k * streamBytes + n];
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return total;
|
|
538
|
+
}
|
|
539
|
+
function osqStagedQueryProducts(document2, documentOffset, documentBits, staged, stagedOffset, codeBytes) {
|
|
540
|
+
if (documentBits === 2) return twoBitStagedProducts(document2, documentOffset, staged, stagedOffset, codeBytes);
|
|
541
|
+
let total = 0;
|
|
542
|
+
for (let p = 0; p < STAGED_QUERY_PLANES; p++) {
|
|
543
|
+
const plane = stagedOffset + p * codeBytes;
|
|
544
|
+
let bitsSet = 0;
|
|
545
|
+
for (let n = 0; n < codeBytes; n++) bitsSet += POPCOUNT[document2[documentOffset + n] & staged[plane + n]];
|
|
546
|
+
total += 2 ** p * bitsSet;
|
|
547
|
+
}
|
|
548
|
+
return total;
|
|
549
|
+
}
|
|
550
|
+
function osqNarrowPairProducts(a, aOffset, b, bOffset, bits, bytes) {
|
|
551
|
+
const mask = LEVEL_BIT_MASK[bits];
|
|
552
|
+
let total = 0;
|
|
553
|
+
for (let j = 0; j < bits; j++) {
|
|
554
|
+
for (let l = 0; l < bits; l++) {
|
|
555
|
+
let bitsSet = 0;
|
|
556
|
+
for (let n = 0; n < bytes; n++) bitsSet += POPCOUNT[a[aOffset + n] >> j & b[bOffset + n] >> l & mask];
|
|
557
|
+
total += 2 ** (j + l) * bitsSet;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return total;
|
|
561
|
+
}
|
|
562
|
+
function osqByteLevelProducts(document2, documentOffset, query, queryOffset, dimension) {
|
|
563
|
+
let total = 0;
|
|
564
|
+
for (let i = 0; i < dimension; i++) total += document2[documentOffset + i] * query[queryOffset + i];
|
|
565
|
+
return total;
|
|
566
|
+
}
|
|
567
|
+
function osqEstimate(products, document2, documentBits, query, queryBits, dimension, centroidDot, metric) {
|
|
568
|
+
const documentStep = (document2.upper - document2.lower) / OSQ_STEPS[documentBits];
|
|
569
|
+
const queryStep = (query.upper - query.lower) / OSQ_STEPS[queryBits];
|
|
570
|
+
const centred = document2.lower * query.lower * dimension + query.lower * documentStep * document2.sum + document2.lower * queryStep * query.sum + documentStep * queryStep * products;
|
|
571
|
+
if (metric === "euclidean") return Math.sqrt(Math.max(0, document2.correction + query.correction - 2 * centred));
|
|
572
|
+
const similarity = centred + document2.correction + query.correction - centroidDot;
|
|
573
|
+
if (metric === "cosine") return Math.min(Math.max(similarity, -1), 1);
|
|
574
|
+
return similarity;
|
|
575
|
+
}
|
|
576
|
+
function osqDistance(estimate, metric) {
|
|
577
|
+
switch (metric) {
|
|
578
|
+
case "cosine":
|
|
579
|
+
return 1 - estimate;
|
|
580
|
+
case "dotProduct":
|
|
581
|
+
return -estimate;
|
|
582
|
+
case "euclidean":
|
|
583
|
+
return estimate;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
function osqQueryBits(bits) {
|
|
587
|
+
return OSQ_QUERY_BITS[bits];
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// src/vector/osq/record.ts
|
|
591
|
+
var OSQ_RECORD_TRAILER_BYTES = 16;
|
|
592
|
+
var TRAILER_LOWER = 0;
|
|
593
|
+
var TRAILER_UPPER = 4;
|
|
594
|
+
var TRAILER_CORRECTION = 8;
|
|
595
|
+
var TRAILER_SUM = 12;
|
|
596
|
+
var QUERY_PLANES_FOR_NARROW_CODES = 4;
|
|
597
|
+
var LEVELS_IN_A_TWO_BIT_BYTE = 4;
|
|
598
|
+
function osqCodeBytes(dimension, bits) {
|
|
599
|
+
return Math.ceil(dimension * bits / 8);
|
|
600
|
+
}
|
|
601
|
+
function osqRecordBytes(dimension, bits) {
|
|
602
|
+
return osqCodeBytes(dimension, bits) + OSQ_RECORD_TRAILER_BYTES;
|
|
603
|
+
}
|
|
604
|
+
function packLevels(levels, bits, target, offset) {
|
|
605
|
+
const dimension = levels.length;
|
|
606
|
+
if (bits === 8) {
|
|
607
|
+
target.set(levels, offset);
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
const perByte = 8 / bits;
|
|
611
|
+
target.fill(0, offset, offset + osqCodeBytes(dimension, bits));
|
|
612
|
+
for (let i = 0; i < dimension; i++) {
|
|
613
|
+
const value = levels[i];
|
|
614
|
+
if (value === 0) continue;
|
|
615
|
+
target[offset + Math.floor(i / perByte)] |= value << i % perByte * bits;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
function unpackLevels(bytes, offset, dimension, bits) {
|
|
619
|
+
const levels = new Uint8Array(dimension);
|
|
620
|
+
if (bits === 8) {
|
|
621
|
+
levels.set(bytes.subarray(offset, offset + dimension));
|
|
622
|
+
return levels;
|
|
623
|
+
}
|
|
624
|
+
const perByte = 8 / bits;
|
|
625
|
+
const mask = (1 << bits) - 1;
|
|
626
|
+
for (let i = 0; i < dimension; i++) {
|
|
627
|
+
levels[i] = bytes[offset + Math.floor(i / perByte)] >> i % perByte * bits & mask;
|
|
628
|
+
}
|
|
629
|
+
return levels;
|
|
630
|
+
}
|
|
631
|
+
function osqStagedQueryBytes(dimension, documentBits) {
|
|
632
|
+
if (documentBits === 8) return dimension;
|
|
633
|
+
if (documentBits === 4) return osqCodeBytes(dimension, 4);
|
|
634
|
+
return QUERY_PLANES_FOR_NARROW_CODES * osqCodeBytes(dimension, documentBits);
|
|
635
|
+
}
|
|
636
|
+
function stageQueryLevels(levels, documentBits, target) {
|
|
637
|
+
if (documentBits === 8 || documentBits === 4) {
|
|
638
|
+
packLevels(levels, documentBits, target, 0);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
const planeBytes = osqCodeBytes(levels.length, documentBits);
|
|
642
|
+
target.fill(0, 0, QUERY_PLANES_FOR_NARROW_CODES * planeBytes);
|
|
643
|
+
if (documentBits === 2) {
|
|
644
|
+
for (let i = 0; i < levels.length; i++) {
|
|
645
|
+
target[i % LEVELS_IN_A_TWO_BIT_BYTE * planeBytes + Math.floor(i / LEVELS_IN_A_TWO_BIT_BYTE)] = levels[i];
|
|
646
|
+
}
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
for (let i = 0; i < levels.length; i++) {
|
|
650
|
+
const value = levels[i];
|
|
651
|
+
if (value === 0) continue;
|
|
652
|
+
const byte = i >> 3;
|
|
653
|
+
const bit = 1 << (i & 7);
|
|
654
|
+
for (let plane = 0; plane < QUERY_PLANES_FOR_NARROW_CODES; plane++) {
|
|
655
|
+
if (value >> plane & 1) target[plane * planeBytes + byte] |= bit;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
function writeRecord(target, offset, code, bits, trailer = new DataView(target.buffer, target.byteOffset, target.byteLength)) {
|
|
660
|
+
packLevels(code.levels, bits, target, offset);
|
|
661
|
+
const at = offset + osqCodeBytes(code.levels.length, bits);
|
|
662
|
+
trailer.setFloat32(at + TRAILER_LOWER, code.lower, true);
|
|
663
|
+
trailer.setFloat32(at + TRAILER_UPPER, code.upper, true);
|
|
664
|
+
trailer.setFloat32(at + TRAILER_CORRECTION, code.correction, true);
|
|
665
|
+
trailer.setUint32(at + TRAILER_SUM, code.sum, true);
|
|
666
|
+
}
|
|
667
|
+
function readTrailer(trailer, codeOffset, codeBytes, into) {
|
|
668
|
+
const at = codeOffset + codeBytes;
|
|
669
|
+
into.lower = trailer.getFloat32(at + TRAILER_LOWER, true);
|
|
670
|
+
into.upper = trailer.getFloat32(at + TRAILER_UPPER, true);
|
|
671
|
+
into.correction = trailer.getFloat32(at + TRAILER_CORRECTION, true);
|
|
672
|
+
into.sum = trailer.getUint32(at + TRAILER_SUM, true);
|
|
673
|
+
return into;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// src/vector/vector-store/code-blocks.ts
|
|
677
|
+
function computeCodeBlockLayout(dimension, bits) {
|
|
678
|
+
const queryBits = osqQueryBits(bits);
|
|
679
|
+
const codeBytes = osqCodeBytes(dimension, bits);
|
|
680
|
+
const queryBytes = osqStagedQueryBytes(dimension, bits);
|
|
681
|
+
const scratchStride = alignUp(queryBytes);
|
|
682
|
+
const slotsOffset = alignUp(VECTOR_SCRATCH_SLOTS * scratchStride);
|
|
683
|
+
const slotStride = osqRecordBytes(dimension, bits);
|
|
684
|
+
return {
|
|
685
|
+
dimension,
|
|
686
|
+
bits,
|
|
687
|
+
queryBits,
|
|
688
|
+
codeBytes,
|
|
689
|
+
queryBytes,
|
|
690
|
+
scratchStride,
|
|
691
|
+
slotsOffset,
|
|
692
|
+
slotStride,
|
|
693
|
+
capacity: Math.floor((BLOCK_MAX_BYTES - slotsOffset) / slotStride)
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// src/vector/vector-store/handles.ts
|
|
698
|
+
var STORE_SLOTS = 0;
|
|
699
|
+
var STORE_LIVE_COUNT = 1;
|
|
700
|
+
var STORE_BLOCK_COUNT = 2;
|
|
701
|
+
var STORE_CALIBRATED = 3;
|
|
702
|
+
var STORE_CODE_COUNT = 4;
|
|
703
|
+
var STORE_DOC_ID_BYTES = 5;
|
|
704
|
+
var STORE_CODE_BLOCK_COUNT = 6;
|
|
705
|
+
var STORE_CALIBRATION_GENERATION = 7;
|
|
706
|
+
var STORE_RELEASED_VECTORS_TO_DISK = 8;
|
|
707
|
+
var STORE_HEADER_WORDS = 16;
|
|
708
|
+
var IN_MEMORY = -1;
|
|
709
|
+
function fixedInt32(words) {
|
|
710
|
+
return new Int32Array(createFixedBuffer(words * 4), 0, words);
|
|
711
|
+
}
|
|
712
|
+
function fixedFloat32(length) {
|
|
713
|
+
return new Float32Array(createFixedBuffer(length * 4), 0, length);
|
|
714
|
+
}
|
|
715
|
+
function perOrdinal2(bytesPerOrdinal) {
|
|
716
|
+
return createGrowableBuffer(
|
|
717
|
+
VECTOR_STORE_INITIAL_CAPACITY * bytesPerOrdinal,
|
|
718
|
+
(MAX_VECTOR_ORDINALS + 1) * bytesPerOrdinal
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
function inMemoryTable() {
|
|
722
|
+
const buffer = perOrdinal2(4);
|
|
723
|
+
new Int32Array(buffer).fill(IN_MEMORY);
|
|
724
|
+
return buffer;
|
|
725
|
+
}
|
|
726
|
+
function createSharedVectorStoreHandles(dimension, codeBits, blockBytes2 = BLOCK_MAX_BYTES) {
|
|
727
|
+
return {
|
|
728
|
+
dimension,
|
|
729
|
+
codeBits,
|
|
730
|
+
layout: computeVectorBlockLayout(dimension, blockBytes2),
|
|
731
|
+
codeLayout: codeBits === null ? null : computeCodeBlockLayout(dimension, codeBits),
|
|
732
|
+
layoutRevision: 0,
|
|
733
|
+
header: fixedInt32(STORE_HEADER_WORDS),
|
|
734
|
+
blocks: [],
|
|
735
|
+
codeBlocks: [],
|
|
736
|
+
vectorFiles: [],
|
|
737
|
+
magnitudes: perOrdinal2(8),
|
|
738
|
+
present: perOrdinal2(1),
|
|
739
|
+
partitions: perOrdinal2(4),
|
|
740
|
+
docIdBytes: createGrowableBuffer(VECTOR_STORE_INITIAL_CAPACITY * 16, MAX_DOC_ID_TABLE_BYTES),
|
|
741
|
+
docIdOffsets: perOrdinal2(4),
|
|
742
|
+
codePresent: perOrdinal2(1),
|
|
743
|
+
diskFile: inMemoryTable(),
|
|
744
|
+
diskOffset: perOrdinal2(4),
|
|
745
|
+
centroid: fixedFloat32(dimension)
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
function sharedVectorStoreBytes(handles) {
|
|
749
|
+
let bytes = handles.header.byteLength + handles.centroid.byteLength;
|
|
750
|
+
for (const block of handles.blocks) {
|
|
751
|
+
if (block !== null) bytes += blockBuffer(block).byteLength;
|
|
752
|
+
}
|
|
753
|
+
for (const block of handles.codeBlocks) bytes += blockBuffer(block).byteLength;
|
|
754
|
+
bytes += handles.magnitudes.byteLength;
|
|
755
|
+
bytes += handles.present.byteLength;
|
|
756
|
+
bytes += handles.partitions.byteLength;
|
|
757
|
+
bytes += handles.docIdBytes.byteLength;
|
|
758
|
+
bytes += handles.docIdOffsets.byteLength;
|
|
759
|
+
bytes += handles.codePresent.byteLength;
|
|
760
|
+
bytes += handles.diskFile.byteLength;
|
|
761
|
+
bytes += handles.diskOffset.byteLength;
|
|
762
|
+
return bytes;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// src/vector/native/types.ts
|
|
766
|
+
var NATIVE_OK = 0;
|
|
767
|
+
var NATIVE_NEEDS_ROOM = 3;
|
|
768
|
+
|
|
769
|
+
// src/vector/native/store.ts
|
|
770
|
+
var METRIC_CODES2 = { cosine: 0, dotProduct: 1, euclidean: 2 };
|
|
771
|
+
var attachedStores = /* @__PURE__ */ new WeakMap();
|
|
772
|
+
function nativeMetricCode(metric) {
|
|
773
|
+
return METRIC_CODES2[metric];
|
|
774
|
+
}
|
|
775
|
+
function isSharedMemory(buffer) {
|
|
776
|
+
return typeof SharedArrayBuffer === "function" && buffer instanceof SharedArrayBuffer;
|
|
777
|
+
}
|
|
778
|
+
function blockBytes(block) {
|
|
779
|
+
const storage = block.storage;
|
|
780
|
+
if (storage.kind === "shared-memory") return new Uint8Array(storage.memory.buffer, block.layout.slotsOffset);
|
|
781
|
+
if (storage.kind === "bytes" && isSharedMemory(storage.buffer)) {
|
|
782
|
+
return new Uint8Array(storage.buffer, block.layout.slotsOffset);
|
|
783
|
+
}
|
|
784
|
+
return null;
|
|
785
|
+
}
|
|
786
|
+
function storeMemory(store) {
|
|
787
|
+
const regions = [
|
|
788
|
+
store.header.buffer,
|
|
789
|
+
store.codePresent,
|
|
790
|
+
store.centroid.buffer,
|
|
791
|
+
store.magnitudes,
|
|
792
|
+
store.present,
|
|
793
|
+
store.diskFile,
|
|
794
|
+
store.diskOffset
|
|
795
|
+
];
|
|
796
|
+
if (!regions.every(isSharedMemory)) return null;
|
|
797
|
+
const codeBlocks = [];
|
|
798
|
+
for (const block of store.codeBlocks) {
|
|
799
|
+
const bytes = blockBytes(block);
|
|
800
|
+
if (bytes === null) return null;
|
|
801
|
+
codeBlocks.push(bytes);
|
|
802
|
+
}
|
|
803
|
+
const vectorBlocks = [];
|
|
804
|
+
for (const block of store.blocks) {
|
|
805
|
+
const bytes = block === null ? null : blockBytes(block);
|
|
806
|
+
if (block !== null && bytes === null) return null;
|
|
807
|
+
vectorBlocks.push(bytes);
|
|
808
|
+
}
|
|
809
|
+
return {
|
|
810
|
+
storeHeader: store.header,
|
|
811
|
+
dimension: store.dimension,
|
|
812
|
+
bits: store.codeBits ?? 0,
|
|
813
|
+
recordsPerBlock: store.codeLayout?.capacity ?? 0,
|
|
814
|
+
vectorsPerBlock: store.layout.capacity,
|
|
815
|
+
vectorStrideBytes: store.layout.slotStride,
|
|
816
|
+
codePresent: fixedView(store.codePresent, Uint8Array),
|
|
817
|
+
centroid: store.centroid,
|
|
818
|
+
magnitudes: fixedView(store.magnitudes, Float64Array),
|
|
819
|
+
present: fixedView(store.present, Uint8Array),
|
|
820
|
+
vectorFile: fixedView(store.diskFile, Int32Array),
|
|
821
|
+
vectorOffset: fixedView(store.diskOffset, Uint32Array),
|
|
822
|
+
vectorFiles: [...store.vectorFiles],
|
|
823
|
+
codeBlocks,
|
|
824
|
+
vectorBlocks
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
function attach(core, store) {
|
|
828
|
+
const memory = storeMemory(store);
|
|
829
|
+
let handle = null;
|
|
830
|
+
if (memory !== null) {
|
|
831
|
+
try {
|
|
832
|
+
handle = core.attachStore(memory);
|
|
833
|
+
} catch {
|
|
834
|
+
handle = null;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
return {
|
|
838
|
+
core,
|
|
839
|
+
handle,
|
|
840
|
+
layoutRevision: store.layoutRevision,
|
|
841
|
+
vectorBlockCount: store.blocks.length,
|
|
842
|
+
codeBlockCount: store.codeBlocks.length,
|
|
843
|
+
retryAt: handle === null ? Date.now() + NATIVE_ATTACH_RETRY_MS : Number.POSITIVE_INFINITY,
|
|
844
|
+
ordinals: new Int32Array(0),
|
|
845
|
+
distances: new Float64Array(0)
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
function stillCurrent(attached, store) {
|
|
849
|
+
return Date.now() < attached.retryAt && attached.layoutRevision === store.layoutRevision && attached.vectorBlockCount === store.blocks.length && attached.codeBlockCount === store.codeBlocks.length;
|
|
850
|
+
}
|
|
851
|
+
function isUsable(attached) {
|
|
852
|
+
return attached.handle !== null;
|
|
853
|
+
}
|
|
854
|
+
function detachNativeStore(store) {
|
|
855
|
+
const attached = attachedStores.get(store);
|
|
856
|
+
if (attached === void 0) return;
|
|
857
|
+
attachedStores.delete(store);
|
|
858
|
+
if (attached.handle === null) return;
|
|
859
|
+
try {
|
|
860
|
+
attached.core.detachStore(attached.handle);
|
|
861
|
+
} catch (error) {
|
|
862
|
+
stopUsingTheNativeSearchCore(error);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
function attachedNativeStore(store) {
|
|
866
|
+
const attached = attachedStores.get(store);
|
|
867
|
+
return attached !== void 0 && isUsable(attached) ? attached : null;
|
|
868
|
+
}
|
|
869
|
+
function nativeStoreScratchBytes(store) {
|
|
870
|
+
const attached = attachedNativeStore(store);
|
|
871
|
+
return attached === null ? 0 : attached.ordinals.byteLength + attached.distances.byteLength;
|
|
872
|
+
}
|
|
873
|
+
function nativeStoreFor(store) {
|
|
874
|
+
if (!nativeSearchCoreIsEnabled()) return null;
|
|
875
|
+
const core = loadNativeCore();
|
|
876
|
+
if (core === null) return null;
|
|
877
|
+
let attached = attachedStores.get(store);
|
|
878
|
+
if (attached !== void 0 && !stillCurrent(attached, store)) {
|
|
879
|
+
detachNativeStore(store);
|
|
880
|
+
attached = void 0;
|
|
881
|
+
}
|
|
882
|
+
if (attached === void 0) {
|
|
883
|
+
attached = attach(core, store);
|
|
884
|
+
attachedStores.set(store, attached);
|
|
885
|
+
}
|
|
886
|
+
return isUsable(attached) ? attached : null;
|
|
887
|
+
}
|
|
888
|
+
function reserveOrdinals(store, count) {
|
|
889
|
+
if (store.ordinals.length >= count) return;
|
|
890
|
+
store.ordinals = new Int32Array(count);
|
|
891
|
+
store.distances = new Float64Array(count);
|
|
892
|
+
}
|
|
893
|
+
function nativeScores(store, query, metric, count) {
|
|
894
|
+
if (count === 0) return store.distances;
|
|
895
|
+
let status;
|
|
896
|
+
try {
|
|
897
|
+
status = store.core.score(store.handle, query, nativeMetricCode(metric), store.ordinals, count, store.distances);
|
|
898
|
+
} catch (error) {
|
|
899
|
+
stopUsingTheNativeSearchCore(error);
|
|
900
|
+
return null;
|
|
901
|
+
}
|
|
902
|
+
return status === NATIVE_OK ? store.distances : null;
|
|
903
|
+
}
|
|
904
|
+
function nativeScoresOf(handles, query, metric, ordinals) {
|
|
905
|
+
const store = nativeStoreFor(handles);
|
|
906
|
+
if (store === null) return null;
|
|
907
|
+
reserveOrdinals(store, ordinals.length);
|
|
908
|
+
store.ordinals.set(ordinals);
|
|
909
|
+
return nativeScores(store, query, metric, ordinals.length);
|
|
910
|
+
}
|
|
911
|
+
function workOnRecords(handles, metric, ordinals, work) {
|
|
912
|
+
if (ordinals.length === 0) return true;
|
|
913
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
914
|
+
const store = nativeStoreFor(handles);
|
|
915
|
+
if (store === null) return false;
|
|
916
|
+
let status;
|
|
917
|
+
try {
|
|
918
|
+
status = store.core[work](store.handle, nativeMetricCode(metric), ordinals, ordinals.length);
|
|
919
|
+
} catch (error) {
|
|
920
|
+
stopUsingTheNativeSearchCore(error);
|
|
921
|
+
return false;
|
|
922
|
+
}
|
|
923
|
+
if (status === NATIVE_OK) return true;
|
|
924
|
+
if (status !== NATIVE_NEEDS_ROOM) return false;
|
|
925
|
+
detachNativeStore(handles);
|
|
926
|
+
}
|
|
927
|
+
return false;
|
|
928
|
+
}
|
|
929
|
+
function nativeCalibrate(handles, metric, ordinals) {
|
|
930
|
+
return workOnRecords(handles, metric, ordinals, "calibrate");
|
|
931
|
+
}
|
|
932
|
+
function nativeQuantise(handles, metric, ordinals) {
|
|
933
|
+
if (Atomics.load(handles.header, STORE_CALIBRATED) !== 1) return false;
|
|
934
|
+
return workOnRecords(handles, metric, ordinals, "quantise");
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// src/vector/native/field.ts
|
|
938
|
+
var WAKE_COUNT_WORDS = 1;
|
|
939
|
+
var WAKES_THE_CORE_KEEPS_PER_CALL = 256;
|
|
940
|
+
var WAKE_LIST_WORDS = WAKE_COUNT_WORDS + WAKES_THE_CORE_KEEPS_PER_CALL;
|
|
941
|
+
var WAKE_ENTRY_LOCK = -1;
|
|
942
|
+
var attachedGraphs = /* @__PURE__ */ new WeakMap();
|
|
943
|
+
function graphMemory(graph) {
|
|
944
|
+
const regions = [
|
|
945
|
+
graph.header.buffer,
|
|
946
|
+
graph.nodeLevels,
|
|
947
|
+
graph.level0,
|
|
948
|
+
graph.upperBase,
|
|
949
|
+
graph.upper,
|
|
950
|
+
graph.locks,
|
|
951
|
+
graph.tombstones,
|
|
952
|
+
graph.heldLocks.buffer
|
|
953
|
+
];
|
|
954
|
+
if (!regions.every(isSharedMemory)) return null;
|
|
955
|
+
return {
|
|
956
|
+
graphHeader: graph.header,
|
|
957
|
+
nodeLevels: fixedView(graph.nodeLevels, Uint8Array),
|
|
958
|
+
level0: fixedView(graph.level0, Int32Array),
|
|
959
|
+
upperBase: fixedView(graph.upperBase, Int32Array),
|
|
960
|
+
upper: fixedView(graph.upper, Int32Array),
|
|
961
|
+
locks: fixedView(graph.locks, Int32Array),
|
|
962
|
+
tombstones: fixedView(graph.tombstones, Uint8Array),
|
|
963
|
+
heldLocks: graph.heldLocks
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
function attach2(core, state) {
|
|
967
|
+
const memory = graphMemory(state.adjacency.handles);
|
|
968
|
+
let handle = null;
|
|
969
|
+
if (memory !== null) {
|
|
970
|
+
try {
|
|
971
|
+
handle = core.attachGraph(memory);
|
|
972
|
+
} catch {
|
|
973
|
+
handle = null;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
return { handle, field: null };
|
|
977
|
+
}
|
|
978
|
+
var workspaceOwner = null;
|
|
979
|
+
function nativeScratchBytes(state, field) {
|
|
980
|
+
const perField = field.store.ordinals.byteLength + field.store.distances.byteLength + field.wakes.byteLength;
|
|
981
|
+
const owner = workspaceOwner?.deref();
|
|
982
|
+
if (owner !== void 0 && owner !== state) return perField;
|
|
983
|
+
workspaceOwner = new WeakRef(state);
|
|
984
|
+
return perField + field.core.workspaceBytes();
|
|
985
|
+
}
|
|
986
|
+
function attachedNativeField(state) {
|
|
987
|
+
if (!nativeSearchCoreIsEnabled()) return null;
|
|
988
|
+
return attachedGraphs.get(state)?.field ?? null;
|
|
989
|
+
}
|
|
990
|
+
function nativeFieldFor(state) {
|
|
991
|
+
if (!nativeSearchCoreIsEnabled()) return null;
|
|
992
|
+
const core = loadNativeCore();
|
|
993
|
+
if (core === null) return null;
|
|
994
|
+
const store = nativeStoreFor(state.store.handles);
|
|
995
|
+
if (store === null) return null;
|
|
996
|
+
let attached = attachedGraphs.get(state);
|
|
997
|
+
if (attached === void 0) {
|
|
998
|
+
attached = attach2(core, state);
|
|
999
|
+
attachedGraphs.set(state, attached);
|
|
1000
|
+
}
|
|
1001
|
+
if (attached.handle === null) return null;
|
|
1002
|
+
if (attached.field === null) {
|
|
1003
|
+
attached.field = { core, graph: attached.handle, store, wakes: new Int32Array(WAKE_LIST_WORDS) };
|
|
1004
|
+
}
|
|
1005
|
+
attached.field.store = store;
|
|
1006
|
+
return attached.field;
|
|
1007
|
+
}
|
|
1008
|
+
function wakeSleepingThreads(state, field) {
|
|
1009
|
+
const count = field.wakes[0];
|
|
1010
|
+
if (count === 0) return;
|
|
1011
|
+
field.wakes[0] = 0;
|
|
1012
|
+
for (let i = 1; i <= count; i++) {
|
|
1013
|
+
const ordinal = field.wakes[i];
|
|
1014
|
+
if (ordinal === WAKE_ENTRY_LOCK) {
|
|
1015
|
+
Atomics.notify(state.header, GRAPH_ENTRY_LOCK);
|
|
1016
|
+
continue;
|
|
1017
|
+
}
|
|
1018
|
+
if (ordinal < 0 || ordinal >= Atomics.load(state.header, GRAPH_SLOTS)) continue;
|
|
1019
|
+
Atomics.notify(state.locks.words(ordinal), ordinal);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// src/vector/native/writes.ts
|
|
1024
|
+
var CALL_RAISED = -1;
|
|
1025
|
+
function writeToTheGraph(state, field, write) {
|
|
1026
|
+
let status;
|
|
1027
|
+
try {
|
|
1028
|
+
status = write();
|
|
1029
|
+
} catch (error) {
|
|
1030
|
+
stopUsingTheNativeSearchCore(error);
|
|
1031
|
+
return CALL_RAISED;
|
|
1032
|
+
}
|
|
1033
|
+
wakeSleepingThreads(state, field);
|
|
1034
|
+
return status;
|
|
1035
|
+
}
|
|
1036
|
+
function nativePlace(state, field, metric, ordinal, topLayer, graphHeldAlone) {
|
|
1037
|
+
const status = writeToTheGraph(
|
|
1038
|
+
state,
|
|
1039
|
+
field,
|
|
1040
|
+
() => field.core.place(
|
|
1041
|
+
field.graph,
|
|
1042
|
+
field.store.handle,
|
|
1043
|
+
nativeMetricCode(metric),
|
|
1044
|
+
ordinal,
|
|
1045
|
+
topLayer,
|
|
1046
|
+
graphHeldAlone ? 1 : 0,
|
|
1047
|
+
state.locks.threadSlot,
|
|
1048
|
+
field.wakes
|
|
1049
|
+
)
|
|
1050
|
+
);
|
|
1051
|
+
if (status === NATIVE_OK) return "placed";
|
|
1052
|
+
return status === NATIVE_NEEDS_ROOM ? "needsRoom" : "failed";
|
|
1053
|
+
}
|
|
1054
|
+
function nativeRemove(state, field, ordinal) {
|
|
1055
|
+
const remove = () => field.core.remove(field.graph, ordinal, state.locks.threadSlot, field.wakes);
|
|
1056
|
+
return writeToTheGraph(state, field, remove) === NATIVE_OK;
|
|
1057
|
+
}
|
|
1058
|
+
function nativeCompact(state, field, metric) {
|
|
1059
|
+
const compact = () => field.core.compact(field.graph, field.store.handle, nativeMetricCode(metric), state.locks.threadSlot, field.wakes);
|
|
1060
|
+
return writeToTheGraph(state, field, compact) === NATIVE_OK;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// src/vector/hnsw/adjacency/views.ts
|
|
1064
|
+
var ABSENT = -1;
|
|
1065
|
+
function openAdjacency(handles, m, mMax0) {
|
|
1066
|
+
return {
|
|
1067
|
+
m,
|
|
1068
|
+
mMax0,
|
|
1069
|
+
level0Stride: mMax0 + 2,
|
|
1070
|
+
upperStride: m + 2,
|
|
1071
|
+
handles,
|
|
1072
|
+
header: handles.header,
|
|
1073
|
+
nodeLevels: fixedView(handles.nodeLevels, Uint8Array),
|
|
1074
|
+
level0: fixedView(handles.level0, Int32Array),
|
|
1075
|
+
upperBase: fixedView(handles.upperBase, Int32Array),
|
|
1076
|
+
upper: fixedView(handles.upper, Int32Array)
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
function rebindNodes(adj) {
|
|
1080
|
+
adj.nodeLevels = fixedView(adj.handles.nodeLevels, Uint8Array);
|
|
1081
|
+
adj.level0 = fixedView(adj.handles.level0, Int32Array);
|
|
1082
|
+
adj.upperBase = fixedView(adj.handles.upperBase, Int32Array);
|
|
1083
|
+
}
|
|
1084
|
+
function rebindUpper(adj) {
|
|
1085
|
+
adj.upper = fixedView(adj.handles.upper, Int32Array);
|
|
1086
|
+
}
|
|
1087
|
+
function reachNode(adj, ord) {
|
|
1088
|
+
if (ord < adj.nodeLevels.length) return true;
|
|
1089
|
+
if (ord >= adj.handles.nodeLevels.byteLength) return false;
|
|
1090
|
+
rebindNodes(adj);
|
|
1091
|
+
return ord < adj.nodeLevels.length;
|
|
1092
|
+
}
|
|
1093
|
+
function reachUpper(adj, end) {
|
|
1094
|
+
if (end <= adj.upper.length) return true;
|
|
1095
|
+
if (end * 4 > adj.handles.upper.byteLength) return false;
|
|
1096
|
+
rebindUpper(adj);
|
|
1097
|
+
return end <= adj.upper.length;
|
|
1098
|
+
}
|
|
1099
|
+
function adjacencySlots(adj) {
|
|
1100
|
+
return Atomics.load(adj.header, GRAPH_SLOTS);
|
|
1101
|
+
}
|
|
1102
|
+
function upperUsed(adj) {
|
|
1103
|
+
return Atomics.load(adj.header, GRAPH_UPPER_USED);
|
|
1104
|
+
}
|
|
1105
|
+
function ensureAdjacencyCapacity(adj, needed) {
|
|
1106
|
+
if (needed <= adj.nodeLevels.length) return;
|
|
1107
|
+
const { handles } = adj;
|
|
1108
|
+
if (needed <= handles.nodeLevels.byteLength) {
|
|
1109
|
+
rebindNodes(adj);
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
const capacity = Math.max(needed, Math.min(handles.nodeLevels.byteLength * 2, handles.nodeLevels.maxByteLength));
|
|
1113
|
+
growBufferToExactly(handles.level0, capacity * adj.level0Stride * 4);
|
|
1114
|
+
growBufferToExactly(handles.upperBase, capacity * 4);
|
|
1115
|
+
growBufferToExactly(handles.locks, capacity * 4);
|
|
1116
|
+
growBufferToExactly(handles.tombstones, capacity);
|
|
1117
|
+
growBufferToExactly(handles.nodeLevels, capacity);
|
|
1118
|
+
publishGraphCapacity(handles);
|
|
1119
|
+
rebindNodes(adj);
|
|
1120
|
+
}
|
|
1121
|
+
function growUpperTo(adj, entries) {
|
|
1122
|
+
const held = adj.handles.upper.byteLength;
|
|
1123
|
+
growBufferTo(adj.handles.upper, entries * 4);
|
|
1124
|
+
if (adj.handles.upper.byteLength !== held) publishGraphCapacity(adj.handles);
|
|
1125
|
+
}
|
|
1126
|
+
function ensureUpperCapacity(adj, entries) {
|
|
1127
|
+
growUpperTo(adj, entries);
|
|
1128
|
+
rebindUpper(adj);
|
|
1129
|
+
}
|
|
1130
|
+
function ensureUpperRoom(adj, levels) {
|
|
1131
|
+
if (levels < 1) return;
|
|
1132
|
+
growUpperTo(adj, Atomics.load(adj.header, GRAPH_UPPER_USED) + levels * adj.upperStride);
|
|
1133
|
+
}
|
|
1134
|
+
function allocateUpperBlock(adj, levels) {
|
|
1135
|
+
const size = levels * adj.upperStride;
|
|
1136
|
+
for (; ; ) {
|
|
1137
|
+
const offset = Atomics.load(adj.header, GRAPH_UPPER_USED);
|
|
1138
|
+
growUpperTo(adj, offset + size);
|
|
1139
|
+
if (Atomics.compareExchange(adj.header, GRAPH_UPPER_USED, offset, offset + size) !== offset) continue;
|
|
1140
|
+
rebindUpper(adj);
|
|
1141
|
+
return offset;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
function raiseSlots(adj, slots) {
|
|
1145
|
+
for (; ; ) {
|
|
1146
|
+
const seen = Atomics.load(adj.header, GRAPH_SLOTS);
|
|
1147
|
+
if (seen >= slots) return;
|
|
1148
|
+
if (Atomics.compareExchange(adj.header, GRAPH_SLOTS, seen, slots) === seen) return;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
function createNode(adj, ord, maxLayer) {
|
|
1152
|
+
ensureAdjacencyCapacity(adj, ord + 1);
|
|
1153
|
+
adj.upperBase[ord] = maxLayer >= 1 ? allocateUpperBlock(adj, maxLayer) + 1 : 0;
|
|
1154
|
+
adj.level0[ord * adj.level0Stride] = 0;
|
|
1155
|
+
raiseSlots(adj, ord + 1);
|
|
1156
|
+
Atomics.store(adj.nodeLevels, ord, maxLayer + 1);
|
|
1157
|
+
}
|
|
1158
|
+
function deleteNode(adj, ord) {
|
|
1159
|
+
if (ord < 0 || !reachNode(adj, ord)) return;
|
|
1160
|
+
Atomics.store(adj.nodeLevels, ord, 0);
|
|
1161
|
+
adj.upperBase[ord] = 0;
|
|
1162
|
+
adj.level0[ord * adj.level0Stride] = 0;
|
|
1163
|
+
}
|
|
1164
|
+
function resetAdjacency(adj) {
|
|
1165
|
+
rebindNodes(adj);
|
|
1166
|
+
rebindUpper(adj);
|
|
1167
|
+
adj.nodeLevels.fill(0);
|
|
1168
|
+
adj.level0.fill(0);
|
|
1169
|
+
adj.upperBase.fill(0);
|
|
1170
|
+
adj.upper.fill(0);
|
|
1171
|
+
Atomics.store(adj.header, GRAPH_UPPER_USED, 0);
|
|
1172
|
+
Atomics.store(adj.header, GRAPH_SLOTS, 0);
|
|
1173
|
+
}
|
|
1174
|
+
function nodeLevel(adj, ord) {
|
|
1175
|
+
if (ord < 0 || !reachNode(adj, ord)) return ABSENT;
|
|
1176
|
+
return adj.nodeLevels[ord] - 1;
|
|
1177
|
+
}
|
|
1178
|
+
function hasNode(adj, ord) {
|
|
1179
|
+
return nodeLevel(adj, ord) !== ABSENT;
|
|
1180
|
+
}
|
|
1181
|
+
function graphBytes(adj) {
|
|
1182
|
+
const handles = adj.handles;
|
|
1183
|
+
return handles.header.byteLength + handles.nodeLevels.byteLength + handles.level0.byteLength + handles.upperBase.byteLength + handles.upper.byteLength + handles.locks.byteLength + handles.tombstones.byteLength + handles.heldLocks.byteLength;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
// src/vector/hnsw/adjacency/neighbors.ts
|
|
1187
|
+
function layerArray(adj, layer) {
|
|
1188
|
+
return layer === 0 ? adj.level0 : adj.upper;
|
|
1189
|
+
}
|
|
1190
|
+
function layerBase(adj, ord, layer) {
|
|
1191
|
+
const level2 = nodeLevel(adj, ord);
|
|
1192
|
+
if (level2 === ABSENT || layer > level2) return ABSENT;
|
|
1193
|
+
if (layer === 0) return ord * adj.level0Stride;
|
|
1194
|
+
const base = adj.upperBase[ord];
|
|
1195
|
+
if (base === 0) return ABSENT;
|
|
1196
|
+
const start = base - 1 + (layer - 1) * adj.upperStride;
|
|
1197
|
+
return reachUpper(adj, start + adj.upperStride) ? start : ABSENT;
|
|
1198
|
+
}
|
|
1199
|
+
function neighborCount(adj, ord, layer) {
|
|
1200
|
+
const base = layerBase(adj, ord, layer);
|
|
1201
|
+
if (base === ABSENT) return 0;
|
|
1202
|
+
return layerArray(adj, layer)[base];
|
|
1203
|
+
}
|
|
1204
|
+
function readNeighbors(adj, ord, layer, out) {
|
|
1205
|
+
const base = layerBase(adj, ord, layer);
|
|
1206
|
+
if (base === ABSENT) return 0;
|
|
1207
|
+
const array = layerArray(adj, layer);
|
|
1208
|
+
const count = Math.min(array[base], out.length);
|
|
1209
|
+
for (let i = 0; i < count; i++) out[i] = array[base + i + 1];
|
|
1210
|
+
return count;
|
|
1211
|
+
}
|
|
1212
|
+
function collectNeighbors(adj, ord, layer) {
|
|
1213
|
+
const base = layerBase(adj, ord, layer);
|
|
1214
|
+
if (base === ABSENT) return [];
|
|
1215
|
+
const array = layerArray(adj, layer);
|
|
1216
|
+
const count = array[base];
|
|
1217
|
+
const collected = new Array(count);
|
|
1218
|
+
for (let i = 0; i < count; i++) collected[i] = array[base + i + 1];
|
|
1219
|
+
return collected;
|
|
1220
|
+
}
|
|
1221
|
+
function addNeighbor(adj, ord, layer, neighborOrd) {
|
|
1222
|
+
const base = layerBase(adj, ord, layer);
|
|
1223
|
+
if (base === ABSENT) return;
|
|
1224
|
+
const array = layerArray(adj, layer);
|
|
1225
|
+
const count = array[base];
|
|
1226
|
+
for (let i = 1; i <= count; i++) {
|
|
1227
|
+
if (array[base + i] === neighborOrd) return;
|
|
1228
|
+
}
|
|
1229
|
+
const stride = layer === 0 ? adj.level0Stride : adj.upperStride;
|
|
1230
|
+
if (count + 1 > stride - 1) return;
|
|
1231
|
+
array[base + count + 1] = neighborOrd;
|
|
1232
|
+
array[base] = count + 1;
|
|
1233
|
+
}
|
|
1234
|
+
function removeNeighbor(adj, ord, layer, neighborOrd) {
|
|
1235
|
+
const base = layerBase(adj, ord, layer);
|
|
1236
|
+
if (base === ABSENT) return;
|
|
1237
|
+
const array = layerArray(adj, layer);
|
|
1238
|
+
const count = array[base];
|
|
1239
|
+
for (let i = 1; i <= count; i++) {
|
|
1240
|
+
if (array[base + i] !== neighborOrd) continue;
|
|
1241
|
+
for (let j = i; j < count; j++) {
|
|
1242
|
+
array[base + j] = array[base + j + 1];
|
|
1243
|
+
}
|
|
1244
|
+
array[base] = count - 1;
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
function replaceNeighbors(adj, ord, layer, neighbors, count) {
|
|
1249
|
+
const base = layerBase(adj, ord, layer);
|
|
1250
|
+
if (base === ABSENT) return;
|
|
1251
|
+
const array = layerArray(adj, layer);
|
|
1252
|
+
const stride = layer === 0 ? adj.level0Stride : adj.upperStride;
|
|
1253
|
+
const limit = Math.min(count, stride - 1);
|
|
1254
|
+
for (let i = 0; i < limit; i++) {
|
|
1255
|
+
array[base + i + 1] = neighbors[i];
|
|
1256
|
+
}
|
|
1257
|
+
array[base] = limit;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
// src/vector/hnsw/locks.ts
|
|
1261
|
+
var FREE = 0;
|
|
1262
|
+
var EXCLUSIVE = -1;
|
|
1263
|
+
function openGraphLocks(handles, threadSlot) {
|
|
1264
|
+
let words = fixedView(handles.locks, Int32Array);
|
|
1265
|
+
return {
|
|
1266
|
+
threadSlot,
|
|
1267
|
+
header: handles.header,
|
|
1268
|
+
held: handles.heldLocks,
|
|
1269
|
+
words(ord) {
|
|
1270
|
+
if (ord < words.length) return words;
|
|
1271
|
+
if (ord * 4 >= handles.locks.byteLength) growBufferTo(handles.locks, (ord + 1) * 4);
|
|
1272
|
+
words = fixedView(handles.locks, Int32Array);
|
|
1273
|
+
return words;
|
|
1274
|
+
}
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
var blockingAllowed = typeof document === "undefined";
|
|
1278
|
+
function canWait(words) {
|
|
1279
|
+
return blockingAllowed && sharedMemoryAvailable() && words.buffer instanceof SharedArrayBuffer;
|
|
1280
|
+
}
|
|
1281
|
+
function waitOn(words, index, seen, sliceMs = LOCK_WAIT_SLICE_MS) {
|
|
1282
|
+
if (!canWait(words)) return;
|
|
1283
|
+
Atomics.wait(words, index, seen, sliceMs);
|
|
1284
|
+
}
|
|
1285
|
+
function heldIndex(locks, kind) {
|
|
1286
|
+
return locks.threadSlot * HELD_WORDS_PER_THREAD + kind;
|
|
1287
|
+
}
|
|
1288
|
+
function acquireExclusive(words, index) {
|
|
1289
|
+
let spins = 0;
|
|
1290
|
+
for (; ; ) {
|
|
1291
|
+
const seen = Atomics.compareExchange(words, index, FREE, EXCLUSIVE);
|
|
1292
|
+
if (seen === FREE) return;
|
|
1293
|
+
if (spins < LOCK_SPIN_ITERATIONS) {
|
|
1294
|
+
spins += 1;
|
|
1295
|
+
continue;
|
|
1296
|
+
}
|
|
1297
|
+
waitOn(words, index, seen, GRAPH_LOCK_WAIT_SLICE_MS);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
function releaseExclusive(words, index) {
|
|
1301
|
+
Atomics.store(words, index, FREE);
|
|
1302
|
+
if (canWait(words)) Atomics.notify(words, index);
|
|
1303
|
+
}
|
|
1304
|
+
var WRITE_HELD = 1;
|
|
1305
|
+
var WRITER_WAITING = 2;
|
|
1306
|
+
var VERSION_STEP = 4;
|
|
1307
|
+
function beginNodeRead(locks, ord) {
|
|
1308
|
+
const words = locks.words(ord);
|
|
1309
|
+
for (; ; ) {
|
|
1310
|
+
const seen = Atomics.load(words, ord);
|
|
1311
|
+
if ((seen & WRITE_HELD) === 0) return seen;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
function nodeReadHeld(locks, ord, version) {
|
|
1315
|
+
Atomics.add(locks.held, heldIndex(locks, HELD_FENCE), 0);
|
|
1316
|
+
return Atomics.load(locks.words(ord), ord) === version;
|
|
1317
|
+
}
|
|
1318
|
+
function lockNodeWrite(locks, ord) {
|
|
1319
|
+
const words = locks.words(ord);
|
|
1320
|
+
let spins = 0;
|
|
1321
|
+
for (; ; ) {
|
|
1322
|
+
const seen = Atomics.load(words, ord);
|
|
1323
|
+
if ((seen & WRITE_HELD) === 0) {
|
|
1324
|
+
const claimed = seen | WRITE_HELD;
|
|
1325
|
+
if (Atomics.compareExchange(words, ord, seen, claimed) === seen) {
|
|
1326
|
+
Atomics.store(locks.held, heldIndex(locks, HELD_WRITE_VERSION), claimed);
|
|
1327
|
+
Atomics.store(locks.held, heldIndex(locks, HELD_WRITE), ord + 1);
|
|
1328
|
+
return;
|
|
1329
|
+
}
|
|
1330
|
+
continue;
|
|
1331
|
+
}
|
|
1332
|
+
if (spins < LOCK_SPIN_ITERATIONS) {
|
|
1333
|
+
spins += 1;
|
|
1334
|
+
continue;
|
|
1335
|
+
}
|
|
1336
|
+
const waiting = seen | WRITER_WAITING;
|
|
1337
|
+
if (waiting === seen || Atomics.compareExchange(words, ord, seen, waiting) === seen) {
|
|
1338
|
+
waitOn(words, ord, waiting);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
function releaseWrite(words, ord) {
|
|
1343
|
+
const seen = Atomics.load(words, ord);
|
|
1344
|
+
const previous = Atomics.exchange(words, ord, (seen & -4) + VERSION_STEP);
|
|
1345
|
+
if ((previous & WRITER_WAITING) !== 0 && canWait(words)) Atomics.notify(words, ord);
|
|
1346
|
+
}
|
|
1347
|
+
function unlockNodeWrite(locks, ord) {
|
|
1348
|
+
releaseWrite(locks.words(ord), ord);
|
|
1349
|
+
Atomics.store(locks.held, heldIndex(locks, HELD_WRITE), 0);
|
|
1350
|
+
}
|
|
1351
|
+
function lockGraphShared(locks) {
|
|
1352
|
+
const { header } = locks;
|
|
1353
|
+
let spins = 0;
|
|
1354
|
+
for (; ; ) {
|
|
1355
|
+
const seen = Atomics.load(header, GRAPH_LOCK);
|
|
1356
|
+
if (seen >= FREE && Atomics.load(header, GRAPH_WRITERS_WAITING) === 0) {
|
|
1357
|
+
if (Atomics.compareExchange(header, GRAPH_LOCK, seen, seen + 1) === seen) {
|
|
1358
|
+
Atomics.store(locks.held, heldIndex(locks, HELD_GRAPH), 1);
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1361
|
+
continue;
|
|
1362
|
+
}
|
|
1363
|
+
if (spins < LOCK_SPIN_ITERATIONS) {
|
|
1364
|
+
spins += 1;
|
|
1365
|
+
continue;
|
|
1366
|
+
}
|
|
1367
|
+
waitOn(header, GRAPH_LOCK, seen, GRAPH_LOCK_WAIT_SLICE_MS);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
function unlockGraphShared(locks) {
|
|
1371
|
+
const { header } = locks;
|
|
1372
|
+
Atomics.store(locks.held, heldIndex(locks, HELD_GRAPH), 0);
|
|
1373
|
+
if (Atomics.sub(header, GRAPH_LOCK, 1) === 1 && canWait(header)) Atomics.notify(header, GRAPH_LOCK);
|
|
1374
|
+
}
|
|
1375
|
+
function lockGraphExclusive(locks) {
|
|
1376
|
+
Atomics.store(locks.held, heldIndex(locks, HELD_GRAPH_WAITING), 1);
|
|
1377
|
+
Atomics.add(locks.header, GRAPH_WRITERS_WAITING, 1);
|
|
1378
|
+
try {
|
|
1379
|
+
acquireExclusive(locks.header, GRAPH_LOCK);
|
|
1380
|
+
} finally {
|
|
1381
|
+
Atomics.sub(locks.header, GRAPH_WRITERS_WAITING, 1);
|
|
1382
|
+
Atomics.store(locks.held, heldIndex(locks, HELD_GRAPH_WAITING), 0);
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
function unlockGraphExclusive(locks) {
|
|
1386
|
+
releaseExclusive(locks.header, GRAPH_LOCK);
|
|
1387
|
+
}
|
|
1388
|
+
function lockEntry(locks) {
|
|
1389
|
+
acquireExclusive(locks.header, GRAPH_ENTRY_LOCK);
|
|
1390
|
+
}
|
|
1391
|
+
function unlockEntry(locks) {
|
|
1392
|
+
releaseExclusive(locks.header, GRAPH_ENTRY_LOCK);
|
|
1393
|
+
}
|
|
1394
|
+
function releaseLocksHeldBy(handles, threadSlot) {
|
|
1395
|
+
const held = handles.heldLocks;
|
|
1396
|
+
const words = new Int32Array(handles.locks);
|
|
1397
|
+
const base = threadSlot * HELD_WORDS_PER_THREAD;
|
|
1398
|
+
if (Atomics.exchange(held, base + HELD_GRAPH_WAITING, 0) === 1) {
|
|
1399
|
+
Atomics.sub(handles.header, GRAPH_WRITERS_WAITING, 1);
|
|
1400
|
+
}
|
|
1401
|
+
const writeOrd = Atomics.exchange(held, base + HELD_WRITE, 0) - 1;
|
|
1402
|
+
const writeVersion = Atomics.load(held, base + HELD_WRITE_VERSION);
|
|
1403
|
+
if (writeOrd >= 0 && writeOrd < words.length) {
|
|
1404
|
+
const seen = Atomics.load(words, writeOrd);
|
|
1405
|
+
if ((seen & ~WRITER_WAITING) === writeVersion) releaseWrite(words, writeOrd);
|
|
1406
|
+
}
|
|
1407
|
+
if (Atomics.exchange(held, base + HELD_GRAPH, 0) !== 1) return;
|
|
1408
|
+
for (; ; ) {
|
|
1409
|
+
const readers = Atomics.load(handles.header, GRAPH_LOCK);
|
|
1410
|
+
if (readers <= FREE) return;
|
|
1411
|
+
if (Atomics.compareExchange(handles.header, GRAPH_LOCK, readers, readers - 1) === readers) {
|
|
1412
|
+
if (canWait(handles.header)) Atomics.notify(handles.header, GRAPH_LOCK);
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
// src/vector/similarity.ts
|
|
1419
|
+
function magnitude(v) {
|
|
1420
|
+
const result = simdMagnitude(v);
|
|
1421
|
+
if (result !== null) return result;
|
|
1422
|
+
let sum = 0;
|
|
1423
|
+
for (let i = 0; i < v.length; i++) {
|
|
1424
|
+
sum += v[i] * v[i];
|
|
1425
|
+
}
|
|
1426
|
+
return Math.sqrt(sum);
|
|
1427
|
+
}
|
|
1428
|
+
function cosineSimilarityWithMagnitudes(a, b, magA, magB) {
|
|
1429
|
+
if (magA === 0 || magB === 0) return 0;
|
|
1430
|
+
return dotProduct(a, b) / (magA * magB);
|
|
1431
|
+
}
|
|
1432
|
+
function dotProduct(a, b) {
|
|
1433
|
+
const result = simdDotProduct(a, b);
|
|
1434
|
+
if (result !== null) return result;
|
|
1435
|
+
let sum = 0;
|
|
1436
|
+
for (let i = 0; i < a.length; i++) {
|
|
1437
|
+
sum += a[i] * b[i];
|
|
1438
|
+
}
|
|
1439
|
+
return sum;
|
|
1440
|
+
}
|
|
1441
|
+
function euclideanDistance(a, b) {
|
|
1442
|
+
return Math.sqrt(squaredEuclideanDistance(a, b));
|
|
1443
|
+
}
|
|
1444
|
+
function squaredEuclideanDistance(a, b) {
|
|
1445
|
+
const result = simdSquaredEuclideanDistance(a, b);
|
|
1446
|
+
if (result !== null) return result;
|
|
1447
|
+
let sum = 0;
|
|
1448
|
+
for (let i = 0; i < a.length; i++) {
|
|
1449
|
+
const diff = a[i] - b[i];
|
|
1450
|
+
sum += diff * diff;
|
|
1451
|
+
}
|
|
1452
|
+
return sum;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
// src/vector/hnsw/shared.ts
|
|
1456
|
+
function entryPointOf(state) {
|
|
1457
|
+
return Atomics.load(state.header, GRAPH_ENTRY_POINT);
|
|
1458
|
+
}
|
|
1459
|
+
function topLayerOf(state) {
|
|
1460
|
+
return Atomics.load(state.header, GRAPH_TOP_LAYER);
|
|
1461
|
+
}
|
|
1462
|
+
function nodeCountOf(state) {
|
|
1463
|
+
return Atomics.load(state.header, GRAPH_NODE_COUNT);
|
|
1464
|
+
}
|
|
1465
|
+
function tombstoneCountOf(state) {
|
|
1466
|
+
return Atomics.load(state.header, GRAPH_TOMBSTONE_COUNT);
|
|
1467
|
+
}
|
|
1468
|
+
function ensureCapacity(state, needed) {
|
|
1469
|
+
ensureAdjacencyCapacity(state.adjacency, needed);
|
|
1470
|
+
ensureVisited(state, needed);
|
|
1471
|
+
}
|
|
1472
|
+
function ensureVisited(state, needed) {
|
|
1473
|
+
if (needed <= state.visited.length) return;
|
|
1474
|
+
let capacity = state.visited.length === 0 ? 16 : state.visited.length;
|
|
1475
|
+
while (capacity < needed) capacity *= 2;
|
|
1476
|
+
const next = new Uint32Array(capacity);
|
|
1477
|
+
next.set(state.visited);
|
|
1478
|
+
state.visited = next;
|
|
1479
|
+
}
|
|
1480
|
+
function nextVisitStamp(state) {
|
|
1481
|
+
state.visitStamp++;
|
|
1482
|
+
if (state.visitStamp === 4294967295) {
|
|
1483
|
+
state.visited.fill(0);
|
|
1484
|
+
state.visitStamp = 1;
|
|
1485
|
+
}
|
|
1486
|
+
return state.visitStamp;
|
|
1487
|
+
}
|
|
1488
|
+
function reachTombstone(state, ord) {
|
|
1489
|
+
if (ord < state.tombstones.length) return true;
|
|
1490
|
+
if (ord >= state.adjacency.handles.tombstones.byteLength) return false;
|
|
1491
|
+
state.tombstones = fixedView(state.adjacency.handles.tombstones, Uint8Array);
|
|
1492
|
+
return ord < state.tombstones.length;
|
|
1493
|
+
}
|
|
1494
|
+
function isTombstoned(state, ord) {
|
|
1495
|
+
return ord >= 0 && reachTombstone(state, ord) && state.tombstones[ord] === 1;
|
|
1496
|
+
}
|
|
1497
|
+
function nodeExists(state, ord) {
|
|
1498
|
+
return hasNode(state.adjacency, ord);
|
|
1499
|
+
}
|
|
1500
|
+
function nodeMaxLayer(state, ord) {
|
|
1501
|
+
return nodeLevel(state.adjacency, ord);
|
|
1502
|
+
}
|
|
1503
|
+
function toDistance(a, b, magA, magB, metric) {
|
|
1504
|
+
switch (metric) {
|
|
1505
|
+
case "cosine":
|
|
1506
|
+
return 1 - cosineSimilarityWithMagnitudes(a, b, magA, magB);
|
|
1507
|
+
case "dotProduct":
|
|
1508
|
+
return -dotProduct(a, b);
|
|
1509
|
+
case "euclidean":
|
|
1510
|
+
return euclideanDistance(a, b);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
function toScore(distance, metric) {
|
|
1514
|
+
switch (metric) {
|
|
1515
|
+
case "cosine":
|
|
1516
|
+
return 1 - distance;
|
|
1517
|
+
case "dotProduct":
|
|
1518
|
+
return -distance;
|
|
1519
|
+
case "euclidean":
|
|
1520
|
+
return 1 / (1 + distance);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
function randomLevel(mL) {
|
|
1524
|
+
let u = Math.random();
|
|
1525
|
+
if (u === 0) u = Number.MIN_VALUE;
|
|
1526
|
+
return Math.min(Math.floor(-Math.log(u) * mL), MAX_LAYER_CAP);
|
|
1527
|
+
}
|
|
1528
|
+
function entryForOrd(state, ord) {
|
|
1529
|
+
return state.store.entryForOrdinal(ord);
|
|
1530
|
+
}
|
|
1531
|
+
function buildsFromCodes(state) {
|
|
1532
|
+
return activeQuantizer(state) !== void 0;
|
|
1533
|
+
}
|
|
1534
|
+
function activeQuantizer(state) {
|
|
1535
|
+
const quantizer = state.quantizer;
|
|
1536
|
+
if (quantizer === void 0 || !quantizer.isCalibrated()) return void 0;
|
|
1537
|
+
return quantizer;
|
|
1538
|
+
}
|
|
1539
|
+
function nodeDistanceByOrd(state, aOrd, bOrd, metric) {
|
|
1540
|
+
if (buildsFromCodes(state) && state.quantizer !== void 0) {
|
|
1541
|
+
const estimated = state.quantizer.distanceBetweenOrdinals(aOrd, bOrd);
|
|
1542
|
+
if (estimated !== Number.POSITIVE_INFINITY) return estimated;
|
|
1543
|
+
}
|
|
1544
|
+
return state.store.distanceByOrdinal(aOrd, bOrd, metric);
|
|
1545
|
+
}
|
|
1546
|
+
function nodeDistanceFunction(state, metric) {
|
|
1547
|
+
const storeDistance = state.store.pairDistance(metric);
|
|
1548
|
+
const quantizer = state.quantizer;
|
|
1549
|
+
if (quantizer === void 0) return storeDistance;
|
|
1550
|
+
const codeDistance = quantizer.pairDistance();
|
|
1551
|
+
return (aOrd, bOrd) => {
|
|
1552
|
+
if (buildsFromCodes(state)) {
|
|
1553
|
+
const estimated = codeDistance(aOrd, bOrd);
|
|
1554
|
+
if (estimated !== Number.POSITIVE_INFINITY) return estimated;
|
|
1555
|
+
}
|
|
1556
|
+
return storeDistance(aOrd, bOrd);
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
function queryDistanceByOrd(state, qVec, qMag, ord, metric) {
|
|
1560
|
+
const entry = state.store.entryForOrdinal(ord);
|
|
1561
|
+
if (!entry) return Number.POSITIVE_INFINITY;
|
|
1562
|
+
return toDistance(qVec, entry.vector, qMag, entry.magnitude, metric);
|
|
1563
|
+
}
|
|
1564
|
+
function maxConns(state, layer) {
|
|
1565
|
+
return layer === 0 ? state.Mmax0 : state.M;
|
|
1566
|
+
}
|
|
1567
|
+
function addConnection(connections, ord) {
|
|
1568
|
+
for (let i = 0; i < connections.length; i++) {
|
|
1569
|
+
if (connections[i] === ord) return;
|
|
1570
|
+
}
|
|
1571
|
+
connections.push(ord);
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
// src/vector/hnsw/workspace.ts
|
|
1575
|
+
function createList(capacity) {
|
|
1576
|
+
return { ords: new Int32Array(capacity), distances: new Float64Array(capacity), size: 0 };
|
|
1577
|
+
}
|
|
1578
|
+
function createHeap(greatestFirst) {
|
|
1579
|
+
return { ...createList(INITIAL_LIST_CAPACITY), greatestFirst, topOrd: -1, topDistance: 0 };
|
|
1580
|
+
}
|
|
1581
|
+
function createHNSWWorkspace() {
|
|
1582
|
+
return {
|
|
1583
|
+
frontier: createHeap(false),
|
|
1584
|
+
found: createHeap(true),
|
|
1585
|
+
entryPoints: new Int32Array(INITIAL_LIST_CAPACITY),
|
|
1586
|
+
entryPointCount: 0,
|
|
1587
|
+
traversal: createList(INITIAL_LIST_CAPACITY),
|
|
1588
|
+
working: createList(INITIAL_LIST_CAPACITY),
|
|
1589
|
+
linkSelections: [],
|
|
1590
|
+
pruneCandidates: createList(INITIAL_LIST_CAPACITY),
|
|
1591
|
+
pruneSelection: createList(INITIAL_LIST_CAPACITY),
|
|
1592
|
+
repairCandidates: createList(INITIAL_LIST_CAPACITY),
|
|
1593
|
+
repairSelection: createList(INITIAL_LIST_CAPACITY)
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
function linkSelectionsFor(workspace, layers) {
|
|
1597
|
+
const selections = workspace.linkSelections;
|
|
1598
|
+
while (selections.length < layers) selections.push(createList(INITIAL_LIST_CAPACITY));
|
|
1599
|
+
return selections;
|
|
1600
|
+
}
|
|
1601
|
+
function ensureListCapacity(list, needed) {
|
|
1602
|
+
if (needed <= list.ords.length) return;
|
|
1603
|
+
let capacity = list.ords.length === 0 ? INITIAL_LIST_CAPACITY : list.ords.length;
|
|
1604
|
+
while (capacity < needed) capacity *= 2;
|
|
1605
|
+
const ords = new Int32Array(capacity);
|
|
1606
|
+
ords.set(list.ords);
|
|
1607
|
+
list.ords = ords;
|
|
1608
|
+
const distances = new Float64Array(capacity);
|
|
1609
|
+
distances.set(list.distances);
|
|
1610
|
+
list.distances = distances;
|
|
1611
|
+
}
|
|
1612
|
+
function appendToList(list, ord, distance) {
|
|
1613
|
+
ensureListCapacity(list, list.size + 1);
|
|
1614
|
+
list.ords[list.size] = ord;
|
|
1615
|
+
list.distances[list.size] = distance;
|
|
1616
|
+
list.size += 1;
|
|
1617
|
+
}
|
|
1618
|
+
function copyList(source, target) {
|
|
1619
|
+
ensureListCapacity(target, source.size);
|
|
1620
|
+
target.ords.set(source.ords.subarray(0, source.size));
|
|
1621
|
+
target.distances.set(source.distances.subarray(0, source.size));
|
|
1622
|
+
target.size = source.size;
|
|
1623
|
+
}
|
|
1624
|
+
function sortListByDistance(list) {
|
|
1625
|
+
const { ords, distances } = list;
|
|
1626
|
+
for (let i = 1; i < list.size; i++) {
|
|
1627
|
+
const ord = ords[i];
|
|
1628
|
+
const distance = distances[i];
|
|
1629
|
+
let j = i - 1;
|
|
1630
|
+
while (j >= 0 && distances[j] > distance) {
|
|
1631
|
+
distances[j + 1] = distances[j];
|
|
1632
|
+
ords[j + 1] = ords[j];
|
|
1633
|
+
j -= 1;
|
|
1634
|
+
}
|
|
1635
|
+
distances[j + 1] = distance;
|
|
1636
|
+
ords[j + 1] = ord;
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
function siftUpGreatestFirst(ords, distances, start, ord, distance) {
|
|
1640
|
+
let index = start;
|
|
1641
|
+
while (index > 0) {
|
|
1642
|
+
const parent = index - 1 >> 1;
|
|
1643
|
+
if (!(distance > distances[parent])) break;
|
|
1644
|
+
ords[index] = ords[parent];
|
|
1645
|
+
distances[index] = distances[parent];
|
|
1646
|
+
index = parent;
|
|
1647
|
+
}
|
|
1648
|
+
ords[index] = ord;
|
|
1649
|
+
distances[index] = distance;
|
|
1650
|
+
}
|
|
1651
|
+
function siftUpLeastFirst(ords, distances, start, ord, distance) {
|
|
1652
|
+
let index = start;
|
|
1653
|
+
while (index > 0) {
|
|
1654
|
+
const parent = index - 1 >> 1;
|
|
1655
|
+
if (!(distance < distances[parent])) break;
|
|
1656
|
+
ords[index] = ords[parent];
|
|
1657
|
+
distances[index] = distances[parent];
|
|
1658
|
+
index = parent;
|
|
1659
|
+
}
|
|
1660
|
+
ords[index] = ord;
|
|
1661
|
+
distances[index] = distance;
|
|
1662
|
+
}
|
|
1663
|
+
function siftDownGreatestFirst(ords, distances, size, ord, distance) {
|
|
1664
|
+
let index = 0;
|
|
1665
|
+
for (; ; ) {
|
|
1666
|
+
const left = 2 * index + 1;
|
|
1667
|
+
const right = left + 1;
|
|
1668
|
+
let first = index;
|
|
1669
|
+
let firstDistance = distance;
|
|
1670
|
+
if (left < size && distances[left] > firstDistance) {
|
|
1671
|
+
first = left;
|
|
1672
|
+
firstDistance = distances[left];
|
|
1673
|
+
}
|
|
1674
|
+
if (right < size && distances[right] > firstDistance) first = right;
|
|
1675
|
+
if (first === index) break;
|
|
1676
|
+
ords[index] = ords[first];
|
|
1677
|
+
distances[index] = distances[first];
|
|
1678
|
+
index = first;
|
|
1679
|
+
}
|
|
1680
|
+
ords[index] = ord;
|
|
1681
|
+
distances[index] = distance;
|
|
1682
|
+
}
|
|
1683
|
+
function siftDownLeastFirst(ords, distances, size, ord, distance) {
|
|
1684
|
+
let index = 0;
|
|
1685
|
+
for (; ; ) {
|
|
1686
|
+
const left = 2 * index + 1;
|
|
1687
|
+
const right = left + 1;
|
|
1688
|
+
let first = index;
|
|
1689
|
+
let firstDistance = distance;
|
|
1690
|
+
if (left < size && distances[left] < firstDistance) {
|
|
1691
|
+
first = left;
|
|
1692
|
+
firstDistance = distances[left];
|
|
1693
|
+
}
|
|
1694
|
+
if (right < size && distances[right] < firstDistance) first = right;
|
|
1695
|
+
if (first === index) break;
|
|
1696
|
+
ords[index] = ords[first];
|
|
1697
|
+
distances[index] = distances[first];
|
|
1698
|
+
index = first;
|
|
1699
|
+
}
|
|
1700
|
+
ords[index] = ord;
|
|
1701
|
+
distances[index] = distance;
|
|
1702
|
+
}
|
|
1703
|
+
function pushHeap(heap, ord, distance) {
|
|
1704
|
+
ensureListCapacity(heap, heap.size + 1);
|
|
1705
|
+
const start = heap.size;
|
|
1706
|
+
heap.size += 1;
|
|
1707
|
+
if (heap.greatestFirst) siftUpGreatestFirst(heap.ords, heap.distances, start, ord, distance);
|
|
1708
|
+
else siftUpLeastFirst(heap.ords, heap.distances, start, ord, distance);
|
|
1709
|
+
}
|
|
1710
|
+
function popHeap(heap) {
|
|
1711
|
+
if (heap.size === 0) return false;
|
|
1712
|
+
heap.topOrd = heap.ords[0];
|
|
1713
|
+
heap.topDistance = heap.distances[0];
|
|
1714
|
+
heap.size -= 1;
|
|
1715
|
+
if (heap.size > 0) {
|
|
1716
|
+
const size = heap.size;
|
|
1717
|
+
const ord = heap.ords[size];
|
|
1718
|
+
const distance = heap.distances[size];
|
|
1719
|
+
if (heap.greatestFirst) siftDownGreatestFirst(heap.ords, heap.distances, size, ord, distance);
|
|
1720
|
+
else siftDownLeastFirst(heap.ords, heap.distances, size, ord, distance);
|
|
1721
|
+
}
|
|
1722
|
+
return true;
|
|
1723
|
+
}
|
|
1724
|
+
function resetHeap(heap) {
|
|
1725
|
+
heap.size = 0;
|
|
1726
|
+
}
|
|
1727
|
+
function drainHeapNearestFirst(heap, list) {
|
|
1728
|
+
const count = heap.size;
|
|
1729
|
+
ensureListCapacity(list, count);
|
|
1730
|
+
list.size = count;
|
|
1731
|
+
for (let i = count - 1; i >= 0; i--) {
|
|
1732
|
+
if (!popHeap(heap)) break;
|
|
1733
|
+
list.ords[i] = heap.topOrd;
|
|
1734
|
+
list.distances[i] = heap.topDistance;
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
function setSingleEntryPoint(workspace, ord) {
|
|
1738
|
+
workspace.entryPoints[0] = ord;
|
|
1739
|
+
workspace.entryPointCount = 1;
|
|
1740
|
+
}
|
|
1741
|
+
function setEntryPointsFromList(workspace, list) {
|
|
1742
|
+
if (list.size > workspace.entryPoints.length) {
|
|
1743
|
+
let capacity = workspace.entryPoints.length;
|
|
1744
|
+
while (capacity < list.size) capacity *= 2;
|
|
1745
|
+
workspace.entryPoints = new Int32Array(capacity);
|
|
1746
|
+
}
|
|
1747
|
+
workspace.entryPoints.set(list.ords.subarray(0, list.size));
|
|
1748
|
+
workspace.entryPointCount = list.size;
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
// src/vector/hnsw/graph-ops.ts
|
|
1752
|
+
function readNeighborsLocked(state, ord, layer) {
|
|
1753
|
+
for (; ; ) {
|
|
1754
|
+
const version = beginNodeRead(state.locks, ord);
|
|
1755
|
+
const count = readNeighbors(state.adjacency, ord, layer, state.neighborScratch);
|
|
1756
|
+
if (nodeReadHeld(state.locks, ord, version)) return count;
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
function searchLayer(state, qVec, qMag, ef, layer, metric, skipTombstones, distFn, results) {
|
|
1760
|
+
const getDistance = distFn ?? ((ord) => queryDistanceByOrd(state, qVec, qMag, ord, metric));
|
|
1761
|
+
const workspace = state.workspace;
|
|
1762
|
+
const frontier = workspace.frontier;
|
|
1763
|
+
const found = workspace.found;
|
|
1764
|
+
const scratch = state.neighborScratch;
|
|
1765
|
+
const stamp = nextVisitStamp(state);
|
|
1766
|
+
resetHeap(frontier);
|
|
1767
|
+
resetHeap(found);
|
|
1768
|
+
let furthestDist = Number.POSITIVE_INFINITY;
|
|
1769
|
+
for (let i = 0; i < workspace.entryPointCount; i++) {
|
|
1770
|
+
const epOrd = workspace.entryPoints[i];
|
|
1771
|
+
ensureVisited(state, epOrd + 1);
|
|
1772
|
+
if (state.visited[epOrd] === stamp) continue;
|
|
1773
|
+
state.visited[epOrd] = stamp;
|
|
1774
|
+
if (!nodeExists(state, epOrd)) continue;
|
|
1775
|
+
if (skipTombstones && isTombstoned(state, epOrd)) continue;
|
|
1776
|
+
const dist = getDistance(epOrd);
|
|
1777
|
+
if (dist === Number.POSITIVE_INFINITY) continue;
|
|
1778
|
+
pushHeap(frontier, epOrd, dist);
|
|
1779
|
+
pushHeap(found, epOrd, dist);
|
|
1780
|
+
if (found.size > ef) {
|
|
1781
|
+
popHeap(found);
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
if (found.size > 0) furthestDist = found.distances[0];
|
|
1785
|
+
while (popHeap(frontier)) {
|
|
1786
|
+
if (frontier.topDistance > furthestDist) break;
|
|
1787
|
+
const count = readNeighborsLocked(state, frontier.topOrd, layer);
|
|
1788
|
+
for (let i = 0; i < count; i++) {
|
|
1789
|
+
const neighborOrd = scratch[i];
|
|
1790
|
+
ensureVisited(state, neighborOrd + 1);
|
|
1791
|
+
if (state.visited[neighborOrd] === stamp) continue;
|
|
1792
|
+
state.visited[neighborOrd] = stamp;
|
|
1793
|
+
if (skipTombstones && isTombstoned(state, neighborOrd)) continue;
|
|
1794
|
+
if (!nodeExists(state, neighborOrd)) continue;
|
|
1795
|
+
const dist = getDistance(neighborOrd);
|
|
1796
|
+
if (dist === Number.POSITIVE_INFINITY) continue;
|
|
1797
|
+
if (dist < furthestDist || found.size < ef) {
|
|
1798
|
+
pushHeap(frontier, neighborOrd, dist);
|
|
1799
|
+
pushHeap(found, neighborOrd, dist);
|
|
1800
|
+
if (found.size > ef) {
|
|
1801
|
+
popHeap(found);
|
|
1802
|
+
}
|
|
1803
|
+
furthestDist = found.distances[0];
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
drainHeapNearestFirst(found, results);
|
|
1808
|
+
}
|
|
1809
|
+
function selectNeighborsHeuristic(state, candidates, maxConnections, distance, selected) {
|
|
1810
|
+
const working = state.workspace.working;
|
|
1811
|
+
copyList(candidates, working);
|
|
1812
|
+
sortListByDistance(working);
|
|
1813
|
+
ensureListCapacity(selected, Math.min(maxConnections, working.size));
|
|
1814
|
+
selected.size = 0;
|
|
1815
|
+
for (let i = 0; i < working.size; i++) {
|
|
1816
|
+
if (selected.size >= maxConnections) break;
|
|
1817
|
+
const candidateOrd = working.ords[i];
|
|
1818
|
+
const candidateDistance = working.distances[i];
|
|
1819
|
+
let accepted = true;
|
|
1820
|
+
for (let s = 0; s < selected.size; s++) {
|
|
1821
|
+
const distBetween = distance(candidateOrd, selected.ords[s]);
|
|
1822
|
+
if (candidateDistance >= distBetween) {
|
|
1823
|
+
accepted = false;
|
|
1824
|
+
break;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
if (!accepted) continue;
|
|
1828
|
+
selected.ords[selected.size] = candidateOrd;
|
|
1829
|
+
selected.distances[selected.size] = candidateDistance;
|
|
1830
|
+
selected.size += 1;
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
function pruneConnections(state, ord, layer, distance) {
|
|
1834
|
+
const adjacency = state.adjacency;
|
|
1835
|
+
const base = layerBase(adjacency, ord, layer);
|
|
1836
|
+
if (base === -1) return;
|
|
1837
|
+
const mc = maxConns(state, layer);
|
|
1838
|
+
const neighbors = layerArray(adjacency, layer);
|
|
1839
|
+
const count = neighbors[base];
|
|
1840
|
+
if (count <= mc) return;
|
|
1841
|
+
const candidates = state.workspace.pruneCandidates;
|
|
1842
|
+
candidates.size = 0;
|
|
1843
|
+
ensureListCapacity(candidates, count);
|
|
1844
|
+
for (let i = 1; i <= count; i++) {
|
|
1845
|
+
const connOrd = neighbors[base + i];
|
|
1846
|
+
const dist = distance(ord, connOrd);
|
|
1847
|
+
if (dist === Number.POSITIVE_INFINITY) continue;
|
|
1848
|
+
candidates.ords[candidates.size] = connOrd;
|
|
1849
|
+
candidates.distances[candidates.size] = dist;
|
|
1850
|
+
candidates.size += 1;
|
|
1851
|
+
}
|
|
1852
|
+
const kept = state.workspace.pruneSelection;
|
|
1853
|
+
selectNeighborsHeuristic(state, candidates, mc, distance, kept);
|
|
1854
|
+
replaceNeighbors(adjacency, ord, layer, kept.ords, kept.size);
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
// src/vector/hnsw/mutation.ts
|
|
1858
|
+
function clearTombstone(state, ord) {
|
|
1859
|
+
if (!reachTombstone(state, ord) || Atomics.load(state.tombstones, ord) !== 1) return;
|
|
1860
|
+
Atomics.sub(state.header, GRAPH_TOMBSTONE_COUNT, 1);
|
|
1861
|
+
Atomics.store(state.tombstones, ord, 0);
|
|
1862
|
+
}
|
|
1863
|
+
function highestNode(state, includeTombstoned) {
|
|
1864
|
+
let bestOrd = -1;
|
|
1865
|
+
let bestLayer = -1;
|
|
1866
|
+
const slots = adjacencySlots(state.adjacency);
|
|
1867
|
+
for (let ord = 0; ord < slots; ord++) {
|
|
1868
|
+
const level2 = nodeMaxLayer(state, ord);
|
|
1869
|
+
if (level2 === -1) continue;
|
|
1870
|
+
if (!includeTombstoned && isTombstoned(state, ord)) continue;
|
|
1871
|
+
if (level2 > bestLayer) {
|
|
1872
|
+
bestLayer = level2;
|
|
1873
|
+
bestOrd = ord;
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
return bestOrd;
|
|
1877
|
+
}
|
|
1878
|
+
function setEntryPoint(state, ord) {
|
|
1879
|
+
Atomics.store(state.header, GRAPH_ENTRY_POINT, ord);
|
|
1880
|
+
Atomics.store(state.header, GRAPH_TOP_LAYER, ord === -1 ? -1 : nodeMaxLayer(state, ord));
|
|
1881
|
+
}
|
|
1882
|
+
function claimEntryIfEmpty(state, ord, level2) {
|
|
1883
|
+
lockEntry(state.locks);
|
|
1884
|
+
try {
|
|
1885
|
+
if (Atomics.load(state.header, GRAPH_ENTRY_POINT) !== -1) return false;
|
|
1886
|
+
Atomics.store(state.header, GRAPH_ENTRY_POINT, ord);
|
|
1887
|
+
Atomics.store(state.header, GRAPH_TOP_LAYER, level2);
|
|
1888
|
+
return true;
|
|
1889
|
+
} finally {
|
|
1890
|
+
unlockEntry(state.locks);
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
function raiseEntry(state, ord, level2) {
|
|
1894
|
+
lockEntry(state.locks);
|
|
1895
|
+
try {
|
|
1896
|
+
if (level2 <= Atomics.load(state.header, GRAPH_TOP_LAYER)) return;
|
|
1897
|
+
Atomics.store(state.header, GRAPH_ENTRY_POINT, ord);
|
|
1898
|
+
Atomics.store(state.header, GRAPH_TOP_LAYER, level2);
|
|
1899
|
+
} finally {
|
|
1900
|
+
unlockEntry(state.locks);
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
function placementDistance(state, ord, vector) {
|
|
1904
|
+
const metric = state.buildMetric;
|
|
1905
|
+
const quantizer = state.quantizer;
|
|
1906
|
+
if (quantizer !== void 0 && buildsFromCodes(state)) {
|
|
1907
|
+
const prepared = quantizer.prepareQuery(vector);
|
|
1908
|
+
if (prepared !== null) return quantizer.preparedDistance(prepared);
|
|
1909
|
+
}
|
|
1910
|
+
if (quantizer === void 0) return state.store.ordinalDistance(ord, metric);
|
|
1911
|
+
return (candOrd) => nodeDistanceByOrd(state, ord, candOrd, metric);
|
|
1912
|
+
}
|
|
1913
|
+
function selectNeighborsPerLayer(state, ord, level2) {
|
|
1914
|
+
const metric = state.buildMetric;
|
|
1915
|
+
const workspace = state.workspace;
|
|
1916
|
+
const candidates = workspace.traversal;
|
|
1917
|
+
const entry = state.store.entryForOrdinal(ord);
|
|
1918
|
+
if (entry === void 0) return -1;
|
|
1919
|
+
const insertDistFn = placementDistance(state, ord, entry.vector);
|
|
1920
|
+
const topLayer = topLayerOf(state);
|
|
1921
|
+
setSingleEntryPoint(workspace, entryPointOf(state));
|
|
1922
|
+
for (let layer = topLayer; layer > level2; layer--) {
|
|
1923
|
+
searchLayer(state, entry.vector, entry.magnitude, 1, layer, metric, false, insertDistFn, candidates);
|
|
1924
|
+
if (candidates.size > 0) setSingleEntryPoint(workspace, candidates.ords[0]);
|
|
1925
|
+
}
|
|
1926
|
+
const linkTop = Math.min(level2, topLayer);
|
|
1927
|
+
const selections = linkSelectionsFor(workspace, linkTop + 1);
|
|
1928
|
+
const distance = nodeDistanceFunction(state, metric);
|
|
1929
|
+
for (let layer = linkTop; layer >= 0; layer--) {
|
|
1930
|
+
searchLayer(state, entry.vector, entry.magnitude, state.efCons, layer, metric, false, insertDistFn, candidates);
|
|
1931
|
+
selectNeighborsHeuristic(state, candidates, maxConns(state, layer), distance, selections[layer]);
|
|
1932
|
+
if (candidates.size > 0) setEntryPointsFromList(workspace, candidates);
|
|
1933
|
+
}
|
|
1934
|
+
return linkTop;
|
|
1935
|
+
}
|
|
1936
|
+
function writeOwnLists(state, ord, linkTop) {
|
|
1937
|
+
const selections = state.workspace.linkSelections;
|
|
1938
|
+
lockNodeWrite(state.locks, ord);
|
|
1939
|
+
try {
|
|
1940
|
+
for (let layer = linkTop; layer >= 0; layer--) {
|
|
1941
|
+
const selected = selections[layer];
|
|
1942
|
+
replaceNeighbors(state.adjacency, ord, layer, selected.ords, selected.size);
|
|
1943
|
+
}
|
|
1944
|
+
} finally {
|
|
1945
|
+
unlockNodeWrite(state.locks, ord);
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
function linkNeighborsBack(state, ord, linkTop) {
|
|
1949
|
+
const distance = nodeDistanceFunction(state, state.buildMetric);
|
|
1950
|
+
const selections = state.workspace.linkSelections;
|
|
1951
|
+
for (let layer = linkTop; layer >= 0; layer--) {
|
|
1952
|
+
const selected = selections[layer];
|
|
1953
|
+
for (let i = 0; i < selected.size; i++) {
|
|
1954
|
+
const neighborOrd = selected.ords[i];
|
|
1955
|
+
if (layer > nodeMaxLayer(state, neighborOrd)) continue;
|
|
1956
|
+
lockNodeWrite(state.locks, neighborOrd);
|
|
1957
|
+
try {
|
|
1958
|
+
addNeighbor(state.adjacency, neighborOrd, layer, ord);
|
|
1959
|
+
pruneConnections(state, neighborOrd, layer, distance);
|
|
1960
|
+
} finally {
|
|
1961
|
+
unlockNodeWrite(state.locks, neighborOrd);
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
function linkNode(state, ord, level2) {
|
|
1967
|
+
const topLayer = topLayerOf(state);
|
|
1968
|
+
const linkTop = selectNeighborsPerLayer(state, ord, level2);
|
|
1969
|
+
if (linkTop < 0) return;
|
|
1970
|
+
writeOwnLists(state, ord, linkTop);
|
|
1971
|
+
linkNeighborsBack(state, ord, linkTop);
|
|
1972
|
+
if (level2 > topLayer) raiseEntry(state, ord, level2);
|
|
1973
|
+
}
|
|
1974
|
+
function writeRecordBeforePlacement(state, ord) {
|
|
1975
|
+
const quantizer = state.quantizer;
|
|
1976
|
+
if (quantizer === void 0 || !buildsFromCodes(state)) return;
|
|
1977
|
+
quantizer.writeCodes(ord);
|
|
1978
|
+
}
|
|
1979
|
+
function placeThroughTheCore(state, ord, holdsGraphLock) {
|
|
1980
|
+
let field = nativeFieldFor(state);
|
|
1981
|
+
if (field === null) return false;
|
|
1982
|
+
const level2 = randomLevel(state.mL);
|
|
1983
|
+
for (let request = 0; request < PLACEMENT_ROOM_REQUESTS && field !== null; request++) {
|
|
1984
|
+
ensureUpperRoom(state.adjacency, level2);
|
|
1985
|
+
const placement = nativePlace(state, field, state.buildMetric, ord, level2, holdsGraphLock);
|
|
1986
|
+
if (placement === "placed") return true;
|
|
1987
|
+
if (placement === "failed") return false;
|
|
1988
|
+
ensureCapacity(state, ord + 1);
|
|
1989
|
+
publishGraphCapacity(state.adjacency.handles);
|
|
1990
|
+
field = nativeFieldFor(state);
|
|
1991
|
+
}
|
|
1992
|
+
return false;
|
|
1993
|
+
}
|
|
1994
|
+
function insertNode(state, ord, holdsGraphLock = false) {
|
|
1995
|
+
if (!state.store.holdsOrdinal(ord)) return false;
|
|
1996
|
+
ensureCapacity(state, ord + 1);
|
|
1997
|
+
if (nodeExists(state, ord)) return false;
|
|
1998
|
+
writeRecordBeforePlacement(state, ord);
|
|
1999
|
+
if (placeThroughTheCore(state, ord, holdsGraphLock)) return true;
|
|
2000
|
+
if (nodeExists(state, ord)) return false;
|
|
2001
|
+
if (!holdsGraphLock) lockGraphShared(state.locks);
|
|
2002
|
+
try {
|
|
2003
|
+
clearTombstone(state, ord);
|
|
2004
|
+
const level2 = randomLevel(state.mL);
|
|
2005
|
+
createNode(state.adjacency, ord, level2);
|
|
2006
|
+
Atomics.add(state.header, GRAPH_NODE_COUNT, 1);
|
|
2007
|
+
if (claimEntryIfEmpty(state, ord, level2)) return true;
|
|
2008
|
+
linkNode(state, ord, level2);
|
|
2009
|
+
} finally {
|
|
2010
|
+
if (!holdsGraphLock) unlockGraphShared(state.locks);
|
|
2011
|
+
}
|
|
2012
|
+
return true;
|
|
2013
|
+
}
|
|
2014
|
+
function removeNodeEager(state, ord, excludeOrds) {
|
|
2015
|
+
const maxLayer = nodeMaxLayer(state, ord);
|
|
2016
|
+
if (maxLayer === -1) return;
|
|
2017
|
+
const distance = nodeDistanceFunction(state, state.buildMetric);
|
|
2018
|
+
for (let layer = 0; layer <= maxLayer; layer++) {
|
|
2019
|
+
const formerNeighbors = collectNeighbors(state.adjacency, ord, layer);
|
|
2020
|
+
for (const neighborOrd of formerNeighbors) {
|
|
2021
|
+
if (layer <= nodeMaxLayer(state, neighborOrd)) {
|
|
2022
|
+
removeNeighbor(state.adjacency, neighborOrd, layer, ord);
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
for (const neighborOrd of formerNeighbors) {
|
|
2026
|
+
if (layer > nodeMaxLayer(state, neighborOrd)) continue;
|
|
2027
|
+
const mc = maxConns(state, layer);
|
|
2028
|
+
if (neighborCount(state.adjacency, neighborOrd, layer) >= mc) continue;
|
|
2029
|
+
const candidateOrds = new Set(collectNeighbors(state.adjacency, neighborOrd, layer));
|
|
2030
|
+
for (const otherOrd of formerNeighbors) {
|
|
2031
|
+
if (otherOrd !== neighborOrd && otherOrd !== ord) {
|
|
2032
|
+
if (excludeOrds?.has(otherOrd)) continue;
|
|
2033
|
+
candidateOrds.add(otherOrd);
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
const candidates = state.workspace.repairCandidates;
|
|
2037
|
+
candidates.size = 0;
|
|
2038
|
+
for (const candOrd of candidateOrds) {
|
|
2039
|
+
const dist = distance(neighborOrd, candOrd);
|
|
2040
|
+
if (dist === Number.POSITIVE_INFINITY) continue;
|
|
2041
|
+
appendToList(candidates, candOrd, dist);
|
|
2042
|
+
}
|
|
2043
|
+
const selected = state.workspace.repairSelection;
|
|
2044
|
+
selectNeighborsHeuristic(state, candidates, mc, distance, selected);
|
|
2045
|
+
replaceNeighbors(state.adjacency, neighborOrd, layer, selected.ords, selected.size);
|
|
2046
|
+
for (let i = 0; i < selected.size; i++) {
|
|
2047
|
+
const newConnOrd = selected.ords[i];
|
|
2048
|
+
if (layer <= nodeMaxLayer(state, newConnOrd)) {
|
|
2049
|
+
addNeighbor(state.adjacency, newConnOrd, layer, neighborOrd);
|
|
2050
|
+
pruneConnections(state, newConnOrd, layer, distance);
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
deleteNode(state.adjacency, ord);
|
|
2056
|
+
Atomics.sub(state.header, GRAPH_NODE_COUNT, 1);
|
|
2057
|
+
clearTombstone(state, ord);
|
|
2058
|
+
if (entryPointOf(state) === ord) {
|
|
2059
|
+
if (Atomics.load(state.header, GRAPH_NODE_COUNT) === 0) {
|
|
2060
|
+
setEntryPoint(state, -1);
|
|
2061
|
+
return;
|
|
2062
|
+
}
|
|
2063
|
+
const live = highestNode(state, false);
|
|
2064
|
+
setEntryPoint(state, live === -1 ? highestNode(state, true) : live);
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
function markTombstone(state, ord) {
|
|
2068
|
+
if (!nodeExists(state, ord) || !reachTombstone(state, ord)) return;
|
|
2069
|
+
const field = nativeFieldFor(state);
|
|
2070
|
+
if (field !== null && nativeRemove(state, field, ord)) return;
|
|
2071
|
+
if (Atomics.compareExchange(state.tombstones, ord, 0, 1) === 0) {
|
|
2072
|
+
Atomics.add(state.header, GRAPH_TOMBSTONE_COUNT, 1);
|
|
2073
|
+
}
|
|
2074
|
+
if (entryPointOf(state) !== ord) return;
|
|
2075
|
+
lockEntry(state.locks);
|
|
2076
|
+
try {
|
|
2077
|
+
if (entryPointOf(state) === ord) setEntryPoint(state, highestNode(state, false));
|
|
2078
|
+
} finally {
|
|
2079
|
+
unlockEntry(state.locks);
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
function compactTombstones(state) {
|
|
2083
|
+
if (Atomics.load(state.header, GRAPH_TOMBSTONE_COUNT) === 0) return;
|
|
2084
|
+
const field = nativeFieldFor(state);
|
|
2085
|
+
if (field !== null && nativeCompact(state, field, state.buildMetric)) return;
|
|
2086
|
+
const tombstonedOrds = [];
|
|
2087
|
+
const slots = adjacencySlots(state.adjacency);
|
|
2088
|
+
for (let ord = 0; ord < slots; ord++) {
|
|
2089
|
+
if (isTombstoned(state, ord) && nodeExists(state, ord)) {
|
|
2090
|
+
tombstonedOrds.push(ord);
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
const excl = new Set(tombstonedOrds);
|
|
2094
|
+
for (const ord of tombstonedOrds) {
|
|
2095
|
+
removeNodeEager(state, ord, excl);
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
function resetGraph(state) {
|
|
2099
|
+
resetAdjacency(state.adjacency);
|
|
2100
|
+
state.tombstones = fixedView(state.adjacency.handles.tombstones, Uint8Array);
|
|
2101
|
+
state.tombstones.fill(0);
|
|
2102
|
+
Atomics.store(state.header, GRAPH_TOMBSTONE_COUNT, 0);
|
|
2103
|
+
Atomics.store(state.header, GRAPH_NODE_COUNT, 0);
|
|
2104
|
+
Atomics.store(state.header, GRAPH_ENTRY_POINT, -1);
|
|
2105
|
+
Atomics.store(state.header, GRAPH_TOP_LAYER, -1);
|
|
2106
|
+
}
|
|
2107
|
+
function rebuild(state) {
|
|
2108
|
+
const nodeCount = Atomics.load(state.header, GRAPH_NODE_COUNT);
|
|
2109
|
+
if (Atomics.load(state.header, GRAPH_TOMBSTONE_COUNT) === 0 && nodeCount === 0) return;
|
|
2110
|
+
const liveOrds = [];
|
|
2111
|
+
const slots = adjacencySlots(state.adjacency);
|
|
2112
|
+
for (let ord = 0; ord < slots; ord++) {
|
|
2113
|
+
if (nodeExists(state, ord) && !isTombstoned(state, ord)) {
|
|
2114
|
+
liveOrds.push(ord);
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
resetGraph(state);
|
|
2118
|
+
for (const ord of liveOrds) {
|
|
2119
|
+
if (!state.store.holdsOrdinal(ord)) continue;
|
|
2120
|
+
insertNode(state, ord, true);
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
// src/vector/ordinal-filter.ts
|
|
2125
|
+
function createOrdinalFilter(slots) {
|
|
2126
|
+
return { bits: new Uint8Array(slots + 7 >>> 3), count: 0 };
|
|
2127
|
+
}
|
|
2128
|
+
function addToOrdinalFilter(filter, ordinal) {
|
|
2129
|
+
const index = ordinal >>> 3;
|
|
2130
|
+
if (index >= filter.bits.length) return;
|
|
2131
|
+
const bit = 1 << (ordinal & 7);
|
|
2132
|
+
if ((filter.bits[index] & bit) !== 0) return;
|
|
2133
|
+
filter.bits[index] |= bit;
|
|
2134
|
+
filter.count += 1;
|
|
2135
|
+
}
|
|
2136
|
+
function ordinalFilterHas(filter, ordinal) {
|
|
2137
|
+
const index = ordinal >>> 3;
|
|
2138
|
+
if (index >= filter.bits.length) return false;
|
|
2139
|
+
return (filter.bits[index] & 1 << (ordinal & 7)) !== 0;
|
|
2140
|
+
}
|
|
2141
|
+
function removeFromOrdinalFilter(filter, ordinal) {
|
|
2142
|
+
const index = ordinal >>> 3;
|
|
2143
|
+
if (index >= filter.bits.length) return;
|
|
2144
|
+
const bit = 1 << (ordinal & 7);
|
|
2145
|
+
if ((filter.bits[index] & bit) === 0) return;
|
|
2146
|
+
filter.bits[index] &= ~bit;
|
|
2147
|
+
filter.count -= 1;
|
|
2148
|
+
}
|
|
2149
|
+
function* ordinalFilterValues(filter) {
|
|
2150
|
+
const bits = filter.bits;
|
|
2151
|
+
for (let index = 0; index < bits.length; index++) {
|
|
2152
|
+
const byte = bits[index];
|
|
2153
|
+
if (byte === 0) continue;
|
|
2154
|
+
for (let offset = 0; offset < 8; offset++) {
|
|
2155
|
+
if ((byte & 1 << offset) !== 0) yield index << 3 | offset;
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
// src/vector/native/walk.ts
|
|
2161
|
+
function copyCandidates(store, count, target) {
|
|
2162
|
+
ensureListCapacity(target, count);
|
|
2163
|
+
target.ords.set(store.ordinals.subarray(0, count));
|
|
2164
|
+
target.distances.set(store.distances.subarray(0, count));
|
|
2165
|
+
target.size = count;
|
|
2166
|
+
}
|
|
2167
|
+
function nativeTraverse(state, field, query, candidateCount, metric, candidates) {
|
|
2168
|
+
const store = field.store;
|
|
2169
|
+
reserveOrdinals(store, candidateCount);
|
|
2170
|
+
let count;
|
|
2171
|
+
try {
|
|
2172
|
+
count = field.core.search(
|
|
2173
|
+
field.graph,
|
|
2174
|
+
store.handle,
|
|
2175
|
+
query,
|
|
2176
|
+
nativeMetricCode(metric),
|
|
2177
|
+
candidateCount,
|
|
2178
|
+
state.locks.threadSlot,
|
|
2179
|
+
store.ordinals,
|
|
2180
|
+
store.distances
|
|
2181
|
+
);
|
|
2182
|
+
} catch (error) {
|
|
2183
|
+
stopUsingTheNativeSearchCore(error);
|
|
2184
|
+
return false;
|
|
2185
|
+
}
|
|
2186
|
+
if (count < 0) return false;
|
|
2187
|
+
copyCandidates(store, count, candidates);
|
|
2188
|
+
return true;
|
|
2189
|
+
}
|
|
2190
|
+
function nativeRescoredHits(store, candidates, query, depth, metric, minSimilarity, filter) {
|
|
2191
|
+
const nearest = Math.min(candidates.size, depth);
|
|
2192
|
+
reserveOrdinals(store, nearest);
|
|
2193
|
+
const ordinals = store.ordinals;
|
|
2194
|
+
let kept = 0;
|
|
2195
|
+
for (let i = 0; i < nearest; i++) {
|
|
2196
|
+
const ord = candidates.ords[i];
|
|
2197
|
+
if (filter && !ordinalFilterHas(filter, ord)) continue;
|
|
2198
|
+
ordinals[kept++] = ord;
|
|
2199
|
+
}
|
|
2200
|
+
const distances = nativeScores(store, query, metric, kept);
|
|
2201
|
+
if (distances === null) return null;
|
|
2202
|
+
const hits = [];
|
|
2203
|
+
for (let i = 0; i < kept; i++) {
|
|
2204
|
+
if (distances[i] === Number.POSITIVE_INFINITY) continue;
|
|
2205
|
+
const score = toScore(distances[i], metric);
|
|
2206
|
+
if (score < minSimilarity) continue;
|
|
2207
|
+
hits.push({ ord: ordinals[i], score });
|
|
2208
|
+
}
|
|
2209
|
+
return hits;
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
// src/vector/hnsw/search.ts
|
|
2213
|
+
function defaultOversample(bits, dimension) {
|
|
2214
|
+
if (bits === 4 && dimension < OSQ4_NARROW_DIMENSION_LIMIT) return DEFAULT_OSQ4_NARROW_OVERSAMPLE;
|
|
2215
|
+
return DEFAULT_OVERSAMPLE_BY_BITS[bits];
|
|
2216
|
+
}
|
|
2217
|
+
function traverse(state, query, k, searchMetric, options) {
|
|
2218
|
+
const liveSize = nodeCountOf(state) - tombstoneCountOf(state);
|
|
2219
|
+
const calibrated = activeQuantizer(state);
|
|
2220
|
+
const quantizer = calibrated === void 0 || calibrated.size === 0 ? void 0 : calibrated;
|
|
2221
|
+
const useQuantized = quantizer !== void 0;
|
|
2222
|
+
const depth = quantizer === void 0 ? k : Math.ceil(k * (options.oversample ?? defaultOversample(quantizer.bits, state.dimension)));
|
|
2223
|
+
let ef = Math.max(options.efSearch ?? DEFAULT_EF_SEARCH, depth);
|
|
2224
|
+
const filter = options.filter;
|
|
2225
|
+
if (filter && filter.count < liveSize) {
|
|
2226
|
+
const selectivity = filter.count / liveSize;
|
|
2227
|
+
ef = Math.max(ef, Math.ceil(k / Math.max(selectivity, 0.01)));
|
|
2228
|
+
ef = Math.min(ef, liveSize);
|
|
2229
|
+
}
|
|
2230
|
+
const workspace = state.workspace;
|
|
2231
|
+
const candidates = workspace.traversal;
|
|
2232
|
+
const native = nativeFieldFor(state);
|
|
2233
|
+
if (native !== null) {
|
|
2234
|
+
candidates.size = 0;
|
|
2235
|
+
if (nativeTraverse(state, native, query, ef, searchMetric, candidates)) {
|
|
2236
|
+
return { candidates, depth: useQuantized ? depth : 0, arenaQuery: null, qMag: 0, native };
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
const store = state.store;
|
|
2240
|
+
const arenaQuery = store.prepareQueryArena(query);
|
|
2241
|
+
const qMag = arenaQuery ? arenaQuery.magnitude : magnitude(query);
|
|
2242
|
+
let distFn;
|
|
2243
|
+
if (quantizer !== void 0) {
|
|
2244
|
+
const prepared = quantizer.prepareQuery(query);
|
|
2245
|
+
if (prepared) distFn = quantizer.preparedDistance(prepared);
|
|
2246
|
+
}
|
|
2247
|
+
if (!distFn && arenaQuery) {
|
|
2248
|
+
distFn = store.queryDistance(arenaQuery, searchMetric);
|
|
2249
|
+
}
|
|
2250
|
+
candidates.size = 0;
|
|
2251
|
+
lockGraphShared(state.locks);
|
|
2252
|
+
try {
|
|
2253
|
+
const entryPoint = entryPointOf(state);
|
|
2254
|
+
if (entryPoint === -1) return { candidates, depth: useQuantized ? depth : 0, arenaQuery, qMag, native: null };
|
|
2255
|
+
setSingleEntryPoint(workspace, entryPoint);
|
|
2256
|
+
for (let layer = topLayerOf(state); layer >= 1; layer--) {
|
|
2257
|
+
searchLayer(state, query, qMag, 1, layer, searchMetric, true, distFn, candidates);
|
|
2258
|
+
if (candidates.size > 0) {
|
|
2259
|
+
setSingleEntryPoint(workspace, candidates.ords[0]);
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
searchLayer(state, query, qMag, ef, 0, searchMetric, true, distFn, candidates);
|
|
2263
|
+
} finally {
|
|
2264
|
+
unlockGraphShared(state.locks);
|
|
2265
|
+
}
|
|
2266
|
+
return { candidates, depth: useQuantized ? depth : 0, arenaQuery, qMag, native: null };
|
|
2267
|
+
}
|
|
2268
|
+
function bestFirst(a, b) {
|
|
2269
|
+
return b.score - a.score || compareCodePoints(a.docId, b.docId);
|
|
2270
|
+
}
|
|
2271
|
+
function resolveEvery(hits, docIdOf) {
|
|
2272
|
+
const resolved = [];
|
|
2273
|
+
for (const hit of hits) {
|
|
2274
|
+
const docId = docIdOf(hit.ord);
|
|
2275
|
+
if (docId !== void 0) resolved.push({ ord: hit.ord, score: hit.score, docId });
|
|
2276
|
+
}
|
|
2277
|
+
return resolved;
|
|
2278
|
+
}
|
|
2279
|
+
function scoresDescend(hits) {
|
|
2280
|
+
for (let i = 1; i < hits.length; i++) {
|
|
2281
|
+
if (!(hits[i].score <= hits[i - 1].score)) return false;
|
|
2282
|
+
}
|
|
2283
|
+
return hits.length === 0 || !Number.isNaN(hits[0].score);
|
|
2284
|
+
}
|
|
2285
|
+
function bestResolvedHits(hits, k, docIdOf) {
|
|
2286
|
+
if (!Number.isInteger(k) || k <= 0) {
|
|
2287
|
+
return resolveEvery(hits, docIdOf).sort(bestFirst).slice(0, k);
|
|
2288
|
+
}
|
|
2289
|
+
let ordered = hits;
|
|
2290
|
+
if (!scoresDescend(ordered)) {
|
|
2291
|
+
if (hits.some((hit) => Number.isNaN(hit.score))) return resolveEvery(hits, docIdOf).sort(bestFirst).slice(0, k);
|
|
2292
|
+
ordered = hits.slice().sort((a, b) => b.score - a.score);
|
|
2293
|
+
}
|
|
2294
|
+
const taken = [];
|
|
2295
|
+
for (const hit of ordered) {
|
|
2296
|
+
if (taken.length >= k && hit.score < taken[k - 1].score) break;
|
|
2297
|
+
const docId = docIdOf(hit.ord);
|
|
2298
|
+
if (docId !== void 0) taken.push({ ord: hit.ord, score: hit.score, docId });
|
|
2299
|
+
}
|
|
2300
|
+
return taken.sort(bestFirst).slice(0, k);
|
|
2301
|
+
}
|
|
2302
|
+
function collectHits(state, query, k, searchMetric, minSimilarity, options) {
|
|
2303
|
+
if (query.length !== state.dimension) {
|
|
2304
|
+
throw new NarsilError(
|
|
2305
|
+
ErrorCodes.VECTOR_DIMENSION_MISMATCH,
|
|
2306
|
+
`Vector dimension mismatch: expected ${state.dimension}, got ${query.length}`,
|
|
2307
|
+
{ expected: state.dimension, received: query.length }
|
|
2308
|
+
);
|
|
2309
|
+
}
|
|
2310
|
+
const liveSize = nodeCountOf(state) - tombstoneCountOf(state);
|
|
2311
|
+
if (entryPointOf(state) === -1 || liveSize === 0) {
|
|
2312
|
+
return [];
|
|
2313
|
+
}
|
|
2314
|
+
const { candidates, depth, arenaQuery, qMag, native } = traverse(state, query, k, searchMetric, options);
|
|
2315
|
+
if (depth > 0) {
|
|
2316
|
+
if (native === null) {
|
|
2317
|
+
return rescoreWithFullPrecision(
|
|
2318
|
+
state,
|
|
2319
|
+
candidates,
|
|
2320
|
+
query,
|
|
2321
|
+
qMag,
|
|
2322
|
+
arenaQuery,
|
|
2323
|
+
depth,
|
|
2324
|
+
searchMetric,
|
|
2325
|
+
minSimilarity,
|
|
2326
|
+
options.filter
|
|
2327
|
+
);
|
|
2328
|
+
}
|
|
2329
|
+
const rescored = nativeRescoredHits(
|
|
2330
|
+
native.store,
|
|
2331
|
+
candidates,
|
|
2332
|
+
query,
|
|
2333
|
+
depth,
|
|
2334
|
+
searchMetric,
|
|
2335
|
+
minSimilarity,
|
|
2336
|
+
options.filter
|
|
2337
|
+
);
|
|
2338
|
+
if (rescored !== null) return rescored;
|
|
2339
|
+
const arena = state.store.prepareQueryArena(query);
|
|
2340
|
+
return rescoreWithFullPrecision(
|
|
2341
|
+
state,
|
|
2342
|
+
candidates,
|
|
2343
|
+
query,
|
|
2344
|
+
arena ? arena.magnitude : magnitude(query),
|
|
2345
|
+
arena,
|
|
2346
|
+
depth,
|
|
2347
|
+
searchMetric,
|
|
2348
|
+
minSimilarity,
|
|
2349
|
+
options.filter
|
|
2350
|
+
);
|
|
2351
|
+
}
|
|
2352
|
+
const hits = [];
|
|
2353
|
+
const filter = options.filter;
|
|
2354
|
+
for (let i = 0; i < candidates.size; i++) {
|
|
2355
|
+
const ord = candidates.ords[i];
|
|
2356
|
+
if (filter && !ordinalFilterHas(filter, ord)) continue;
|
|
2357
|
+
const score = toScore(candidates.distances[i], searchMetric);
|
|
2358
|
+
if (score < minSimilarity) continue;
|
|
2359
|
+
hits.push({ ord, score });
|
|
2360
|
+
}
|
|
2361
|
+
return hits;
|
|
2362
|
+
}
|
|
2363
|
+
function rescoreWithFullPrecision(state, candidates, query, qMag, arenaQuery, depth, metric, minSimilarity, filter) {
|
|
2364
|
+
const rescored = [];
|
|
2365
|
+
const nearest = Math.min(candidates.size, depth);
|
|
2366
|
+
for (let i = 0; i < nearest; i++) {
|
|
2367
|
+
const ord = candidates.ords[i];
|
|
2368
|
+
if (filter && !ordinalFilterHas(filter, ord)) continue;
|
|
2369
|
+
let fullDistance;
|
|
2370
|
+
if (arenaQuery) {
|
|
2371
|
+
fullDistance = state.store.distanceFromArena(arenaQuery, ord, metric);
|
|
2372
|
+
if (fullDistance === Number.POSITIVE_INFINITY) continue;
|
|
2373
|
+
} else {
|
|
2374
|
+
const entry = entryForOrd(state, ord);
|
|
2375
|
+
if (!entry) continue;
|
|
2376
|
+
fullDistance = toDistance(query, entry.vector, qMag, entry.magnitude, metric);
|
|
2377
|
+
}
|
|
2378
|
+
const score = toScore(fullDistance, metric);
|
|
2379
|
+
if (score < minSimilarity) continue;
|
|
2380
|
+
rescored.push({ ord, score });
|
|
2381
|
+
}
|
|
2382
|
+
return rescored;
|
|
2383
|
+
}
|
|
2384
|
+
function search(state, docIdOf, query, k, searchMetric, minSimilarity, options = {}) {
|
|
2385
|
+
const hits = collectHits(state, query, k, searchMetric, minSimilarity, options);
|
|
2386
|
+
return bestResolvedHits(hits, k, docIdOf).map((hit) => ({
|
|
2387
|
+
docId: hit.docId,
|
|
2388
|
+
score: hit.score,
|
|
2389
|
+
termFrequencies: {},
|
|
2390
|
+
fieldLengths: {},
|
|
2391
|
+
idf: {}
|
|
2392
|
+
}));
|
|
2393
|
+
}
|
|
2394
|
+
function searchOrdinals(state, docIdOf, query, k, searchMetric, minSimilarity, options = {}) {
|
|
2395
|
+
const hits = collectHits(state, query, k, searchMetric, minSimilarity, options);
|
|
2396
|
+
return bestResolvedHits(hits, k, docIdOf);
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
// src/vector/hnsw/state.ts
|
|
2400
|
+
function openGraphState(handles, dimension, store, quantizer, threadSlot) {
|
|
2401
|
+
const shape = graphShapeOf(handles.header);
|
|
2402
|
+
const adjacency = openAdjacency(handles, shape.m, shape.mMax0);
|
|
2403
|
+
return {
|
|
2404
|
+
dimension,
|
|
2405
|
+
store,
|
|
2406
|
+
quantizer,
|
|
2407
|
+
adjacency,
|
|
2408
|
+
locks: openGraphLocks(handles, threadSlot),
|
|
2409
|
+
header: handles.header,
|
|
2410
|
+
tombstones: fixedView(handles.tombstones, Uint8Array),
|
|
2411
|
+
visited: new Uint32Array(0),
|
|
2412
|
+
visitStamp: 0,
|
|
2413
|
+
workspace: createHNSWWorkspace(),
|
|
2414
|
+
neighborScratch: new Int32Array(adjacency.level0Stride),
|
|
2415
|
+
M: shape.m,
|
|
2416
|
+
Mmax0: shape.mMax0,
|
|
2417
|
+
efCons: shape.efConstruction,
|
|
2418
|
+
buildMetric: shape.metric,
|
|
2419
|
+
mL: 1 / Math.log(shape.m)
|
|
2420
|
+
};
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
// src/vector/osq/quantize.ts
|
|
2424
|
+
function osqBitsOf(mode) {
|
|
2425
|
+
switch (mode) {
|
|
2426
|
+
case "osq8":
|
|
2427
|
+
return 8;
|
|
2428
|
+
case "osq4":
|
|
2429
|
+
return 4;
|
|
2430
|
+
case "osq2":
|
|
2431
|
+
return 2;
|
|
2432
|
+
case "osq1":
|
|
2433
|
+
return 1;
|
|
2434
|
+
default:
|
|
2435
|
+
return null;
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
function clamp(value, low, high) {
|
|
2439
|
+
return Math.min(Math.max(value, low), high);
|
|
2440
|
+
}
|
|
2441
|
+
function unitNormaliseInto(target, vector) {
|
|
2442
|
+
let squares = 0;
|
|
2443
|
+
for (let i = 0; i < vector.length; i++) squares += vector[i] * vector[i];
|
|
2444
|
+
const length = Math.sqrt(squares);
|
|
2445
|
+
if (length === 0) {
|
|
2446
|
+
for (let i = 0; i < vector.length; i++) target[i] = vector[i];
|
|
2447
|
+
return;
|
|
2448
|
+
}
|
|
2449
|
+
for (let i = 0; i < vector.length; i++) target[i] = vector[i] / length;
|
|
2450
|
+
}
|
|
2451
|
+
function osqCentroid(vectors, dimension, metric) {
|
|
2452
|
+
const sums = new Float64Array(dimension);
|
|
2453
|
+
const scratch = new Float64Array(dimension);
|
|
2454
|
+
let count = 0;
|
|
2455
|
+
for (const vector of vectors) {
|
|
2456
|
+
if (metric === "cosine") {
|
|
2457
|
+
unitNormaliseInto(scratch, vector);
|
|
2458
|
+
for (let i = 0; i < dimension; i++) sums[i] += scratch[i];
|
|
2459
|
+
} else {
|
|
2460
|
+
for (let i = 0; i < dimension; i++) sums[i] += vector[i];
|
|
2461
|
+
}
|
|
2462
|
+
count += 1;
|
|
2463
|
+
}
|
|
2464
|
+
if (count === 0) return null;
|
|
2465
|
+
const centroid = new Float32Array(dimension);
|
|
2466
|
+
if (metric === "cosine") {
|
|
2467
|
+
let squares = 0;
|
|
2468
|
+
for (let i = 0; i < dimension; i++) {
|
|
2469
|
+
sums[i] /= count;
|
|
2470
|
+
squares += sums[i] * sums[i];
|
|
2471
|
+
}
|
|
2472
|
+
const length = Math.sqrt(squares);
|
|
2473
|
+
for (let i = 0; i < dimension; i++) centroid[i] = length === 0 ? sums[i] : sums[i] / length;
|
|
2474
|
+
return centroid;
|
|
2475
|
+
}
|
|
2476
|
+
for (let i = 0; i < dimension; i++) centroid[i] = sums[i] / count;
|
|
2477
|
+
return centroid;
|
|
2478
|
+
}
|
|
2479
|
+
function level(value, lower, upper, steps) {
|
|
2480
|
+
if (upper === lower) return 0;
|
|
2481
|
+
return Math.floor((clamp(value, lower, upper) - lower) * steps / (upper - lower) + 0.5);
|
|
2482
|
+
}
|
|
2483
|
+
function loss(x, lower, upper, steps, norm) {
|
|
2484
|
+
let xe = 0;
|
|
2485
|
+
let e = 0;
|
|
2486
|
+
const width = (upper - lower) / steps;
|
|
2487
|
+
for (let i = 0; i < x.length; i++) {
|
|
2488
|
+
const q = lower + width * level(x[i], lower, upper, steps);
|
|
2489
|
+
xe += x[i] * (x[i] - q);
|
|
2490
|
+
e += (x[i] - q) * (x[i] - q);
|
|
2491
|
+
}
|
|
2492
|
+
return (1 - OSQ_LAMBDA) * xe * xe / norm + OSQ_LAMBDA * e;
|
|
2493
|
+
}
|
|
2494
|
+
function refine(x, lowerStart, upperStart, steps) {
|
|
2495
|
+
let lower = lowerStart;
|
|
2496
|
+
let upper = upperStart;
|
|
2497
|
+
let norm = 0;
|
|
2498
|
+
for (let i = 0; i < x.length; i++) norm += x[i] * x[i];
|
|
2499
|
+
if (norm === 0 || upper === lower) return [lower, upper];
|
|
2500
|
+
let best = loss(x, lower, upper, steps, norm);
|
|
2501
|
+
for (let round = 0; round < OSQ_REFINE_ROUNDS; round++) {
|
|
2502
|
+
let daa = 0;
|
|
2503
|
+
let dab = 0;
|
|
2504
|
+
let dbb = 0;
|
|
2505
|
+
let dax = 0;
|
|
2506
|
+
let dbx = 0;
|
|
2507
|
+
for (let i = 0; i < x.length; i++) {
|
|
2508
|
+
const s = level(x[i], lower, upper, steps) / steps;
|
|
2509
|
+
daa += (1 - s) * (1 - s);
|
|
2510
|
+
dab += (1 - s) * s;
|
|
2511
|
+
dbb += s * s;
|
|
2512
|
+
dax += x[i] * (1 - s);
|
|
2513
|
+
dbx += x[i] * s;
|
|
2514
|
+
}
|
|
2515
|
+
const m0 = (1 - OSQ_LAMBDA) * dax * dax / norm + OSQ_LAMBDA * daa;
|
|
2516
|
+
const m1 = (1 - OSQ_LAMBDA) * dax * dbx / norm + OSQ_LAMBDA * dab;
|
|
2517
|
+
const m2 = (1 - OSQ_LAMBDA) * dbx * dbx / norm + OSQ_LAMBDA * dbb;
|
|
2518
|
+
const det = m0 * m2 - m1 * m1;
|
|
2519
|
+
if (det === 0) return [lower, upper];
|
|
2520
|
+
const a = (m2 * dax - m1 * dbx) / det;
|
|
2521
|
+
const b = (m0 * dbx - m1 * dax) / det;
|
|
2522
|
+
if (Math.abs(a - lower) < OSQ_REFINE_TOLERANCE && Math.abs(b - upper) < OSQ_REFINE_TOLERANCE) return [lower, upper];
|
|
2523
|
+
const candidate = loss(x, a, b, steps, norm);
|
|
2524
|
+
if (candidate > best) return [lower, upper];
|
|
2525
|
+
lower = a;
|
|
2526
|
+
upper = b;
|
|
2527
|
+
best = candidate;
|
|
2528
|
+
}
|
|
2529
|
+
return [lower, upper];
|
|
2530
|
+
}
|
|
2531
|
+
function createOsqScratch(dimension) {
|
|
2532
|
+
return { normalised: new Float64Array(dimension), centred: new Float64Array(dimension) };
|
|
2533
|
+
}
|
|
2534
|
+
function osqQuantize(vector, centroid, bits, metric, scratch) {
|
|
2535
|
+
const dimension = vector.length;
|
|
2536
|
+
const v = scratch.normalised;
|
|
2537
|
+
if (metric === "cosine") unitNormaliseInto(v, vector);
|
|
2538
|
+
else for (let i = 0; i < dimension; i++) v[i] = vector[i];
|
|
2539
|
+
const x = scratch.centred;
|
|
2540
|
+
let correction = 0;
|
|
2541
|
+
let total = 0;
|
|
2542
|
+
let minimum = Number.POSITIVE_INFINITY;
|
|
2543
|
+
let maximum = Number.NEGATIVE_INFINITY;
|
|
2544
|
+
for (let i = 0; i < dimension; i++) {
|
|
2545
|
+
x[i] = v[i] - centroid[i];
|
|
2546
|
+
correction += metric === "euclidean" ? x[i] * x[i] : v[i] * centroid[i];
|
|
2547
|
+
total += x[i];
|
|
2548
|
+
if (x[i] < minimum) minimum = x[i];
|
|
2549
|
+
if (x[i] > maximum) maximum = x[i];
|
|
2550
|
+
}
|
|
2551
|
+
const mean = total / dimension;
|
|
2552
|
+
let spread = 0;
|
|
2553
|
+
for (let i = 0; i < dimension; i++) spread += (x[i] - mean) * (x[i] - mean);
|
|
2554
|
+
const std = Math.sqrt(spread / dimension);
|
|
2555
|
+
const steps = OSQ_STEPS[bits];
|
|
2556
|
+
const [lower, upper] = refine(
|
|
2557
|
+
x,
|
|
2558
|
+
clamp(mean - OSQ_GRID[bits] * std, minimum, maximum),
|
|
2559
|
+
clamp(mean + OSQ_GRID[bits] * std, minimum, maximum),
|
|
2560
|
+
steps
|
|
2561
|
+
);
|
|
2562
|
+
const levels = new Uint8Array(dimension);
|
|
2563
|
+
let sum = 0;
|
|
2564
|
+
for (let i = 0; i < dimension; i++) {
|
|
2565
|
+
levels[i] = level(x[i], lower, upper, steps);
|
|
2566
|
+
sum += levels[i];
|
|
2567
|
+
}
|
|
2568
|
+
return { levels, lower, upper, correction, sum };
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
// src/vector/osq/view.ts
|
|
2572
|
+
function openSharedQuantizer(store, layout, metric) {
|
|
2573
|
+
const { dimension, bits, queryBits, codeBytes, capacity } = layout;
|
|
2574
|
+
const scratch = createOsqScratch(dimension);
|
|
2575
|
+
const documentTrailer = { lower: 0, upper: 0, correction: 0, sum: 0 };
|
|
2576
|
+
const otherTrailer = { lower: 0, upper: 0, correction: 0, sum: 0 };
|
|
2577
|
+
let codePresent = fixedView(store.handles.codePresent, Uint8Array);
|
|
2578
|
+
let centroidDot = 0;
|
|
2579
|
+
let centroidGeneration = -1;
|
|
2580
|
+
let currentQuery = null;
|
|
2581
|
+
const singleOrdinal = new Int32Array(1);
|
|
2582
|
+
function reach(ordinal) {
|
|
2583
|
+
if (ordinal < codePresent.length) return true;
|
|
2584
|
+
if (ordinal >= store.handles.codePresent.byteLength) return false;
|
|
2585
|
+
codePresent = fixedView(store.handles.codePresent, Uint8Array);
|
|
2586
|
+
return ordinal < codePresent.length;
|
|
2587
|
+
}
|
|
2588
|
+
function isCalibrated() {
|
|
2589
|
+
return Atomics.load(store.handles.header, STORE_CALIBRATED) === 1;
|
|
2590
|
+
}
|
|
2591
|
+
function recomputeCentroidDot(generation) {
|
|
2592
|
+
const centroid = store.handles.centroid;
|
|
2593
|
+
let total = 0;
|
|
2594
|
+
for (let i = 0; i < dimension; i++) total += centroid[i] * centroid[i];
|
|
2595
|
+
centroidDot = total;
|
|
2596
|
+
centroidGeneration = generation;
|
|
2597
|
+
}
|
|
2598
|
+
function centroidDotNow() {
|
|
2599
|
+
const generation = Atomics.load(store.handles.header, STORE_CALIBRATION_GENERATION);
|
|
2600
|
+
if (generation !== centroidGeneration) recomputeCentroidDot(generation);
|
|
2601
|
+
return centroidDot;
|
|
2602
|
+
}
|
|
2603
|
+
function holds(ordinal) {
|
|
2604
|
+
return ordinal >= 0 && reach(ordinal) && codePresent[ordinal] === 1;
|
|
2605
|
+
}
|
|
2606
|
+
function recordOffset(ordinal) {
|
|
2607
|
+
return slotByteOffset(layout, ordinal % capacity);
|
|
2608
|
+
}
|
|
2609
|
+
function markPresent(ordinal) {
|
|
2610
|
+
if (Atomics.load(codePresent, ordinal) !== 0) return;
|
|
2611
|
+
Atomics.add(store.handles.header, STORE_CODE_COUNT, 1);
|
|
2612
|
+
Atomics.store(codePresent, ordinal, 1);
|
|
2613
|
+
}
|
|
2614
|
+
function writeCentroid(centroid) {
|
|
2615
|
+
store.handles.centroid.set(centroid);
|
|
2616
|
+
Atomics.add(store.handles.header, STORE_CALIBRATION_GENERATION, 1);
|
|
2617
|
+
Atomics.store(store.handles.header, STORE_CALIBRATED, 1);
|
|
2618
|
+
}
|
|
2619
|
+
function* storedVectors(ordinals) {
|
|
2620
|
+
for (const ordinal of ordinals) {
|
|
2621
|
+
if (store.holdsOrdinal(ordinal)) yield store.vectorAt(ordinal);
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
function writeRecordOf(ordinal) {
|
|
2625
|
+
if (!reach(ordinal) || !store.holdsOrdinal(ordinal)) return;
|
|
2626
|
+
const code = osqQuantize(store.vectorAt(ordinal), store.handles.centroid, bits, metric, scratch);
|
|
2627
|
+
const block = store.codeBlockOf(ordinal);
|
|
2628
|
+
const offset = recordOffset(ordinal);
|
|
2629
|
+
writeRecord(block.bytes(offset + layout.slotStride), offset, code, bits, block.data(offset + layout.slotStride));
|
|
2630
|
+
markPresent(ordinal);
|
|
2631
|
+
}
|
|
2632
|
+
function writeCodesOf(ordinals) {
|
|
2633
|
+
if (!isCalibrated() || ordinals.length === 0) return;
|
|
2634
|
+
if (nativeQuantise(store.handles, metric, ordinals)) return;
|
|
2635
|
+
for (const ordinal of ordinals) writeRecordOf(ordinal);
|
|
2636
|
+
}
|
|
2637
|
+
function stageQuery(block, query) {
|
|
2638
|
+
if (block.simd === null || !block.hasScratch) return false;
|
|
2639
|
+
if (block.stagedOrdinal !== QUERY_STAGED) {
|
|
2640
|
+
block.bytes(block.scratchByteOffset + query.packed.length).set(query.packed, block.scratchByteOffset);
|
|
2641
|
+
block.stagedOrdinal = QUERY_STAGED;
|
|
2642
|
+
}
|
|
2643
|
+
return true;
|
|
2644
|
+
}
|
|
2645
|
+
function simdQueryProducts(simd, documentOffset, queryOffset) {
|
|
2646
|
+
if (bits === 8) return simd.dot_u8(documentOffset, queryOffset, dimension);
|
|
2647
|
+
if (bits === 4) return simd.osq_dot_nibbles_4x4(documentOffset, queryOffset, codeBytes);
|
|
2648
|
+
return simd.osq_dot_bits(documentOffset, queryOffset, codeBytes, bits);
|
|
2649
|
+
}
|
|
2650
|
+
function simdPairProducts(simd, offsetA, offsetB) {
|
|
2651
|
+
if (bits === 8) return simd.dot_u8(offsetA, offsetB, dimension);
|
|
2652
|
+
if (bits === 4) return simd.osq_dot_nibbles_4x4(offsetA, offsetB, codeBytes);
|
|
2653
|
+
return simd.osq_dot_bits_pair(offsetA, offsetB, codeBytes, bits);
|
|
2654
|
+
}
|
|
2655
|
+
function pairProducts(bytesA, offsetA, bytesB, offsetB) {
|
|
2656
|
+
if (bits === 8) return osqByteLevelProducts(bytesA, offsetA, bytesB, offsetB, dimension);
|
|
2657
|
+
if (bits === 4) return osqNibbleProducts(bytesA, offsetA, bytesB, offsetB, codeBytes);
|
|
2658
|
+
return osqNarrowPairProducts(bytesA, offsetA, bytesB, offsetB, bits, codeBytes);
|
|
2659
|
+
}
|
|
2660
|
+
function productsAgainstQuery(block, documentOffset, query) {
|
|
2661
|
+
if (block.simd !== null && stageQuery(block, query)) {
|
|
2662
|
+
return simdQueryProducts(block.simd, documentOffset, block.scratchByteOffset);
|
|
2663
|
+
}
|
|
2664
|
+
const bytes = block.bytes(documentOffset + codeBytes);
|
|
2665
|
+
if (bits === 8) return osqByteLevelProducts(bytes, documentOffset, query.packed, 0, dimension);
|
|
2666
|
+
if (bits === 4) return osqNibbleProducts(bytes, documentOffset, query.packed, 0, codeBytes);
|
|
2667
|
+
return osqStagedQueryProducts(bytes, documentOffset, bits, query.packed, 0, codeBytes);
|
|
2668
|
+
}
|
|
2669
|
+
function estimateDistance(ordinal, query) {
|
|
2670
|
+
const block = store.codeBlockOf(ordinal);
|
|
2671
|
+
const offset = recordOffset(ordinal);
|
|
2672
|
+
const products = productsAgainstQuery(block, offset, query);
|
|
2673
|
+
readTrailer(block.data(offset + layout.slotStride), offset, codeBytes, documentTrailer);
|
|
2674
|
+
return osqDistance(
|
|
2675
|
+
osqEstimate(products, documentTrailer, bits, query, queryBits, dimension, centroidDotNow(), metric),
|
|
2676
|
+
metric
|
|
2677
|
+
);
|
|
2678
|
+
}
|
|
2679
|
+
function prepareQuery(query) {
|
|
2680
|
+
if (!isCalibrated() || query.length !== dimension) return null;
|
|
2681
|
+
const code = osqQuantize(query, store.handles.centroid, queryBits, metric, scratch);
|
|
2682
|
+
const packed = new Uint8Array(layout.queryBytes);
|
|
2683
|
+
stageQueryLevels(code.levels, bits, packed);
|
|
2684
|
+
for (const block of store.openCodeBlocks()) block.stagedOrdinal = NOTHING_STAGED;
|
|
2685
|
+
currentQuery = {
|
|
2686
|
+
packed,
|
|
2687
|
+
lower: code.lower,
|
|
2688
|
+
upper: code.upper,
|
|
2689
|
+
correction: code.correction,
|
|
2690
|
+
sum: code.sum
|
|
2691
|
+
};
|
|
2692
|
+
return currentQuery;
|
|
2693
|
+
}
|
|
2694
|
+
function distanceBetween(ordA, ordB) {
|
|
2695
|
+
if (!holds(ordA) || !holds(ordB)) return Number.POSITIVE_INFINITY;
|
|
2696
|
+
const blockA = store.codeBlockOf(ordA);
|
|
2697
|
+
const blockB = store.codeBlockOf(ordB);
|
|
2698
|
+
const offsetA = recordOffset(ordA);
|
|
2699
|
+
const offsetB = recordOffset(ordB);
|
|
2700
|
+
let products;
|
|
2701
|
+
if (blockA === blockB && blockA.simd !== null) {
|
|
2702
|
+
products = simdPairProducts(blockA.simd, offsetA, offsetB);
|
|
2703
|
+
} else {
|
|
2704
|
+
products = pairProducts(blockA.bytes(offsetA + codeBytes), offsetA, blockB.bytes(offsetB + codeBytes), offsetB);
|
|
2705
|
+
}
|
|
2706
|
+
readTrailer(blockA.data(offsetA + layout.slotStride), offsetA, codeBytes, documentTrailer);
|
|
2707
|
+
readTrailer(blockB.data(offsetB + layout.slotStride), offsetB, codeBytes, otherTrailer);
|
|
2708
|
+
return osqDistance(
|
|
2709
|
+
osqEstimate(products, documentTrailer, bits, otherTrailer, bits, dimension, centroidDotNow(), metric),
|
|
2710
|
+
metric
|
|
2711
|
+
);
|
|
2712
|
+
}
|
|
2713
|
+
return {
|
|
2714
|
+
bits,
|
|
2715
|
+
metric,
|
|
2716
|
+
get size() {
|
|
2717
|
+
return Atomics.load(store.handles.header, STORE_CODE_COUNT);
|
|
2718
|
+
},
|
|
2719
|
+
get centroid() {
|
|
2720
|
+
return isCalibrated() ? store.handles.centroid : null;
|
|
2721
|
+
},
|
|
2722
|
+
isCalibrated,
|
|
2723
|
+
holdsOrdinal: holds,
|
|
2724
|
+
writeCodes(ordinal) {
|
|
2725
|
+
singleOrdinal[0] = ordinal;
|
|
2726
|
+
writeCodesOf(singleOrdinal);
|
|
2727
|
+
},
|
|
2728
|
+
writeCodesOf,
|
|
2729
|
+
calibrateFrom(ordinals) {
|
|
2730
|
+
if (nativeCalibrate(store.handles, metric, ordinals)) return isCalibrated();
|
|
2731
|
+
const centroid = osqCentroid(storedVectors(ordinals), dimension, metric);
|
|
2732
|
+
if (centroid === null) return false;
|
|
2733
|
+
writeCentroid(centroid);
|
|
2734
|
+
return true;
|
|
2735
|
+
},
|
|
2736
|
+
restoreRecord(ordinal, record) {
|
|
2737
|
+
if (!reach(ordinal) || record.length !== layout.slotStride) return;
|
|
2738
|
+
const block = store.codeBlockOf(ordinal);
|
|
2739
|
+
const offset = recordOffset(ordinal);
|
|
2740
|
+
block.bytes(offset + layout.slotStride).set(record, offset);
|
|
2741
|
+
markPresent(ordinal);
|
|
2742
|
+
},
|
|
2743
|
+
recordAt(ordinal) {
|
|
2744
|
+
const block = store.codeBlockOf(ordinal);
|
|
2745
|
+
const offset = recordOffset(ordinal);
|
|
2746
|
+
return block.bytes(offset + layout.slotStride).subarray(offset, offset + layout.slotStride);
|
|
2747
|
+
},
|
|
2748
|
+
clearCodes(ordinal) {
|
|
2749
|
+
if (ordinal < 0 || !reach(ordinal)) return;
|
|
2750
|
+
if (Atomics.exchange(codePresent, ordinal, 0) === 1) Atomics.sub(store.handles.header, STORE_CODE_COUNT, 1);
|
|
2751
|
+
},
|
|
2752
|
+
writeCentroid,
|
|
2753
|
+
resetCalibration() {
|
|
2754
|
+
Atomics.store(store.handles.header, STORE_CALIBRATED, 0);
|
|
2755
|
+
store.handles.centroid.fill(0);
|
|
2756
|
+
Atomics.add(store.handles.header, STORE_CALIBRATION_GENERATION, 1);
|
|
2757
|
+
},
|
|
2758
|
+
resetCodes() {
|
|
2759
|
+
codePresent = fixedView(store.handles.codePresent, Uint8Array);
|
|
2760
|
+
codePresent.fill(0);
|
|
2761
|
+
Atomics.store(store.handles.header, STORE_CODE_COUNT, 0);
|
|
2762
|
+
},
|
|
2763
|
+
prepareQuery,
|
|
2764
|
+
distanceFromPreparedByOrdinal(prepared, ordinal) {
|
|
2765
|
+
if (!holds(ordinal)) return Number.POSITIVE_INFINITY;
|
|
2766
|
+
return estimateDistance(ordinal, prepared);
|
|
2767
|
+
},
|
|
2768
|
+
preparedDistance(prepared) {
|
|
2769
|
+
const generic = (ordinal) => holds(ordinal) ? estimateDistance(ordinal, prepared) : Number.POSITIVE_INFINITY;
|
|
2770
|
+
if (store.handles.codeBlocks.length === 0) return generic;
|
|
2771
|
+
const block = store.codeBlockOf(0);
|
|
2772
|
+
const simd = block.simd;
|
|
2773
|
+
if (simd === null || !block.hasScratch) return generic;
|
|
2774
|
+
const presentView = codePresent;
|
|
2775
|
+
const limit = Math.min(presentView.length, capacity);
|
|
2776
|
+
const data = block.data(0);
|
|
2777
|
+
const dataLength = data.byteLength;
|
|
2778
|
+
const queryOffset = block.scratchByteOffset;
|
|
2779
|
+
const { slotsOffset, slotStride } = layout;
|
|
2780
|
+
return (ordinal) => {
|
|
2781
|
+
if (ordinal < 0 || ordinal >= limit || block.stagedOrdinal !== QUERY_STAGED) return generic(ordinal);
|
|
2782
|
+
if (presentView[ordinal] !== 1) return Number.POSITIVE_INFINITY;
|
|
2783
|
+
const offset = slotsOffset + ordinal * slotStride;
|
|
2784
|
+
if (offset + slotStride > dataLength) return generic(ordinal);
|
|
2785
|
+
const products = simdQueryProducts(simd, offset, queryOffset);
|
|
2786
|
+
readTrailer(data, offset, codeBytes, documentTrailer);
|
|
2787
|
+
const estimate = osqEstimate(
|
|
2788
|
+
products,
|
|
2789
|
+
documentTrailer,
|
|
2790
|
+
bits,
|
|
2791
|
+
prepared,
|
|
2792
|
+
queryBits,
|
|
2793
|
+
dimension,
|
|
2794
|
+
centroidDotNow(),
|
|
2795
|
+
metric
|
|
2796
|
+
);
|
|
2797
|
+
return osqDistance(estimate, metric);
|
|
2798
|
+
};
|
|
2799
|
+
},
|
|
2800
|
+
distanceBetweenOrdinals: distanceBetween,
|
|
2801
|
+
pairDistance() {
|
|
2802
|
+
if (store.handles.codeBlocks.length === 0) return distanceBetween;
|
|
2803
|
+
const block = store.codeBlockOf(0);
|
|
2804
|
+
const simd = block.simd;
|
|
2805
|
+
if (simd === null) return distanceBetween;
|
|
2806
|
+
const presentView = codePresent;
|
|
2807
|
+
const limit = Math.min(presentView.length, capacity);
|
|
2808
|
+
const data = block.data(0);
|
|
2809
|
+
const dataLength = data.byteLength;
|
|
2810
|
+
const { slotsOffset, slotStride } = layout;
|
|
2811
|
+
return (ordA, ordB) => {
|
|
2812
|
+
if (ordA < 0 || ordB < 0 || ordA >= limit || ordB >= limit) return distanceBetween(ordA, ordB);
|
|
2813
|
+
if (presentView[ordA] !== 1 || presentView[ordB] !== 1) return Number.POSITIVE_INFINITY;
|
|
2814
|
+
const offsetA = slotsOffset + ordA * slotStride;
|
|
2815
|
+
const offsetB = slotsOffset + ordB * slotStride;
|
|
2816
|
+
if (Math.max(offsetA, offsetB) + slotStride > dataLength) return distanceBetween(ordA, ordB);
|
|
2817
|
+
const products = simdPairProducts(simd, offsetA, offsetB);
|
|
2818
|
+
readTrailer(data, offsetA, codeBytes, documentTrailer);
|
|
2819
|
+
readTrailer(data, offsetB, codeBytes, otherTrailer);
|
|
2820
|
+
const estimate = osqEstimate(
|
|
2821
|
+
products,
|
|
2822
|
+
documentTrailer,
|
|
2823
|
+
bits,
|
|
2824
|
+
otherTrailer,
|
|
2825
|
+
bits,
|
|
2826
|
+
dimension,
|
|
2827
|
+
centroidDotNow(),
|
|
2828
|
+
metric
|
|
2829
|
+
);
|
|
2830
|
+
return osqDistance(estimate, metric);
|
|
2831
|
+
};
|
|
2832
|
+
}
|
|
2833
|
+
};
|
|
2834
|
+
}
|
|
2835
|
+
function createVectorFileReader() {
|
|
2836
|
+
const descriptors = /* @__PURE__ */ new Map();
|
|
2837
|
+
function descriptorOf(path) {
|
|
2838
|
+
const open = descriptors.get(path);
|
|
2839
|
+
if (open !== void 0) return open;
|
|
2840
|
+
try {
|
|
2841
|
+
const descriptor = openSync(path, "r");
|
|
2842
|
+
descriptors.set(path, descriptor);
|
|
2843
|
+
return descriptor;
|
|
2844
|
+
} catch {
|
|
2845
|
+
return null;
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
function closeDescriptor(path, descriptor) {
|
|
2849
|
+
descriptors.delete(path);
|
|
2850
|
+
try {
|
|
2851
|
+
closeSync(descriptor);
|
|
2852
|
+
} catch {
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
return {
|
|
2856
|
+
readInto(path, offset, target) {
|
|
2857
|
+
const descriptor = descriptorOf(path);
|
|
2858
|
+
if (descriptor === null) return false;
|
|
2859
|
+
let read = 0;
|
|
2860
|
+
while (read < target.byteLength) {
|
|
2861
|
+
let chunk;
|
|
2862
|
+
try {
|
|
2863
|
+
chunk = readSync(descriptor, target, read, target.byteLength - read, offset + read);
|
|
2864
|
+
} catch {
|
|
2865
|
+
closeDescriptor(path, descriptor);
|
|
2866
|
+
return false;
|
|
2867
|
+
}
|
|
2868
|
+
if (chunk <= 0) return false;
|
|
2869
|
+
read += chunk;
|
|
2870
|
+
}
|
|
2871
|
+
return true;
|
|
2872
|
+
},
|
|
2873
|
+
retainOnly(paths) {
|
|
2874
|
+
for (const [path, descriptor] of descriptors) {
|
|
2875
|
+
if (!paths.has(path)) closeDescriptor(path, descriptor);
|
|
2876
|
+
}
|
|
2877
|
+
},
|
|
2878
|
+
close() {
|
|
2879
|
+
for (const [path, descriptor] of descriptors) closeDescriptor(path, descriptor);
|
|
2880
|
+
}
|
|
2881
|
+
};
|
|
2882
|
+
}
|
|
2883
|
+
|
|
2884
|
+
// src/vector/vector-store/hot-block-distance.ts
|
|
2885
|
+
function jsDistance(a, b, magA, magB, metric) {
|
|
2886
|
+
switch (metric) {
|
|
2887
|
+
case "cosine":
|
|
2888
|
+
return 1 - cosineSimilarityWithMagnitudes(a, b, magA, magB);
|
|
2889
|
+
case "dotProduct":
|
|
2890
|
+
return -dotProduct(a, b);
|
|
2891
|
+
case "euclidean":
|
|
2892
|
+
return euclideanDistance(a, b);
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
function inMemory(reads, ordinal) {
|
|
2896
|
+
return ordinal >= 0 && ordinal < reads.limit && reads.diskFile[ordinal] === IN_MEMORY;
|
|
2897
|
+
}
|
|
2898
|
+
function queryDistanceOf(source, prepared, metric) {
|
|
2899
|
+
const generic = (ordinal) => source.distanceFromArena(prepared, ordinal, metric);
|
|
2900
|
+
const reads = source.hotBlockReads();
|
|
2901
|
+
const simd = reads?.block.simd ?? null;
|
|
2902
|
+
if (reads === null || simd === null || !reads.block.hasScratch) return generic;
|
|
2903
|
+
const { block, present, magnitudes, slotsOffset, slotStride, dimension } = reads;
|
|
2904
|
+
const scratchOffset = block.scratchByteOffset;
|
|
2905
|
+
const queryMagnitude = prepared.magnitude;
|
|
2906
|
+
return (ordinal) => {
|
|
2907
|
+
if (!inMemory(reads, ordinal) || block.stagedOrdinal !== QUERY_STAGED) return generic(ordinal);
|
|
2908
|
+
if (present[ordinal] !== 1) return Number.POSITIVE_INFINITY;
|
|
2909
|
+
const slotOffset = slotsOffset + ordinal * slotStride;
|
|
2910
|
+
return arenaFloat32Distance(simd, scratchOffset, slotOffset, dimension, metric, queryMagnitude, magnitudes[ordinal]);
|
|
2911
|
+
};
|
|
2912
|
+
}
|
|
2913
|
+
function pairDistanceOf(source, metric) {
|
|
2914
|
+
const generic = (ordA, ordB) => source.distanceByOrdinal(ordA, ordB, metric);
|
|
2915
|
+
const reads = source.hotBlockReads();
|
|
2916
|
+
const simd = reads?.block.simd ?? null;
|
|
2917
|
+
if (reads === null || simd === null) return generic;
|
|
2918
|
+
const { present, magnitudes, slotsOffset, slotStride, dimension } = reads;
|
|
2919
|
+
return (ordA, ordB) => {
|
|
2920
|
+
if (!inMemory(reads, ordA) || !inMemory(reads, ordB) || present[ordA] !== 1) return generic(ordA, ordB);
|
|
2921
|
+
if (present[ordB] !== 1) return Number.POSITIVE_INFINITY;
|
|
2922
|
+
const offsetA = slotsOffset + ordA * slotStride;
|
|
2923
|
+
const offsetB = slotsOffset + ordB * slotStride;
|
|
2924
|
+
return arenaFloat32Distance(simd, offsetA, offsetB, dimension, metric, magnitudes[ordA], magnitudes[ordB]);
|
|
2925
|
+
};
|
|
2926
|
+
}
|
|
2927
|
+
function ordinalDistanceOf(source, from, metric) {
|
|
2928
|
+
const pair = pairDistanceOf(source, metric);
|
|
2929
|
+
return (ordinal) => pair(from, ordinal);
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2932
|
+
// src/vector/vector-store/view.ts
|
|
2933
|
+
var decoder = new TextDecoder();
|
|
2934
|
+
var NOT_HELD = 0;
|
|
2935
|
+
var HELD_COLD = 1;
|
|
2936
|
+
var HELD_HOT = 2;
|
|
2937
|
+
function openSharedVectorStore(initial, threadSlot) {
|
|
2938
|
+
let handles = initial;
|
|
2939
|
+
const { dimension, layout } = initial;
|
|
2940
|
+
const capacity = layout.capacity;
|
|
2941
|
+
const codeCapacity = initial.codeLayout?.capacity ?? 1;
|
|
2942
|
+
const blocks = [];
|
|
2943
|
+
const codeBlocks = [];
|
|
2944
|
+
let present = fixedView(handles.present, Uint8Array);
|
|
2945
|
+
let magnitudes = fixedView(handles.magnitudes, Float64Array);
|
|
2946
|
+
let partitions = fixedView(handles.partitions, Int32Array);
|
|
2947
|
+
let docIdBytes = fixedView(handles.docIdBytes, Uint8Array);
|
|
2948
|
+
let docIdOffsets = fixedView(handles.docIdOffsets, Uint32Array);
|
|
2949
|
+
let diskFile = fixedView(handles.diskFile, Int32Array);
|
|
2950
|
+
let diskOffset = fixedView(handles.diskOffset, Uint32Array);
|
|
2951
|
+
let currentQuery = null;
|
|
2952
|
+
let reader = null;
|
|
2953
|
+
const coldScratchA = new Float32Array(dimension);
|
|
2954
|
+
const coldScratchB = new Float32Array(dimension);
|
|
2955
|
+
function rebind() {
|
|
2956
|
+
present = fixedView(handles.present, Uint8Array);
|
|
2957
|
+
magnitudes = fixedView(handles.magnitudes, Float64Array);
|
|
2958
|
+
partitions = fixedView(handles.partitions, Int32Array);
|
|
2959
|
+
docIdBytes = fixedView(handles.docIdBytes, Uint8Array);
|
|
2960
|
+
docIdOffsets = fixedView(handles.docIdOffsets, Uint32Array);
|
|
2961
|
+
diskFile = fixedView(handles.diskFile, Int32Array);
|
|
2962
|
+
diskOffset = fixedView(handles.diskOffset, Uint32Array);
|
|
2963
|
+
}
|
|
2964
|
+
function blockAt(index) {
|
|
2965
|
+
let block = blocks[index];
|
|
2966
|
+
if (block === void 0) {
|
|
2967
|
+
const handle = handles.blocks[index];
|
|
2968
|
+
if (handle === null || handle === void 0) {
|
|
2969
|
+
throw new Error(`Vector block ${index} holds no vectors on this thread`);
|
|
2970
|
+
}
|
|
2971
|
+
block = openVectorBlock(handle, threadSlot);
|
|
2972
|
+
blocks[index] = block;
|
|
2973
|
+
}
|
|
2974
|
+
return block;
|
|
2975
|
+
}
|
|
2976
|
+
function codeBlockAt(index) {
|
|
2977
|
+
let block = codeBlocks[index];
|
|
2978
|
+
if (block === void 0) {
|
|
2979
|
+
block = openVectorBlock(handles.codeBlocks[index], threadSlot);
|
|
2980
|
+
codeBlocks[index] = block;
|
|
2981
|
+
}
|
|
2982
|
+
return block;
|
|
2983
|
+
}
|
|
2984
|
+
function fileOf(ordinal) {
|
|
2985
|
+
if (ordinal >= diskFile.length) {
|
|
2986
|
+
if (ordinal * 4 >= handles.diskFile.byteLength) return IN_MEMORY;
|
|
2987
|
+
rebind();
|
|
2988
|
+
}
|
|
2989
|
+
return diskFile[ordinal];
|
|
2990
|
+
}
|
|
2991
|
+
function isCold(ordinal) {
|
|
2992
|
+
return fileOf(ordinal) !== IN_MEMORY;
|
|
2993
|
+
}
|
|
2994
|
+
function holdState(ordinal) {
|
|
2995
|
+
if (ordinal < 0) return NOT_HELD;
|
|
2996
|
+
if (ordinal >= present.length) {
|
|
2997
|
+
if (ordinal >= handles.present.byteLength) return NOT_HELD;
|
|
2998
|
+
rebind();
|
|
2999
|
+
}
|
|
3000
|
+
if (present[ordinal] !== 1) return NOT_HELD;
|
|
3001
|
+
if (isCold(ordinal)) return HELD_COLD;
|
|
3002
|
+
const index = Math.floor(ordinal / capacity);
|
|
3003
|
+
return index < handles.blocks.length && handles.blocks[index] !== null ? HELD_HOT : NOT_HELD;
|
|
3004
|
+
}
|
|
3005
|
+
function holds(ordinal) {
|
|
3006
|
+
return holdState(ordinal) !== NOT_HELD;
|
|
3007
|
+
}
|
|
3008
|
+
function readFromFile(file, offset, target) {
|
|
3009
|
+
const path = handles.vectorFiles[file];
|
|
3010
|
+
if (path === void 0 || path.length === 0) return false;
|
|
3011
|
+
if (reader === null) reader = createVectorFileReader();
|
|
3012
|
+
const bytes = new Uint8Array(target.buffer, target.byteOffset, target.byteLength);
|
|
3013
|
+
return reader.readInto(path, offset, bytes);
|
|
3014
|
+
}
|
|
3015
|
+
function readColdInto(ordinal, target) {
|
|
3016
|
+
const file = fileOf(ordinal);
|
|
3017
|
+
if (file === IN_MEMORY) return false;
|
|
3018
|
+
if (ordinal >= diskOffset.length) rebind();
|
|
3019
|
+
return readFromFile(file, diskOffset[ordinal], target);
|
|
3020
|
+
}
|
|
3021
|
+
function vectorIn(block, local) {
|
|
3022
|
+
const base = slotByteOffset(layout, local) / 4;
|
|
3023
|
+
return block.float32(base + dimension).subarray(base, base + dimension);
|
|
3024
|
+
}
|
|
3025
|
+
function hotVector(ordinal) {
|
|
3026
|
+
return vectorIn(blockAt(ordinal / capacity | 0), ordinal % capacity);
|
|
3027
|
+
}
|
|
3028
|
+
function vectorFor(ordinal, scratch) {
|
|
3029
|
+
if (!isCold(ordinal)) return hotVector(ordinal);
|
|
3030
|
+
return readColdInto(ordinal, scratch) ? scratch : null;
|
|
3031
|
+
}
|
|
3032
|
+
function stageVector(block, ordinal) {
|
|
3033
|
+
if (block.stagedOrdinal === ordinal) return true;
|
|
3034
|
+
const source = vectorFor(ordinal, coldScratchA);
|
|
3035
|
+
if (source === null) return false;
|
|
3036
|
+
block.float32(0).set(source, block.scratchByteOffset / 4);
|
|
3037
|
+
block.stagedOrdinal = ordinal;
|
|
3038
|
+
return true;
|
|
3039
|
+
}
|
|
3040
|
+
function hotBlockReads() {
|
|
3041
|
+
const handle = handles.blocks[0];
|
|
3042
|
+
if (handle === null || handle === void 0) return null;
|
|
3043
|
+
const { slotsOffset, slotStride } = layout;
|
|
3044
|
+
const limit = Math.min(present.length, diskFile.length, magnitudes.length, capacity);
|
|
3045
|
+
return { block: blockAt(0), present, diskFile, magnitudes, limit, slotsOffset, slotStride, dimension };
|
|
3046
|
+
}
|
|
3047
|
+
function stageQuery(block) {
|
|
3048
|
+
if (currentQuery === null) return false;
|
|
3049
|
+
if (block.stagedOrdinal !== QUERY_STAGED) {
|
|
3050
|
+
block.float32(0).set(currentQuery, block.scratchByteOffset / 4);
|
|
3051
|
+
block.stagedOrdinal = QUERY_STAGED;
|
|
3052
|
+
}
|
|
3053
|
+
return true;
|
|
3054
|
+
}
|
|
3055
|
+
const view = {
|
|
3056
|
+
dimension,
|
|
3057
|
+
threadSlot,
|
|
3058
|
+
get handles() {
|
|
3059
|
+
return handles;
|
|
3060
|
+
},
|
|
3061
|
+
get slots() {
|
|
3062
|
+
return Atomics.load(handles.header, STORE_SLOTS);
|
|
3063
|
+
},
|
|
3064
|
+
get simdAvailable() {
|
|
3065
|
+
const first = handles.blocks.find((block2) => block2 !== null);
|
|
3066
|
+
if (first === void 0) return false;
|
|
3067
|
+
const block = blockAt(handles.blocks.indexOf(first));
|
|
3068
|
+
return block.simd !== null && block.hasScratch;
|
|
3069
|
+
},
|
|
3070
|
+
get holdsEveryBlock() {
|
|
3071
|
+
return Atomics.load(handles.header, STORE_BLOCK_COUNT) <= handles.blocks.length && Atomics.load(handles.header, STORE_CODE_BLOCK_COUNT) <= handles.codeBlocks.length;
|
|
3072
|
+
},
|
|
3073
|
+
adoptHandles(next) {
|
|
3074
|
+
detachNativeStore(handles);
|
|
3075
|
+
handles = next;
|
|
3076
|
+
rebind();
|
|
3077
|
+
for (let index = 0; index < next.blocks.length; index++) {
|
|
3078
|
+
if (next.blocks[index] === null) blocks[index] = void 0;
|
|
3079
|
+
else if (blocks[index] === void 0) blockAt(index);
|
|
3080
|
+
}
|
|
3081
|
+
for (let index = codeBlocks.length; index < next.codeBlocks.length; index++) codeBlockAt(index);
|
|
3082
|
+
reader?.retainOnly(new Set(next.vectorFiles.filter((path) => path.length > 0)));
|
|
3083
|
+
},
|
|
3084
|
+
rebind,
|
|
3085
|
+
holdsOrdinal: holds,
|
|
3086
|
+
isCold,
|
|
3087
|
+
readColdInto,
|
|
3088
|
+
readFromFile,
|
|
3089
|
+
close() {
|
|
3090
|
+
detachNativeStore(handles);
|
|
3091
|
+
reader?.close();
|
|
3092
|
+
reader = null;
|
|
3093
|
+
},
|
|
3094
|
+
blockOf(ordinal) {
|
|
3095
|
+
return blockAt(ordinal / capacity | 0);
|
|
3096
|
+
},
|
|
3097
|
+
localOrdinal(ordinal) {
|
|
3098
|
+
return ordinal % capacity;
|
|
3099
|
+
},
|
|
3100
|
+
codeBlockOf(ordinal) {
|
|
3101
|
+
return codeBlockAt(ordinal / codeCapacity | 0);
|
|
3102
|
+
},
|
|
3103
|
+
codeLocalOrdinal(ordinal) {
|
|
3104
|
+
return ordinal % codeCapacity;
|
|
3105
|
+
},
|
|
3106
|
+
openCodeBlocks() {
|
|
3107
|
+
return codeBlocks;
|
|
3108
|
+
},
|
|
3109
|
+
vectorAt(ordinal) {
|
|
3110
|
+
if (!isCold(ordinal)) return hotVector(ordinal);
|
|
3111
|
+
const copy = new Float32Array(dimension);
|
|
3112
|
+
if (!readColdInto(ordinal, copy)) {
|
|
3113
|
+
throw new NarsilError(
|
|
3114
|
+
ErrorCodes.PERSISTENCE_LOAD_FAILED,
|
|
3115
|
+
`The vector at ordinal ${ordinal} is missing from its checkpoint file`,
|
|
3116
|
+
{ ordinal, path: handles.vectorFiles[fileOf(ordinal)] }
|
|
3117
|
+
);
|
|
3118
|
+
}
|
|
3119
|
+
return copy;
|
|
3120
|
+
},
|
|
3121
|
+
magnitudeAt(ordinal) {
|
|
3122
|
+
if (ordinal >= magnitudes.length) rebind();
|
|
3123
|
+
return magnitudes[ordinal];
|
|
3124
|
+
},
|
|
3125
|
+
entryForOrdinal(ordinal) {
|
|
3126
|
+
if (!holds(ordinal)) return void 0;
|
|
3127
|
+
return { vector: view.vectorAt(ordinal), magnitude: magnitudes[ordinal] };
|
|
3128
|
+
},
|
|
3129
|
+
docIdAt(ordinal) {
|
|
3130
|
+
if (ordinal < 0 || ordinal >= view.slots) return void 0;
|
|
3131
|
+
if (ordinal + 1 >= docIdOffsets.length) rebind();
|
|
3132
|
+
const start = docIdOffsets[ordinal];
|
|
3133
|
+
const end = docIdOffsets[ordinal + 1];
|
|
3134
|
+
if (end <= start) return void 0;
|
|
3135
|
+
if (end > docIdBytes.length) rebind();
|
|
3136
|
+
return decoder.decode(docIdBytes.subarray(start, end));
|
|
3137
|
+
},
|
|
3138
|
+
partitionAt(ordinal) {
|
|
3139
|
+
if (ordinal < 0) return void 0;
|
|
3140
|
+
if (ordinal >= partitions.length) {
|
|
3141
|
+
if (ordinal * 4 >= handles.partitions.byteLength) return void 0;
|
|
3142
|
+
rebind();
|
|
3143
|
+
}
|
|
3144
|
+
const partition = partitions[ordinal];
|
|
3145
|
+
return partition < 0 ? void 0 : partition;
|
|
3146
|
+
},
|
|
3147
|
+
distanceByOrdinal(ordA, ordB, metric) {
|
|
3148
|
+
if (!holds(ordA) || !holds(ordB)) return Number.POSITIVE_INFINITY;
|
|
3149
|
+
if (!isCold(ordB)) {
|
|
3150
|
+
const indexA = ordA / capacity | 0;
|
|
3151
|
+
const indexB = ordB / capacity | 0;
|
|
3152
|
+
const blockB = blockAt(indexB);
|
|
3153
|
+
const localB = ordB % capacity;
|
|
3154
|
+
if (blockB.simd !== null) {
|
|
3155
|
+
if (indexA === indexB && !isCold(ordA)) {
|
|
3156
|
+
return arenaFloat32Distance(
|
|
3157
|
+
blockB.simd,
|
|
3158
|
+
slotByteOffset(layout, ordA % capacity),
|
|
3159
|
+
slotByteOffset(layout, localB),
|
|
3160
|
+
dimension,
|
|
3161
|
+
metric,
|
|
3162
|
+
magnitudes[ordA],
|
|
3163
|
+
magnitudes[ordB]
|
|
3164
|
+
);
|
|
3165
|
+
}
|
|
3166
|
+
if (blockB.hasScratch && stageVector(blockB, ordA)) {
|
|
3167
|
+
return arenaFloat32Distance(
|
|
3168
|
+
blockB.simd,
|
|
3169
|
+
blockB.scratchByteOffset,
|
|
3170
|
+
slotByteOffset(layout, localB),
|
|
3171
|
+
dimension,
|
|
3172
|
+
metric,
|
|
3173
|
+
magnitudes[ordA],
|
|
3174
|
+
magnitudes[ordB]
|
|
3175
|
+
);
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
const a = vectorFor(ordA, coldScratchA);
|
|
3180
|
+
const b = vectorFor(ordB, coldScratchB);
|
|
3181
|
+
if (a === null || b === null) return Number.POSITIVE_INFINITY;
|
|
3182
|
+
return jsDistance(a, b, magnitudes[ordA], magnitudes[ordB], metric);
|
|
3183
|
+
},
|
|
3184
|
+
prepareQueryArena(query) {
|
|
3185
|
+
if (query.length !== dimension || !view.simdAvailable) return null;
|
|
3186
|
+
currentQuery = query;
|
|
3187
|
+
for (const block of blocks) {
|
|
3188
|
+
if (block !== void 0) block.stagedOrdinal = NOTHING_STAGED;
|
|
3189
|
+
}
|
|
3190
|
+
return { magnitude: magnitude(query) };
|
|
3191
|
+
},
|
|
3192
|
+
distanceFromArena(prepared, ordinal, metric) {
|
|
3193
|
+
const state = holdState(ordinal);
|
|
3194
|
+
if (state === NOT_HELD) return Number.POSITIVE_INFINITY;
|
|
3195
|
+
if (currentQuery === null) return Number.POSITIVE_INFINITY;
|
|
3196
|
+
if (state === HELD_COLD) {
|
|
3197
|
+
if (!readColdInto(ordinal, coldScratchA)) return Number.POSITIVE_INFINITY;
|
|
3198
|
+
return jsDistance(currentQuery, coldScratchA, prepared.magnitude, magnitudes[ordinal], metric);
|
|
3199
|
+
}
|
|
3200
|
+
const block = blockAt(ordinal / capacity | 0);
|
|
3201
|
+
const local = ordinal % capacity;
|
|
3202
|
+
if (block.simd !== null && block.hasScratch && stageQuery(block)) {
|
|
3203
|
+
return arenaFloat32Distance(
|
|
3204
|
+
block.simd,
|
|
3205
|
+
block.scratchByteOffset,
|
|
3206
|
+
slotByteOffset(layout, local),
|
|
3207
|
+
dimension,
|
|
3208
|
+
metric,
|
|
3209
|
+
prepared.magnitude,
|
|
3210
|
+
magnitudes[ordinal]
|
|
3211
|
+
);
|
|
3212
|
+
}
|
|
3213
|
+
return jsDistance(currentQuery, vectorIn(block, local), prepared.magnitude, magnitudes[ordinal], metric);
|
|
3214
|
+
},
|
|
3215
|
+
hotBlockReads,
|
|
3216
|
+
queryDistance: (prepared, metric) => queryDistanceOf(view, prepared, metric),
|
|
3217
|
+
ordinalDistance: (from, metric) => ordinalDistanceOf(view, from, metric),
|
|
3218
|
+
pairDistance: (metric) => pairDistanceOf(view, metric)
|
|
3219
|
+
};
|
|
3220
|
+
for (let index = 0; index < handles.blocks.length; index++) {
|
|
3221
|
+
if (handles.blocks[index] !== null) blockAt(index);
|
|
3222
|
+
}
|
|
3223
|
+
for (let index = 0; index < handles.codeBlocks.length; index++) codeBlockAt(index);
|
|
3224
|
+
return view;
|
|
3225
|
+
}
|
|
3226
|
+
|
|
3227
|
+
// src/vector/shared-field/view.ts
|
|
3228
|
+
function openSharedVectorField(initial, threadSlot) {
|
|
3229
|
+
let handles = initial;
|
|
3230
|
+
const store = openSharedVectorStore(initial.store, threadSlot);
|
|
3231
|
+
const codeLayout = initial.store.codeLayout;
|
|
3232
|
+
const quantizer = initial.quantization !== "none" && codeLayout !== null ? openSharedQuantizer(store, codeLayout, initial.metric) : void 0;
|
|
3233
|
+
let graph = null;
|
|
3234
|
+
let ordinals = null;
|
|
3235
|
+
let scanned = 0;
|
|
3236
|
+
let placed = 0;
|
|
3237
|
+
function openGraph(next) {
|
|
3238
|
+
graph = next.graph === null ? null : openGraphState(next.graph, next.dimension, store, quantizer, threadSlot);
|
|
3239
|
+
}
|
|
3240
|
+
openGraph(initial);
|
|
3241
|
+
function docIdOf(ordinal) {
|
|
3242
|
+
if (!store.holdsOrdinal(ordinal)) return void 0;
|
|
3243
|
+
return store.docIdAt(ordinal);
|
|
3244
|
+
}
|
|
3245
|
+
function ordinalOf(docId) {
|
|
3246
|
+
const slots = store.slots;
|
|
3247
|
+
if (ordinals === null) ordinals = /* @__PURE__ */ new Map();
|
|
3248
|
+
for (; scanned < slots; scanned++) {
|
|
3249
|
+
const id = store.docIdAt(scanned);
|
|
3250
|
+
if (id !== void 0) ordinals.set(id, scanned);
|
|
3251
|
+
}
|
|
3252
|
+
const ordinal = ordinals.get(docId);
|
|
3253
|
+
return ordinal !== void 0 && store.holdsOrdinal(ordinal) ? ordinal : void 0;
|
|
3254
|
+
}
|
|
3255
|
+
return {
|
|
3256
|
+
get handles() {
|
|
3257
|
+
return handles;
|
|
3258
|
+
},
|
|
3259
|
+
store,
|
|
3260
|
+
quantizer,
|
|
3261
|
+
get graph() {
|
|
3262
|
+
return graph;
|
|
3263
|
+
},
|
|
3264
|
+
get liveSize() {
|
|
3265
|
+
return graph === null ? 0 : nodeCountOf(graph) - tombstoneCountOf(graph);
|
|
3266
|
+
},
|
|
3267
|
+
get readsEveryVector() {
|
|
3268
|
+
return store.holdsEveryBlock;
|
|
3269
|
+
},
|
|
3270
|
+
docIdOf,
|
|
3271
|
+
ordinalOf,
|
|
3272
|
+
vectorOf(docId) {
|
|
3273
|
+
const ordinal = ordinalOf(docId);
|
|
3274
|
+
return ordinal === void 0 ? void 0 : store.vectorAt(ordinal);
|
|
3275
|
+
},
|
|
3276
|
+
adopt(next) {
|
|
3277
|
+
store.adoptHandles(next.store);
|
|
3278
|
+
if (next.graph?.header !== handles.graph?.header) openGraph(next);
|
|
3279
|
+
handles = next;
|
|
3280
|
+
},
|
|
3281
|
+
close() {
|
|
3282
|
+
store.close();
|
|
3283
|
+
},
|
|
3284
|
+
insertOrdinal(ordinal) {
|
|
3285
|
+
if (graph === null) return false;
|
|
3286
|
+
const fresh = insertNode(graph, ordinal);
|
|
3287
|
+
if (fresh) placed += 1;
|
|
3288
|
+
return fresh;
|
|
3289
|
+
},
|
|
3290
|
+
takeOutcome() {
|
|
3291
|
+
const outcome = { placed };
|
|
3292
|
+
placed = 0;
|
|
3293
|
+
return outcome;
|
|
3294
|
+
},
|
|
3295
|
+
searchOrdinals(query, k, metric, minSimilarity, options) {
|
|
3296
|
+
if (graph === null) return [];
|
|
3297
|
+
return searchOrdinals(graph, docIdOf, query, k, metric, minSimilarity, options);
|
|
3298
|
+
}
|
|
3299
|
+
};
|
|
3300
|
+
}
|
|
3301
|
+
|
|
3302
|
+
// src/vector/osq/index.ts
|
|
3303
|
+
function createOsqQuantizer(dimension, bits, metric, store) {
|
|
3304
|
+
let opened = null;
|
|
3305
|
+
function shared() {
|
|
3306
|
+
if (opened === null && store.dimension === dimension && store.handles.codeLayout !== null) {
|
|
3307
|
+
opened = openSharedQuantizer(store.view, store.handles.codeLayout, metric);
|
|
3308
|
+
}
|
|
3309
|
+
return opened;
|
|
3310
|
+
}
|
|
3311
|
+
function requireShared() {
|
|
3312
|
+
const view = shared();
|
|
3313
|
+
if (view === null) throw new Error("The vector store holds no vector for the quantizer to work on");
|
|
3314
|
+
return view;
|
|
3315
|
+
}
|
|
3316
|
+
function ordinalOf(docId) {
|
|
3317
|
+
const ordinal = store.getOrdinal(docId);
|
|
3318
|
+
if (ordinal === void 0) {
|
|
3319
|
+
throw new Error(`Cannot quantize "${docId}": the vector store holds no vector for it`);
|
|
3320
|
+
}
|
|
3321
|
+
return ordinal;
|
|
3322
|
+
}
|
|
3323
|
+
return {
|
|
3324
|
+
dimension,
|
|
3325
|
+
bits,
|
|
3326
|
+
metric,
|
|
3327
|
+
get size() {
|
|
3328
|
+
return shared()?.size ?? 0;
|
|
3329
|
+
},
|
|
3330
|
+
get centroid() {
|
|
3331
|
+
return shared()?.centroid ?? null;
|
|
3332
|
+
},
|
|
3333
|
+
isCalibrated: () => shared()?.isCalibrated() ?? false,
|
|
3334
|
+
calibrate(ordinals) {
|
|
3335
|
+
if (ordinals.length > 0) requireShared().calibrateFrom(ordinals);
|
|
3336
|
+
},
|
|
3337
|
+
quantize(docId) {
|
|
3338
|
+
const ordinal = ordinalOf(docId);
|
|
3339
|
+
const view = requireShared();
|
|
3340
|
+
if (!view.isCalibrated()) view.calibrateFrom(Int32Array.of(ordinal));
|
|
3341
|
+
view.writeCodes(ordinal);
|
|
3342
|
+
},
|
|
3343
|
+
writeCodes(ordinal) {
|
|
3344
|
+
shared()?.writeCodes(ordinal);
|
|
3345
|
+
},
|
|
3346
|
+
remove(docId) {
|
|
3347
|
+
const ordinal = store.getOrdinal(docId);
|
|
3348
|
+
if (ordinal !== void 0) shared()?.clearCodes(ordinal);
|
|
3349
|
+
},
|
|
3350
|
+
removeOrdinal(ordinal) {
|
|
3351
|
+
shared()?.clearCodes(ordinal);
|
|
3352
|
+
},
|
|
3353
|
+
hasOrdinal: (ordinal) => shared()?.holdsOrdinal(ordinal) ?? false,
|
|
3354
|
+
getLevels(docId) {
|
|
3355
|
+
const view = shared();
|
|
3356
|
+
const ordinal = store.getOrdinal(docId);
|
|
3357
|
+
if (view === null || ordinal === void 0 || !view.holdsOrdinal(ordinal)) return void 0;
|
|
3358
|
+
return unpackLevels(view.recordAt(ordinal), 0, dimension, bits);
|
|
3359
|
+
},
|
|
3360
|
+
recordOf(docId) {
|
|
3361
|
+
const view = shared();
|
|
3362
|
+
const ordinal = store.getOrdinal(docId);
|
|
3363
|
+
if (view === null || ordinal === void 0 || !view.holdsOrdinal(ordinal)) return void 0;
|
|
3364
|
+
return view.recordAt(ordinal);
|
|
3365
|
+
},
|
|
3366
|
+
restoreRecord(docId, record) {
|
|
3367
|
+
requireShared().restoreRecord(ordinalOf(docId), record);
|
|
3368
|
+
},
|
|
3369
|
+
restoreCentroid(centroid) {
|
|
3370
|
+
requireShared().writeCentroid(centroid);
|
|
3371
|
+
},
|
|
3372
|
+
recalibrate(ordinals) {
|
|
3373
|
+
if (ordinals.length === 0) return;
|
|
3374
|
+
const view = requireShared();
|
|
3375
|
+
view.resetCodes();
|
|
3376
|
+
view.calibrateFrom(ordinals);
|
|
3377
|
+
view.writeCodesOf(ordinals);
|
|
3378
|
+
},
|
|
3379
|
+
prepareQuery: (query) => shared()?.prepareQuery(query) ?? null,
|
|
3380
|
+
distanceFromPreparedByOrdinal: (prepared, ordinal) => shared()?.distanceFromPreparedByOrdinal(prepared, ordinal) ?? Number.POSITIVE_INFINITY,
|
|
3381
|
+
preparedDistance(prepared) {
|
|
3382
|
+
const view = shared();
|
|
3383
|
+
if (view === null) return () => Number.POSITIVE_INFINITY;
|
|
3384
|
+
return view.preparedDistance(prepared);
|
|
3385
|
+
},
|
|
3386
|
+
pairDistance() {
|
|
3387
|
+
const view = shared();
|
|
3388
|
+
if (view === null) return () => Number.POSITIVE_INFINITY;
|
|
3389
|
+
return view.pairDistance();
|
|
3390
|
+
},
|
|
3391
|
+
distanceBetweenOrdinals: (ordA, ordB) => shared()?.distanceBetweenOrdinals(ordA, ordB) ?? Number.POSITIVE_INFINITY,
|
|
3392
|
+
clear() {
|
|
3393
|
+
const view = shared();
|
|
3394
|
+
if (view === null) return;
|
|
3395
|
+
view.resetCodes();
|
|
3396
|
+
view.resetCalibration();
|
|
3397
|
+
}
|
|
3398
|
+
};
|
|
3399
|
+
}
|
|
3400
|
+
|
|
3401
|
+
// src/vector/vector-store/doc-ids.ts
|
|
3402
|
+
var encoder = new TextEncoder();
|
|
3403
|
+
function appendDocId(handles, ordinal, docId) {
|
|
3404
|
+
const bytes = encoder.encode(docId);
|
|
3405
|
+
const start = Atomics.load(handles.header, STORE_DOC_ID_BYTES);
|
|
3406
|
+
const end = start + bytes.length;
|
|
3407
|
+
growBufferTo(handles.docIdBytes, end);
|
|
3408
|
+
growBufferTo(handles.docIdOffsets, (ordinal + 2) * 4);
|
|
3409
|
+
new Uint8Array(handles.docIdBytes).set(bytes, start);
|
|
3410
|
+
const offsets = new Uint32Array(handles.docIdOffsets);
|
|
3411
|
+
offsets[ordinal] = start;
|
|
3412
|
+
offsets[ordinal + 1] = end;
|
|
3413
|
+
Atomics.store(handles.header, STORE_DOC_ID_BYTES, end);
|
|
3414
|
+
}
|
|
3415
|
+
function resetDocIds(handles) {
|
|
3416
|
+
new Uint32Array(handles.docIdOffsets).fill(0);
|
|
3417
|
+
Atomics.store(handles.header, STORE_DOC_ID_BYTES, 0);
|
|
3418
|
+
}
|
|
3419
|
+
|
|
3420
|
+
// src/vector/vector-store/open-store.ts
|
|
3421
|
+
function openHandles(dimension, codeBits, blockBytes2) {
|
|
3422
|
+
const handles = createSharedVectorStoreHandles(dimension, codeBits, blockBytes2);
|
|
3423
|
+
return {
|
|
3424
|
+
handles,
|
|
3425
|
+
view: openSharedVectorStore(handles, 0),
|
|
3426
|
+
magnitudes: new Float64Array(handles.magnitudes),
|
|
3427
|
+
present: new Uint8Array(handles.present),
|
|
3428
|
+
partitions: new Int32Array(handles.partitions),
|
|
3429
|
+
diskFile: new Int32Array(handles.diskFile),
|
|
3430
|
+
diskOffset: new Uint32Array(handles.diskOffset)
|
|
3431
|
+
};
|
|
3432
|
+
}
|
|
3433
|
+
function relayout(store) {
|
|
3434
|
+
store.handles.layoutRevision += 1;
|
|
3435
|
+
store.view.adoptHandles(store.handles);
|
|
3436
|
+
}
|
|
3437
|
+
function ensureCodeSlot(store, ordinal) {
|
|
3438
|
+
const { handles } = store;
|
|
3439
|
+
if (handles.codeLayout === null) return;
|
|
3440
|
+
const blockIndex = Math.floor(ordinal / handles.codeLayout.capacity);
|
|
3441
|
+
while (handles.codeBlocks.length <= blockIndex) {
|
|
3442
|
+
handles.codeBlocks.push(createVectorBlock(handles.codeLayout));
|
|
3443
|
+
Atomics.store(handles.header, STORE_CODE_BLOCK_COUNT, handles.codeBlocks.length);
|
|
3444
|
+
relayout(store);
|
|
3445
|
+
}
|
|
3446
|
+
ensureBlockSlots(handles.codeBlocks[blockIndex], ordinal % handles.codeLayout.capacity + 1);
|
|
3447
|
+
}
|
|
3448
|
+
function ensureFloatSlot(store, ordinal) {
|
|
3449
|
+
const { handles } = store;
|
|
3450
|
+
const blockIndex = Math.floor(ordinal / handles.layout.capacity);
|
|
3451
|
+
let changed = false;
|
|
3452
|
+
while (handles.blocks.length <= blockIndex) {
|
|
3453
|
+
handles.blocks.push(null);
|
|
3454
|
+
changed = true;
|
|
3455
|
+
}
|
|
3456
|
+
if (handles.blocks[blockIndex] === null) {
|
|
3457
|
+
handles.blocks[blockIndex] = createVectorBlock(handles.layout);
|
|
3458
|
+
changed = true;
|
|
3459
|
+
}
|
|
3460
|
+
if (changed) {
|
|
3461
|
+
Atomics.store(handles.header, STORE_BLOCK_COUNT, handles.blocks.length);
|
|
3462
|
+
relayout(store);
|
|
3463
|
+
}
|
|
3464
|
+
const block = handles.blocks[blockIndex];
|
|
3465
|
+
if (block !== null) ensureBlockSlots(block, ordinal % handles.layout.capacity + 1);
|
|
3466
|
+
}
|
|
3467
|
+
function ensureOrdinal(store, ordinal, cold) {
|
|
3468
|
+
if (ordinal >= MAX_VECTOR_ORDINALS) {
|
|
3469
|
+
throw new NarsilError(
|
|
3470
|
+
ErrorCodes.PARTITION_CAPACITY_EXCEEDED,
|
|
3471
|
+
`A vector field holds at most ${MAX_VECTOR_ORDINALS} vectors, deleted ones included, until it is rebuilt`,
|
|
3472
|
+
{ ordinal, maxOrdinals: MAX_VECTOR_ORDINALS }
|
|
3473
|
+
);
|
|
3474
|
+
}
|
|
3475
|
+
const { handles } = store;
|
|
3476
|
+
growBufferTo(handles.magnitudes, (ordinal + 1) * 8);
|
|
3477
|
+
growBufferTo(handles.present, ordinal + 1);
|
|
3478
|
+
growBufferTo(handles.partitions, (ordinal + 1) * 4);
|
|
3479
|
+
growBufferTo(handles.codePresent, ordinal + 1);
|
|
3480
|
+
const diskFileBytes = handles.diskFile.byteLength;
|
|
3481
|
+
growBufferTo(handles.diskFile, (ordinal + 1) * 4);
|
|
3482
|
+
if (handles.diskFile.byteLength > diskFileBytes) {
|
|
3483
|
+
new Int32Array(handles.diskFile).fill(IN_MEMORY, diskFileBytes / 4);
|
|
3484
|
+
}
|
|
3485
|
+
growBufferTo(handles.diskOffset, (ordinal + 1) * 4);
|
|
3486
|
+
if (!cold) ensureFloatSlot(store, ordinal);
|
|
3487
|
+
ensureCodeSlot(store, ordinal);
|
|
3488
|
+
}
|
|
3489
|
+
function writeVector(store, ordinal, vector) {
|
|
3490
|
+
const block = store.view.blockOf(ordinal);
|
|
3491
|
+
const base = slotByteOffset(store.handles.layout, store.view.localOrdinal(ordinal)) / 4;
|
|
3492
|
+
block.float32(base + vector.length).set(vector, base);
|
|
3493
|
+
store.magnitudes[ordinal] = magnitude(vector);
|
|
3494
|
+
}
|
|
3495
|
+
function blockFullyCold(store, blockIndex, documents) {
|
|
3496
|
+
const capacity = store.handles.layout.capacity;
|
|
3497
|
+
const slots = Atomics.load(store.handles.header, STORE_SLOTS);
|
|
3498
|
+
const end = Math.min(slots, (blockIndex + 1) * capacity);
|
|
3499
|
+
if (end < (blockIndex + 1) * capacity) return false;
|
|
3500
|
+
for (let ordinal = blockIndex * capacity; ordinal < end; ordinal++) {
|
|
3501
|
+
if (documents[ordinal] !== void 0 && store.diskFile[ordinal] === IN_MEMORY) return false;
|
|
3502
|
+
}
|
|
3503
|
+
return true;
|
|
3504
|
+
}
|
|
3505
|
+
function fileHoldsSameBytes(store, ordinal, location) {
|
|
3506
|
+
const fromFile = new Float32Array(store.handles.dimension);
|
|
3507
|
+
if (!store.view.readFromFile(location.fileIndex, location.offset, fromFile)) return false;
|
|
3508
|
+
const held = store.view.vectorAt(ordinal);
|
|
3509
|
+
const a = new Uint8Array(fromFile.buffer);
|
|
3510
|
+
const b = new Uint8Array(held.buffer, held.byteOffset, held.byteLength);
|
|
3511
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
3512
|
+
return true;
|
|
3513
|
+
}
|
|
3514
|
+
function forgetUnreferencedFiles(store, documents) {
|
|
3515
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
3516
|
+
for (let ordinal = 0; ordinal < documents.length; ordinal++) {
|
|
3517
|
+
if (documents[ordinal] !== void 0 && store.diskFile[ordinal] !== IN_MEMORY) {
|
|
3518
|
+
referenced.add(store.diskFile[ordinal]);
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
let forgotten = 0;
|
|
3522
|
+
for (let file = 0; file < store.handles.vectorFiles.length; file++) {
|
|
3523
|
+
if (referenced.has(file) || store.handles.vectorFiles[file] === "") continue;
|
|
3524
|
+
store.handles.vectorFiles[file] = "";
|
|
3525
|
+
forgotten += 1;
|
|
3526
|
+
}
|
|
3527
|
+
return forgotten;
|
|
3528
|
+
}
|
|
3529
|
+
function partitionFilterOf(store, documents, partitionIds) {
|
|
3530
|
+
const filter = createOrdinalFilter(documents.length);
|
|
3531
|
+
if (store === null) return filter;
|
|
3532
|
+
let highest = -1;
|
|
3533
|
+
for (const partitionId of partitionIds) {
|
|
3534
|
+
if (partitionId > highest) highest = partitionId;
|
|
3535
|
+
}
|
|
3536
|
+
if (highest < 0) return filter;
|
|
3537
|
+
const wanted = new Uint8Array(highest + 1);
|
|
3538
|
+
for (const partitionId of partitionIds) {
|
|
3539
|
+
if (partitionId >= 0) wanted[partitionId] = 1;
|
|
3540
|
+
}
|
|
3541
|
+
for (let ordinal = 0; ordinal < documents.length; ordinal++) {
|
|
3542
|
+
if (documents[ordinal] === void 0) continue;
|
|
3543
|
+
const partitionId = store.partitions[ordinal];
|
|
3544
|
+
if (partitionId < 0 || partitionId > highest || wanted[partitionId] === 0) continue;
|
|
3545
|
+
addToOrdinalFilter(filter, ordinal);
|
|
3546
|
+
}
|
|
3547
|
+
return filter;
|
|
3548
|
+
}
|
|
3549
|
+
|
|
3550
|
+
// src/vector/vector-store/index.ts
|
|
3551
|
+
var UNKNOWN_PARTITION = -1;
|
|
3552
|
+
var NO_PARTITION = -2;
|
|
3553
|
+
function createVectorStore(options) {
|
|
3554
|
+
const codeBits = options?.codeBits ?? null;
|
|
3555
|
+
const blockBytes2 = options?.blockBytes ?? BLOCK_MAX_BYTES;
|
|
3556
|
+
const docToOrd = /* @__PURE__ */ new Map();
|
|
3557
|
+
const ordToDoc = [];
|
|
3558
|
+
let unknownPartitions = 0;
|
|
3559
|
+
let liveCount = 0;
|
|
3560
|
+
let open = options?.dimension === void 0 ? null : openHandles(options.dimension, codeBits, blockBytes2);
|
|
3561
|
+
function forgetDocuments() {
|
|
3562
|
+
docToOrd.clear();
|
|
3563
|
+
ordToDoc.length = 0;
|
|
3564
|
+
unknownPartitions = 0;
|
|
3565
|
+
liveCount = 0;
|
|
3566
|
+
}
|
|
3567
|
+
function requireOpen() {
|
|
3568
|
+
if (open === null) {
|
|
3569
|
+
throw new NarsilError(ErrorCodes.VECTOR_DIMENSION_MISMATCH, "The vector store holds no vector yet");
|
|
3570
|
+
}
|
|
3571
|
+
return open;
|
|
3572
|
+
}
|
|
3573
|
+
function recordPartition(store, ordinal, partitionId) {
|
|
3574
|
+
const given = partitionId !== void 0 && partitionId >= 0 ? partitionId : UNKNOWN_PARTITION;
|
|
3575
|
+
store.partitions[ordinal] = given;
|
|
3576
|
+
if (given === UNKNOWN_PARTITION) unknownPartitions += 1;
|
|
3577
|
+
}
|
|
3578
|
+
function retireOrdinal(store, ordinal) {
|
|
3579
|
+
ordToDoc[ordinal] = void 0;
|
|
3580
|
+
Atomics.store(store.present, ordinal, 0);
|
|
3581
|
+
if (store.partitions[ordinal] === UNKNOWN_PARTITION) unknownPartitions -= 1;
|
|
3582
|
+
}
|
|
3583
|
+
function claimOrdinal(store, docId, cold, partitionId) {
|
|
3584
|
+
const ordinal = Atomics.load(store.handles.header, STORE_SLOTS);
|
|
3585
|
+
ensureOrdinal(store, ordinal, cold);
|
|
3586
|
+
store.diskFile[ordinal] = IN_MEMORY;
|
|
3587
|
+
appendDocId(store.handles, ordinal, docId);
|
|
3588
|
+
recordPartition(store, ordinal, partitionId);
|
|
3589
|
+
ordToDoc[ordinal] = docId;
|
|
3590
|
+
docToOrd.set(docId, ordinal);
|
|
3591
|
+
return ordinal;
|
|
3592
|
+
}
|
|
3593
|
+
function publishOrdinal(store, ordinal) {
|
|
3594
|
+
Atomics.store(store.handles.header, STORE_SLOTS, ordinal + 1);
|
|
3595
|
+
Atomics.store(store.present, ordinal, 1);
|
|
3596
|
+
}
|
|
3597
|
+
function appendOrdinal(store, docId, vector, partitionId) {
|
|
3598
|
+
const ordinal = claimOrdinal(store, docId, false, partitionId);
|
|
3599
|
+
writeVector(store, ordinal, vector);
|
|
3600
|
+
publishOrdinal(store, ordinal);
|
|
3601
|
+
return ordinal;
|
|
3602
|
+
}
|
|
3603
|
+
function settleReplacement(store, ordinal, previous) {
|
|
3604
|
+
if (previous !== void 0) {
|
|
3605
|
+
retireOrdinal(store, previous);
|
|
3606
|
+
return ordinal;
|
|
3607
|
+
}
|
|
3608
|
+
liveCount++;
|
|
3609
|
+
Atomics.add(store.handles.header, STORE_LIVE_COUNT, 1);
|
|
3610
|
+
return ordinal;
|
|
3611
|
+
}
|
|
3612
|
+
function entryAt(store, ordinal) {
|
|
3613
|
+
return { vector: store.view.vectorAt(ordinal), magnitude: store.magnitudes[ordinal] };
|
|
3614
|
+
}
|
|
3615
|
+
return {
|
|
3616
|
+
get size() {
|
|
3617
|
+
return liveCount;
|
|
3618
|
+
},
|
|
3619
|
+
get slots() {
|
|
3620
|
+
return ordToDoc.length;
|
|
3621
|
+
},
|
|
3622
|
+
get dimension() {
|
|
3623
|
+
return open === null ? 0 : open.handles.dimension;
|
|
3624
|
+
},
|
|
3625
|
+
get codeBits() {
|
|
3626
|
+
return codeBits;
|
|
3627
|
+
},
|
|
3628
|
+
get handles() {
|
|
3629
|
+
return requireOpen().handles;
|
|
3630
|
+
},
|
|
3631
|
+
get view() {
|
|
3632
|
+
return requireOpen().view;
|
|
3633
|
+
},
|
|
3634
|
+
insert(docId, vector, partitionId) {
|
|
3635
|
+
if (open === null) open = openHandles(vector.length, codeBits, blockBytes2);
|
|
3636
|
+
const store = open;
|
|
3637
|
+
if (vector.length !== store.handles.dimension) {
|
|
3638
|
+
throw new NarsilError(
|
|
3639
|
+
ErrorCodes.VECTOR_DIMENSION_MISMATCH,
|
|
3640
|
+
`Vector dimension mismatch: expected ${store.handles.dimension}, got ${vector.length}`,
|
|
3641
|
+
{ expected: store.handles.dimension, received: vector.length }
|
|
3642
|
+
);
|
|
3643
|
+
}
|
|
3644
|
+
const previous = docToOrd.get(docId);
|
|
3645
|
+
const ordinal = appendOrdinal(store, docId, vector, partitionId);
|
|
3646
|
+
return settleReplacement(store, ordinal, previous);
|
|
3647
|
+
},
|
|
3648
|
+
insertCold(docId, vectorMagnitude, location, partitionId) {
|
|
3649
|
+
const store = requireOpen();
|
|
3650
|
+
const previous = docToOrd.get(docId);
|
|
3651
|
+
Atomics.store(store.handles.header, STORE_RELEASED_VECTORS_TO_DISK, 1);
|
|
3652
|
+
const ordinal = claimOrdinal(store, docId, true, partitionId);
|
|
3653
|
+
store.magnitudes[ordinal] = vectorMagnitude;
|
|
3654
|
+
store.diskOffset[ordinal] = location.offset;
|
|
3655
|
+
store.diskFile[ordinal] = location.fileIndex;
|
|
3656
|
+
publishOrdinal(store, ordinal);
|
|
3657
|
+
return settleReplacement(store, ordinal, previous);
|
|
3658
|
+
},
|
|
3659
|
+
addVectorFile(path) {
|
|
3660
|
+
const store = requireOpen();
|
|
3661
|
+
const existing = store.handles.vectorFiles.indexOf(path);
|
|
3662
|
+
if (existing !== -1) return existing;
|
|
3663
|
+
store.handles.vectorFiles.push(path);
|
|
3664
|
+
relayout(store);
|
|
3665
|
+
return store.handles.vectorFiles.length - 1;
|
|
3666
|
+
},
|
|
3667
|
+
isCold(ordinal) {
|
|
3668
|
+
if (open === null) return false;
|
|
3669
|
+
return open.view.isCold(ordinal);
|
|
3670
|
+
},
|
|
3671
|
+
releaseToFile(ordinal, location) {
|
|
3672
|
+
const store = requireOpen();
|
|
3673
|
+
if (ordToDoc[ordinal] === void 0) return false;
|
|
3674
|
+
if (store.diskFile[ordinal] === IN_MEMORY && !fileHoldsSameBytes(store, ordinal, location)) return false;
|
|
3675
|
+
Atomics.store(store.handles.header, STORE_RELEASED_VECTORS_TO_DISK, 1);
|
|
3676
|
+
store.diskOffset[ordinal] = location.offset;
|
|
3677
|
+
store.diskFile[ordinal] = location.fileIndex;
|
|
3678
|
+
return true;
|
|
3679
|
+
},
|
|
3680
|
+
releaseColdBlocks() {
|
|
3681
|
+
const store = requireOpen();
|
|
3682
|
+
let released = 0;
|
|
3683
|
+
for (let index = 0; index < store.handles.blocks.length; index++) {
|
|
3684
|
+
if (store.handles.blocks[index] === null || !blockFullyCold(store, index, ordToDoc)) continue;
|
|
3685
|
+
store.handles.blocks[index] = null;
|
|
3686
|
+
released += 1;
|
|
3687
|
+
}
|
|
3688
|
+
if (released + forgetUnreferencedFiles(store, ordToDoc) > 0) relayout(store);
|
|
3689
|
+
return released;
|
|
3690
|
+
},
|
|
3691
|
+
setPartition(docId, partitionId) {
|
|
3692
|
+
const ordinal = docToOrd.get(docId);
|
|
3693
|
+
if (ordinal === void 0 || open === null || partitionId < 0) return;
|
|
3694
|
+
if (open.partitions[ordinal] === UNKNOWN_PARTITION) unknownPartitions -= 1;
|
|
3695
|
+
open.partitions[ordinal] = partitionId;
|
|
3696
|
+
},
|
|
3697
|
+
forgetPartition(docId) {
|
|
3698
|
+
const ordinal = docToOrd.get(docId);
|
|
3699
|
+
if (ordinal === void 0 || open === null) return;
|
|
3700
|
+
if (open.partitions[ordinal] === UNKNOWN_PARTITION) unknownPartitions -= 1;
|
|
3701
|
+
open.partitions[ordinal] = NO_PARTITION;
|
|
3702
|
+
},
|
|
3703
|
+
partitionOfOrdinal(ordinal) {
|
|
3704
|
+
if (open === null || ordinal < 0 || ordinal >= ordToDoc.length) return void 0;
|
|
3705
|
+
const partitionId = open.partitions[ordinal];
|
|
3706
|
+
return partitionId < 0 ? void 0 : partitionId;
|
|
3707
|
+
},
|
|
3708
|
+
get partitionsKnown() {
|
|
3709
|
+
return unknownPartitions === 0;
|
|
3710
|
+
},
|
|
3711
|
+
partitionFilter(partitionIds) {
|
|
3712
|
+
return partitionFilterOf(open, ordToDoc, partitionIds);
|
|
3713
|
+
},
|
|
3714
|
+
remove(docId) {
|
|
3715
|
+
const ordinal = docToOrd.get(docId);
|
|
3716
|
+
if (ordinal === void 0 || open === null) return;
|
|
3717
|
+
docToOrd.delete(docId);
|
|
3718
|
+
retireOrdinal(open, ordinal);
|
|
3719
|
+
liveCount--;
|
|
3720
|
+
Atomics.sub(open.handles.header, STORE_LIVE_COUNT, 1);
|
|
3721
|
+
},
|
|
3722
|
+
get(docId) {
|
|
3723
|
+
const ordinal = docToOrd.get(docId);
|
|
3724
|
+
return ordinal === void 0 || open === null ? void 0 : entryAt(open, ordinal);
|
|
3725
|
+
},
|
|
3726
|
+
has(docId) {
|
|
3727
|
+
return docToOrd.has(docId);
|
|
3728
|
+
},
|
|
3729
|
+
holdsOrdinal(ordinal) {
|
|
3730
|
+
return ordinal >= 0 && ordinal < ordToDoc.length && ordToDoc[ordinal] !== void 0;
|
|
3731
|
+
},
|
|
3732
|
+
*entries() {
|
|
3733
|
+
if (open === null) return;
|
|
3734
|
+
for (let ordinal = 0; ordinal < ordToDoc.length; ordinal++) {
|
|
3735
|
+
const docId = ordToDoc[ordinal];
|
|
3736
|
+
if (docId === void 0) continue;
|
|
3737
|
+
yield [docId, entryAt(open, ordinal)];
|
|
3738
|
+
}
|
|
3739
|
+
},
|
|
3740
|
+
clear() {
|
|
3741
|
+
forgetDocuments();
|
|
3742
|
+
if (open === null) return;
|
|
3743
|
+
open.present.fill(0);
|
|
3744
|
+
new Uint8Array(open.handles.codePresent).fill(0);
|
|
3745
|
+
new Int32Array(open.handles.diskFile).fill(IN_MEMORY);
|
|
3746
|
+
open.handles.centroid.fill(0);
|
|
3747
|
+
if (open.handles.vectorFiles.length > 0) {
|
|
3748
|
+
open.handles.vectorFiles.length = 0;
|
|
3749
|
+
relayout(open);
|
|
3750
|
+
}
|
|
3751
|
+
resetDocIds(open.handles);
|
|
3752
|
+
Atomics.store(open.handles.header, STORE_SLOTS, 0);
|
|
3753
|
+
Atomics.store(open.handles.header, STORE_RELEASED_VECTORS_TO_DISK, 0);
|
|
3754
|
+
Atomics.store(open.handles.header, STORE_LIVE_COUNT, 0);
|
|
3755
|
+
Atomics.store(open.handles.header, STORE_CODE_COUNT, 0);
|
|
3756
|
+
Atomics.store(open.handles.header, STORE_CALIBRATED, 0);
|
|
3757
|
+
Atomics.add(open.handles.header, STORE_CALIBRATION_GENERATION, 1);
|
|
3758
|
+
},
|
|
3759
|
+
release() {
|
|
3760
|
+
forgetDocuments();
|
|
3761
|
+
open?.view.close();
|
|
3762
|
+
open = null;
|
|
3763
|
+
},
|
|
3764
|
+
closeFiles: () => open?.view.close(),
|
|
3765
|
+
getOrdinal(docId) {
|
|
3766
|
+
return docToOrd.get(docId);
|
|
3767
|
+
},
|
|
3768
|
+
docIdForOrdinal(ordinal) {
|
|
3769
|
+
return ordToDoc[ordinal];
|
|
3770
|
+
},
|
|
3771
|
+
entryForOrdinal(ordinal) {
|
|
3772
|
+
if (open === null || ordinal < 0 || ordinal >= ordToDoc.length || ordToDoc[ordinal] === void 0) {
|
|
3773
|
+
return void 0;
|
|
3774
|
+
}
|
|
3775
|
+
return entryAt(open, ordinal);
|
|
3776
|
+
},
|
|
3777
|
+
distanceByOrdinal(ordA, ordB, metric) {
|
|
3778
|
+
if (open === null || ordToDoc[ordA] === void 0 || ordToDoc[ordB] === void 0) {
|
|
3779
|
+
return Number.POSITIVE_INFINITY;
|
|
3780
|
+
}
|
|
3781
|
+
return open.view.distanceByOrdinal(ordA, ordB, metric);
|
|
3782
|
+
},
|
|
3783
|
+
prepareQueryArena(query) {
|
|
3784
|
+
return open === null ? null : open.view.prepareQueryArena(query);
|
|
3785
|
+
},
|
|
3786
|
+
distanceFromArena(prepared, ordinal, metric) {
|
|
3787
|
+
if (open === null || ordToDoc[ordinal] === void 0) return Number.POSITIVE_INFINITY;
|
|
3788
|
+
return open.view.distanceFromArena(prepared, ordinal, metric);
|
|
3789
|
+
},
|
|
3790
|
+
queryDistance(prepared, metric) {
|
|
3791
|
+
if (open === null) return () => Number.POSITIVE_INFINITY;
|
|
3792
|
+
const inner = open.view.queryDistance(prepared, metric);
|
|
3793
|
+
return (ordinal) => ordToDoc[ordinal] === void 0 ? Number.POSITIVE_INFINITY : inner(ordinal);
|
|
3794
|
+
},
|
|
3795
|
+
ordinalDistance(from, metric) {
|
|
3796
|
+
const pair = this.pairDistance(metric);
|
|
3797
|
+
return (ordinal) => pair(from, ordinal);
|
|
3798
|
+
},
|
|
3799
|
+
pairDistance(metric) {
|
|
3800
|
+
if (open === null) return () => Number.POSITIVE_INFINITY;
|
|
3801
|
+
const inner = open.view.pairDistance(metric);
|
|
3802
|
+
return (ordA, ordB) => ordToDoc[ordA] === void 0 || ordToDoc[ordB] === void 0 ? Number.POSITIVE_INFINITY : inner(ordA, ordB);
|
|
3803
|
+
},
|
|
3804
|
+
exportSnapshot() {
|
|
3805
|
+
const slots = ordToDoc.length;
|
|
3806
|
+
const dimension = open === null ? 0 : open.handles.dimension;
|
|
3807
|
+
const vectors = new Float32Array(slots * dimension);
|
|
3808
|
+
const magnitudes = new Float64Array(slots);
|
|
3809
|
+
const docIds = new Array(slots);
|
|
3810
|
+
for (let ordinal = 0; ordinal < slots; ordinal++) {
|
|
3811
|
+
const docId = ordToDoc[ordinal];
|
|
3812
|
+
docIds[ordinal] = docId ?? null;
|
|
3813
|
+
if (docId === void 0 || open === null) continue;
|
|
3814
|
+
vectors.set(open.view.vectorAt(ordinal), ordinal * dimension);
|
|
3815
|
+
magnitudes[ordinal] = open.magnitudes[ordinal];
|
|
3816
|
+
}
|
|
3817
|
+
return { dimension, slots, vectors, magnitudes, docIds };
|
|
3818
|
+
},
|
|
3819
|
+
restoreSnapshot(snapshot) {
|
|
3820
|
+
this.clear();
|
|
3821
|
+
if (snapshot.dimension === 0 || snapshot.slots === 0) return;
|
|
3822
|
+
if (open === null) open = openHandles(snapshot.dimension, codeBits, blockBytes2);
|
|
3823
|
+
const store = open;
|
|
3824
|
+
for (let ordinal = 0; ordinal < snapshot.slots; ordinal++) {
|
|
3825
|
+
const docId = snapshot.docIds[ordinal];
|
|
3826
|
+
const vector = snapshot.vectors.subarray(ordinal * snapshot.dimension, (ordinal + 1) * snapshot.dimension);
|
|
3827
|
+
if (docId === null || docId === void 0) {
|
|
3828
|
+
ensureOrdinal(store, ordinal, false);
|
|
3829
|
+
ordToDoc[ordinal] = void 0;
|
|
3830
|
+
store.partitions[ordinal] = NO_PARTITION;
|
|
3831
|
+
Atomics.store(store.handles.header, STORE_SLOTS, ordinal + 1);
|
|
3832
|
+
continue;
|
|
3833
|
+
}
|
|
3834
|
+
appendOrdinal(store, docId, vector);
|
|
3835
|
+
liveCount++;
|
|
3836
|
+
}
|
|
3837
|
+
Atomics.store(store.handles.header, STORE_LIVE_COUNT, liveCount);
|
|
3838
|
+
},
|
|
3839
|
+
memoryBytes() {
|
|
3840
|
+
return open === null ? 0 : sharedVectorStoreBytes(open.handles);
|
|
3841
|
+
}
|
|
3842
|
+
};
|
|
3843
|
+
}
|
|
3844
|
+
|
|
3845
|
+
// src/vector/hnsw/numbered-graph.ts
|
|
3846
|
+
var NO_NUMBER = -1;
|
|
3847
|
+
var NO_ORDINAL = -1;
|
|
3848
|
+
var NO_LAYER = -1;
|
|
3849
|
+
var BYTES_PER_VALUE = 4;
|
|
3850
|
+
var LITTLE_ENDIAN = new Uint8Array(new Uint16Array([1]).buffer)[0] === 1;
|
|
3851
|
+
function swapEveryValue(bytes) {
|
|
3852
|
+
for (let i = 0; i + 3 < bytes.length; i += BYTES_PER_VALUE) {
|
|
3853
|
+
const first = bytes[i];
|
|
3854
|
+
const second = bytes[i + 1];
|
|
3855
|
+
bytes[i] = bytes[i + 3];
|
|
3856
|
+
bytes[i + 1] = bytes[i + 2];
|
|
3857
|
+
bytes[i + 2] = second;
|
|
3858
|
+
bytes[i + 3] = first;
|
|
3859
|
+
}
|
|
3860
|
+
}
|
|
3861
|
+
function littleEndianBytesOf(values) {
|
|
3862
|
+
const bytes = new Uint8Array(values.buffer, values.byteOffset, values.byteLength);
|
|
3863
|
+
if (!LITTLE_ENDIAN) swapEveryValue(bytes);
|
|
3864
|
+
return bytes;
|
|
3865
|
+
}
|
|
3866
|
+
function valuesOf(bytes) {
|
|
3867
|
+
if (LITTLE_ENDIAN && bytes.byteOffset % BYTES_PER_VALUE === 0) {
|
|
3868
|
+
return new Uint32Array(bytes.buffer, bytes.byteOffset, Math.floor(bytes.byteLength / BYTES_PER_VALUE));
|
|
3869
|
+
}
|
|
3870
|
+
const copy = bytes.slice(0, bytes.byteLength - bytes.byteLength % BYTES_PER_VALUE);
|
|
3871
|
+
if (!LITTLE_ENDIAN) swapEveryValue(copy);
|
|
3872
|
+
return new Uint32Array(copy.buffer, 0, copy.byteLength / BYTES_PER_VALUE);
|
|
3873
|
+
}
|
|
3874
|
+
function savedTopLayer(state, ordinal) {
|
|
3875
|
+
if (ordinal === NO_ORDINAL || isTombstoned(state, ordinal)) return NO_LAYER;
|
|
3876
|
+
return Math.min(nodeMaxLayer(state, ordinal), MAX_LAYER_CAP);
|
|
3877
|
+
}
|
|
3878
|
+
function largestNeighbourValueCount(state, topLayers) {
|
|
3879
|
+
let values = 0;
|
|
3880
|
+
for (let number = 0; number < topLayers.length; number++) {
|
|
3881
|
+
const top = topLayers[number];
|
|
3882
|
+
if (top < 0) continue;
|
|
3883
|
+
values += state.adjacency.level0Stride + top * state.adjacency.upperStride;
|
|
3884
|
+
}
|
|
3885
|
+
return values;
|
|
3886
|
+
}
|
|
3887
|
+
function serializeNumberedGraph(state, numberOfOrdinal, ordinalOfNumber) {
|
|
3888
|
+
const adjacency = state.adjacency;
|
|
3889
|
+
const slots = adjacencySlots(adjacency);
|
|
3890
|
+
const topLayers = new Int8Array(ordinalOfNumber.length).fill(-1);
|
|
3891
|
+
const levels = new Uint8Array(ordinalOfNumber.length);
|
|
3892
|
+
for (let number = 0; number < ordinalOfNumber.length; number++) {
|
|
3893
|
+
const ordinal = ordinalOfNumber[number];
|
|
3894
|
+
if (ordinal >= slots) continue;
|
|
3895
|
+
topLayers[number] = savedTopLayer(state, ordinal);
|
|
3896
|
+
levels[number] = topLayers[number] + 1;
|
|
3897
|
+
}
|
|
3898
|
+
const values = new Uint32Array(largestNeighbourValueCount(state, topLayers));
|
|
3899
|
+
let cursor = 0;
|
|
3900
|
+
for (let number = 0; number < ordinalOfNumber.length; number++) {
|
|
3901
|
+
const top = topLayers[number];
|
|
3902
|
+
for (let layer = 0; layer <= top; layer++) {
|
|
3903
|
+
const base = layerBase(adjacency, ordinalOfNumber[number], layer);
|
|
3904
|
+
const array = layerArray(adjacency, layer);
|
|
3905
|
+
const held = base === -1 ? 0 : Math.min(array[base], maxConns(state, layer));
|
|
3906
|
+
const countAt = cursor;
|
|
3907
|
+
cursor += 1;
|
|
3908
|
+
for (let i = 1; i <= held; i++) {
|
|
3909
|
+
const neighbourOrdinal = array[base + i];
|
|
3910
|
+
if (neighbourOrdinal < 0 || neighbourOrdinal >= numberOfOrdinal.length) continue;
|
|
3911
|
+
const neighbour = numberOfOrdinal[neighbourOrdinal];
|
|
3912
|
+
if (neighbour === NO_NUMBER || neighbour === number || topLayers[neighbour] < layer) continue;
|
|
3913
|
+
values[cursor] = neighbour;
|
|
3914
|
+
cursor += 1;
|
|
3915
|
+
}
|
|
3916
|
+
values[countAt] = cursor - countAt - 1;
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3919
|
+
const entryOrdinal = entryPointOf(state);
|
|
3920
|
+
const entryNumber = entryOrdinal >= 0 && entryOrdinal < numberOfOrdinal.length ? numberOfOrdinal[entryOrdinal] : NO_NUMBER;
|
|
3921
|
+
const entryHoldsNode = entryNumber !== NO_NUMBER && topLayers[entryNumber] >= 0;
|
|
3922
|
+
return {
|
|
3923
|
+
entryPoint: entryHoldsNode ? entryNumber : null,
|
|
3924
|
+
maxLayer: Math.max(0, Math.min(topLayerOf(state), MAX_LAYER_CAP)),
|
|
3925
|
+
m: state.M,
|
|
3926
|
+
efConstruction: state.efCons,
|
|
3927
|
+
metric: state.buildMetric,
|
|
3928
|
+
levels,
|
|
3929
|
+
neighbours: littleEndianBytesOf(values.subarray(0, cursor))
|
|
3930
|
+
};
|
|
3931
|
+
}
|
|
3932
|
+
function endsInsideAList() {
|
|
3933
|
+
throw new NarsilError(
|
|
3934
|
+
ErrorCodes.PERSISTENCE_LOAD_FAILED,
|
|
3935
|
+
"Invalid vector graph payload: neighbours ends inside a list"
|
|
3936
|
+
);
|
|
3937
|
+
}
|
|
3938
|
+
function requireWholeNeighbourLists(graph) {
|
|
3939
|
+
const values = valuesOf(graph.neighbours);
|
|
3940
|
+
if (values.length * BYTES_PER_VALUE !== graph.neighbours.byteLength) endsInsideAList();
|
|
3941
|
+
let cursor = 0;
|
|
3942
|
+
for (let number = 0; number < graph.levels.length; number++) {
|
|
3943
|
+
for (let layer = 0; layer < graph.levels[number]; layer++) {
|
|
3944
|
+
if (cursor >= values.length) endsInsideAList();
|
|
3945
|
+
const count = values[cursor];
|
|
3946
|
+
if (count > values.length - cursor - 1) endsInsideAList();
|
|
3947
|
+
cursor += count + 1;
|
|
3948
|
+
}
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
function topLayerIn(graph, number) {
|
|
3952
|
+
return number < graph.levels.length ? graph.levels[number] - 1 : -1;
|
|
3953
|
+
}
|
|
3954
|
+
function deserializeNumberedGraph(state, graph, ordinalOfNumber) {
|
|
3955
|
+
requireWholeNeighbourLists(graph);
|
|
3956
|
+
resetGraph(state);
|
|
3957
|
+
const values = valuesOf(graph.neighbours);
|
|
3958
|
+
const ordinalAt = (number) => number < ordinalOfNumber.length ? ordinalOfNumber[number] : NO_ORDINAL;
|
|
3959
|
+
let highestOrdinal = -1;
|
|
3960
|
+
for (let number = 0; number < graph.levels.length; number++) {
|
|
3961
|
+
if (graph.levels[number] > 0 && ordinalAt(number) > highestOrdinal) highestOrdinal = ordinalAt(number);
|
|
3962
|
+
}
|
|
3963
|
+
if (highestOrdinal >= 0) ensureCapacity(state, highestOrdinal + 1);
|
|
3964
|
+
const kept = [];
|
|
3965
|
+
let cursor = 0;
|
|
3966
|
+
let highestLayer = -1;
|
|
3967
|
+
let nodeOnHighestLayer = NO_ORDINAL;
|
|
3968
|
+
for (let number = 0; number < graph.levels.length; number++) {
|
|
3969
|
+
const top = graph.levels[number] - 1;
|
|
3970
|
+
if (top < 0) continue;
|
|
3971
|
+
const ordinal = ordinalAt(number);
|
|
3972
|
+
const keptTop = Math.min(top, MAX_LAYER_CAP);
|
|
3973
|
+
if (ordinal !== NO_ORDINAL) {
|
|
3974
|
+
createNode(state.adjacency, ordinal, keptTop);
|
|
3975
|
+
Atomics.add(state.header, GRAPH_NODE_COUNT, 1);
|
|
3976
|
+
if (keptTop > highestLayer) {
|
|
3977
|
+
highestLayer = keptTop;
|
|
3978
|
+
nodeOnHighestLayer = ordinal;
|
|
3979
|
+
}
|
|
3980
|
+
}
|
|
3981
|
+
for (let layer = 0; layer <= top; layer++) {
|
|
3982
|
+
const count = values[cursor];
|
|
3983
|
+
kept.length = 0;
|
|
3984
|
+
const limit = maxConns(state, layer);
|
|
3985
|
+
for (let i = 1; i <= count && kept.length < limit; i++) {
|
|
3986
|
+
const neighbour = values[cursor + i];
|
|
3987
|
+
const neighbourOrdinal = ordinalAt(neighbour);
|
|
3988
|
+
if (neighbourOrdinal === NO_ORDINAL || neighbour === number || topLayerIn(graph, neighbour) < layer) continue;
|
|
3989
|
+
if (!kept.includes(neighbourOrdinal)) kept.push(neighbourOrdinal);
|
|
3990
|
+
}
|
|
3991
|
+
cursor += count + 1;
|
|
3992
|
+
if (ordinal !== NO_ORDINAL && layer <= keptTop) {
|
|
3993
|
+
replaceNeighbors(state.adjacency, ordinal, layer, kept, kept.length);
|
|
3994
|
+
}
|
|
3995
|
+
}
|
|
3996
|
+
}
|
|
3997
|
+
const entryOrdinal = graph.entryPoint === null ? NO_ORDINAL : ordinalAt(graph.entryPoint);
|
|
3998
|
+
const entryTop = graph.entryPoint === null ? -1 : Math.min(topLayerIn(graph, graph.entryPoint), MAX_LAYER_CAP);
|
|
3999
|
+
const entryIsUsable = entryOrdinal !== NO_ORDINAL && entryTop >= 0;
|
|
4000
|
+
Atomics.store(state.header, GRAPH_ENTRY_POINT, entryIsUsable ? entryOrdinal : nodeOnHighestLayer);
|
|
4001
|
+
Atomics.store(state.header, GRAPH_TOP_LAYER, entryIsUsable ? entryTop : highestLayer);
|
|
4002
|
+
}
|
|
4003
|
+
|
|
4004
|
+
// src/vector/hnsw/persistence.ts
|
|
4005
|
+
function serializeGraph(state, store) {
|
|
4006
|
+
const nodeArray = [];
|
|
4007
|
+
const slots = adjacencySlots(state.adjacency);
|
|
4008
|
+
for (let ord = 0; ord < slots; ord++) {
|
|
4009
|
+
const maxLayer = nodeMaxLayer(state, ord);
|
|
4010
|
+
if (maxLayer === -1) continue;
|
|
4011
|
+
if (isTombstoned(state, ord)) continue;
|
|
4012
|
+
const docId = store.docIdForOrdinal(ord);
|
|
4013
|
+
if (docId === void 0) continue;
|
|
4014
|
+
const layerConns = [];
|
|
4015
|
+
for (let layer = 0; layer <= maxLayer; layer++) {
|
|
4016
|
+
const liveNeighbors = [];
|
|
4017
|
+
for (const neighborOrd of collectNeighbors(state.adjacency, ord, layer)) {
|
|
4018
|
+
if (isTombstoned(state, neighborOrd)) continue;
|
|
4019
|
+
const neighborDoc = store.docIdForOrdinal(neighborOrd);
|
|
4020
|
+
if (neighborDoc === void 0) continue;
|
|
4021
|
+
liveNeighbors.push(neighborDoc);
|
|
4022
|
+
}
|
|
4023
|
+
if (liveNeighbors.length > 0) {
|
|
4024
|
+
layerConns.push([layer, liveNeighbors]);
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
nodeArray.push([docId, maxLayer, layerConns]);
|
|
4028
|
+
}
|
|
4029
|
+
const entryOrd = entryPointOf(state);
|
|
4030
|
+
const entryPoint = entryOrd === -1 ? null : store.docIdForOrdinal(entryOrd) ?? null;
|
|
4031
|
+
return {
|
|
4032
|
+
entryPoint,
|
|
4033
|
+
maxLayer: topLayerOf(state),
|
|
4034
|
+
m: state.M,
|
|
4035
|
+
efConstruction: state.efCons,
|
|
4036
|
+
metric: state.buildMetric,
|
|
4037
|
+
nodes: nodeArray
|
|
4038
|
+
};
|
|
4039
|
+
}
|
|
4040
|
+
function resolveNodes(state, store, data) {
|
|
4041
|
+
const nodes = [];
|
|
4042
|
+
let maxOrd = -1;
|
|
4043
|
+
for (const [docId, maxLayer, layerConns] of data.nodes) {
|
|
4044
|
+
const ord = store.getOrdinal(docId);
|
|
4045
|
+
if (ord === void 0 || !store.holdsOrdinal(ord)) continue;
|
|
4046
|
+
const clampedMaxLayer = Math.min(Math.max(maxLayer, 0), MAX_LAYER_CAP);
|
|
4047
|
+
const layers = [];
|
|
4048
|
+
for (const [layer, neighbors] of layerConns) {
|
|
4049
|
+
if (layer < 0 || layer > clampedMaxLayer) continue;
|
|
4050
|
+
const limit = maxConns(state, layer);
|
|
4051
|
+
const resolved = [];
|
|
4052
|
+
for (const neighborDoc of neighbors) {
|
|
4053
|
+
if (resolved.length >= limit) break;
|
|
4054
|
+
const neighborOrd = store.getOrdinal(neighborDoc);
|
|
4055
|
+
if (neighborOrd === void 0) continue;
|
|
4056
|
+
addConnection(resolved, neighborOrd);
|
|
4057
|
+
if (neighborOrd > maxOrd) maxOrd = neighborOrd;
|
|
4058
|
+
}
|
|
4059
|
+
layers.push([layer, resolved]);
|
|
4060
|
+
}
|
|
4061
|
+
if (ord > maxOrd) maxOrd = ord;
|
|
4062
|
+
nodes.push({ ord, maxLayer: clampedMaxLayer, layers });
|
|
4063
|
+
}
|
|
4064
|
+
return { nodes, maxOrd };
|
|
4065
|
+
}
|
|
4066
|
+
function deserializeGraph(state, store, data) {
|
|
4067
|
+
resetGraph(state);
|
|
4068
|
+
const { nodes, maxOrd } = resolveNodes(state, store, data);
|
|
4069
|
+
if (maxOrd >= 0) {
|
|
4070
|
+
ensureCapacity(state, maxOrd + 1);
|
|
4071
|
+
}
|
|
4072
|
+
for (const node of nodes) {
|
|
4073
|
+
createNode(state.adjacency, node.ord, node.maxLayer);
|
|
4074
|
+
for (const [layer, neighbors] of node.layers) {
|
|
4075
|
+
replaceNeighbors(state.adjacency, node.ord, layer, neighbors, neighbors.length);
|
|
4076
|
+
}
|
|
4077
|
+
Atomics.add(state.header, GRAPH_NODE_COUNT, 1);
|
|
4078
|
+
}
|
|
4079
|
+
if (data.entryPoint != null && data.entryPoint !== "") {
|
|
4080
|
+
const epOrd = store.getOrdinal(data.entryPoint);
|
|
4081
|
+
if (epOrd !== void 0 && nodeExists(state, epOrd)) {
|
|
4082
|
+
Atomics.store(state.header, GRAPH_ENTRY_POINT, epOrd);
|
|
4083
|
+
Atomics.store(state.header, GRAPH_TOP_LAYER, Math.min(Math.max(data.maxLayer, 0), MAX_LAYER_CAP));
|
|
4084
|
+
}
|
|
4085
|
+
}
|
|
4086
|
+
if (entryPointOf(state) === -1 && Atomics.load(state.header, GRAPH_NODE_COUNT) > 0) {
|
|
4087
|
+
let bestOrd = -1;
|
|
4088
|
+
let bestLayer = -1;
|
|
4089
|
+
const slots = adjacencySlots(state.adjacency);
|
|
4090
|
+
for (let ord = 0; ord < slots; ord++) {
|
|
4091
|
+
const level2 = nodeMaxLayer(state, ord);
|
|
4092
|
+
if (level2 === -1) continue;
|
|
4093
|
+
if (level2 > bestLayer) {
|
|
4094
|
+
bestLayer = level2;
|
|
4095
|
+
bestOrd = ord;
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
Atomics.store(state.header, GRAPH_ENTRY_POINT, bestOrd);
|
|
4099
|
+
Atomics.store(state.header, GRAPH_TOP_LAYER, bestLayer);
|
|
4100
|
+
}
|
|
4101
|
+
}
|
|
4102
|
+
|
|
4103
|
+
// src/vector/hnsw/scratch-bytes.ts
|
|
4104
|
+
function listBytes(list) {
|
|
4105
|
+
return list.ords.byteLength + list.distances.byteLength;
|
|
4106
|
+
}
|
|
4107
|
+
function workspaceBytes(workspace) {
|
|
4108
|
+
let bytes = listBytes(workspace.frontier) + listBytes(workspace.found) + listBytes(workspace.traversal) + listBytes(workspace.working) + listBytes(workspace.pruneCandidates) + listBytes(workspace.pruneSelection) + listBytes(workspace.repairCandidates) + listBytes(workspace.repairSelection) + workspace.entryPoints.byteLength;
|
|
4109
|
+
for (const list of workspace.linkSelections) bytes += listBytes(list);
|
|
4110
|
+
return bytes;
|
|
4111
|
+
}
|
|
4112
|
+
function searchScratchBytes(state) {
|
|
4113
|
+
const held = workspaceBytes(state.workspace) + state.neighborScratch.byteLength + state.visited.byteLength;
|
|
4114
|
+
const native = attachedNativeField(state);
|
|
4115
|
+
return native === null ? held : held + nativeScratchBytes(state, native);
|
|
4116
|
+
}
|
|
4117
|
+
|
|
4118
|
+
// src/vector/hnsw/snapshot.ts
|
|
4119
|
+
function exportSnapshot(state) {
|
|
4120
|
+
const adj = state.adjacency;
|
|
4121
|
+
const slots = adjacencySlots(adj);
|
|
4122
|
+
const used = upperUsed(adj);
|
|
4123
|
+
const stride = adj.level0Stride;
|
|
4124
|
+
rebindNodes(adj);
|
|
4125
|
+
reachTombstone(state, slots);
|
|
4126
|
+
return {
|
|
4127
|
+
dimension: state.dimension,
|
|
4128
|
+
m: state.M,
|
|
4129
|
+
efConstruction: state.efCons,
|
|
4130
|
+
metric: state.buildMetric,
|
|
4131
|
+
slots,
|
|
4132
|
+
upperUsed: used,
|
|
4133
|
+
nodeLevels: adj.nodeLevels.slice(0, slots),
|
|
4134
|
+
level0: adj.level0.slice(0, slots * stride),
|
|
4135
|
+
upperBase: adj.upperBase.slice(0, slots),
|
|
4136
|
+
upper: adj.upper.slice(0, used),
|
|
4137
|
+
tombstones: state.tombstones.slice(0, slots),
|
|
4138
|
+
tombstoneCount: Atomics.load(state.header, GRAPH_TOMBSTONE_COUNT),
|
|
4139
|
+
nodeCount: Atomics.load(state.header, GRAPH_NODE_COUNT),
|
|
4140
|
+
entryPointOrd: Atomics.load(state.header, GRAPH_ENTRY_POINT),
|
|
4141
|
+
topLayer: Atomics.load(state.header, GRAPH_TOP_LAYER)
|
|
4142
|
+
};
|
|
4143
|
+
}
|
|
4144
|
+
function restoreSnapshot(state, snapshot) {
|
|
4145
|
+
resetGraph(state);
|
|
4146
|
+
const adj = state.adjacency;
|
|
4147
|
+
ensureAdjacencyCapacity(adj, snapshot.slots);
|
|
4148
|
+
adj.nodeLevels.set(snapshot.nodeLevels);
|
|
4149
|
+
adj.level0.set(snapshot.level0);
|
|
4150
|
+
adj.upperBase.set(snapshot.upperBase);
|
|
4151
|
+
ensureUpperCapacity(adj, snapshot.upper.length);
|
|
4152
|
+
adj.upper.set(snapshot.upper);
|
|
4153
|
+
Atomics.store(state.header, GRAPH_UPPER_USED, snapshot.upperUsed);
|
|
4154
|
+
Atomics.store(state.header, GRAPH_SLOTS, snapshot.slots);
|
|
4155
|
+
state.tombstones = fixedView(adj.handles.tombstones, Uint8Array);
|
|
4156
|
+
state.tombstones.set(snapshot.tombstones);
|
|
4157
|
+
Atomics.store(state.header, GRAPH_TOMBSTONE_COUNT, snapshot.tombstoneCount);
|
|
4158
|
+
Atomics.store(state.header, GRAPH_NODE_COUNT, snapshot.nodeCount);
|
|
4159
|
+
Atomics.store(state.header, GRAPH_ENTRY_POINT, snapshot.entryPointOrd);
|
|
4160
|
+
Atomics.store(state.header, GRAPH_TOP_LAYER, snapshot.topLayer);
|
|
4161
|
+
}
|
|
4162
|
+
|
|
4163
|
+
// src/vector/hnsw/index.ts
|
|
4164
|
+
function createHNSWIndex(dimension, store, config, quantizer, handles) {
|
|
4165
|
+
const M = config?.m ?? 16;
|
|
4166
|
+
const shared = handles ?? createSharedGraphHandles({
|
|
4167
|
+
m: M,
|
|
4168
|
+
mMax0: M * 2,
|
|
4169
|
+
efConstruction: config?.efConstruction ?? 200,
|
|
4170
|
+
metric: config?.metric ?? "cosine"
|
|
4171
|
+
});
|
|
4172
|
+
const state = openGraphState(shared, dimension, store, quantizer, 0);
|
|
4173
|
+
const docIdOf = (ord) => store.docIdForOrdinal(ord);
|
|
4174
|
+
function exclusively(operation) {
|
|
4175
|
+
lockGraphExclusive(state.locks);
|
|
4176
|
+
try {
|
|
4177
|
+
return operation();
|
|
4178
|
+
} finally {
|
|
4179
|
+
unlockGraphExclusive(state.locks);
|
|
4180
|
+
}
|
|
4181
|
+
}
|
|
4182
|
+
function insertNode2(docId) {
|
|
4183
|
+
const ord = store.getOrdinal(docId);
|
|
4184
|
+
if (ord === void 0) {
|
|
4185
|
+
throw new Error(`Cannot insert HNSW node: vector for "${docId}" not found in VectorStore`);
|
|
4186
|
+
}
|
|
4187
|
+
const entry = store.entryForOrdinal(ord);
|
|
4188
|
+
if (!entry) {
|
|
4189
|
+
throw new Error(`Cannot insert HNSW node: vector for "${docId}" not found in VectorStore`);
|
|
4190
|
+
}
|
|
4191
|
+
if (entry.vector.length !== dimension) {
|
|
4192
|
+
throw new Error(`Vector dimension mismatch: expected ${dimension}, got ${entry.vector.length}`);
|
|
4193
|
+
}
|
|
4194
|
+
insertNode(state, ord);
|
|
4195
|
+
}
|
|
4196
|
+
function* entriesIterator() {
|
|
4197
|
+
const slots = adjacencySlots(state.adjacency);
|
|
4198
|
+
for (let ord = 0; ord < slots; ord++) {
|
|
4199
|
+
if (!hasNode(state.adjacency, ord)) continue;
|
|
4200
|
+
if (isTombstoned(state, ord)) continue;
|
|
4201
|
+
const docId = store.docIdForOrdinal(ord);
|
|
4202
|
+
if (docId === void 0) continue;
|
|
4203
|
+
const entry = store.entryForOrdinal(ord);
|
|
4204
|
+
if (!entry) continue;
|
|
4205
|
+
yield [docId, { docId, vector: entry.vector, magnitude: entry.magnitude }];
|
|
4206
|
+
}
|
|
4207
|
+
}
|
|
4208
|
+
function compactionNeeded() {
|
|
4209
|
+
const nodeCount = nodeCountOf(state);
|
|
4210
|
+
if (nodeCount === 0) return false;
|
|
4211
|
+
const tombstones = tombstoneCountOf(state);
|
|
4212
|
+
return tombstones / nodeCount > COMPACTION_TOMBSTONE_RATIO || tombstones > COMPACTION_ABSOLUTE_THRESHOLD;
|
|
4213
|
+
}
|
|
4214
|
+
return {
|
|
4215
|
+
get dimension() {
|
|
4216
|
+
return dimension;
|
|
4217
|
+
},
|
|
4218
|
+
get size() {
|
|
4219
|
+
return nodeCountOf(state) - tombstoneCountOf(state);
|
|
4220
|
+
},
|
|
4221
|
+
get tombstoneCount() {
|
|
4222
|
+
return tombstoneCountOf(state);
|
|
4223
|
+
},
|
|
4224
|
+
get entryPointId() {
|
|
4225
|
+
const entryPoint = entryPointOf(state);
|
|
4226
|
+
return entryPoint === -1 ? null : store.docIdForOrdinal(entryPoint) ?? null;
|
|
4227
|
+
},
|
|
4228
|
+
get topLayer() {
|
|
4229
|
+
return topLayerOf(state);
|
|
4230
|
+
},
|
|
4231
|
+
get m() {
|
|
4232
|
+
return state.M;
|
|
4233
|
+
},
|
|
4234
|
+
get efConstruction() {
|
|
4235
|
+
return state.efCons;
|
|
4236
|
+
},
|
|
4237
|
+
get metric() {
|
|
4238
|
+
return state.buildMetric;
|
|
4239
|
+
},
|
|
4240
|
+
get graphBytes() {
|
|
4241
|
+
return graphBytes(state.adjacency);
|
|
4242
|
+
},
|
|
4243
|
+
get searchScratchBytes() {
|
|
4244
|
+
return searchScratchBytes(state);
|
|
4245
|
+
},
|
|
4246
|
+
get handles() {
|
|
4247
|
+
return shared;
|
|
4248
|
+
},
|
|
4249
|
+
insertNode: insertNode2,
|
|
4250
|
+
insertOrdinal: (ordinal) => insertNode(state, ordinal),
|
|
4251
|
+
markTombstone: (docId) => {
|
|
4252
|
+
const ord = store.getOrdinal(docId);
|
|
4253
|
+
if (ord !== void 0) markTombstone(state, ord);
|
|
4254
|
+
},
|
|
4255
|
+
markTombstoneOrdinal: (ordinal) => markTombstone(state, ordinal),
|
|
4256
|
+
has: (docId) => {
|
|
4257
|
+
const ord = store.getOrdinal(docId);
|
|
4258
|
+
return ord !== void 0 && hasNode(state.adjacency, ord) && !isTombstoned(state, ord);
|
|
4259
|
+
},
|
|
4260
|
+
isTombstoned: (docId) => {
|
|
4261
|
+
const ord = store.getOrdinal(docId);
|
|
4262
|
+
return ord !== void 0 && isTombstoned(state, ord);
|
|
4263
|
+
},
|
|
4264
|
+
search: (query, k, searchMetric, minSimilarity, options) => search(state, docIdOf, query, k, searchMetric, minSimilarity, options),
|
|
4265
|
+
clear: () => exclusively(() => resetGraph(state)),
|
|
4266
|
+
entries: entriesIterator,
|
|
4267
|
+
compactionNeeded,
|
|
4268
|
+
compactTombstones: () => exclusively(() => compactTombstones(state)),
|
|
4269
|
+
rebuild: () => exclusively(() => rebuild(state)),
|
|
4270
|
+
exclusively,
|
|
4271
|
+
serialize: () => serializeGraph(state, store),
|
|
4272
|
+
deserialize: (data) => exclusively(() => deserializeGraph(state, store, data)),
|
|
4273
|
+
serializeNumbered: (numberOfOrdinal, ordinalOfNumber) => serializeNumberedGraph(state, numberOfOrdinal, ordinalOfNumber),
|
|
4274
|
+
deserializeNumbered: (graph, ordinalOfNumber) => exclusively(() => deserializeNumberedGraph(state, graph, ordinalOfNumber)),
|
|
4275
|
+
exportSnapshot: () => exportSnapshot(state),
|
|
4276
|
+
restoreSnapshot: (snapshot) => exclusively(() => restoreSnapshot(state, snapshot))
|
|
4277
|
+
};
|
|
4278
|
+
}
|
|
4279
|
+
|
|
4280
|
+
export { DISK_STORAGE_BLOCK_BYTES, GRAPH_BUILD_CHUNKS_IN_FLIGHT_PER_WORKER, GRAPH_BUILD_DISPATCH_CHUNK, MAX_WORKER_COPIES, SCRATCH_SLOTS_PER_THREAD_POOL, STORE_CALIBRATION_GENERATION, VECTOR_INSERT_TIMEOUT_MS, VECTOR_SCRATCH_SLOTS, VECTOR_SEARCH_LOAD_TIMEOUT_MS, VECTOR_SEARCH_TIMEOUT_MS, addToOrdinalFilter, cosineSimilarityWithMagnitudes, createHNSWIndex, createOrdinalFilter, createOsqQuantizer, createVectorStore, dotProduct, entryForOrd, euclideanDistance, isSimdAvailable, isTombstoned, magnitude, nativeScoresOf, nativeStoreScratchBytes, noteFallback, openSharedVectorField, ordinalFilterHas, ordinalFilterValues, osqBitsOf, osqRecordBytes, releaseLocksHeldBy, removeFromOrdinalFilter, requireWholeNeighbourLists, sharedMemoryAvailable, toDistance, toScore };
|