usearch 1.2.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/.github/workflows/prerelease.yml +71 -0
  2. package/.github/workflows/release.yml +77 -2
  3. package/.github/workflows/update_version.sh +1 -0
  4. package/.gitignore +11 -1
  5. package/.vscode/settings.json +5 -1
  6. package/CMakeLists.txt +1 -0
  7. package/Cargo.toml +1 -1
  8. package/README.md +31 -18
  9. package/c/lib.cpp +38 -4
  10. package/c/test.c +117 -114
  11. package/c/usearch.h +28 -1
  12. package/conanfile.py +1 -1
  13. package/cpp/test.cpp +2 -2
  14. package/csharp/.editorconfig +361 -0
  15. package/csharp/Cloud.Unum.USearch.sln +34 -0
  16. package/csharp/DEV_SETUP.md +51 -0
  17. package/csharp/Directory.Build.props +29 -0
  18. package/csharp/README.md +77 -0
  19. package/csharp/build_and_test.cmd +31 -0
  20. package/csharp/build_and_test.sh +30 -0
  21. package/csharp/eng/build_lib.sh +65 -0
  22. package/csharp/eng/clean.sh +12 -0
  23. package/csharp/nuget/NUGET.md +11 -0
  24. package/csharp/nuget/nuget-package.props +33 -0
  25. package/csharp/pack.sh +56 -0
  26. package/csharp/src/Cloud.Unum.USearch.Tests/Cloud.Unum.USearch.Tests.csproj +39 -0
  27. package/csharp/src/Cloud.Unum.USearch.Tests/InitTests.cs +306 -0
  28. package/csharp/src/Cloud.Unum.USearch.Tests/Usings.cs +1 -0
  29. package/docs/benchmarks.md +6 -6
  30. package/docs/compilation.md +1 -1
  31. package/include/usearch/index.hpp +172 -52
  32. package/include/usearch/index_dense.hpp +201 -64
  33. package/include/usearch/index_plugins.hpp +90 -69
  34. package/package.json +2 -2
  35. package/python/lib.cpp +99 -37
  36. package/python/scripts/test_index.py +38 -10
  37. package/python/usearch/index.py +87 -37
  38. package/rust/README.md +2 -2
  39. package/rust/lib.cpp +42 -19
  40. package/rust/lib.hpp +14 -0
  41. package/rust/lib.rs +85 -43
  42. package/wasmer.toml +1 -1
