@delali/narsil-react-native 0.0.0-stage → 0.3.1-next.146
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/LICENSE +201 -0
- package/NarsilReactNative.podspec +57 -0
- package/README.md +46 -2
- package/android/CMakeLists.txt +48 -0
- package/android/build.gradle +151 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/AndroidEnvironment.cpp +44 -0
- package/android/src/main/cpp/AndroidEnvironment.hpp +11 -0
- package/android/src/main/cpp/cpp-adapter.cpp +11 -0
- package/android/src/main/java/com/margelo/nitro/narsil/NarsilAndroidEnvironment.kt +29 -0
- package/android/src/main/java/com/margelo/nitro/narsil/NarsilReactNativePackage.kt +22 -0
- package/app.plugin.js +25 -0
- package/cpp/AttachedField.cpp +234 -0
- package/cpp/AttachedField.hpp +61 -0
- package/cpp/Compression.cpp +113 -0
- package/cpp/Compression.hpp +16 -0
- package/cpp/GraphHeldAlone.cpp +29 -0
- package/cpp/GraphHeldAlone.hpp +25 -0
- package/cpp/HostMemory.cpp +123 -0
- package/cpp/HostMemory.hpp +53 -0
- package/cpp/HybridNarsilCoreImpl.cpp +390 -0
- package/cpp/HybridNarsilCoreImpl.hpp +68 -0
- package/cpp/HybridNarsilFilesImpl.cpp +245 -0
- package/cpp/HybridNarsilFilesImpl.hpp +34 -0
- package/cpp/HybridNarsilHostImpl.cpp +126 -0
- package/cpp/HybridNarsilHostImpl.hpp +42 -0
- package/cpp/NativeAtomics.cpp +110 -0
- package/cpp/NativeAtomics.hpp +11 -0
- package/cpp/ProcessInfo.cpp +83 -0
- package/cpp/ProcessInfo.hpp +11 -0
- package/cpp/ThreadPriority.cpp +77 -0
- package/cpp/ThreadPriority.hpp +8 -0
- package/cpp/WorkletRuntimeDispatcher.cpp +61 -0
- package/cpp/WorkletRuntimeDispatcher.hpp +9 -0
- package/cpp/core/include/narsil_core.h +208 -0
- package/cpp/core/src/connect.c +213 -0
- package/cpp/core/src/distance.c +103 -0
- package/cpp/core/src/files.c +99 -0
- package/cpp/core/src/heap.c +84 -0
- package/cpp/core/src/internal.h +284 -0
- package/cpp/core/src/kernels_code.c +209 -0
- package/cpp/core/src/kernels_float.c +197 -0
- package/cpp/core/src/kernels_pair.c +133 -0
- package/cpp/core/src/last_links.c +145 -0
- package/cpp/core/src/links.c +195 -0
- package/cpp/core/src/locks.c +168 -0
- package/cpp/core/src/nearest_links.c +147 -0
- package/cpp/core/src/place.c +215 -0
- package/cpp/core/src/quantize.c +226 -0
- package/cpp/core/src/records.c +97 -0
- package/cpp/core/src/remove.c +205 -0
- package/cpp/core/src/vectors.c +139 -0
- package/cpp/core/src/walk.c +231 -0
- package/cpp/core/src/workspace.c +167 -0
- package/ios/PrivacyInfo.xcprivacy +23 -0
- package/lib/client.d.ts +19 -0
- package/lib/client.js +41 -0
- package/lib/constants.d.ts +9 -0
- package/lib/constants.js +9 -0
- package/lib/engine/byte-files.d.ts +5 -0
- package/lib/engine/byte-files.js +97 -0
- package/lib/engine/core.d.ts +3 -0
- package/lib/engine/core.js +113 -0
- package/lib/engine/files.d.ts +3 -0
- package/lib/engine/files.js +188 -0
- package/lib/engine/host.d.ts +13 -0
- package/lib/engine/host.js +102 -0
- package/lib/engine/install.d.ts +4 -0
- package/lib/engine/install.js +29 -0
- package/lib/engine/native-error.d.ts +1 -0
- package/lib/engine/native-error.js +12 -0
- package/lib/engine/stand-ins/atomics.d.ts +5 -0
- package/lib/engine/stand-ins/atomics.js +236 -0
- package/lib/engine/stand-ins/crypto.d.ts +2 -0
- package/lib/engine/stand-ins/crypto.js +65 -0
- package/lib/engine/stand-ins/process.d.ts +2 -0
- package/lib/engine/stand-ins/process.js +22 -0
- package/lib/engine/stand-ins/streams.d.ts +1 -0
- package/lib/engine/stand-ins/streams.js +31 -0
- package/lib/engine/stand-ins/structured-clone.d.ts +1 -0
- package/lib/engine/stand-ins/structured-clone.js +121 -0
- package/lib/engine/streamed.d.ts +6 -0
- package/lib/engine/streamed.js +29 -0
- package/lib/engine/transfer.d.ts +3 -0
- package/lib/engine/transfer.js +77 -0
- package/lib/errors.d.ts +25 -0
- package/lib/errors.js +59 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +2 -0
- package/lib/methods.d.ts +5 -0
- package/lib/methods.js +47 -0
- package/lib/native-objects.d.ts +2 -0
- package/lib/native-objects.js +24 -0
- package/lib/specs/core.nitro.d.ts +79 -0
- package/lib/specs/core.nitro.js +1 -0
- package/lib/specs/files.nitro.d.ts +36 -0
- package/lib/specs/files.nitro.js +1 -0
- package/lib/specs/host.nitro.d.ts +24 -0
- package/lib/specs/host.nitro.js +1 -0
- package/lib/transferred.d.ts +15 -0
- package/lib/transferred.js +6 -0
- package/lib/types.d.ts +136 -0
- package/lib/types.js +1 -0
- package/lib/worklets/app-turns.d.ts +1 -0
- package/lib/worklets/app-turns.js +44 -0
- package/lib/worklets/client.d.ts +30 -0
- package/lib/worklets/client.js +144 -0
- package/lib/worklets/index.d.ts +4 -0
- package/lib/worklets/index.js +2 -0
- package/lib/worklets/instances.d.ts +15 -0
- package/lib/worklets/instances.js +194 -0
- package/lib/worklets/json-part.d.ts +1 -0
- package/lib/worklets/json-part.js +42 -0
- package/lib/worklets/runtime.d.ts +25 -0
- package/lib/worklets/runtime.js +80 -0
- package/lib/worklets/transfer-queue.d.ts +7 -0
- package/lib/worklets/transfer-queue.js +79 -0
- package/lib/worklets/types.d.ts +26 -0
- package/lib/worklets/types.js +1 -0
- package/nitro.json +32 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/NarsilReactNative+autolinking.cmake +85 -0
- package/nitrogen/generated/android/NarsilReactNative+autolinking.gradle +27 -0
- package/nitrogen/generated/android/NarsilReactNativeOnLoad.cpp +69 -0
- package/nitrogen/generated/android/NarsilReactNativeOnLoad.hpp +34 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/narsil/NarsilReactNativeOnLoad.kt +35 -0
- package/nitrogen/generated/ios/NarsilReactNative+autolinking.rb +62 -0
- package/nitrogen/generated/ios/NarsilReactNative-Swift-Cxx-Bridge.cpp +17 -0
- package/nitrogen/generated/ios/NarsilReactNative-Swift-Cxx-Bridge.hpp +27 -0
- package/nitrogen/generated/ios/NarsilReactNative-Swift-Cxx-Umbrella.hpp +38 -0
- package/nitrogen/generated/ios/NarsilReactNativeAutolinking.mm +55 -0
- package/nitrogen/generated/ios/NarsilReactNativeAutolinking.swift +16 -0
- package/nitrogen/generated/shared/c++/DirectoryEntry.hpp +91 -0
- package/nitrogen/generated/shared/c++/FileStatus.hpp +103 -0
- package/nitrogen/generated/shared/c++/GraphMemory.hpp +120 -0
- package/nitrogen/generated/shared/c++/HybridNarsilAttachedGraphSpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridNarsilAttachedGraphSpec.hpp +62 -0
- package/nitrogen/generated/shared/c++/HybridNarsilAttachedStoreSpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridNarsilAttachedStoreSpec.hpp +62 -0
- package/nitrogen/generated/shared/c++/HybridNarsilCoreSpec.cpp +35 -0
- package/nitrogen/generated/shared/c++/HybridNarsilCoreSpec.hpp +100 -0
- package/nitrogen/generated/shared/c++/HybridNarsilFilesSpec.cpp +37 -0
- package/nitrogen/generated/shared/c++/HybridNarsilFilesSpec.hpp +86 -0
- package/nitrogen/generated/shared/c++/HybridNarsilHostSpec.cpp +34 -0
- package/nitrogen/generated/shared/c++/HybridNarsilHostSpec.hpp +78 -0
- package/nitrogen/generated/shared/c++/MemoryView.hpp +91 -0
- package/nitrogen/generated/shared/c++/NativeConnectOutcome.hpp +87 -0
- package/nitrogen/generated/shared/c++/NativeScores.hpp +87 -0
- package/nitrogen/generated/shared/c++/NativeSearchHits.hpp +91 -0
- package/nitrogen/generated/shared/c++/ProcessMemory.hpp +87 -0
- package/nitrogen/generated/shared/c++/StoreMemory.hpp +142 -0
- package/package.json +87 -3
- package/react-native.config.js +8 -0
- package/src/client.ts +44 -0
- package/src/constants.ts +9 -0
- package/src/engine/byte-files.ts +99 -0
- package/src/engine/core.ts +162 -0
- package/src/engine/files.ts +186 -0
- package/src/engine/host.ts +111 -0
- package/src/engine/install.ts +31 -0
- package/src/engine/native-error.ts +10 -0
- package/src/engine/stand-ins/atomics.ts +288 -0
- package/src/engine/stand-ins/crypto.ts +81 -0
- package/src/engine/stand-ins/process.ts +30 -0
- package/src/engine/stand-ins/streams.ts +45 -0
- package/src/engine/stand-ins/structured-clone.ts +118 -0
- package/src/engine/streamed.ts +40 -0
- package/src/engine/transfer.ts +72 -0
- package/src/errors.ts +73 -0
- package/src/index.ts +3 -0
- package/src/methods.ts +52 -0
- package/src/native-objects.ts +30 -0
- package/src/specs/core.nitro.ts +140 -0
- package/src/specs/files.nitro.ts +42 -0
- package/src/specs/host.nitro.ts +30 -0
- package/src/transferred.ts +18 -0
- package/src/types.ts +164 -0
- package/src/worklets/app-turns.ts +48 -0
- package/src/worklets/client.ts +165 -0
- package/src/worklets/index.ts +4 -0
- package/src/worklets/instances.ts +231 -0
- package/src/worklets/json-part.ts +38 -0
- package/src/worklets/runtime.ts +190 -0
- package/src/worklets/transfer-queue.ts +98 -0
- package/src/worklets/types.ts +37 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
#include "internal.h"
|
|
2
|
+
|
|
3
|
+
#include "../include/narsil_core.h"
|
|
4
|
+
|
|
5
|
+
#include <math.h>
|
|
6
|
+
#include <stddef.h>
|
|
7
|
+
#include <stdint.h>
|
|
8
|
+
#include <string.h>
|
|
9
|
+
|
|
10
|
+
#define OSQ_LAMBDA 0.1
|
|
11
|
+
#define OSQ_REFINE_ROUNDS 5
|
|
12
|
+
#define OSQ_REFINE_TOLERANCE 1e-8
|
|
13
|
+
#define OSQ_GRID_ONE_BIT 0.798
|
|
14
|
+
#define OSQ_GRID_TWO_BITS 1.493
|
|
15
|
+
#define OSQ_GRID_FOUR_BITS 2.514
|
|
16
|
+
#define OSQ_GRID_EIGHT_BITS 3.922
|
|
17
|
+
#define NARROW_CODE_QUERY_BITS 4
|
|
18
|
+
#define NIBBLE_CODE_BITS 4U
|
|
19
|
+
#define ROUND_HALF_UP 0.5
|
|
20
|
+
|
|
21
|
+
typedef struct {
|
|
22
|
+
const double *centred;
|
|
23
|
+
uint32_t dimension;
|
|
24
|
+
double steps;
|
|
25
|
+
double norm;
|
|
26
|
+
} refinement;
|
|
27
|
+
|
|
28
|
+
typedef struct {
|
|
29
|
+
double correction;
|
|
30
|
+
double total;
|
|
31
|
+
double minimum;
|
|
32
|
+
double maximum;
|
|
33
|
+
} centred_summary;
|
|
34
|
+
|
|
35
|
+
uint32_t code_bytes_of(uint32_t dimension, uint32_t bits) {
|
|
36
|
+
return ((dimension * bits) + (BITS_PER_BYTE - 1)) / BITS_PER_BYTE;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
uint32_t narsil_record_bytes(uint32_t dimension, uint32_t bits) {
|
|
40
|
+
if (bits == 0) { return 0; }
|
|
41
|
+
return code_bytes_of(dimension, bits) + NARSIL_OSQ_TRAILER_BYTES;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
uint32_t query_bits_of(uint32_t bits) { return bits == BITS_PER_BYTE ? BITS_PER_BYTE : NARROW_CODE_QUERY_BITS; }
|
|
45
|
+
|
|
46
|
+
static double steps_of(uint32_t bits) { return (double)((1U << bits) - 1); }
|
|
47
|
+
|
|
48
|
+
static double grid_of(uint32_t bits) {
|
|
49
|
+
switch (bits) {
|
|
50
|
+
case 1:
|
|
51
|
+
return OSQ_GRID_ONE_BIT;
|
|
52
|
+
case 2:
|
|
53
|
+
return OSQ_GRID_TWO_BITS;
|
|
54
|
+
case 4:
|
|
55
|
+
return OSQ_GRID_FOUR_BITS;
|
|
56
|
+
default:
|
|
57
|
+
return OSQ_GRID_EIGHT_BITS;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static double clamp(double value, level_range range) {
|
|
62
|
+
double raised = value > range.lower ? value : range.lower;
|
|
63
|
+
return raised < range.upper ? raised : range.upper;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static double level_of(double value, level_range range, double steps) {
|
|
67
|
+
if (range.upper == range.lower) { return 0; }
|
|
68
|
+
return floor((((clamp(value, range) - range.lower) * steps) / (range.upper - range.lower)) + ROUND_HALF_UP);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static double loss_of(const refinement *problem, level_range range) {
|
|
72
|
+
const double *centred = problem->centred;
|
|
73
|
+
double projected_error = 0;
|
|
74
|
+
double squared_error = 0;
|
|
75
|
+
double width = (range.upper - range.lower) / problem->steps;
|
|
76
|
+
for (uint32_t i = 0; i < problem->dimension; i++) {
|
|
77
|
+
double reconstructed = range.lower + (width * level_of(centred[i], range, problem->steps));
|
|
78
|
+
projected_error += centred[i] * (centred[i] - reconstructed);
|
|
79
|
+
squared_error += (centred[i] - reconstructed) * (centred[i] - reconstructed);
|
|
80
|
+
}
|
|
81
|
+
return (((1 - OSQ_LAMBDA) * projected_error * projected_error) / problem->norm) + (OSQ_LAMBDA * squared_error);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static level_range refine(refinement problem, level_range start) {
|
|
85
|
+
level_range range = start;
|
|
86
|
+
const double *centred = problem.centred;
|
|
87
|
+
for (uint32_t i = 0; i < problem.dimension; i++) { problem.norm += centred[i] * centred[i]; }
|
|
88
|
+
if (problem.norm == 0 || range.upper == range.lower) { return range; }
|
|
89
|
+
double best = loss_of(&problem, range);
|
|
90
|
+
for (int round = 0; round < OSQ_REFINE_ROUNDS; round++) {
|
|
91
|
+
double daa = 0;
|
|
92
|
+
double dab = 0;
|
|
93
|
+
double dbb = 0;
|
|
94
|
+
double dax = 0;
|
|
95
|
+
double dbx = 0;
|
|
96
|
+
for (uint32_t i = 0; i < problem.dimension; i++) {
|
|
97
|
+
double fraction = level_of(centred[i], range, problem.steps) / problem.steps;
|
|
98
|
+
daa += (1 - fraction) * (1 - fraction);
|
|
99
|
+
dab += (1 - fraction) * fraction;
|
|
100
|
+
dbb += fraction * fraction;
|
|
101
|
+
dax += centred[i] * (1 - fraction);
|
|
102
|
+
dbx += centred[i] * fraction;
|
|
103
|
+
}
|
|
104
|
+
double gram_aa = (((1 - OSQ_LAMBDA) * dax * dax) / problem.norm) + (OSQ_LAMBDA * daa);
|
|
105
|
+
double gram_ab = (((1 - OSQ_LAMBDA) * dax * dbx) / problem.norm) + (OSQ_LAMBDA * dab);
|
|
106
|
+
double gram_bb = (((1 - OSQ_LAMBDA) * dbx * dbx) / problem.norm) + (OSQ_LAMBDA * dbb);
|
|
107
|
+
double determinant = (gram_aa * gram_bb) - (gram_ab * gram_ab);
|
|
108
|
+
if (determinant == 0) { break; }
|
|
109
|
+
level_range next = {((gram_bb * dax) - (gram_ab * dbx)) / determinant,
|
|
110
|
+
((gram_aa * dbx) - (gram_ab * dax)) / determinant};
|
|
111
|
+
if (fabs(next.lower - range.lower) < OSQ_REFINE_TOLERANCE &&
|
|
112
|
+
fabs(next.upper - range.upper) < OSQ_REFINE_TOLERANCE) {
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
double candidate = loss_of(&problem, next);
|
|
116
|
+
if (candidate > best) { break; }
|
|
117
|
+
range = next;
|
|
118
|
+
best = candidate;
|
|
119
|
+
}
|
|
120
|
+
return range;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
void normalise_into(double *normalised, const float *vector, uint32_t dimension) {
|
|
124
|
+
double squares = 0;
|
|
125
|
+
for (uint32_t i = 0; i < dimension; i++) { squares += (double)vector[i] * (double)vector[i]; }
|
|
126
|
+
double length = sqrt(squares);
|
|
127
|
+
for (uint32_t i = 0; i < dimension; i++) {
|
|
128
|
+
normalised[i] = length == 0 ? (double)vector[i] : (double)vector[i] / length;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
static centred_summary centre_into(const narsil_store *store, narsil_metric metric, const double *normalised,
|
|
133
|
+
double *centred) {
|
|
134
|
+
centred_summary summary = {0, 0, HUGE_VAL, -HUGE_VAL};
|
|
135
|
+
for (uint32_t i = 0; i < store->dimension; i++) {
|
|
136
|
+
double centre = (double)store->centroid[i];
|
|
137
|
+
centred[i] = normalised[i] - centre;
|
|
138
|
+
summary.correction += metric == NARSIL_METRIC_EUCLIDEAN ? centred[i] * centred[i] : normalised[i] * centre;
|
|
139
|
+
summary.total += centred[i];
|
|
140
|
+
if (centred[i] < summary.minimum) { summary.minimum = centred[i]; }
|
|
141
|
+
if (centred[i] > summary.maximum) { summary.maximum = centred[i]; }
|
|
142
|
+
}
|
|
143
|
+
return summary;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static level_range initial_range(const narsil_store *store, const double *centred, centred_summary summary,
|
|
147
|
+
uint32_t query_bits) {
|
|
148
|
+
uint32_t dimension = store->dimension;
|
|
149
|
+
double mean = summary.total / dimension;
|
|
150
|
+
double spread = 0;
|
|
151
|
+
for (uint32_t i = 0; i < dimension; i++) { spread += (centred[i] - mean) * (centred[i] - mean); }
|
|
152
|
+
double deviation = sqrt(spread / dimension);
|
|
153
|
+
level_range observed = {summary.minimum, summary.maximum};
|
|
154
|
+
level_range start = {clamp(mean - (grid_of(query_bits) * deviation), observed),
|
|
155
|
+
clamp(mean + (grid_of(query_bits) * deviation), observed)};
|
|
156
|
+
refinement problem = {centred, dimension, steps_of(query_bits), 0};
|
|
157
|
+
return refine(problem, start);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
static void fill_levels(const narsil_store *store, const double *centred, prepared_code *code) {
|
|
161
|
+
uint32_t dimension = store->dimension;
|
|
162
|
+
uint32_t document_bits = store->bits;
|
|
163
|
+
uint32_t per_byte = document_bits == 1 || document_bits == 2 ? BITS_PER_BYTE / document_bits : 0;
|
|
164
|
+
uint32_t plane_bytes = per_byte == 0 ? 0 : code_bytes_of(dimension, document_bits);
|
|
165
|
+
uint32_t pairs = (dimension + 1) / 2;
|
|
166
|
+
double steps = steps_of(code->bits);
|
|
167
|
+
memset(code->low_levels, 0, pairs);
|
|
168
|
+
memset(code->high_levels, 0, pairs);
|
|
169
|
+
memset(code->planes, 0, (size_t)QUERY_PLANES * plane_bytes);
|
|
170
|
+
double sum = 0;
|
|
171
|
+
for (uint32_t i = 0; i < dimension; i++) {
|
|
172
|
+
uint8_t level = (uint8_t)level_of(centred[i], code->range, steps);
|
|
173
|
+
code->levels[i] = level;
|
|
174
|
+
sum += level;
|
|
175
|
+
if ((i & 1) == 0) {
|
|
176
|
+
code->low_levels[i >> 1] = level;
|
|
177
|
+
} else {
|
|
178
|
+
code->high_levels[i >> 1] = level;
|
|
179
|
+
}
|
|
180
|
+
if (per_byte == 0 || level == 0) { continue; }
|
|
181
|
+
uint8_t bit = (uint8_t)(1U << ((i % per_byte) * document_bits));
|
|
182
|
+
for (uint32_t plane = 0; plane < QUERY_PLANES; plane++) {
|
|
183
|
+
if (((level >> plane) & 1) != 0) { code->planes[((size_t)plane * plane_bytes) + (i / per_byte)] |= bit; }
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
code->sum = sum;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
void quantise_vector(narsil_workspace *workspace, const narsil_store *store, narsil_metric metric, const float *vector,
|
|
190
|
+
uint32_t bits, quantised_vector *quantised) {
|
|
191
|
+
double *normalised = workspace->normalised;
|
|
192
|
+
double *centred = workspace->centred;
|
|
193
|
+
if (metric == NARSIL_METRIC_COSINE) {
|
|
194
|
+
normalise_into(normalised, vector, store->dimension);
|
|
195
|
+
} else {
|
|
196
|
+
for (uint32_t i = 0; i < store->dimension; i++) { normalised[i] = (double)vector[i]; }
|
|
197
|
+
}
|
|
198
|
+
centred_summary summary = centre_into(store, metric, normalised, centred);
|
|
199
|
+
quantised->range = initial_range(store, centred, summary, bits);
|
|
200
|
+
quantised->correction = summary.correction;
|
|
201
|
+
quantised->centred = centred;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
double quantised_level(double value, level_range range, uint32_t bits) {
|
|
205
|
+
return level_of(value, range, steps_of(bits));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
uint32_t placement_query_bits_of(uint32_t bits) {
|
|
209
|
+
return bits == NIBBLE_CODE_BITS ? BITS_PER_BYTE : query_bits_of(bits);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
void prepare_code(narsil_workspace *workspace, const narsil_store *store, narsil_metric metric, const float *vector,
|
|
213
|
+
prepared_code *code) {
|
|
214
|
+
prepare_code_with_bits(workspace, store, metric, vector, query_bits_of(store->bits), code);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
void prepare_code_with_bits(narsil_workspace *workspace, const narsil_store *store, narsil_metric metric,
|
|
218
|
+
const float *vector, uint32_t query_bits, prepared_code *code) {
|
|
219
|
+
quantised_vector quantised;
|
|
220
|
+
*code = workspace->code;
|
|
221
|
+
code->bits = query_bits;
|
|
222
|
+
quantise_vector(workspace, store, metric, vector, code->bits, &quantised);
|
|
223
|
+
code->range = quantised.range;
|
|
224
|
+
code->correction = quantised.correction;
|
|
225
|
+
fill_levels(store, quantised.centred, code);
|
|
226
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#include "internal.h"
|
|
2
|
+
|
|
3
|
+
#include "../include/narsil_core.h"
|
|
4
|
+
|
|
5
|
+
#include <stdatomic.h>
|
|
6
|
+
#include <stddef.h>
|
|
7
|
+
#include <stdint.h>
|
|
8
|
+
#include <string.h>
|
|
9
|
+
|
|
10
|
+
static narsil_status begin_record_work(narsil_workspace *workspace, const narsil_store *store,
|
|
11
|
+
const narsil_ordinals_request *request) {
|
|
12
|
+
if (workspace == NULL || store == NULL || request == NULL) { return NARSIL_INVALID_ARGUMENT; }
|
|
13
|
+
if (request->count > 0 && request->ordinals == NULL) { return NARSIL_INVALID_ARGUMENT; }
|
|
14
|
+
if (!store_is_complete(store) || !is_metric(request->metric) || store->dimension == 0) {
|
|
15
|
+
return NARSIL_INVALID_ARGUMENT;
|
|
16
|
+
}
|
|
17
|
+
if (store->bits == 0 || !is_code_width(store->bits) || store->records_per_block == 0) {
|
|
18
|
+
return NARSIL_INVALID_ARGUMENT;
|
|
19
|
+
}
|
|
20
|
+
return workspace_reserve_dimension(workspace, store->dimension);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
narsil_status narsil_measure(narsil_workspace *workspace, const narsil_store *store,
|
|
24
|
+
const narsil_ordinals_request *request, double *totals) {
|
|
25
|
+
if (totals == NULL) { return NARSIL_INVALID_ARGUMENT; }
|
|
26
|
+
narsil_status status = begin_record_work(workspace, store, request);
|
|
27
|
+
if (status != NARSIL_OK) { return status; }
|
|
28
|
+
double squared_lengths = totals[store->dimension];
|
|
29
|
+
for (uint32_t i = 0; i < request->count; i++) {
|
|
30
|
+
const float *vector = stored_vector(store, request->ordinals[i], workspace->stored_vector);
|
|
31
|
+
if (vector == NULL) { continue; }
|
|
32
|
+
if (request->metric == NARSIL_METRIC_COSINE) {
|
|
33
|
+
normalise_into(workspace->normalised, vector, store->dimension);
|
|
34
|
+
for (uint32_t axis = 0; axis < store->dimension; axis++) {
|
|
35
|
+
totals[axis] += workspace->normalised[axis];
|
|
36
|
+
squared_lengths += workspace->normalised[axis] * workspace->normalised[axis];
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
for (uint32_t axis = 0; axis < store->dimension; axis++) {
|
|
40
|
+
double value = (double)vector[axis];
|
|
41
|
+
totals[axis] += value;
|
|
42
|
+
squared_lengths += value * value;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
totals[store->dimension + 1] += 1;
|
|
46
|
+
}
|
|
47
|
+
totals[store->dimension] = squared_lengths;
|
|
48
|
+
return NARSIL_OK;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static uint32_t pack_levels(const narsil_store *store, const quantised_vector *quantised, uint8_t *record) {
|
|
52
|
+
uint32_t bits = store->bits;
|
|
53
|
+
uint32_t per_byte = BITS_PER_BYTE / bits;
|
|
54
|
+
uint32_t sum = 0;
|
|
55
|
+
memset(record, 0, code_bytes_of(store->dimension, bits));
|
|
56
|
+
for (uint32_t i = 0; i < store->dimension; i++) {
|
|
57
|
+
uint32_t level = (uint32_t)quantised_level(quantised->centred[i], quantised->range, bits);
|
|
58
|
+
sum += level;
|
|
59
|
+
record[i / per_byte] |= (uint8_t)(level << ((i % per_byte) * bits));
|
|
60
|
+
}
|
|
61
|
+
return sum;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static void write_record(const narsil_store *store, const quantised_vector *quantised, uint8_t *record) {
|
|
65
|
+
uint32_t sum = pack_levels(store, quantised, record);
|
|
66
|
+
uint8_t *trailer = record + code_bytes_of(store->dimension, store->bits);
|
|
67
|
+
float lower = (float)quantised->range.lower;
|
|
68
|
+
float upper = (float)quantised->range.upper;
|
|
69
|
+
float correction = (float)quantised->correction;
|
|
70
|
+
memcpy(trailer + TRAILER_LOWER, &lower, sizeof lower);
|
|
71
|
+
memcpy(trailer + TRAILER_UPPER, &upper, sizeof upper);
|
|
72
|
+
memcpy(trailer + TRAILER_CORRECTION, &correction, sizeof correction);
|
|
73
|
+
memcpy(trailer + TRAILER_SUM, &sum, sizeof sum);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
narsil_status narsil_quantise(narsil_workspace *workspace, const narsil_store *store,
|
|
77
|
+
const narsil_ordinals_request *request) {
|
|
78
|
+
narsil_status status = begin_record_work(workspace, store, request);
|
|
79
|
+
if (status != NARSIL_OK) { return status; }
|
|
80
|
+
if (load_word(store->header, NARSIL_STORE_WORD_CALIBRATED) != 1) { return NARSIL_INVALID_ARGUMENT; }
|
|
81
|
+
for (uint32_t i = 0; i < request->count; i++) {
|
|
82
|
+
int32_t ordinal = request->ordinals[i];
|
|
83
|
+
const float *vector = stored_vector(store, ordinal, workspace->stored_vector);
|
|
84
|
+
uint8_t *record = stored_record(store, ordinal);
|
|
85
|
+
if (vector == NULL) { continue; }
|
|
86
|
+
if (record == NULL) { return NARSIL_NEEDS_ROOM; }
|
|
87
|
+
quantised_vector quantised;
|
|
88
|
+
quantise_vector(workspace, store, (narsil_metric)request->metric, vector, store->bits, &quantised);
|
|
89
|
+
write_record(store, &quantised, record);
|
|
90
|
+
_Atomic(uint8_t) *holds_a_record = atomic_byte(store->code_present, (uint32_t)ordinal);
|
|
91
|
+
if (atomic_load(holds_a_record) == 0) {
|
|
92
|
+
atomic_fetch_add(atomic_word(store->header, NARSIL_STORE_WORD_CODE_COUNT), 1);
|
|
93
|
+
atomic_store(holds_a_record, 1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return NARSIL_OK;
|
|
97
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#include "internal.h"
|
|
2
|
+
|
|
3
|
+
#include "../include/narsil_core.h"
|
|
4
|
+
|
|
5
|
+
#include <math.h>
|
|
6
|
+
#include <stdatomic.h>
|
|
7
|
+
#include <stddef.h>
|
|
8
|
+
#include <stdint.h>
|
|
9
|
+
#include <string.h>
|
|
10
|
+
|
|
11
|
+
static void begin_graph_write(walk_context *context, const narsil_graph *graph, uint32_t thread_slot) {
|
|
12
|
+
memset(context, 0, sizeof *context);
|
|
13
|
+
context->graph = graph;
|
|
14
|
+
context->thread_slot = thread_slot;
|
|
15
|
+
int32_t slots = load_word(graph->header, NARSIL_GRAPH_WORD_SLOTS);
|
|
16
|
+
context->slots = slots < 0 ? 0 : (uint32_t)slots;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
narsil_status narsil_remove(const narsil_graph *graph, const narsil_remove_request *request) {
|
|
20
|
+
if (graph == NULL || request == NULL || !graph_is_complete(graph) || request->thread_slot >= graph->thread_slots) {
|
|
21
|
+
return NARSIL_INVALID_ARGUMENT;
|
|
22
|
+
}
|
|
23
|
+
int32_t ordinal = request->ordinal;
|
|
24
|
+
graph_lock_shared(graph, request->thread_slot);
|
|
25
|
+
walk_context context;
|
|
26
|
+
begin_graph_write(&context, graph, request->thread_slot);
|
|
27
|
+
narsil_status status = NARSIL_OK;
|
|
28
|
+
if (ordinal < 0 || (uint32_t)ordinal >= context.slots) {
|
|
29
|
+
status = NARSIL_INVALID_ARGUMENT;
|
|
30
|
+
} else if (node_top_layer(&context, ordinal) >= 0) {
|
|
31
|
+
uint8_t live = 0;
|
|
32
|
+
if (atomic_compare_exchange_strong(atomic_byte(graph->tombstones, (uint32_t)ordinal), &live, 1)) {
|
|
33
|
+
atomic_fetch_add(atomic_word(graph->header, NARSIL_GRAPH_WORD_TOMBSTONE_COUNT), 1);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
graph_unlock_shared(graph, request->thread_slot);
|
|
37
|
+
return status;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static uint32_t copy_list(const walk_context *context, node_layer position, int32_t *out) {
|
|
41
|
+
uint32_t capacity = 0;
|
|
42
|
+
const int32_t *list = list_at(context, position, &capacity);
|
|
43
|
+
if (list == NULL || list[0] < 0) { return 0; }
|
|
44
|
+
uint32_t count = (uint32_t)list[0] > capacity ? capacity : (uint32_t)list[0];
|
|
45
|
+
memcpy(out, list + 1, (size_t)count * sizeof *out);
|
|
46
|
+
return count;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static void offer(const walk_context *context, int32_t node, int32_t candidate) {
|
|
50
|
+
scored_list *candidates = &context->workspace->repair_candidates;
|
|
51
|
+
for (uint32_t i = 0; i < candidates->count; i++) {
|
|
52
|
+
if (candidates->ordinals[i] == candidate) { return; }
|
|
53
|
+
}
|
|
54
|
+
candidates->ordinals[candidates->count] = candidate;
|
|
55
|
+
candidates->distances[candidates->count] = pair_distance(context, node, candidate);
|
|
56
|
+
candidates->count += 1;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static void drop_unreachable_candidates(const walk_context *context, node_layer position, held_links held) {
|
|
60
|
+
scored_list *candidates = &context->workspace->repair_candidates;
|
|
61
|
+
uint32_t kept = 0;
|
|
62
|
+
for (uint32_t i = 0; i < candidates->count; i++) {
|
|
63
|
+
if (candidates->distances[i] == HUGE_VAL) {
|
|
64
|
+
if (position.layer == 0 && list_holds(held, candidates->ordinals[i])) {
|
|
65
|
+
note_unlinked(context, candidates->ordinals[i]);
|
|
66
|
+
}
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
candidates->ordinals[kept] = candidates->ordinals[i];
|
|
70
|
+
candidates->distances[kept] = candidates->distances[i];
|
|
71
|
+
kept += 1;
|
|
72
|
+
}
|
|
73
|
+
candidates->count = kept;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static int leaves_with_this_compaction(const walk_context *context, int32_t ordinal) {
|
|
77
|
+
return node_top_layer(context, ordinal) < 0 || is_tombstoned(context, ordinal);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static held_links gather_repair_candidates(const walk_context *context, node_layer position, removed_node removed) {
|
|
81
|
+
narsil_workspace *workspace = context->workspace;
|
|
82
|
+
workspace->repair_candidates.count = 0;
|
|
83
|
+
held_links held = {workspace->neighbours, copy_list(context, position, workspace->neighbours), 0};
|
|
84
|
+
for (uint32_t i = 0; i < held.count; i++) { offer(context, position.ordinal, workspace->neighbours[i]); }
|
|
85
|
+
for (uint32_t i = 0; i < removed.former_count; i++) {
|
|
86
|
+
int32_t other = workspace->former[i];
|
|
87
|
+
if (other == position.ordinal || other == removed.ordinal || leaves_with_this_compaction(context, other)) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
offer(context, position.ordinal, other);
|
|
91
|
+
}
|
|
92
|
+
drop_unreachable_candidates(context, position, held);
|
|
93
|
+
return held;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
static void relink(const walk_context *context, node_layer position, uint32_t selected) {
|
|
97
|
+
const narsil_workspace *workspace = context->workspace;
|
|
98
|
+
for (uint32_t i = 0; i < selected; i++) {
|
|
99
|
+
node_layer other = {workspace->repaired[i], position.layer};
|
|
100
|
+
if (position.layer > node_top_layer(context, other.ordinal)) { continue; }
|
|
101
|
+
node_lock(context, other.ordinal);
|
|
102
|
+
list_add(context, other, position.ordinal);
|
|
103
|
+
list_prune(context, other);
|
|
104
|
+
node_unlock(context, other.ordinal);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
static void repair_neighbour(const walk_context *context, node_layer position, removed_node removed) {
|
|
109
|
+
narsil_workspace *workspace = context->workspace;
|
|
110
|
+
uint32_t limit = list_limit(context->graph, position.layer);
|
|
111
|
+
uint32_t capacity = 0;
|
|
112
|
+
const int32_t *list = list_at(context, position, &capacity);
|
|
113
|
+
if (list == NULL || list[0] >= (int32_t)limit) { return; }
|
|
114
|
+
held_links held = gather_repair_candidates(context, position, removed);
|
|
115
|
+
uint32_t selected = select_neighbours(context, &workspace->repair_candidates, limit, workspace->repaired);
|
|
116
|
+
node_lock(context, position.ordinal);
|
|
117
|
+
if (position.layer == 0) {
|
|
118
|
+
selected = write_keeping_last_links(context, position.ordinal, &workspace->repair_candidates, workspace->repaired,
|
|
119
|
+
selected, held);
|
|
120
|
+
} else {
|
|
121
|
+
list_replace(context, position, workspace->repaired, selected);
|
|
122
|
+
}
|
|
123
|
+
node_unlock(context, position.ordinal);
|
|
124
|
+
relink(context, position, selected);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
static void unlink_layer(const walk_context *context, node_layer layer_of_the_removed) {
|
|
128
|
+
narsil_workspace *workspace = context->workspace;
|
|
129
|
+
int32_t layer = layer_of_the_removed.layer;
|
|
130
|
+
removed_node removed = {layer_of_the_removed.ordinal, copy_list(context, layer_of_the_removed, workspace->former)};
|
|
131
|
+
for (uint32_t i = 0; i < removed.former_count; i++) {
|
|
132
|
+
node_layer position = {workspace->former[i], layer};
|
|
133
|
+
if (layer > node_top_layer(context, position.ordinal)) { continue; }
|
|
134
|
+
node_lock(context, position.ordinal);
|
|
135
|
+
list_take_out(context, position, removed.ordinal);
|
|
136
|
+
node_unlock(context, position.ordinal);
|
|
137
|
+
}
|
|
138
|
+
for (uint32_t i = 0; i < removed.former_count; i++) {
|
|
139
|
+
node_layer position = {workspace->former[i], layer};
|
|
140
|
+
if (layer > node_top_layer(context, position.ordinal)) { continue; }
|
|
141
|
+
repair_neighbour(context, position, removed);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
static void move_entry_after_removal(const walk_context *context, int32_t removed) {
|
|
146
|
+
if (load_word(context->graph->header, NARSIL_GRAPH_WORD_ENTRY_POINT) != removed) { return; }
|
|
147
|
+
entry_lock(context);
|
|
148
|
+
int32_t next = NO_NODE;
|
|
149
|
+
if (load_word(context->graph->header, NARSIL_GRAPH_WORD_NODE_COUNT) > 0) {
|
|
150
|
+
next = highest_node(context, 0);
|
|
151
|
+
if (next == NO_NODE) { next = highest_node(context, 1); }
|
|
152
|
+
}
|
|
153
|
+
store_entry_point(context, next);
|
|
154
|
+
entry_unlock(context);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
static void take_node_out(const walk_context *context, int32_t ordinal) {
|
|
158
|
+
const narsil_graph *graph = context->graph;
|
|
159
|
+
int32_t top_layer = node_top_layer(context, ordinal);
|
|
160
|
+
for (int32_t layer = 0; layer <= top_layer; layer++) {
|
|
161
|
+
node_layer removed = {ordinal, layer};
|
|
162
|
+
unlink_layer(context, removed);
|
|
163
|
+
}
|
|
164
|
+
int32_t *former_base = context->workspace->former;
|
|
165
|
+
node_layer base = {ordinal, 0};
|
|
166
|
+
uint32_t former_base_count = copy_list(context, base, former_base);
|
|
167
|
+
for (uint32_t i = 0; i < former_base_count; i++) { note_unlinked(context, former_base[i]); }
|
|
168
|
+
node_lock(context, ordinal);
|
|
169
|
+
atomic_store(atomic_byte(graph->node_levels, (uint32_t)ordinal), 0);
|
|
170
|
+
graph->upper_base[ordinal] = 0;
|
|
171
|
+
graph->level0[(size_t)ordinal * ((size_t)list_limit(graph, 0) + NARSIL_LIST_WORDS_OVER_NEIGHBOURS)] = 0;
|
|
172
|
+
atomic_store(atomic_word(graph->incoming, (uint32_t)ordinal), 0);
|
|
173
|
+
atomic_store(atomic_byte(graph->nearest_link_due, (uint32_t)ordinal), 0);
|
|
174
|
+
node_unlock(context, ordinal);
|
|
175
|
+
atomic_fetch_sub(atomic_word(graph->header, NARSIL_GRAPH_WORD_NODE_COUNT), 1);
|
|
176
|
+
uint8_t tombstoned = 1;
|
|
177
|
+
if (atomic_compare_exchange_strong(atomic_byte(graph->tombstones, (uint32_t)ordinal), &tombstoned, 0)) {
|
|
178
|
+
atomic_fetch_sub(atomic_word(graph->header, NARSIL_GRAPH_WORD_TOMBSTONE_COUNT), 1);
|
|
179
|
+
}
|
|
180
|
+
move_entry_after_removal(context, ordinal);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
narsil_status narsil_compact(narsil_workspace *workspace, const narsil_graph *graph, const narsil_store *store,
|
|
184
|
+
const narsil_compact_request *request) {
|
|
185
|
+
if (request == NULL) { return NARSIL_INVALID_ARGUMENT; }
|
|
186
|
+
walk_context context;
|
|
187
|
+
narsil_status status = begin_walk(&context, workspace, graph, store, request->metric);
|
|
188
|
+
if (status != NARSIL_OK) { return status; }
|
|
189
|
+
if (request->thread_slot >= graph->thread_slots) { return NARSIL_INVALID_ARGUMENT; }
|
|
190
|
+
if (load_word(graph->header, NARSIL_GRAPH_WORD_LOCK) != NARSIL_GRAPH_HELD_ALONE) {
|
|
191
|
+
return NARSIL_GRAPH_NOT_HELD_ALONE;
|
|
192
|
+
}
|
|
193
|
+
context.thread_slot = request->thread_slot;
|
|
194
|
+
status = read_graph_extent(&context);
|
|
195
|
+
if (status == NARSIL_OK) { status = list_reserve(&workspace->repair_candidates, MAX_REPAIR_CANDIDATES); }
|
|
196
|
+
if (status == NARSIL_OK) { status = list_reserve(&workspace->link_candidates, MAX_NEIGHBOURS_PER_LIST); }
|
|
197
|
+
if (status == NARSIL_OK) { status = reserve_last_links(workspace); }
|
|
198
|
+
if (status != NARSIL_OK) { return status; }
|
|
199
|
+
if (load_word(graph->header, NARSIL_GRAPH_WORD_TOMBSTONE_COUNT) == 0) { return NARSIL_OK; }
|
|
200
|
+
for (uint32_t ordinal = 0; ordinal < context.slots; ordinal++) {
|
|
201
|
+
if (node_top_layer(&context, (int32_t)ordinal) < 0 || !is_tombstoned(&context, (int32_t)ordinal)) { continue; }
|
|
202
|
+
take_node_out(&context, (int32_t)ordinal);
|
|
203
|
+
}
|
|
204
|
+
return NARSIL_OK;
|
|
205
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#include "internal.h"
|
|
2
|
+
|
|
3
|
+
#include "../include/narsil_core.h"
|
|
4
|
+
|
|
5
|
+
#include <math.h>
|
|
6
|
+
#include <stddef.h>
|
|
7
|
+
#include <stdint.h>
|
|
8
|
+
#include <string.h>
|
|
9
|
+
|
|
10
|
+
#define CACHE_LINE_BYTES 64
|
|
11
|
+
#define PREFETCH_FOR_READ 0
|
|
12
|
+
#define PREFETCH_KEEP_IN_ALL_CACHES 3
|
|
13
|
+
|
|
14
|
+
int is_metric(uint32_t metric) {
|
|
15
|
+
return metric == NARSIL_METRIC_COSINE || metric == NARSIL_METRIC_DOT_PRODUCT || metric == NARSIL_METRIC_EUCLIDEAN;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
int is_code_width(uint32_t bits) {
|
|
19
|
+
switch (bits) {
|
|
20
|
+
case 0:
|
|
21
|
+
case 1:
|
|
22
|
+
case 2:
|
|
23
|
+
case 4:
|
|
24
|
+
case BITS_PER_BYTE:
|
|
25
|
+
return 1;
|
|
26
|
+
default:
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
int store_is_complete(const narsil_store *store) {
|
|
32
|
+
return store->header != NULL && store->code_present != NULL && store->centroid != NULL && store->magnitudes != NULL &&
|
|
33
|
+
store->present != NULL && (store->code_block_count == 0 || store->code_blocks != NULL) &&
|
|
34
|
+
(store->vector_block_count == 0 || store->vector_blocks != NULL) &&
|
|
35
|
+
(store->file_count == 0 ||
|
|
36
|
+
(store->files != NULL && store->vector_file != NULL && store->vector_offset != NULL));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static int holds_a_live_vector(const narsil_store *store, int32_t ordinal) {
|
|
40
|
+
if (ordinal < 0) { return 0; }
|
|
41
|
+
if ((uint32_t)ordinal >= (uint32_t)load_word(store->header, NARSIL_STORE_WORD_SLOTS)) { return 0; }
|
|
42
|
+
return store->present[ordinal] == 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static const float *vector_in_a_file(const narsil_store *store, int32_t ordinal, float *scratch) {
|
|
46
|
+
uint32_t position = (uint32_t)store->vector_file[ordinal];
|
|
47
|
+
if (position >= store->file_count) { return NULL; }
|
|
48
|
+
const narsil_vector_file *file = &store->files[position];
|
|
49
|
+
uint64_t offset = store->vector_offset[ordinal];
|
|
50
|
+
uint64_t bytes = (uint64_t)store->dimension * sizeof(float);
|
|
51
|
+
if (file->bytes == NULL || offset + bytes > file->length) { return NULL; }
|
|
52
|
+
memcpy(scratch, file->bytes + offset, (size_t)bytes);
|
|
53
|
+
return scratch;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static const float *vector_in_a_block(const narsil_store *store, int32_t ordinal) {
|
|
57
|
+
if (store->vectors_per_block == 0) { return NULL; }
|
|
58
|
+
uint32_t block = (uint32_t)ordinal / store->vectors_per_block;
|
|
59
|
+
if (block >= store->vector_block_count || store->vector_blocks[block] == NULL) { return NULL; }
|
|
60
|
+
size_t entry = (size_t)((uint32_t)ordinal % store->vectors_per_block);
|
|
61
|
+
return store->vector_blocks[block] + (entry * store->vector_stride_floats);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const float *stored_vector(const narsil_store *store, int32_t ordinal, float *scratch) {
|
|
65
|
+
if (!holds_a_live_vector(store, ordinal)) { return NULL; }
|
|
66
|
+
if (store->vector_file != NULL && store->vector_file[ordinal] >= 0) {
|
|
67
|
+
return vector_in_a_file(store, ordinal, scratch);
|
|
68
|
+
}
|
|
69
|
+
return vector_in_a_block(store, ordinal);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
uint8_t *stored_record(const narsil_store *store, int32_t ordinal) {
|
|
73
|
+
if (ordinal < 0 || store->records_per_block == 0 || store->bits == 0) { return NULL; }
|
|
74
|
+
if ((uint32_t)ordinal >= (uint32_t)load_word(store->header, NARSIL_STORE_WORD_SLOTS)) { return NULL; }
|
|
75
|
+
uint32_t block = (uint32_t)ordinal / store->records_per_block;
|
|
76
|
+
if (block >= store->code_block_count || store->code_blocks[block] == NULL) { return NULL; }
|
|
77
|
+
size_t entry = (size_t)((uint32_t)ordinal % store->records_per_block);
|
|
78
|
+
return store->code_blocks[block] + (entry * narsil_record_bytes(store->dimension, store->bits));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static double metric_distance(const walk_context *context, float_query query, const float *vector, double magnitude) {
|
|
82
|
+
uint32_t dimension = context->store->dimension;
|
|
83
|
+
if (context->metric == NARSIL_METRIC_EUCLIDEAN) {
|
|
84
|
+
return sqrt((double)kernel_squared_distance(query.values, vector, dimension));
|
|
85
|
+
}
|
|
86
|
+
double dot = (double)kernel_dot(query.values, vector, dimension);
|
|
87
|
+
if (context->metric == NARSIL_METRIC_DOT_PRODUCT) { return -dot; }
|
|
88
|
+
if (query.magnitude == 0 || magnitude == 0) { return 1; }
|
|
89
|
+
return 1 - (dot / (query.magnitude * magnitude));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
double float_distance(const walk_context *context, float_query query, int32_t ordinal) {
|
|
93
|
+
const float *vector = stored_vector(context->store, ordinal, context->workspace->stored_vector);
|
|
94
|
+
if (vector == NULL) { return HUGE_VAL; }
|
|
95
|
+
return metric_distance(context, query, vector, context->store->magnitudes[ordinal]);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
static double vector_pair_distance(const walk_context *context, int32_t first, int32_t second) {
|
|
99
|
+
const narsil_store *store = context->store;
|
|
100
|
+
const float *first_vector = stored_vector(store, first, context->workspace->other_vector);
|
|
101
|
+
if (first_vector == NULL) { return HUGE_VAL; }
|
|
102
|
+
float_query query = {first_vector, store->magnitudes[first]};
|
|
103
|
+
return float_distance(context, query, second);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
double pair_distance(const walk_context *context, int32_t first, int32_t second) {
|
|
107
|
+
if (context->pairs_use_codes) {
|
|
108
|
+
const narsil_store *store = context->store;
|
|
109
|
+
const uint8_t *first_record = stored_record(store, first);
|
|
110
|
+
const uint8_t *second_record = stored_record(store, second);
|
|
111
|
+
if (first_record != NULL && second_record != NULL && store->code_present[first] == 1 &&
|
|
112
|
+
store->code_present[second] == 1) {
|
|
113
|
+
return estimate_pair_distance(context, first_record, second_record);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return vector_pair_distance(context, first, second);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
void prefetch_for_walk(const walk_context *context, int32_t ordinal) {
|
|
120
|
+
#if defined(__GNUC__) || defined(__clang__)
|
|
121
|
+
const uint8_t *bytes = NULL;
|
|
122
|
+
size_t length = 0;
|
|
123
|
+
if (context->uses_codes) {
|
|
124
|
+
bytes = stored_record(context->store, ordinal);
|
|
125
|
+
length = context->record_bytes;
|
|
126
|
+
} else if (context->store->file_count == 0 && ordinal >= 0 && (uint32_t)ordinal < context->store_slots) {
|
|
127
|
+
bytes = (const uint8_t *)vector_in_a_block(context->store, ordinal);
|
|
128
|
+
length = (size_t)context->store->dimension * sizeof(float);
|
|
129
|
+
}
|
|
130
|
+
if (bytes == NULL || length == 0) { return; }
|
|
131
|
+
for (size_t at = 0; at < length; at += CACHE_LINE_BYTES) {
|
|
132
|
+
__builtin_prefetch(bytes + at, PREFETCH_FOR_READ, PREFETCH_KEEP_IN_ALL_CACHES);
|
|
133
|
+
}
|
|
134
|
+
__builtin_prefetch(bytes + length - 1, PREFETCH_FOR_READ, PREFETCH_KEEP_IN_ALL_CACHES);
|
|
135
|
+
#else
|
|
136
|
+
(void)context;
|
|
137
|
+
(void)ordinal;
|
|
138
|
+
#endif
|
|
139
|
+
}
|