@delali/narsil 0.2.4-next.45 → 0.2.4-next.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-OBXS7OZM.mjs → chunk-4COAUBLJ.mjs} +1 -1
- package/dist/{chunk-WDL632CN.mjs → chunk-DFZDV5FV.mjs} +244 -239
- package/dist/{chunk-XNCJFU2E.mjs → chunk-EV45L35L.mjs} +225 -81
- package/dist/{chunk-WASNWIEK.mjs → chunk-UZTT2BKQ.mjs} +1 -1
- package/dist/{direct-executor-IMXNRS7O.mjs → direct-executor-EOTFGXTO.mjs} +3 -3
- package/dist/distribution.mjs +4 -4
- package/dist/index.browser.mjs +467 -318
- package/dist/index.mjs +4 -4
- package/dist/persistence/durability/checkpoint-worker.mjs +4 -4
- package/dist/vector/hnsw-build-worker.mjs +1 -1
- package/dist/vector/search-worker.mjs +4 -3
- package/dist/workers/entry.mjs +1 -1
- package/package.json +1 -1
|
@@ -536,90 +536,6 @@ function createVectorStore() {
|
|
|
536
536
|
};
|
|
537
537
|
}
|
|
538
538
|
|
|
539
|
-
// src/core/heap.ts
|
|
540
|
-
function createMinHeap(compare) {
|
|
541
|
-
return createHeap(compare);
|
|
542
|
-
}
|
|
543
|
-
function createMaxHeap(compare) {
|
|
544
|
-
return createHeap((a, b) => compare(b, a));
|
|
545
|
-
}
|
|
546
|
-
function createBoundedMaxHeap(compare, capacity) {
|
|
547
|
-
const heap = createMaxHeap(compare);
|
|
548
|
-
const innerPush = heap.push;
|
|
549
|
-
return {
|
|
550
|
-
push(item) {
|
|
551
|
-
if (heap.size < capacity) {
|
|
552
|
-
innerPush(item);
|
|
553
|
-
return;
|
|
554
|
-
}
|
|
555
|
-
const top = heap.peek();
|
|
556
|
-
if (top !== void 0 && compare(item, top) < 0) {
|
|
557
|
-
heap.pop();
|
|
558
|
-
innerPush(item);
|
|
559
|
-
}
|
|
560
|
-
},
|
|
561
|
-
pop: heap.pop,
|
|
562
|
-
peek: heap.peek,
|
|
563
|
-
get size() {
|
|
564
|
-
return heap.size;
|
|
565
|
-
},
|
|
566
|
-
toSortedArray: heap.toSortedArray
|
|
567
|
-
};
|
|
568
|
-
}
|
|
569
|
-
function createHeap(compare) {
|
|
570
|
-
const items = [];
|
|
571
|
-
function siftUp(idx) {
|
|
572
|
-
while (idx > 0) {
|
|
573
|
-
const parent = idx - 1 >> 1;
|
|
574
|
-
if (compare(items[idx], items[parent]) >= 0) break;
|
|
575
|
-
const tmp = items[idx];
|
|
576
|
-
items[idx] = items[parent];
|
|
577
|
-
items[parent] = tmp;
|
|
578
|
-
idx = parent;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
function siftDown(idx) {
|
|
582
|
-
const len = items.length;
|
|
583
|
-
while (true) {
|
|
584
|
-
let smallest = idx;
|
|
585
|
-
const left = 2 * idx + 1;
|
|
586
|
-
const right = 2 * idx + 2;
|
|
587
|
-
if (left < len && compare(items[left], items[smallest]) < 0) smallest = left;
|
|
588
|
-
if (right < len && compare(items[right], items[smallest]) < 0) smallest = right;
|
|
589
|
-
if (smallest === idx) break;
|
|
590
|
-
const tmp = items[idx];
|
|
591
|
-
items[idx] = items[smallest];
|
|
592
|
-
items[smallest] = tmp;
|
|
593
|
-
idx = smallest;
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
return {
|
|
597
|
-
push(item) {
|
|
598
|
-
items.push(item);
|
|
599
|
-
siftUp(items.length - 1);
|
|
600
|
-
},
|
|
601
|
-
pop() {
|
|
602
|
-
if (items.length === 0) return void 0;
|
|
603
|
-
const top = items[0];
|
|
604
|
-
const last = items.pop();
|
|
605
|
-
if (items.length > 0) {
|
|
606
|
-
items[0] = last;
|
|
607
|
-
siftDown(0);
|
|
608
|
-
}
|
|
609
|
-
return top;
|
|
610
|
-
},
|
|
611
|
-
peek() {
|
|
612
|
-
return items[0];
|
|
613
|
-
},
|
|
614
|
-
get size() {
|
|
615
|
-
return items.length;
|
|
616
|
-
},
|
|
617
|
-
toSortedArray() {
|
|
618
|
-
return [...items].sort(compare);
|
|
619
|
-
}
|
|
620
|
-
};
|
|
621
|
-
}
|
|
622
|
-
|
|
623
539
|
// src/vector/hnsw/adjacency.ts
|
|
624
540
|
var MAX_LAYER_CAP = 32;
|
|
625
541
|
var MAX_M = 512;
|
|
@@ -770,12 +686,12 @@ function removeNeighbor(adj, ord, layer, neighborOrd) {
|
|
|
770
686
|
return;
|
|
771
687
|
}
|
|
772
688
|
}
|
|
773
|
-
function replaceNeighbors(adj, ord, layer, neighbors) {
|
|
689
|
+
function replaceNeighbors(adj, ord, layer, neighbors, count) {
|
|
774
690
|
const base = layerBase(adj, ord, layer);
|
|
775
691
|
if (base === ABSENT) return;
|
|
776
692
|
const array = layerArray(adj, layer);
|
|
777
693
|
const stride = layer === 0 ? adj.level0Stride : adj.upperStride;
|
|
778
|
-
const limit = Math.min(
|
|
694
|
+
const limit = Math.min(count, stride - 1);
|
|
779
695
|
for (let i = 0; i < limit; i++) {
|
|
780
696
|
array[base + i + 1] = neighbors[i];
|
|
781
697
|
}
|
|
@@ -859,8 +775,6 @@ function toScore(distance, metric) {
|
|
|
859
775
|
return 1 / (1 + distance);
|
|
860
776
|
}
|
|
861
777
|
}
|
|
862
|
-
var distanceAsc = (a, b) => a.distance - b.distance;
|
|
863
|
-
var distanceDesc = (a, b) => b.distance - a.distance;
|
|
864
778
|
function randomLevel(mL) {
|
|
865
779
|
let u = Math.random();
|
|
866
780
|
if (u === 0) u = Number.MIN_VALUE;
|
|
@@ -887,37 +801,168 @@ function addConnection(connections, ord) {
|
|
|
887
801
|
connections.push(ord);
|
|
888
802
|
}
|
|
889
803
|
|
|
804
|
+
// src/vector/hnsw/workspace.ts
|
|
805
|
+
var INITIAL_LIST_CAPACITY = 64;
|
|
806
|
+
function createList(capacity) {
|
|
807
|
+
return { ords: new Int32Array(capacity), distances: new Float64Array(capacity), size: 0 };
|
|
808
|
+
}
|
|
809
|
+
function createHeap(greatestFirst) {
|
|
810
|
+
return { ...createList(INITIAL_LIST_CAPACITY), greatestFirst, topOrd: -1, topDistance: 0 };
|
|
811
|
+
}
|
|
812
|
+
function createHNSWWorkspace() {
|
|
813
|
+
return {
|
|
814
|
+
frontier: createHeap(false),
|
|
815
|
+
found: createHeap(true),
|
|
816
|
+
entryPoints: new Int32Array(INITIAL_LIST_CAPACITY),
|
|
817
|
+
entryPointCount: 0,
|
|
818
|
+
traversal: createList(INITIAL_LIST_CAPACITY),
|
|
819
|
+
working: createList(INITIAL_LIST_CAPACITY),
|
|
820
|
+
insertSelection: createList(INITIAL_LIST_CAPACITY),
|
|
821
|
+
pruneCandidates: createList(INITIAL_LIST_CAPACITY),
|
|
822
|
+
pruneSelection: createList(INITIAL_LIST_CAPACITY),
|
|
823
|
+
repairCandidates: createList(INITIAL_LIST_CAPACITY),
|
|
824
|
+
repairSelection: createList(INITIAL_LIST_CAPACITY)
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
function ensureListCapacity(list, needed) {
|
|
828
|
+
if (needed <= list.ords.length) return;
|
|
829
|
+
let capacity = list.ords.length === 0 ? INITIAL_LIST_CAPACITY : list.ords.length;
|
|
830
|
+
while (capacity < needed) capacity *= 2;
|
|
831
|
+
const ords = new Int32Array(capacity);
|
|
832
|
+
ords.set(list.ords);
|
|
833
|
+
list.ords = ords;
|
|
834
|
+
const distances = new Float64Array(capacity);
|
|
835
|
+
distances.set(list.distances);
|
|
836
|
+
list.distances = distances;
|
|
837
|
+
}
|
|
838
|
+
function appendToList(list, ord, distance) {
|
|
839
|
+
ensureListCapacity(list, list.size + 1);
|
|
840
|
+
list.ords[list.size] = ord;
|
|
841
|
+
list.distances[list.size] = distance;
|
|
842
|
+
list.size += 1;
|
|
843
|
+
}
|
|
844
|
+
function copyList(source, target) {
|
|
845
|
+
ensureListCapacity(target, source.size);
|
|
846
|
+
target.ords.set(source.ords.subarray(0, source.size));
|
|
847
|
+
target.distances.set(source.distances.subarray(0, source.size));
|
|
848
|
+
target.size = source.size;
|
|
849
|
+
}
|
|
850
|
+
function sortListByDistance(list) {
|
|
851
|
+
const { ords, distances } = list;
|
|
852
|
+
for (let i = 1; i < list.size; i++) {
|
|
853
|
+
const ord = ords[i];
|
|
854
|
+
const distance = distances[i];
|
|
855
|
+
let j = i - 1;
|
|
856
|
+
while (j >= 0 && distances[j] > distance) {
|
|
857
|
+
distances[j + 1] = distances[j];
|
|
858
|
+
ords[j + 1] = ords[j];
|
|
859
|
+
j -= 1;
|
|
860
|
+
}
|
|
861
|
+
distances[j + 1] = distance;
|
|
862
|
+
ords[j + 1] = ord;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
function heapPrecedes(heap, a, b) {
|
|
866
|
+
return heap.greatestFirst ? heap.distances[a] > heap.distances[b] : heap.distances[a] < heap.distances[b];
|
|
867
|
+
}
|
|
868
|
+
function swapHeapEntries(heap, a, b) {
|
|
869
|
+
const ord = heap.ords[a];
|
|
870
|
+
heap.ords[a] = heap.ords[b];
|
|
871
|
+
heap.ords[b] = ord;
|
|
872
|
+
const distance = heap.distances[a];
|
|
873
|
+
heap.distances[a] = heap.distances[b];
|
|
874
|
+
heap.distances[b] = distance;
|
|
875
|
+
}
|
|
876
|
+
function pushHeap(heap, ord, distance) {
|
|
877
|
+
appendToList(heap, ord, distance);
|
|
878
|
+
let index = heap.size - 1;
|
|
879
|
+
while (index > 0) {
|
|
880
|
+
const parent = index - 1 >> 1;
|
|
881
|
+
if (!heapPrecedes(heap, index, parent)) break;
|
|
882
|
+
swapHeapEntries(heap, index, parent);
|
|
883
|
+
index = parent;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
function popHeap(heap) {
|
|
887
|
+
if (heap.size === 0) return false;
|
|
888
|
+
heap.topOrd = heap.ords[0];
|
|
889
|
+
heap.topDistance = heap.distances[0];
|
|
890
|
+
heap.size -= 1;
|
|
891
|
+
if (heap.size > 0) {
|
|
892
|
+
heap.ords[0] = heap.ords[heap.size];
|
|
893
|
+
heap.distances[0] = heap.distances[heap.size];
|
|
894
|
+
let index = 0;
|
|
895
|
+
for (; ; ) {
|
|
896
|
+
const left = 2 * index + 1;
|
|
897
|
+
const right = left + 1;
|
|
898
|
+
let first = index;
|
|
899
|
+
if (left < heap.size && heapPrecedes(heap, left, first)) first = left;
|
|
900
|
+
if (right < heap.size && heapPrecedes(heap, right, first)) first = right;
|
|
901
|
+
if (first === index) break;
|
|
902
|
+
swapHeapEntries(heap, index, first);
|
|
903
|
+
index = first;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
return true;
|
|
907
|
+
}
|
|
908
|
+
function resetHeap(heap) {
|
|
909
|
+
heap.size = 0;
|
|
910
|
+
}
|
|
911
|
+
function drainHeapNearestFirst(heap, list) {
|
|
912
|
+
const count = heap.size;
|
|
913
|
+
ensureListCapacity(list, count);
|
|
914
|
+
list.size = count;
|
|
915
|
+
for (let i = count - 1; i >= 0; i--) {
|
|
916
|
+
if (!popHeap(heap)) break;
|
|
917
|
+
list.ords[i] = heap.topOrd;
|
|
918
|
+
list.distances[i] = heap.topDistance;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
function setSingleEntryPoint(workspace, ord) {
|
|
922
|
+
workspace.entryPoints[0] = ord;
|
|
923
|
+
workspace.entryPointCount = 1;
|
|
924
|
+
}
|
|
925
|
+
function setEntryPointsFromList(workspace, list) {
|
|
926
|
+
if (list.size > workspace.entryPoints.length) {
|
|
927
|
+
let capacity = workspace.entryPoints.length;
|
|
928
|
+
while (capacity < list.size) capacity *= 2;
|
|
929
|
+
workspace.entryPoints = new Int32Array(capacity);
|
|
930
|
+
}
|
|
931
|
+
workspace.entryPoints.set(list.ords.subarray(0, list.size));
|
|
932
|
+
workspace.entryPointCount = list.size;
|
|
933
|
+
}
|
|
934
|
+
|
|
890
935
|
// src/vector/hnsw/graph-ops.ts
|
|
891
|
-
function searchLayer(state, qVec, qMag,
|
|
936
|
+
function searchLayer(state, qVec, qMag, ef, layer, metric, skipTombstones, distFn, results) {
|
|
892
937
|
const getDistance = distFn ?? ((ord) => queryDistanceByOrd(state, qVec, qMag, ord, metric));
|
|
938
|
+
const workspace = state.workspace;
|
|
939
|
+
const frontier = workspace.frontier;
|
|
940
|
+
const found = workspace.found;
|
|
893
941
|
const adjacency = state.adjacency;
|
|
894
942
|
const neighbors = layerArray(adjacency, layer);
|
|
895
943
|
const visited = state.visited;
|
|
896
944
|
const stamp = nextVisitStamp(state);
|
|
897
|
-
|
|
898
|
-
|
|
945
|
+
resetHeap(frontier);
|
|
946
|
+
resetHeap(found);
|
|
899
947
|
let furthestDist = Number.POSITIVE_INFINITY;
|
|
900
|
-
for (
|
|
948
|
+
for (let i = 0; i < workspace.entryPointCount; i++) {
|
|
949
|
+
const epOrd = workspace.entryPoints[i];
|
|
901
950
|
if (visited[epOrd] === stamp) continue;
|
|
902
951
|
visited[epOrd] = stamp;
|
|
903
952
|
if (!nodeExists(state, epOrd)) continue;
|
|
904
953
|
if (skipTombstones && state.tombstones[epOrd] === 1) continue;
|
|
905
954
|
const dist = getDistance(epOrd);
|
|
906
955
|
if (dist === Number.POSITIVE_INFINITY) continue;
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
results.pop();
|
|
956
|
+
pushHeap(frontier, epOrd, dist);
|
|
957
|
+
pushHeap(found, epOrd, dist);
|
|
958
|
+
if (found.size > ef) {
|
|
959
|
+
popHeap(found);
|
|
912
960
|
}
|
|
913
961
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
const
|
|
918
|
-
if (!nearest) break;
|
|
919
|
-
if (nearest.distance > furthestDist) break;
|
|
920
|
-
const base = layerBase(adjacency, nearest.ord, layer);
|
|
962
|
+
if (found.size > 0) furthestDist = found.distances[0];
|
|
963
|
+
while (popHeap(frontier)) {
|
|
964
|
+
if (frontier.topDistance > furthestDist) break;
|
|
965
|
+
const base = layerBase(adjacency, frontier.topOrd, layer);
|
|
921
966
|
if (base === -1) continue;
|
|
922
967
|
const count = neighbors[base];
|
|
923
968
|
for (let i = 1; i <= count; i++) {
|
|
@@ -928,55 +973,41 @@ function searchLayer(state, qVec, qMag, eps, ef, layer, metric, skipTombstones,
|
|
|
928
973
|
if (!nodeExists(state, neighborOrd)) continue;
|
|
929
974
|
const dist = getDistance(neighborOrd);
|
|
930
975
|
if (dist === Number.POSITIVE_INFINITY) continue;
|
|
931
|
-
if (dist < furthestDist ||
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
results.pop();
|
|
976
|
+
if (dist < furthestDist || found.size < ef) {
|
|
977
|
+
pushHeap(frontier, neighborOrd, dist);
|
|
978
|
+
pushHeap(found, neighborOrd, dist);
|
|
979
|
+
if (found.size > ef) {
|
|
980
|
+
popHeap(found);
|
|
937
981
|
}
|
|
938
|
-
|
|
939
|
-
if (newTop) furthestDist = newTop.distance;
|
|
982
|
+
furthestDist = found.distances[0];
|
|
940
983
|
}
|
|
941
984
|
}
|
|
942
985
|
}
|
|
943
|
-
|
|
944
|
-
}
|
|
945
|
-
function
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
const selected = [];
|
|
956
|
-
const discarded = [];
|
|
957
|
-
for (const candidate of working) {
|
|
958
|
-
if (selected.length >= maxConnections) break;
|
|
986
|
+
drainHeapNearestFirst(found, results);
|
|
987
|
+
}
|
|
988
|
+
function selectNeighborsHeuristic(state, candidates, maxConnections, metric, selected) {
|
|
989
|
+
const working = state.workspace.working;
|
|
990
|
+
copyList(candidates, working);
|
|
991
|
+
sortListByDistance(working);
|
|
992
|
+
ensureListCapacity(selected, Math.min(maxConnections, working.size));
|
|
993
|
+
selected.size = 0;
|
|
994
|
+
for (let i = 0; i < working.size; i++) {
|
|
995
|
+
if (selected.size >= maxConnections) break;
|
|
996
|
+
const candidateOrd = working.ords[i];
|
|
997
|
+
const candidateDistance = working.distances[i];
|
|
959
998
|
let accepted = true;
|
|
960
|
-
for (
|
|
961
|
-
const distBetween = nodeDistanceByOrd(state,
|
|
962
|
-
if (
|
|
999
|
+
for (let s = 0; s < selected.size; s++) {
|
|
1000
|
+
const distBetween = nodeDistanceByOrd(state, candidateOrd, selected.ords[s], metric);
|
|
1001
|
+
if (candidateDistance >= distBetween) {
|
|
963
1002
|
accepted = false;
|
|
964
1003
|
break;
|
|
965
1004
|
}
|
|
966
1005
|
}
|
|
967
|
-
if (accepted)
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
}
|
|
1006
|
+
if (!accepted) continue;
|
|
1007
|
+
selected.ords[selected.size] = candidateOrd;
|
|
1008
|
+
selected.distances[selected.size] = candidateDistance;
|
|
1009
|
+
selected.size += 1;
|
|
972
1010
|
}
|
|
973
|
-
{
|
|
974
|
-
for (const disc of discarded) {
|
|
975
|
-
if (selected.length >= maxConnections) break;
|
|
976
|
-
selected.push(disc);
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
return selected;
|
|
980
1011
|
}
|
|
981
1012
|
function pruneConnections(state, ord, layer, metric) {
|
|
982
1013
|
const adjacency = state.adjacency;
|
|
@@ -986,17 +1017,20 @@ function pruneConnections(state, ord, layer, metric) {
|
|
|
986
1017
|
const neighbors = layerArray(adjacency, layer);
|
|
987
1018
|
const count = neighbors[base];
|
|
988
1019
|
if (count <= mc) return;
|
|
989
|
-
const
|
|
1020
|
+
const candidates = state.workspace.pruneCandidates;
|
|
1021
|
+
candidates.size = 0;
|
|
1022
|
+
ensureListCapacity(candidates, count);
|
|
990
1023
|
for (let i = 1; i <= count; i++) {
|
|
991
1024
|
const connOrd = neighbors[base + i];
|
|
992
1025
|
const dist = nodeDistanceByOrd(state, ord, connOrd, metric);
|
|
993
1026
|
if (dist === Number.POSITIVE_INFINITY) continue;
|
|
994
|
-
|
|
1027
|
+
candidates.ords[candidates.size] = connOrd;
|
|
1028
|
+
candidates.distances[candidates.size] = dist;
|
|
1029
|
+
candidates.size += 1;
|
|
995
1030
|
}
|
|
996
|
-
const
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
replaceNeighbors(adjacency, ord, layer, next);
|
|
1031
|
+
const kept = state.workspace.pruneSelection;
|
|
1032
|
+
selectNeighborsHeuristic(state, candidates, mc, metric, kept);
|
|
1033
|
+
replaceNeighbors(adjacency, ord, layer, kept.ords, kept.size);
|
|
1000
1034
|
}
|
|
1001
1035
|
|
|
1002
1036
|
// src/vector/hnsw/search.ts
|
|
@@ -1039,20 +1073,20 @@ function collectHits(state, query, k, searchMetric, minSimilarity, filter, efSea
|
|
|
1039
1073
|
const metric = searchMetric;
|
|
1040
1074
|
distFn = (ord) => store.distanceFromArena(arenaQuery, ord, metric);
|
|
1041
1075
|
}
|
|
1042
|
-
|
|
1076
|
+
const workspace = state.workspace;
|
|
1077
|
+
const candidates = workspace.traversal;
|
|
1078
|
+
setSingleEntryPoint(workspace, state.entryPointOrd);
|
|
1043
1079
|
for (let layer = state.topLayer; layer >= 1; layer--) {
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
currentEPs = [nearest.ord];
|
|
1080
|
+
searchLayer(state, query, qMag, 1, layer, searchMetric, true, distFn, candidates);
|
|
1081
|
+
if (candidates.size > 0) {
|
|
1082
|
+
setSingleEntryPoint(workspace, candidates.ords[0]);
|
|
1048
1083
|
}
|
|
1049
1084
|
}
|
|
1050
|
-
|
|
1051
|
-
const candidateArray = candidateHeap.toSortedArray().reverse();
|
|
1085
|
+
searchLayer(state, query, qMag, ef, 0, searchMetric, true, distFn, candidates);
|
|
1052
1086
|
if (useQuantized) {
|
|
1053
1087
|
return rerankWithFullPrecision(
|
|
1054
1088
|
state,
|
|
1055
|
-
|
|
1089
|
+
candidates,
|
|
1056
1090
|
query,
|
|
1057
1091
|
qMag,
|
|
1058
1092
|
arenaQuery,
|
|
@@ -1064,33 +1098,35 @@ function collectHits(state, query, k, searchMetric, minSimilarity, filter, efSea
|
|
|
1064
1098
|
);
|
|
1065
1099
|
}
|
|
1066
1100
|
const hits = [];
|
|
1067
|
-
for (
|
|
1068
|
-
|
|
1069
|
-
|
|
1101
|
+
for (let i = 0; i < candidates.size; i++) {
|
|
1102
|
+
const ord = candidates.ords[i];
|
|
1103
|
+
if (filter && !ordinalFilterHas(filter, ord)) continue;
|
|
1104
|
+
const score = toScore(candidates.distances[i], searchMetric);
|
|
1070
1105
|
if (score < minSimilarity) continue;
|
|
1071
|
-
if (!hasDocument(
|
|
1072
|
-
hits.push({ ord
|
|
1106
|
+
if (!hasDocument(ord)) continue;
|
|
1107
|
+
hits.push({ ord, score });
|
|
1073
1108
|
}
|
|
1074
1109
|
return hits;
|
|
1075
1110
|
}
|
|
1076
1111
|
function rerankWithFullPrecision(state, candidates, query, qMag, arenaQuery, k, metric, minSimilarity, filter, hasDocument) {
|
|
1077
1112
|
const reranked = [];
|
|
1078
1113
|
const rerankLimit = Math.max(k * SQ8_OVERSELECTION_FACTOR, 10);
|
|
1079
|
-
for (
|
|
1080
|
-
|
|
1114
|
+
for (let i = 0; i < candidates.size; i++) {
|
|
1115
|
+
const ord = candidates.ords[i];
|
|
1116
|
+
if (filter && !ordinalFilterHas(filter, ord)) continue;
|
|
1081
1117
|
let fullDistance;
|
|
1082
1118
|
if (arenaQuery) {
|
|
1083
|
-
fullDistance = state.store.distanceFromArena(arenaQuery,
|
|
1119
|
+
fullDistance = state.store.distanceFromArena(arenaQuery, ord, metric);
|
|
1084
1120
|
if (fullDistance === Number.POSITIVE_INFINITY) continue;
|
|
1085
1121
|
} else {
|
|
1086
|
-
const entry = entryForOrd(state,
|
|
1122
|
+
const entry = entryForOrd(state, ord);
|
|
1087
1123
|
if (!entry) continue;
|
|
1088
1124
|
fullDistance = toDistance(query, entry.vector, qMag, entry.magnitude, metric);
|
|
1089
1125
|
}
|
|
1090
1126
|
const score = toScore(fullDistance, metric);
|
|
1091
1127
|
if (score < minSimilarity) continue;
|
|
1092
|
-
if (!hasDocument(
|
|
1093
|
-
reranked.push({ ord
|
|
1128
|
+
if (!hasDocument(ord)) continue;
|
|
1129
|
+
reranked.push({ ord, score });
|
|
1094
1130
|
if (reranked.length >= rerankLimit) break;
|
|
1095
1131
|
}
|
|
1096
1132
|
return reranked;
|
|
@@ -1127,30 +1163,23 @@ function clearTombstone(state, ord) {
|
|
|
1127
1163
|
state.tombstoneCount--;
|
|
1128
1164
|
}
|
|
1129
1165
|
}
|
|
1130
|
-
function
|
|
1166
|
+
function highestNode(state, includeTombstoned) {
|
|
1131
1167
|
let bestOrd = -1;
|
|
1132
1168
|
let bestLayer = -1;
|
|
1133
1169
|
for (let ord = 0; ord < state.adjacency.slots; ord++) {
|
|
1134
1170
|
const level = nodeMaxLayer(state, ord);
|
|
1135
1171
|
if (level === -1) continue;
|
|
1136
|
-
if (state.tombstones[ord] === 1) continue;
|
|
1172
|
+
if (!includeTombstoned && state.tombstones[ord] === 1) continue;
|
|
1137
1173
|
if (level > bestLayer) {
|
|
1138
1174
|
bestLayer = level;
|
|
1139
1175
|
bestOrd = ord;
|
|
1140
1176
|
}
|
|
1141
1177
|
}
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
bestLayer = level;
|
|
1148
|
-
bestOrd = ord;
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
state.entryPointOrd = bestOrd;
|
|
1153
|
-
state.topLayer = bestOrd === -1 ? -1 : bestLayer;
|
|
1178
|
+
return bestOrd;
|
|
1179
|
+
}
|
|
1180
|
+
function setEntryPoint(state, ord) {
|
|
1181
|
+
state.entryPointOrd = ord;
|
|
1182
|
+
state.topLayer = ord === -1 ? -1 : nodeMaxLayer(state, ord);
|
|
1154
1183
|
}
|
|
1155
1184
|
function insertNode(state, docId) {
|
|
1156
1185
|
const ord = state.store.getOrdinal(docId);
|
|
@@ -1178,39 +1207,30 @@ function insertNode(state, docId) {
|
|
|
1178
1207
|
return;
|
|
1179
1208
|
}
|
|
1180
1209
|
const metric = state.buildMetric;
|
|
1210
|
+
const workspace = state.workspace;
|
|
1211
|
+
const candidates = workspace.traversal;
|
|
1212
|
+
const selected = workspace.insertSelection;
|
|
1181
1213
|
const insertDistFn = (candOrd) => nodeDistanceByOrd(state, ord, candOrd, metric);
|
|
1182
|
-
|
|
1214
|
+
setSingleEntryPoint(workspace, state.entryPointOrd);
|
|
1183
1215
|
for (let layer = state.topLayer; layer > l; layer--) {
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
currentEPs = [nearest.ord];
|
|
1216
|
+
searchLayer(state, entry.vector, entry.magnitude, 1, layer, metric, false, insertDistFn, candidates);
|
|
1217
|
+
if (candidates.size > 0) {
|
|
1218
|
+
setSingleEntryPoint(workspace, candidates.ords[0]);
|
|
1188
1219
|
}
|
|
1189
1220
|
}
|
|
1190
1221
|
for (let layer = Math.min(l, state.topLayer); layer >= 0; layer--) {
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
state
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
false,
|
|
1200
|
-
insertDistFn
|
|
1201
|
-
);
|
|
1202
|
-
const candidates = heap.toSortedArray().reverse();
|
|
1203
|
-
const mc = maxConns(state, layer);
|
|
1204
|
-
const neighbors = selectNeighborsHeuristic(state, ord, candidates, mc, layer, metric);
|
|
1205
|
-
for (const neighbor of neighbors) {
|
|
1206
|
-
addNeighbor(state.adjacency, ord, layer, neighbor.ord);
|
|
1207
|
-
if (layer <= nodeMaxLayer(state, neighbor.ord)) {
|
|
1208
|
-
addNeighbor(state.adjacency, neighbor.ord, layer, ord);
|
|
1209
|
-
pruneConnections(state, neighbor.ord, layer, metric);
|
|
1222
|
+
searchLayer(state, entry.vector, entry.magnitude, state.efCons, layer, metric, false, insertDistFn, candidates);
|
|
1223
|
+
selectNeighborsHeuristic(state, candidates, maxConns(state, layer), metric, selected);
|
|
1224
|
+
for (let i = 0; i < selected.size; i++) {
|
|
1225
|
+
const neighborOrd = selected.ords[i];
|
|
1226
|
+
addNeighbor(state.adjacency, ord, layer, neighborOrd);
|
|
1227
|
+
if (layer <= nodeMaxLayer(state, neighborOrd)) {
|
|
1228
|
+
addNeighbor(state.adjacency, neighborOrd, layer, ord);
|
|
1229
|
+
pruneConnections(state, neighborOrd, layer, metric);
|
|
1210
1230
|
}
|
|
1211
1231
|
}
|
|
1212
|
-
if (candidates.
|
|
1213
|
-
|
|
1232
|
+
if (candidates.size > 0) {
|
|
1233
|
+
setEntryPointsFromList(workspace, candidates);
|
|
1214
1234
|
}
|
|
1215
1235
|
}
|
|
1216
1236
|
if (l > state.topLayer) {
|
|
@@ -1240,17 +1260,18 @@ function removeNodeEager(state, ord, excludeOrds) {
|
|
|
1240
1260
|
candidateOrds.add(otherOrd);
|
|
1241
1261
|
}
|
|
1242
1262
|
}
|
|
1243
|
-
const candidates =
|
|
1263
|
+
const candidates = state.workspace.repairCandidates;
|
|
1264
|
+
candidates.size = 0;
|
|
1244
1265
|
for (const candOrd of candidateOrds) {
|
|
1245
1266
|
const dist = nodeDistanceByOrd(state, neighborOrd, candOrd, metric);
|
|
1246
1267
|
if (dist === Number.POSITIVE_INFINITY) continue;
|
|
1247
|
-
candidates
|
|
1268
|
+
appendToList(candidates, candOrd, dist);
|
|
1248
1269
|
}
|
|
1249
|
-
const selected =
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1270
|
+
const selected = state.workspace.repairSelection;
|
|
1271
|
+
selectNeighborsHeuristic(state, candidates, mc, metric, selected);
|
|
1272
|
+
replaceNeighbors(state.adjacency, neighborOrd, layer, selected.ords, selected.size);
|
|
1273
|
+
for (let i = 0; i < selected.size; i++) {
|
|
1274
|
+
const newConnOrd = selected.ords[i];
|
|
1254
1275
|
if (layer <= nodeMaxLayer(state, newConnOrd)) {
|
|
1255
1276
|
addNeighbor(state.adjacency, newConnOrd, layer, neighborOrd);
|
|
1256
1277
|
pruneConnections(state, newConnOrd, layer, metric);
|
|
@@ -1263,11 +1284,11 @@ function removeNodeEager(state, ord, excludeOrds) {
|
|
|
1263
1284
|
clearTombstone(state, ord);
|
|
1264
1285
|
if (state.entryPointOrd === ord) {
|
|
1265
1286
|
if (state.nodeCount === 0) {
|
|
1266
|
-
state
|
|
1267
|
-
state.topLayer = -1;
|
|
1287
|
+
setEntryPoint(state, -1);
|
|
1268
1288
|
return;
|
|
1269
1289
|
}
|
|
1270
|
-
|
|
1290
|
+
const live = highestNode(state, false);
|
|
1291
|
+
setEntryPoint(state, live === -1 ? highestNode(state, true) : live);
|
|
1271
1292
|
}
|
|
1272
1293
|
}
|
|
1273
1294
|
function markTombstone(state, docId) {
|
|
@@ -1278,24 +1299,7 @@ function markTombstone(state, docId) {
|
|
|
1278
1299
|
state.tombstoneCount++;
|
|
1279
1300
|
}
|
|
1280
1301
|
if (state.entryPointOrd === ord) {
|
|
1281
|
-
|
|
1282
|
-
let bestLayer = -1;
|
|
1283
|
-
for (let o = 0; o < state.adjacency.slots; o++) {
|
|
1284
|
-
const level = nodeMaxLayer(state, o);
|
|
1285
|
-
if (level === -1) continue;
|
|
1286
|
-
if (state.tombstones[o] === 1) continue;
|
|
1287
|
-
if (level > bestLayer) {
|
|
1288
|
-
bestLayer = level;
|
|
1289
|
-
bestOrd = o;
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1292
|
-
if (bestOrd !== -1) {
|
|
1293
|
-
state.entryPointOrd = bestOrd;
|
|
1294
|
-
state.topLayer = bestLayer;
|
|
1295
|
-
} else {
|
|
1296
|
-
state.entryPointOrd = -1;
|
|
1297
|
-
state.topLayer = -1;
|
|
1298
|
-
}
|
|
1302
|
+
setEntryPoint(state, highestNode(state, false));
|
|
1299
1303
|
}
|
|
1300
1304
|
}
|
|
1301
1305
|
function compactTombstones(state) {
|
|
@@ -1408,7 +1412,7 @@ function deserializeGraph(state, data) {
|
|
|
1408
1412
|
for (const node of nodes) {
|
|
1409
1413
|
createNode(state.adjacency, node.ord, node.maxLayer);
|
|
1410
1414
|
for (const [layer, neighbors] of node.layers) {
|
|
1411
|
-
replaceNeighbors(state.adjacency, node.ord, layer, neighbors);
|
|
1415
|
+
replaceNeighbors(state.adjacency, node.ord, layer, neighbors, neighbors.length);
|
|
1412
1416
|
}
|
|
1413
1417
|
state.nodeCount++;
|
|
1414
1418
|
}
|
|
@@ -1459,7 +1463,8 @@ function createHNSWIndex(dimension, store, config, quantizer) {
|
|
|
1459
1463
|
visited: new Uint32Array(0),
|
|
1460
1464
|
visitStamp: 0,
|
|
1461
1465
|
entryPointOrd: -1,
|
|
1462
|
-
topLayer: -1
|
|
1466
|
+
topLayer: -1,
|
|
1467
|
+
workspace: createHNSWWorkspace()
|
|
1463
1468
|
};
|
|
1464
1469
|
function clear() {
|
|
1465
1470
|
resetAdjacency(state.adjacency);
|
|
@@ -1560,4 +1565,4 @@ function createHNSWIndex(dimension, store, config, quantizer) {
|
|
|
1560
1565
|
};
|
|
1561
1566
|
}
|
|
1562
1567
|
|
|
1563
|
-
export { ABSENT_DOCUMENT_RANK, MAX_M, addToOrdinalFilter, arenaFloat32Distance, cosineSimilarityWithMagnitudes, createArenaSimd,
|
|
1568
|
+
export { ABSENT_DOCUMENT_RANK, MAX_M, addToOrdinalFilter, arenaFloat32Distance, cosineSimilarityWithMagnitudes, createArenaSimd, createHNSWIndex, createHNSWWorkspace, createOrdinalFilter, createSharedArenaSimd, createVectorStore, dotProduct, euclideanDistance, importAdjacency, isSimdAvailable, magnitude, ordinalFilterHas, ordinalFilterValues, removeFromOrdinalFilter, searchOrdinals, toScore };
|