usearch 1.3.0 → 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.
- package/.github/workflows/prerelease.yml +45 -0
- package/.github/workflows/release.yml +76 -1
- package/.github/workflows/update_version.sh +1 -0
- package/.gitignore +11 -1
- package/.vscode/settings.json +5 -1
- package/Cargo.toml +1 -1
- package/README.md +15 -8
- package/c/lib.cpp +3 -3
- package/conanfile.py +1 -1
- package/csharp/.editorconfig +361 -0
- package/csharp/Cloud.Unum.USearch.sln +34 -0
- package/csharp/DEV_SETUP.md +51 -0
- package/csharp/Directory.Build.props +29 -0
- package/csharp/README.md +77 -0
- package/csharp/build_and_test.cmd +31 -0
- package/csharp/build_and_test.sh +30 -0
- package/csharp/eng/build_lib.sh +65 -0
- package/csharp/eng/clean.sh +12 -0
- package/csharp/nuget/NUGET.md +11 -0
- package/csharp/nuget/nuget-package.props +33 -0
- package/csharp/pack.sh +56 -0
- package/csharp/src/Cloud.Unum.USearch.Tests/Cloud.Unum.USearch.Tests.csproj +39 -0
- package/csharp/src/Cloud.Unum.USearch.Tests/InitTests.cs +306 -0
- package/csharp/src/Cloud.Unum.USearch.Tests/Usings.cs +1 -0
- package/include/usearch/index_dense.hpp +8 -8
- package/include/usearch/index_plugins.hpp +56 -49
- package/package.json +1 -1
- package/python/lib.cpp +6 -6
- package/rust/lib.cpp +27 -15
- package/rust/lib.hpp +9 -0
- package/rust/lib.rs +10 -0
- 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;
|
|
@@ -584,31 +584,31 @@ class index_dense_gt {
|
|
|
584
584
|
|
|
585
585
|
// clang-format off
|
|
586
586
|
add_result_t add(key_t key, b1x8_t const* vector, std::size_t thread = any_thread(), bool force_vector_copy = true) { return add_(key, vector, thread, force_vector_copy, casts_.from_b1x8); }
|
|
587
|
-
add_result_t add(key_t key,
|
|
587
|
+
add_result_t add(key_t key, i8_t const* vector, std::size_t thread = any_thread(), bool force_vector_copy = true) { return add_(key, vector, thread, force_vector_copy, casts_.from_i8); }
|
|
588
588
|
add_result_t add(key_t key, f16_t const* vector, std::size_t thread = any_thread(), bool force_vector_copy = true) { return add_(key, vector, thread, force_vector_copy, casts_.from_f16); }
|
|
589
589
|
add_result_t add(key_t key, f32_t const* vector, std::size_t thread = any_thread(), bool force_vector_copy = true) { return add_(key, vector, thread, force_vector_copy, casts_.from_f32); }
|
|
590
590
|
add_result_t add(key_t key, f64_t const* vector, std::size_t thread = any_thread(), bool force_vector_copy = true) { return add_(key, vector, thread, force_vector_copy, casts_.from_f64); }
|
|
591
591
|
|
|
592
592
|
search_result_t search(b1x8_t const* vector, std::size_t wanted, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, thread, exact, casts_.from_b1x8); }
|
|
593
|
-
search_result_t search(
|
|
593
|
+
search_result_t search(i8_t const* vector, std::size_t wanted, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, thread, exact, casts_.from_i8); }
|
|
594
594
|
search_result_t search(f16_t const* vector, std::size_t wanted, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, thread, exact, casts_.from_f16); }
|
|
595
595
|
search_result_t search(f32_t const* vector, std::size_t wanted, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, thread, exact, casts_.from_f32); }
|
|
596
596
|
search_result_t search(f64_t const* vector, std::size_t wanted, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, thread, exact, casts_.from_f64); }
|
|
597
597
|
|
|
598
598
|
std::size_t get(key_t key, b1x8_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.to_b1x8); }
|
|
599
|
-
std::size_t get(key_t key,
|
|
599
|
+
std::size_t get(key_t key, i8_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.to_i8); }
|
|
600
600
|
std::size_t get(key_t key, f16_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.to_f16); }
|
|
601
601
|
std::size_t get(key_t key, f32_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.to_f32); }
|
|
602
602
|
std::size_t get(key_t key, f64_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.to_f64); }
|
|
603
603
|
|
|
604
604
|
cluster_result_t cluster(b1x8_t const* vector, std::size_t level, std::size_t thread = any_thread()) const { return cluster_(vector, level, thread, casts_.from_b1x8); }
|
|
605
|
-
cluster_result_t cluster(
|
|
605
|
+
cluster_result_t cluster(i8_t const* vector, std::size_t level, std::size_t thread = any_thread()) const { return cluster_(vector, level, thread, casts_.from_i8); }
|
|
606
606
|
cluster_result_t cluster(f16_t const* vector, std::size_t level, std::size_t thread = any_thread()) const { return cluster_(vector, level, thread, casts_.from_f16); }
|
|
607
607
|
cluster_result_t cluster(f32_t const* vector, std::size_t level, std::size_t thread = any_thread()) const { return cluster_(vector, level, thread, casts_.from_f32); }
|
|
608
608
|
cluster_result_t cluster(f64_t const* vector, std::size_t level, std::size_t thread = any_thread()) const { return cluster_(vector, level, thread, casts_.from_f64); }
|
|
609
609
|
|
|
610
610
|
aggregated_distances_t distance_between(key_t key, b1x8_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.to_b1x8); }
|
|
611
|
-
aggregated_distances_t distance_between(key_t key,
|
|
611
|
+
aggregated_distances_t distance_between(key_t key, i8_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.to_i8); }
|
|
612
612
|
aggregated_distances_t distance_between(key_t key, f16_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.to_f16); }
|
|
613
613
|
aggregated_distances_t distance_between(key_t key, f32_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.to_f32); }
|
|
614
614
|
aggregated_distances_t distance_between(key_t key, f64_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.to_f64); }
|
|
@@ -1872,13 +1872,13 @@ class index_dense_gt {
|
|
|
1872
1872
|
casts_t result;
|
|
1873
1873
|
|
|
1874
1874
|
result.from_b1x8 = cast_gt<b1x8_t, to_scalar_at>{};
|
|
1875
|
-
result.from_i8 = cast_gt<
|
|
1875
|
+
result.from_i8 = cast_gt<i8_t, to_scalar_at>{};
|
|
1876
1876
|
result.from_f16 = cast_gt<f16_t, to_scalar_at>{};
|
|
1877
1877
|
result.from_f32 = cast_gt<f32_t, to_scalar_at>{};
|
|
1878
1878
|
result.from_f64 = cast_gt<f64_t, to_scalar_at>{};
|
|
1879
1879
|
|
|
1880
1880
|
result.to_b1x8 = cast_gt<to_scalar_at, b1x8_t>{};
|
|
1881
|
-
result.to_i8 = cast_gt<to_scalar_at,
|
|
1881
|
+
result.to_i8 = cast_gt<to_scalar_at, i8_t>{};
|
|
1882
1882
|
result.to_f16 = cast_gt<to_scalar_at, f16_t>{};
|
|
1883
1883
|
result.to_f32 = cast_gt<to_scalar_at, f32_t>{};
|
|
1884
1884
|
result.to_f64 = cast_gt<to_scalar_at, f64_t>{};
|
|
@@ -1891,7 +1891,7 @@ class index_dense_gt {
|
|
|
1891
1891
|
case scalar_kind_t::f64_k: return make_casts_<f64_t>();
|
|
1892
1892
|
case scalar_kind_t::f32_k: return make_casts_<f32_t>();
|
|
1893
1893
|
case scalar_kind_t::f16_k: return make_casts_<f16_t>();
|
|
1894
|
-
case scalar_kind_t::i8_k: return make_casts_<
|
|
1894
|
+
case scalar_kind_t::i8_k: return make_casts_<i8_t>();
|
|
1895
1895
|
case scalar_kind_t::b1x8_k: return make_casts_<b1x8_t>();
|
|
1896
1896
|
default: return {};
|
|
1897
1897
|
}
|