@@ -0,0 +1,65 @@
1
+ #!/bin/bash
2
+
3
+ # Get absolute path to the usearch dir
4
+ # FIX: realpath is not available on OSX, so use pwd
5
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
6
+ SCRIPT_DIR="${SCRIPT_DIR}/"
7
+
8
+ # Define project paths
9
+ REPO_ROOT=`cd -P "${SCRIPT_DIR}../.." && pwd`
10
+ REPO_ROOT="${REPO_ROOT}/"
11
+ C_DIR="${REPO_ROOT}c/"
12
+ CSHARP_DIR="${REPO_ROOT}csharp/"
13
+ BUILD_ARTIFACTS_DIR="${CSHARP_DIR}lib/"
14
+
15
+ LIB_FILE="libusearch_c"
16
+
17
+ # Determine the architecture type (only x64 is supported)
18
+ ARCH_TYPE="$(uname -m)"
19
+ case "$ARCH_TYPE" in
20
+ x86_64)
21
+ ARCH="x64"
22
+ ;;
23
+ # i386|i486|i586|i686)
24
+ # ARCH="x86"
25
+ # ;;
26
+ *)
27
+ echo "Unsupported architecture"
28
+ exit 1
29
+ ;;
30
+ esac
31
+ echo "Detected architecture: $ARCH"
32
+
33
+ # Determine the OS type (linux or macos) and set the shared library extension accordingly
34
+ case "$(uname -s)" in
35
+ Darwin)
36
+ BUILD_ARTIFACTS_DIR+="runtimes/osx-${ARCH}/native/"
37
+ LIB_FILE+=".dylib"
38
+ ;;
39
+ Linux)
40
+ BUILD_ARTIFACTS_DIR+="runtimes/linux-${ARCH}/native/"
41
+ LIB_FILE+=".so"
42
+ ;;
43
+ *)
44
+ echo "Unsupported operating system"
45
+ exit 1
46
+ ;;
47
+ esac
48
+
49
+ MAKE_TARGET="${LIB_FILE}"
50
+
51
+ # Build the native dynamic lib
52
+ cd "$C_DIR" || { echo "C directory not found in $C_DIR"; exit 1; }
53
+ if [ ! -f Makefile ]; then
54
+ echo "Makefile not found in $C_DIR"
55
+ exit 1
56
+ fi
57
+ make "${MAKE_TARGET}"
58
+ if [ $? -ne 0 ]; then
59
+ echo "Build failed!"
60
+ exit 1
61
+ fi
62
+
63
+ # Create output dir for shared lib and copy it there
64
+ mkdir -p "$BUILD_ARTIFACTS_DIR"
65
+ cp "${C_DIR}/${LIB_FILE}" "$BUILD_ARTIFACTS_DIR"
@@ -0,0 +1,12 @@
1
+ #!/bin/bash
2
+
3
+ # Get absolute path to the usearch csharp dir
4
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5
+ SCRIPT_DIR="${SCRIPT_DIR}/"
6
+ CSHARP_DIR=`cd -P "${SCRIPT_DIR}.." && pwd`
7
+ CSHARP_DIR="${CSHARP_DIR}/"
8
+
9
+ rm -rf ${CSHARP_DIR}lib # Local temp path for native libs
10
+ rm -rf ${CSHARP_DIR}packages # Local temp path for nuget packages
11
+ find ${CSHARP_DIR} -type d -name 'bin' -exec rm -rf {} +
12
+ find ${CSHARP_DIR} -type d -name 'obj' -exec rm -rf {} +
@@ -0,0 +1,11 @@
1
+ **Cloud.Unum.USearch** is a wrapper for the USearch vector search engine, offering fast searches, multiple metrics, and features like SIMD-optimized metrics.
2
+
3
+ **Supported metrics**: Euclidean, Angular, Jaccard, Hamming, Haversine, User-Defined Metrics
4
+
5
+ **Platforms**: Linux, MacOS, Windows
6
+
7
+ # Getting Started ⚡
8
+
9
+ - [Quickstart](https://github.com/unum-cloud/usearch/blob/main/csharp/README.md)
10
+ - Join the [Discord community](https://discord.gg/A6wxt6dS9j)
11
+ - Check out the [GitHub repository](https://github.com/unum-cloud/usearch)
@@ -0,0 +1,33 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <Project>
3
+ <PropertyGroup>
4
+ <Version Condition="'$(Version)' == ''">0.0.2</Version>
5
+
6
+ <Authors>Unum</Authors>
7
+ <Company>Unum</Company>
8
+ <PackageTags>USearch Vector Search Disk-based Multi-Modal Semantic-Search Molecular-Search</PackageTags>
9
+ <PackageId>Cloud.Unum.USearch</PackageId>
10
+
11
+ <PackageLicenseFile>LICENSE</PackageLicenseFile>
12
+ <PackageProjectUrl>https://github.com/unum-cloud/usearch</PackageProjectUrl>
13
+ <RepositoryUrl>https://github.com/unum-cloud/usearch</RepositoryUrl>
14
+ <PublishRepositoryUrl>true</PublishRepositoryUrl>
15
+
16
+ <PackageIcon>unum.png</PackageIcon>
17
+ <PackageIconUrl>unum.png</PackageIconUrl>
18
+ <PackageReadmeFile>NUGET.md</PackageReadmeFile>
19
+ </PropertyGroup>
20
+
21
+ <ItemGroup>
22
+ <None Include="$(NativeLibDir)runtimes\**\*.*" Pack="true" PackagePath="runtimes" />
23
+ <None Include="$(RepoRoot)LICENSE" Pack="true" PackagePath=""/>
24
+ <None Include="$(RepoRoot)assets\unum.png" Pack="true" PackagePath=""/>
25
+ <None Include="$(CSharpDir)nuget\NUGET.md" Pack="true" PackagePath=""/>
26
+ </ItemGroup>
27
+
28
+ <!-- Package validation https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/overview?WT.mc_id=DT-MVP-5003978 -->
29
+ <PropertyGroup Condition="'$(IsPackable)' == 'true'">
30
+ <EnablePackageValidation Condition="'$(EnablePackageValidation)' == ''">true</EnablePackageValidation>
31
+ </PropertyGroup>
32
+
33
+ </Project>
package/csharp/pack.sh ADDED
@@ -0,0 +1,56 @@
1
+ #!/bin/bash
2
+
3
+ check_task_status() {
4
+ local task_name=$1
5
+ local exit_code=$2
6
+
7
+ if [ $exit_code -ne 0 ]; then
8
+ echo "$task_name failed!"
9
+ exit 1
10
+ else
11
+ echo "$task_name finished successfully!"
12
+ fi
13
+ }
14
+
15
+ # Check vars for pack and push are set
16
+ required_vars=("PACKAGE_ID" "PACKAGE_VERSION" "NUGET_APIKEY" "NUGET_SERVER")
17
+ unset_vars=""
18
+
19
+ for var in "${required_vars[@]}"; do
20
+ if [ -z "${!var}" ]; then
21
+ unset_vars="$unset_vars $var"
22
+ fi
23
+ done
24
+
25
+ if [ -n "$unset_vars" ]; then
26
+ echo "The following variables are not set:$unset_vars"
27
+ exit 1
28
+ fi
29
+
30
+ # Get absolute path to the usearch dir
31
+ # FIX: realpath is not available on OSX, so use pwd
32
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
33
+ SCRIPT_DIR="${SCRIPT_DIR}/"
34
+
35
+ # Define project paths
36
+ REPO_ROOT=`cd -P "$SCRIPT_DIR../" && pwd`
37
+ REPO_ROOT="${REPO_ROOT}/"
38
+
39
+ # Clear package output folder
40
+ rm -rf ./packages/*
41
+
42
+ # Clean previous outputs
43
+ dotnet clean -c Release -p:RepoRoot="$REPO_ROOT"
44
+
45
+ # Build
46
+ dotnet build -c Release -p:Version=${PACKAGE_VERSION} -p:RepoRoot="$REPO_ROOT"
47
+
48
+ # Pack
49
+ dotnet pack -c Release -p:Version=${PACKAGE_VERSION} -p:RepoRoot="$REPO_ROOT"
50
+ EXIT_CODE=$?
51
+ check_task_status "Pack" $EXIT_CODE
52
+
53
+ # Push
54
+ dotnet nuget push "./packages/${PACKAGE_ID}.*.nupkg" -k ${NUGET_APIKEY} -s ${NUGET_SERVER}
55
+ EXIT_CODE=$?
56
+ check_task_status "NuGet Push" $EXIT_CODE
@@ -0,0 +1,39 @@
1
+ <Project Sdk="Microsoft.NET.Sdk">
2
+
3
+ <PropertyGroup>
4
+ <TargetFramework>net7.0</TargetFramework>
5
+ <ImplicitUsings>enable</ImplicitUsings>
6
+ <IsPackable>false</IsPackable>
7
+ <Nullable>enable</Nullable>
8
+ <IsTestProject>true</IsTestProject>
9
+
10
+ </PropertyGroup>
11
+
12
+ <ItemGroup>
13
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
14
+ <PackageReference Include="xunit" Version="2.4.2" />
15
+ <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
16
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
17
+ <PrivateAssets>all</PrivateAssets>
18
+ </PackageReference>
19
+ <PackageReference Include="coverlet.collector" Version="3.1.2">
20
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
21
+ <PrivateAssets>all</PrivateAssets>
22
+ </PackageReference>
23
+ </ItemGroup>
24
+
25
+ <ItemGroup>
26
+ <ProjectReference Include="..\Cloud.Unum.USearch\Cloud.Unum.USearch.csproj" />
27
+ </ItemGroup>
28
+
29
+ <Target Name="PlaceNativeLibrary" AfterTargets="Build">
30
+ <ItemGroup>
31
+ <NativeBinaries Include="$(NativeLibDir)runtimes\$(TargetOS)-$(TargetArchitecture)\native\*.*"/>
32
+ </ItemGroup>
33
+
34
+ <Copy SourceFiles="@(NativeBinaries)" DestinationFolder="$(OutputPath)" />
35
+ <Message Text="NativeLibDir: $(NativeLibDir)" Importance="high"/>
36
+ <Message Text="Files: @(NativeBinaries)" Importance="high"/>
37
+ <Message Text="$(OutputPath)" Importance="high" />
38
+ </Target>
39
+ </Project>
@@ -0,0 +1,306 @@
1
+ using Cloud.Unum.USearch;
2
+
3
+ namespace Cloud.Unum.USearch.Tests;
4
+
5
+ public class InitTests
6
+ {
7
+ // Epsilon for real values comparisons
8
+ private const float Epsilon = 0.000001f;
9
+
10
+ [Fact]
11
+ public void InitializedAndDisposedSucceeds()
12
+ {
13
+ using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 3))
14
+ {
15
+ Assert.NotNull(index);
16
+ Assert.Equal((uint)3, index.Dimensions());
17
+ }
18
+ }
19
+
20
+ [Fact]
21
+ public void AddDoubleVectorSucceeds()
22
+ {
23
+ using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float64, 3))
24
+ {
25
+ index.Add(1, new double[] { 1.0, 2.0, 3.0 });
26
+ Assert.True(index.Contains(1));
27
+ Assert.False(index.Contains(2));
28
+ Assert.Equal((uint)1, index.Size());
29
+ }
30
+ }
31
+
32
+ [Fact]
33
+ public void AddDoubleVectorsUnderSameKeySucceeds()
34
+ {
35
+ using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float64, 3, multi: true))
36
+ {
37
+ index.Add(1, new double[] { 1.0, 2.0, 3.0 });
38
+ index.Add(2, new double[] { 1.0, 2.0, 3.0 });
39
+ index.Add(3, new double[] { 1.0, 2.0, 3.0 });
40
+ index.Add(1, new double[] { 1.0, 2.0, 3.0 });
41
+ index.Add(2, new double[] { 1.0, 2.0, 3.0 });
42
+ Assert.True(index.Contains(1));
43
+ Assert.True(index.Contains(2));
44
+ Assert.True(index.Contains(3));
45
+ Assert.Equal((uint)5, index.Size());
46
+ }
47
+ }
48
+
49
+ [Fact]
50
+ public void ItCanGetMultipleFloatVectorsForSingleKey()
51
+ {
52
+ using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 3, multi: true))
53
+ {
54
+ var inputVectors = new float[][] {
55
+ new float[] { 1.0f, 6.0f, 11.0f },
56
+ new float[] { 2.0f, 7.0f, 12.0f },
57
+ new float[] { 3.0f, 8.0f, 13.0f },
58
+ new float[] { 5.0f, 10.0f, 15.0f }
59
+ };
60
+ index.Add(
61
+ new ulong[] { 2, 2, 2, 2 },
62
+ inputVectors
63
+ );
64
+ Assert.True(index.Contains(2));
65
+ Assert.Equal((uint)4, index.Size());
66
+ Assert.Equal(4, index.Count(2));
67
+
68
+ int foundVectorsCount = index.Get(2, 3, out float[][] retrievedVectors);
69
+ Assert.Equal(3, foundVectorsCount);
70
+ }
71
+ }
72
+
73
+ [Fact]
74
+ public void ItCanGetMultipleDoubleVectorsForSingleKey()
75
+ {
76
+ using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float64, 3, multi: true))
77
+ {
78
+ var inputVectors = new double[][] {
79
+ new double[] { 1.0, 6.0, 11.0 },
80
+ new double[] { 2.0, 7.0, 12.0 },
81
+ new double[] { 3.0, 8.0, 13.0 },
82
+ new double[] { 5.0, 10.0, 15.0 }
83
+ };
84
+ index.Add(
85
+ new ulong[] { 2, 2, 2, 2 },
86
+ inputVectors
87
+ );
88
+ Assert.True(index.Contains(2));
89
+ Assert.Equal((uint)4, index.Size());
90
+ Assert.Equal(4, index.Count(2));
91
+
92
+ int foundVectorsCount = index.Get(2, 3, out double[][] retrievedVectors);
93
+ Assert.Equal(3, foundVectorsCount);
94
+ }
95
+ }
96
+
97
+ [Fact]
98
+ public void AddFloatVectorSucceeds()
99
+ {
100
+ using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 3))
101
+ {
102
+ index.Add(1, new float[] { 1.0f, 2.0f, 3.0f });
103
+ Assert.Equal((uint)1, index.Size());
104
+ Assert.True(index.Contains(1));
105
+ Assert.False(index.Contains(2));
106
+ }
107
+ }
108
+
109
+ [Fact]
110
+ public void AddFloatVectorsUnderSameKeySucceeds()
111
+ {
112
+ using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 3, multi: true))
113
+ {
114
+ index.Add(1, new float[] { 1.0f, 2.0f, 3.0f });
115
+ index.Add(2, new float[] { 1.0f, 2.0f, 3.0f });
116
+ index.Add(3, new float[] { 1.0f, 2.0f, 3.0f });
117
+ index.Add(1, new float[] { 1.0f, 2.0f, 3.0f });
118
+ index.Add(2, new float[] { 1.0f, 2.0f, 3.0f });
119
+ Assert.True(index.Contains(1));
120
+ Assert.True(index.Contains(2));
121
+ Assert.True(index.Contains(3));
122
+ Assert.Equal((uint)5, index.Size());
123
+ }
124
+ }
125
+
126
+ [Fact]
127
+ public void AddGetFloatVectorSucceeds()
128
+ {
129
+ using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 3))
130
+ {
131
+ var inputVector = new float[] { 1.0f, 2.0f, 3.0f };
132
+ index.Add(1, inputVector);
133
+ Assert.Equal((uint)1, index.Size());
134
+ Assert.True(index.Contains(1));
135
+ Assert.False(index.Contains(2));
136
+ Assert.True(index.Get(1, out float[] retrievedVector) == 1);
137
+ Assert.NotNull(retrievedVector);
138
+ bool areEqual = inputVector
139
+ .Zip(retrievedVector, (a, b) => (a, b))
140
+ .All(pair => Math.Abs(pair.a - pair.b) <= Epsilon);
141
+
142
+ Assert.True(areEqual);
143
+ }
144
+ }
145
+
146
+ [Fact]
147
+ public void AddGetDoubleVectorSucceeds()
148
+ {
149
+ using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 3))
150
+ {
151
+ var inputVector = new double[] { 1.0, 2.0, 3.33 };
152
+ index.Add(1, inputVector);
153
+ Assert.Equal((uint)1, index.Size());
154
+ Assert.True(index.Contains(1));
155
+ Assert.False(index.Contains(2));
156
+ Assert.True(index.Get(1, out double[] retrievedVector) == 1);
157
+ Assert.NotNull(retrievedVector);
158
+ bool areEqual = inputVector
159
+ .Zip(retrievedVector, (a, b) => (a, b))
160
+ .All(pair => Math.Abs(pair.a - pair.b) <= Epsilon);
161
+
162
+ Assert.True(areEqual);
163
+ }
164
+ }
165
+
166
+ [Fact]
167
+ public void ItThrowsException_AddAddWithSameKeyAndGetVector()
168
+ {
169
+ using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 3))
170
+ {
171
+ ulong key = 1;
172
+ float[] vector1 = { 1.0f, 2.0f, 33.3f };
173
+ index.Add(key, vector1);
174
+ float[] vector2 = { 3.0f, 4.0f, 34.2f };
175
+ Assert.Throws<USearchException>(() => index.Add(key, vector2));
176
+ }
177
+ }
178
+
179
+ [Fact]
180
+ public void TestAddRemoveAddAndGet()
181
+ {
182
+ using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 2))
183
+ {
184
+ ulong key = 1;
185
+ float[] vector = { 1.0f, 2.0f };
186
+
187
+ index.Add(key, vector);
188
+ Assert.True(index.Contains(key));
189
+ index.Remove(key);
190
+ Assert.False(index.Contains(key));
191
+ index.Add(key, vector);
192
+ Assert.True(index.Contains(key));
193
+ Assert.True(index.Get(key, out float[] retrievedVector) == 1);
194
+ Assert.Equal(vector, retrievedVector);
195
+ }
196
+ }
197
+
198
+ [Fact]
199
+ public void TestAddRemoveAddWithSameKeyVectorAndGet()
200
+ {
201
+ using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 2))
202
+ {
203
+ ulong key = 1;
204
+ float[] vector = { 1.0f, 2.0f };
205
+
206
+ index.Add(key, vector);
207
+ Assert.True(index.Contains(key));
208
+ index.Remove(key);
209
+ Assert.False(index.Contains(key));
210
+ index.Add(key, vector);
211
+ Assert.True(index.Contains(key));
212
+ Assert.True(index.Get(key, out float[] retrievedVector) == 1);
213
+ Assert.Equal(vector, retrievedVector);
214
+ }
215
+ }
216
+
217
+ [Fact]
218
+ public void TestGetKeyThatNotExist()
219
+ {
220
+ using (var index = new USearchIndex(MetricKind.Cos, ScalarKind.Float32, 2))
221
+ {
222
+ Assert.True(index.Get(1, out float[] retrievedVector) == 0);
223
+ Assert.False(index.Contains(1));
224
+ }
225
+ }
226
+
227
+ [Fact]
228
+ public void Search_FindsExpectedResults()
229
+ {
230
+ using (var index = new USearchIndex(MetricKind.L2sq, ScalarKind.Float32, 2))
231
+ {
232
+ index.Add(
233
+ Enumerable.Range(1, 5).Select(i => (ulong)i).ToArray(),
234
+ new float[][]
235
+ {
236
+ new float[] { -0.5f, -0.6f },
237
+ new float[] { -0.1f, -0.1f },
238
+ new float[] { 0.2f, 0.2f },
239
+ new float[] { 0.7f, 0.8f },
240
+ new float[] { 0.9f, 1.0f }
241
+ }
242
+ );
243
+ var queryVector = new float[] { 0.0f, 0.0f };
244
+
245
+ var matches = index.Search(queryVector, 2, out ulong[] keys, out float[] distances);
246
+
247
+ Assert.Equal(2, matches);
248
+ Assert.Contains(2UL, keys);
249
+ Assert.Contains(3UL, keys);
250
+ Assert.Equal(0.02, distances[0], Epsilon);
251
+ Assert.Equal(0.08, distances[1], Epsilon);
252
+ }
253
+ }
254
+
255
+ [Fact]
256
+ public void Search_AfterRemovingKey_FindsExpectedResults()
257
+ {
258
+ using (var index = new USearchIndex(MetricKind.L2sq, ScalarKind.Float32, 2))
259
+ {
260
+ index.Add(1, new float[] { 0.1f, 0.2f });
261
+ index.Add(2, new float[] { 0.2f, 0.3f });
262
+ index.Add(3, new float[] { 0.3f, 0.4f });
263
+ index.Remove(2);
264
+ var queryVector = new float[] { 0.2f, 0.3f };
265
+
266
+ var matches = index.Search(queryVector, 2, out ulong[] keys, out float[] distances);
267
+
268
+ Assert.Equal(2, matches);
269
+ Assert.Contains(1UL, keys);
270
+ Assert.Contains(3UL, keys);
271
+ Assert.DoesNotContain(2UL, keys);
272
+ }
273
+ }
274
+
275
+ [Fact]
276
+ public void Search_WithVectorsLessThanResultsLimit()
277
+ {
278
+ using (var index = new USearchIndex(MetricKind.L2sq, ScalarKind.Float32, 2))
279
+ {
280
+ index.Add(1, new float[] { 0.9f, 0.9f });
281
+ index.Add(2, new float[] { 0.8f, 0.8f });
282
+ var queryVector = new float[] { 0.1f, 0.2f };
283
+
284
+ var matches = index.Search(queryVector, 10, out ulong[] keys, out float[] distances);
285
+
286
+ Assert.Equal(2, matches);
287
+ Assert.Contains(1UL, keys);
288
+ Assert.Contains(2UL, keys);
289
+ }
290
+ }
291
+
292
+ [Fact]
293
+ public void ItCanRenameKey()
294
+ {
295
+ using (var index = new USearchIndex(MetricKind.L2sq, ScalarKind.Float32, 2))
296
+ {
297
+ index.Add(1, new float[] { 0.9f, 0.9f });
298
+ index.Add(2, new float[] { 0.8f, 0.8f });
299
+ index.Rename(1, 3);
300
+ Assert.False(index.Contains(1));
301
+ Assert.True(index.Contains(2));
302
+ Assert.True(index.Contains(3));
303
+ }
304
+ }
305
+
306
+ }
@@ -0,0 +1 @@
1
+ global using Xunit;
@@ -19,13 +19,13 @@ The default values vary drastically.
19
19
  Below are the performance numbers for a benchmark running on the 64 cores of AWS `c7g.metal` "Graviton 3"-based instances.
20
20
  The main columns are:
21
21
 
22
- - **Add**: Number of insertion Queries Per Second.
23
- - **Search**: Number search Queries Per Second.
24
- - **Recall @1**: How often does approximate search yield the exact best match?
22
+ - __Add__: Number of insertion Queries Per Second.
23
+ - __Search__: Number search Queries Per Second.
24
+ - __Recall @1__: How often does approximate search yield the exact best match?
25
25
 
26
26
  ### Different "connectivity"
27
27
 
28
- | Vectors | Connectivity | EF @ A | EF @ S | **Add**, QPS | **Search**, QPS | **Recall @1** |
28
+ | Vectors | Connectivity | EF @ A | EF @ S | __Add__, QPS | __Search__, QPS | __Recall @1__ |
29
29
  | :--------- | :----------: | :----: | :----: | :----------: | :-------------: | ------------: |
30
30
  | `f32` x256 | 16 | 128 | 64 | 75'640 | 131'654 | 99.3% |
31
31
  | `f32` x256 | 12 | 128 | 64 | 81'747 | 149'728 | 99.0% |
@@ -33,7 +33,7 @@ The main columns are:
33
33
 
34
34
  ### Different "expansion factors"
35
35
 
36
- | Vectors | Connectivity | EF @ A | EF @ S | **Add**, QPS | **Search**, QPS | **Recall @1** |
36
+ | Vectors | Connectivity | EF @ A | EF @ S | __Add__, QPS | __Search__, QPS | __Recall @1__ |
37
37
  | :--------- | :----------: | :----: | :----: | :----------: | :-------------: | ------------: |
38
38
  | `f32` x256 | 16 | 128 | 64 | 75'640 | 131'654 | 99.3% |
39
39
  | `f32` x256 | 16 | 64 | 32 | 128'644 | 228'422 | 97.2% |
@@ -41,7 +41,7 @@ The main columns are:
41
41
 
42
42
  ### Different vectors "quantization"
43
43
 
44
- | Vectors | Connectivity | EF @ A | EF @ S | **Add**, QPS | **Search**, QPS | **Recall @1** |
44
+ | Vectors | Connectivity | EF @ A | EF @ S | __Add__, QPS | __Search__, QPS | __Recall @1__ |
45
45
  | :----------- | :----------: | :----: | :----: | :----------: | :-------------: | ------------: |
46
46
  | `f32` x256 | 16 | 128 | 64 | 87'995 | 171'856 | 99.1% |
47
47
  | `f16` x256 | 16 | 128 | 64 | 87'270 | 153'788 | 98.4% |
@@ -155,7 +155,7 @@ There are a few ways to compile the C 99 USearch SDK.
155
155
  Using the Makefile:
156
156
 
157
157
  ```sh
158
- make -C ./c libusearch_c.so -C ./c libusearch_c.so
158
+ make -C ./c libusearch_c.so
159
159
  ```
160
160
 
161
161
  Using CMake: