usearch 0.22.2 → 1.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 +3 -3
- package/.releaserc +9 -0
- package/.vscode/launch.json +15 -11
- package/.vscode/settings.json +12 -2
- package/.vscode/tasks.json +4 -4
- package/CMakeLists.txt +6 -1
- package/Cargo.toml +2 -2
- package/README.md +39 -46
- package/build.rs +2 -2
- package/c/CMakeLists.txt +48 -0
- package/c/README.md +34 -0
- package/c/lib.cpp +83 -72
- package/c/test.c +20 -20
- package/c/usearch.h +132 -32
- package/conanfile.py +1 -1
- package/cpp/CMakeLists.txt +3 -1
- package/cpp/README.md +16 -16
- package/cpp/bench.cpp +50 -126
- package/cpp/test.cpp +164 -77
- package/docs/benchmarks.md +26 -5
- package/docs/c/reference.rst +3 -3
- package/docs/compilation.md +49 -45
- package/docs/cpp/index.rst +2 -2
- package/docs/cpp/reference.rst +13 -3
- package/docs/index.rst +8 -8
- package/docs/java/reference.rst +1 -1
- package/golang/lib.go +275 -141
- package/golang/lib_test.go +105 -0
- package/include/usearch/index.hpp +1733 -1659
- package/include/usearch/index_dense.hpp +1400 -0
- package/include/usearch/index_plugins.hpp +1263 -0
- package/java/README.md +1 -1
- package/java/cloud/unum/usearch/Index.java +10 -10
- package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +34 -37
- package/java/test/IndexTest.java +1 -1
- package/javascript/README.md +7 -7
- package/javascript/lib.cpp +94 -55
- package/javascript/test.js +25 -11
- package/javascript/usearch.d.ts +87 -0
- package/javascript/usearch.js +2 -118
- package/objc/USearchObjective.mm +57 -31
- package/objc/include/USearchObjective.h +32 -22
- package/package.json +5 -3
- package/pyproject.toml +3 -3
- package/python/README.md +23 -14
- package/python/lib.cpp +472 -444
- package/python/scripts/bench.ipynb +3 -2
- package/python/scripts/bench.py +4 -4
- package/python/scripts/index_faiss.py +5 -5
- package/python/scripts/join.py +4 -4
- package/python/scripts/test.py +152 -61
- package/python/scripts/test_jit.py +10 -12
- package/python/scripts/test_sparse.py +32 -0
- package/python/usearch/client.py +26 -28
- package/python/usearch/eval.py +73 -23
- package/python/usearch/index.py +520 -228
- package/python/usearch/io.py +57 -25
- package/python/usearch/numba.py +4 -4
- package/python/usearch/server.py +10 -10
- package/rust/lib.cpp +22 -27
- package/rust/lib.hpp +11 -12
- package/rust/lib.rs +3 -3
- package/setup.py +5 -2
- package/simsimd/.github/workflows/prerelease.yml +73 -0
- package/simsimd/.github/workflows/release.yml +110 -0
- package/simsimd/.github/workflows/update_version.sh +3 -0
- package/simsimd/.releaserc +77 -0
- package/simsimd/.vscode/launch.json +27 -1
- package/simsimd/.vscode/settings.json +22 -1
- package/simsimd/.vscode/tasks.json +6 -0
- package/simsimd/CMakeLists.txt +3 -2
- package/simsimd/README.md +82 -46
- package/simsimd/bench.cxx +63 -24
- package/simsimd/include/simsimd/simsimd.h +88 -66
- package/simsimd/include/simsimd/simsimd_chem.h +126 -0
- package/simsimd/package.json +8 -0
- package/simsimd/pyproject.toml +42 -0
- package/simsimd/python/python.c +81 -0
- package/simsimd/python/run.sh +1 -0
- package/simsimd/python/test.c +109 -0
- package/simsimd/python/test.py +23 -0
- package/simsimd/setup.py +44 -0
- package/swift/Index+Sugar.swift +75 -23
- package/swift/README.md +26 -2
- package/swift/Test.swift +19 -7
- package/swift/USearch.swift +2 -2
- package/tsconfig.json +8 -0
- package/wasm/CMakeLists.txt +1 -0
- package/wasmer.toml +5 -1
- package/wolfram/CMakeLists.txt +1 -0
- package/wolfram/lib.cpp +11 -13
- package/golang/usearch.h +0 -9
- package/golang/usearch_par_test.go +0 -40
- package/golang/usearch_test.go +0 -82
- package/include/usearch/index_punned_dense.hpp +0 -871
- package/include/usearch/index_punned_helpers.hpp +0 -566
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#include <stdio.h>
|
|
2
|
+
|
|
3
|
+
#define PY_SSIZE_T_CLEAN
|
|
4
|
+
#include <Python.h>
|
|
5
|
+
|
|
6
|
+
#include "simsimd/simsimd.h"
|
|
7
|
+
|
|
8
|
+
#define stringify_value_m(a) stringify_m(a)
|
|
9
|
+
#define stringify_m(a) #a
|
|
10
|
+
#define concat_m(A, B) A##B
|
|
11
|
+
#define macro_concat_m(A, B) concat_m(A, B)
|
|
12
|
+
#define pyinit_f_m macro_concat_m(PyInit_, SIMDSIMD_PYTHON_MODULE_NAME)
|
|
13
|
+
|
|
14
|
+
char const* names[14] = {
|
|
15
|
+
"dot_f32_sve", "cos_f32_sve", "l2sq_f32_sve", "cos_f32x4_avx2", "cos_f32x4_neon",
|
|
16
|
+
"dot_f32x4_neon", "cos_f16x4_neon", "cos_f16x16_avx512", "l2sq_f16_sve", "cos_i8x16_neon",
|
|
17
|
+
"dot_i8x16_avx2", "hamming_b1x8_sve", "hamming_b1x128_sve", "hamming_b1x128_avx512",
|
|
18
|
+
};
|
|
19
|
+
void* callables[14] = {NULL};
|
|
20
|
+
|
|
21
|
+
void assign(size_t idx, void* function) { callables[idx] = function; }
|
|
22
|
+
|
|
23
|
+
typedef simsimd_f32_t (*function_f32_t)(simsimd_f32_t*, simsimd_f32_t*, size_t);
|
|
24
|
+
typedef simsimd_f32_t (*function_f32x16_t)(simsimd_f16_t*, simsimd_f16_t*, size_t);
|
|
25
|
+
typedef simsimd_f16_t (*function_f16_t)(simsimd_f16_t*, simsimd_f16_t*, size_t);
|
|
26
|
+
typedef simsimd_f32_t (*function_fi8_t)(int8_t*, int8_t*, size_t);
|
|
27
|
+
typedef int32_t (*function_i8_t)(int8_t*, int8_t*, size_t);
|
|
28
|
+
typedef size_t (*function_ui8_t)(uint8_t*, uint8_t*, size_t);
|
|
29
|
+
|
|
30
|
+
int main(int argc, char* argv[]) {
|
|
31
|
+
Py_Initialize();
|
|
32
|
+
PyObject* module = PyImport_ImportModule("simsimd");
|
|
33
|
+
PyErr_Print();
|
|
34
|
+
if (module == NULL)
|
|
35
|
+
return -1;
|
|
36
|
+
|
|
37
|
+
PyObject* dictionary = PyModule_GetDict(module);
|
|
38
|
+
PyObject *key, *value;
|
|
39
|
+
Py_ssize_t pos = 0;
|
|
40
|
+
|
|
41
|
+
size_t idx = 0;
|
|
42
|
+
while (PyDict_Next(dictionary, &pos, &key, &value)) {
|
|
43
|
+
if (PyCapsule_CheckExact(value)) {
|
|
44
|
+
char const* name = PyCapsule_GetName(value);
|
|
45
|
+
void* pointer = PyCapsule_GetPointer(value, NULL);
|
|
46
|
+
assign(idx, pointer);
|
|
47
|
+
++idx;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
size_t d = 3;
|
|
52
|
+
{
|
|
53
|
+
simsimd_f32_t a[] = {1, 2, 3};
|
|
54
|
+
simsimd_f32_t b[] = {4, 5, 6};
|
|
55
|
+
for (idx = 0; idx < 6; ++idx) {
|
|
56
|
+
function_f32_t function = (function_f32_t)callables[idx];
|
|
57
|
+
simsimd_f32_t result = function(a, b, d);
|
|
58
|
+
printf("%f\n", result);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
{
|
|
63
|
+
simsimd_f16_t a[] = {1, 2, 3};
|
|
64
|
+
simsimd_f16_t b[] = {4, 5, 6};
|
|
65
|
+
function_f32x16_t function = (function_f32x16_t)callables[6];
|
|
66
|
+
simsimd_f32_t result = function(a, b, d);
|
|
67
|
+
printf("%f\n", result);
|
|
68
|
+
function = (function_f32x16_t)callables[7];
|
|
69
|
+
result = function(a, b, d);
|
|
70
|
+
printf("%f\n", result);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
{
|
|
74
|
+
simsimd_f16_t a[] = {1, 2, 3};
|
|
75
|
+
simsimd_f16_t b[] = {4, 5, 6};
|
|
76
|
+
function_f16_t function = (function_f16_t)callables[8];
|
|
77
|
+
simsimd_f16_t result = function(a, b, d);
|
|
78
|
+
printf("%i\n", result);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
{
|
|
82
|
+
int8_t a[] = {1, 2, 3};
|
|
83
|
+
int8_t b[] = {4, 5, 6};
|
|
84
|
+
function_fi8_t function = (function_fi8_t)callables[9];
|
|
85
|
+
simsimd_f32_t result = function(a, b, d);
|
|
86
|
+
printf("%f\n", result);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
{
|
|
90
|
+
int8_t a[] = {1, 2, 3};
|
|
91
|
+
int8_t b[] = {4, 5, 6};
|
|
92
|
+
function_i8_t function = (function_i8_t)callables[10];
|
|
93
|
+
int32_t result = function(a, b, d);
|
|
94
|
+
printf("%i\n", result);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
{
|
|
98
|
+
uint8_t a[] = {1, 2, 3};
|
|
99
|
+
uint8_t b[] = {4, 5, 6};
|
|
100
|
+
for (idx = 11; idx < 14; ++idx) {
|
|
101
|
+
function_ui8_t function = (function_ui8_t)callables[idx];
|
|
102
|
+
size_t result = function(a, b, d);
|
|
103
|
+
printf("%zu\n", result);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
Py_DECREF(module);
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import simsimd as sisi
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_non_null():
|
|
5
|
+
assert sisi.to_int(sisi.dot_f32_sve) != 0
|
|
6
|
+
assert sisi.to_int(sisi.dot_f32x4_neon) != 0
|
|
7
|
+
assert sisi.to_int(sisi.cos_f32_sve) != 0
|
|
8
|
+
assert sisi.to_int(sisi.cos_f16_sve) != 0
|
|
9
|
+
assert sisi.to_int(sisi.cos_f16x4_neon) != 0
|
|
10
|
+
assert sisi.to_int(sisi.cos_i8x16_neon) != 0
|
|
11
|
+
assert sisi.to_int(sisi.cos_f32x4_neon) != 0
|
|
12
|
+
assert sisi.to_int(sisi.cos_f16x16_avx512) != 0
|
|
13
|
+
assert sisi.to_int(sisi.cos_f32x4_avx2) != 0
|
|
14
|
+
assert sisi.to_int(sisi.l2sq_f32_sve) != 0
|
|
15
|
+
assert sisi.to_int(sisi.l2sq_f16_sve) != 0
|
|
16
|
+
assert sisi.to_int(sisi.hamming_b1x8_sve) != 0
|
|
17
|
+
assert sisi.to_int(sisi.hamming_b1x128_sve) != 0
|
|
18
|
+
assert sisi.to_int(sisi.hamming_b1x128_avx512) != 0
|
|
19
|
+
assert sisi.to_int(sisi.tanimoto_b1x8_naive) != 0
|
|
20
|
+
assert sisi.to_int(sisi.tanimoto_maccs_naive) != 0
|
|
21
|
+
assert sisi.to_int(sisi.tanimoto_maccs_neon) != 0
|
|
22
|
+
assert sisi.to_int(sisi.tanimoto_maccs_sve) != 0
|
|
23
|
+
assert sisi.to_int(sisi.tanimoto_maccs_avx512) != 0
|
package/simsimd/setup.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from os.path import dirname
|
|
3
|
+
from setuptools import setup, Extension
|
|
4
|
+
|
|
5
|
+
__version__ = open("VERSION", "r").read().strip()
|
|
6
|
+
__lib_name__ = "simsimd"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
this_directory = os.path.abspath(dirname(__file__))
|
|
10
|
+
with open(os.path.join(this_directory, "README.md"), "r", encoding="utf8") as f:
|
|
11
|
+
long_description = f.read()
|
|
12
|
+
|
|
13
|
+
"-march=armv8.2-a+sve"
|
|
14
|
+
"-march=sapphirerapids"
|
|
15
|
+
|
|
16
|
+
setup(
|
|
17
|
+
name=__lib_name__,
|
|
18
|
+
version=__version__,
|
|
19
|
+
author="Ashot Vardanian",
|
|
20
|
+
author_email="1983160+ashvardanian@users.noreply.github.com",
|
|
21
|
+
url="https://github.com/ashvardanian/simsimd",
|
|
22
|
+
description="SIMD-accelerated similarity measures for x86 and Arm",
|
|
23
|
+
long_description=long_description,
|
|
24
|
+
long_description_content_type="text/markdown",
|
|
25
|
+
license="Apache-2.0",
|
|
26
|
+
classifiers=[
|
|
27
|
+
"Development Status :: 5 - Production/Stable",
|
|
28
|
+
"Natural Language :: English",
|
|
29
|
+
"Intended Audience :: Developers",
|
|
30
|
+
"Intended Audience :: Information Technology",
|
|
31
|
+
"License :: OSI Approved :: Apache Software License",
|
|
32
|
+
"Operating System :: Linux",
|
|
33
|
+
"Programming Language :: C",
|
|
34
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
35
|
+
],
|
|
36
|
+
ext_modules=[
|
|
37
|
+
Extension(
|
|
38
|
+
"simsimd",
|
|
39
|
+
sources=["python/python.c"],
|
|
40
|
+
include_dirs=["include"],
|
|
41
|
+
)
|
|
42
|
+
],
|
|
43
|
+
zip_safe=False,
|
|
44
|
+
)
|
package/swift/Index+Sugar.swift
CHANGED
|
@@ -2,98 +2,150 @@
|
|
|
2
2
|
// Index+Sugar.swift
|
|
3
3
|
//
|
|
4
4
|
//
|
|
5
|
-
// Created by
|
|
5
|
+
// Created by Ash Vardanian on 5/11/23.
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
@available(iOS 13, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
9
9
|
extension USearchIndex {
|
|
10
|
-
public typealias
|
|
10
|
+
public typealias Key = USearchKey
|
|
11
11
|
public typealias Metric = USearchMetric
|
|
12
12
|
public typealias Scalar = USearchScalar
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
/// Adds a labeled vector to the index.
|
|
15
|
-
/// - Parameter
|
|
15
|
+
/// - Parameter key: Unique identifer for that object.
|
|
16
16
|
/// - Parameter vector: Single-precision vector.
|
|
17
17
|
/// - Throws: If runs out of memory.
|
|
18
|
-
public func add(
|
|
18
|
+
public func add(key: USearchKey, vector: ArraySlice<Float32>) {
|
|
19
19
|
vector.withContiguousStorageIfAvailable {
|
|
20
|
-
addSingle(
|
|
20
|
+
addSingle(key: key, vector: $0.baseAddress!)
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
/// Adds a labeled vector to the index.
|
|
25
|
+
/// - Parameter key: Unique identifer for that object.
|
|
26
|
+
/// - Parameter vector: Single-precision vector.
|
|
27
|
+
/// - Throws: If runs out of memory.
|
|
28
|
+
public func add(key: USearchKey, vector: Array<Float32>) {
|
|
29
|
+
add(key: key, vector: vector[...])
|
|
30
|
+
}
|
|
31
|
+
|
|
25
32
|
/// Approximate nearest neighbors search.
|
|
26
33
|
/// - Parameter vector: Single-precision query vector.
|
|
27
34
|
/// - Parameter count: Upper limit on the number of matches to retrieve.
|
|
28
35
|
/// - Returns: Labels and distances to closest approximate matches in decreasing similarity order.
|
|
29
36
|
/// - Throws: If runs out of memory.
|
|
30
|
-
public func search(vector: ArraySlice<Float32>, count: Int) -> ([
|
|
31
|
-
var matches: [
|
|
37
|
+
public func search(vector: ArraySlice<Float32>, count: Int) -> ([Key], [Float]) {
|
|
38
|
+
var matches: [Key] = Array(repeating: 0, count: count)
|
|
32
39
|
var distances: [Float] = Array(repeating: 0, count: count)
|
|
33
40
|
let results = vector.withContiguousStorageIfAvailable {
|
|
34
|
-
searchSingle(vector: $0.baseAddress!, count: CUnsignedInt(count),
|
|
41
|
+
searchSingle(vector: $0.baseAddress!, count: CUnsignedInt(count), keys: &matches, distances: &distances)
|
|
35
42
|
}
|
|
36
43
|
matches.removeLast(count - Int(results!))
|
|
37
44
|
distances.removeLast(count - Int(results!))
|
|
38
45
|
return (matches, distances)
|
|
39
46
|
}
|
|
40
47
|
|
|
48
|
+
/// Approximate nearest neighbors search.
|
|
49
|
+
/// - Parameter vector: Single-precision query vector.
|
|
50
|
+
/// - Parameter count: Upper limit on the number of matches to retrieve.
|
|
51
|
+
/// - Returns: Labels and distances to closest approximate matches in decreasing similarity order.
|
|
52
|
+
/// - Throws: If runs out of memory.
|
|
53
|
+
public func search(vector: Array<Float32>, count: Int) -> ([Key], [Float]) {
|
|
54
|
+
return search(vector: vector[...], count: count)
|
|
55
|
+
}
|
|
56
|
+
|
|
41
57
|
/// Adds a labeled vector to the index.
|
|
42
|
-
/// - Parameter
|
|
58
|
+
/// - Parameter key: Unique identifer for that object.
|
|
43
59
|
/// - Parameter vector: Double-precision vector.
|
|
44
60
|
/// - Throws: If runs out of memory.
|
|
45
|
-
public func add(
|
|
61
|
+
public func add(key: Key, vector: ArraySlice<Float64>) {
|
|
46
62
|
vector.withContiguousStorageIfAvailable {
|
|
47
|
-
addDouble(
|
|
63
|
+
addDouble(key: key, vector: $0.baseAddress!)
|
|
48
64
|
}
|
|
49
65
|
}
|
|
50
|
-
|
|
66
|
+
|
|
67
|
+
/// Adds a labeled vector to the index.
|
|
68
|
+
/// - Parameter key: Unique identifer for that object.
|
|
69
|
+
/// - Parameter vector: Double-precision vector.
|
|
70
|
+
/// - Throws: If runs out of memory.
|
|
71
|
+
public func add(key: Key, vector: Array<Float64>) {
|
|
72
|
+
add(key: key, vector: vector[...])
|
|
73
|
+
}
|
|
74
|
+
|
|
51
75
|
/// Approximate nearest neighbors search.
|
|
52
76
|
/// - Parameter vector: Double-precision query vector.
|
|
53
77
|
/// - Parameter count: Upper limit on the number of matches to retrieve.
|
|
54
78
|
/// - Returns: Labels and distances to closest approximate matches in decreasing similarity order.
|
|
55
79
|
/// - Throws: If runs out of memory.
|
|
56
|
-
public func search(vector: ArraySlice<Float64>, count: Int) -> ([
|
|
57
|
-
var matches: [
|
|
80
|
+
public func search(vector: ArraySlice<Float64>, count: Int) -> ([Key], [Float]) {
|
|
81
|
+
var matches: [Key] = Array(repeating: 0, count: count)
|
|
58
82
|
var distances: [Float] = Array(repeating: 0, count: count)
|
|
59
83
|
let results = vector.withContiguousStorageIfAvailable {
|
|
60
|
-
searchDouble(vector: $0.baseAddress!, count: CUnsignedInt(count),
|
|
84
|
+
searchDouble(vector: $0.baseAddress!, count: CUnsignedInt(count), keys: &matches, distances: &distances)
|
|
61
85
|
}
|
|
62
86
|
matches.removeLast(count - Int(results!))
|
|
63
87
|
distances.removeLast(count - Int(results!))
|
|
64
88
|
return (matches, distances)
|
|
65
89
|
}
|
|
66
90
|
|
|
91
|
+
/// Approximate nearest neighbors search.
|
|
92
|
+
/// - Parameter vector: Double-precision query vector.
|
|
93
|
+
/// - Parameter count: Upper limit on the number of matches to retrieve.
|
|
94
|
+
/// - Returns: Labels and distances to closest approximate matches in decreasing similarity order.
|
|
95
|
+
/// - Throws: If runs out of memory.
|
|
96
|
+
public func search(vector: Array<Float64>, count: Int) -> ([Key], [Float]) {
|
|
97
|
+
search(vector: vector[...], count: count)
|
|
98
|
+
}
|
|
99
|
+
|
|
67
100
|
#if arch(arm64)
|
|
68
101
|
|
|
69
102
|
/// Adds a labeled vector to the index.
|
|
70
|
-
/// - Parameter
|
|
103
|
+
/// - Parameter key: Unique identifer for that object.
|
|
71
104
|
/// - Parameter vector: Half-precision vector.
|
|
72
105
|
/// - Throws: If runs out of memory.
|
|
73
106
|
@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
|
|
74
|
-
public func add(
|
|
107
|
+
public func add(key: Key, vector: ArraySlice<Float16>) {
|
|
75
108
|
vector.withContiguousStorageIfAvailable { buffer in
|
|
76
|
-
addHalf(
|
|
109
|
+
addHalf(key: key, vector: buffer.baseAddress!)
|
|
77
110
|
}
|
|
78
111
|
}
|
|
79
112
|
|
|
113
|
+
/// Adds a labeled vector to the index.
|
|
114
|
+
/// - Parameter key: Unique identifer for that object.
|
|
115
|
+
/// - Parameter vector: Half-precision vector.
|
|
116
|
+
/// - Throws: If runs out of memory.
|
|
117
|
+
@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
|
|
118
|
+
public func add(key: Key, vector: Array<Float16>) {
|
|
119
|
+
add(key: key, vector: vector[...])
|
|
120
|
+
}
|
|
121
|
+
|
|
80
122
|
/// Approximate nearest neighbors search.
|
|
81
123
|
/// - Parameter vector: Half-precision query vector.
|
|
82
124
|
/// - Parameter count: Upper limit on the number of matches to retrieve.
|
|
83
125
|
/// - Returns: Labels and distances to closest approximate matches in decreasing similarity order.
|
|
84
126
|
/// - Throws: If runs out of memory.
|
|
85
127
|
@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
|
|
86
|
-
public func search(vector: ArraySlice<Float16>, count: Int) -> ([
|
|
87
|
-
var matches: [
|
|
128
|
+
public func search(vector: ArraySlice<Float16>, count: Int) -> ([Key], [Float]) {
|
|
129
|
+
var matches: [Key] = Array(repeating: 0, count: count)
|
|
88
130
|
var distances: [Float] = Array(repeating: 0, count: count)
|
|
89
131
|
let results = vector.withContiguousStorageIfAvailable {
|
|
90
|
-
searchHalf(vector: $0.baseAddress!, count: CUnsignedInt(count),
|
|
132
|
+
searchHalf(vector: $0.baseAddress!, count: CUnsignedInt(count), keys: &matches, distances: &distances)
|
|
91
133
|
}
|
|
92
134
|
matches.removeLast(count - Int(results!))
|
|
93
135
|
distances.removeLast(count - Int(results!))
|
|
94
136
|
return (matches, distances)
|
|
95
137
|
}
|
|
96
138
|
|
|
139
|
+
/// Approximate nearest neighbors search.
|
|
140
|
+
/// - Parameter vector: Half-precision query vector.
|
|
141
|
+
/// - Parameter count: Upper limit on the number of matches to retrieve.
|
|
142
|
+
/// - Returns: Labels and distances to closest approximate matches in decreasing similarity order.
|
|
143
|
+
/// - Throws: If runs out of memory.
|
|
144
|
+
@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
|
|
145
|
+
public func search(vector: Array<Float16>, count: Int) -> ([Key], [Float]) {
|
|
146
|
+
search(vector: vector[...], count: count)
|
|
147
|
+
}
|
|
148
|
+
|
|
97
149
|
#endif
|
|
98
150
|
|
|
99
151
|
/// Number of vectors in the index.
|
package/swift/README.md
CHANGED
|
@@ -12,9 +12,33 @@ https://github.com/unum-cloud/usearch
|
|
|
12
12
|
let index = Index.l2sq(dimensions: 3, connectivity: 8)
|
|
13
13
|
let vectorA: [Float32] = [0.3, 0.5, 1.2]
|
|
14
14
|
let vectorB: [Float32] = [0.4, 0.2, 1.2]
|
|
15
|
-
index.add(
|
|
16
|
-
index.add(
|
|
15
|
+
index.add(key: 42, vector: vectorA[...])
|
|
16
|
+
index.add(key: 43, vector: vectorB[...])
|
|
17
17
|
|
|
18
18
|
let results = index.search(vector: vectorA[...], count: 10)
|
|
19
19
|
assert(results.0[0] == 42)
|
|
20
20
|
```
|
|
21
|
+
|
|
22
|
+
If using in a SwiftUI application, make sure to annulate the void responses:
|
|
23
|
+
|
|
24
|
+
```swift
|
|
25
|
+
import SwiftUI
|
|
26
|
+
import USearch
|
|
27
|
+
|
|
28
|
+
@main
|
|
29
|
+
struct USearchMobileApp: App {
|
|
30
|
+
var body: some Scene {
|
|
31
|
+
WindowGroup {
|
|
32
|
+
let index = USearchIndex.make(metric: .IP, dimensions: 2, connectivity: 16, quantization: .F32)
|
|
33
|
+
let _ = index.reserve(10)
|
|
34
|
+
let coordiantes: Array<Float32> = [40.177200, 44.503490]
|
|
35
|
+
let _ = index.add(key: 10, vector: coordiantes[...])
|
|
36
|
+
VStack {
|
|
37
|
+
Text("USearch index contains \(index.length) vectors")
|
|
38
|
+
Spacer()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
package/swift/Test.swift
CHANGED
|
@@ -1,25 +1,37 @@
|
|
|
1
1
|
//
|
|
2
2
|
// File.swift
|
|
3
|
-
//
|
|
4
3
|
//
|
|
5
|
-
//
|
|
4
|
+
//
|
|
5
|
+
// Created by Ash Vardanian on 5/11/23.
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
import Foundation
|
|
9
|
-
import XCTest
|
|
10
9
|
import USearch
|
|
10
|
+
import XCTest
|
|
11
11
|
|
|
12
12
|
@available(iOS 13, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
13
13
|
class Test: XCTestCase {
|
|
14
14
|
func testUnit() throws {
|
|
15
|
-
let index = USearchIndex.make(metric:USearchMetric.l2sq, dimensions: 4, connectivity: 8, quantization:USearchScalar.F32)
|
|
15
|
+
let index = USearchIndex.make(metric: USearchMetric.l2sq, dimensions: 4, connectivity: 8, quantization: USearchScalar.F32)
|
|
16
16
|
let vectorA: [Float32] = [0.3, 0.5, 1.2, 1.4]
|
|
17
17
|
let vectorB: [Float32] = [0.4, 0.2, 1.2, 1.1]
|
|
18
18
|
index.reserve(2)
|
|
19
|
-
index.add(label: 42, vector: vectorA[...])
|
|
20
|
-
index.add(label: 43, vector: vectorB[...])
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
// Adding a slice
|
|
21
|
+
index.add(key: 42, vector: vectorA[...])
|
|
22
|
+
|
|
23
|
+
// Adding a vector
|
|
24
|
+
index.add(key: 43, vector: vectorB)
|
|
25
|
+
|
|
26
|
+
let results = index.search(vector: vectorA, count: 10)
|
|
23
27
|
assert(results.0[0] == 42)
|
|
28
|
+
|
|
29
|
+
assert(index.contains(key: 42))
|
|
30
|
+
assert(index.count(key: 42) == 1)
|
|
31
|
+
assert(index.count(key: 49) == 0)
|
|
32
|
+
index.rename(from: 42, to: 49)
|
|
33
|
+
assert(index.count(key: 49) == 1)
|
|
34
|
+
index.remove(key: 49)
|
|
35
|
+
assert(index.count(key: 49) == 0)
|
|
24
36
|
}
|
|
25
37
|
}
|
package/swift/USearch.swift
CHANGED
package/tsconfig.json
ADDED
package/wasm/CMakeLists.txt
CHANGED
|
@@ -13,3 +13,4 @@ set(USEARCH_PUNNED_INCLUDE_DIRS
|
|
|
13
13
|
add_executable(index lib.cpp)
|
|
14
14
|
target_include_directories(index PRIVATE ${USEARCH_PUNNED_INCLUDE_DIRS})
|
|
15
15
|
set_target_properties(index PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/wasm)
|
|
16
|
+
set_target_properties(index PROPERTIES CXX_STANDARD 11)
|
package/wasmer.toml
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name="unum/usearch"
|
|
3
|
-
version="0.
|
|
3
|
+
version="1.0.0"
|
|
4
4
|
description="Smaller & Faster Single-File Vector Search Engine from Unum"
|
|
5
|
+
license="Apache-2.0"
|
|
6
|
+
readme="README.md"
|
|
7
|
+
repository="https://github.com/unum-cloud/usearch"
|
|
8
|
+
homepage="https://unum-cloud.github.io/usearch/javascript"
|
|
5
9
|
|
|
6
10
|
[[module]]
|
|
7
11
|
name="index"
|
package/wolfram/CMakeLists.txt
CHANGED
|
@@ -15,3 +15,4 @@ target_link_libraries(usearchWFM WSTP::STATIC_LIBRARY)
|
|
|
15
15
|
|
|
16
16
|
# Set the output name of the library to be compatible with Wolfram Language
|
|
17
17
|
set_target_properties(usearchWFM PROPERTIES PREFIX "" OUTPUT_NAME "usearchWFM")
|
|
18
|
+
set_target_properties(usearchWFM PROPERTIES CXX_STANDARD 11)
|
package/wolfram/lib.cpp
CHANGED
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
#include "WolframLibrary.h"
|
|
2
|
-
#include <usearch/
|
|
2
|
+
#include <usearch/index_dense.hpp>
|
|
3
3
|
|
|
4
4
|
using namespace unum::usearch;
|
|
5
5
|
|
|
6
|
-
using
|
|
7
|
-
using
|
|
8
|
-
using index_t = punned_small_t;
|
|
6
|
+
using distance_t = distance_punned_t;
|
|
7
|
+
using index_t = index_dense_t;
|
|
9
8
|
using vector_view_t = span_gt<float>;
|
|
10
9
|
|
|
11
10
|
using add_result_t = typename index_t::add_result_t;
|
|
12
11
|
using search_result_t = typename index_t::search_result_t;
|
|
13
|
-
using
|
|
14
|
-
using id_t = typename index_t::id_t;
|
|
12
|
+
using key_t = typename index_t::key_t;
|
|
15
13
|
|
|
16
14
|
EXTERN_C DLLEXPORT int WolframLibrary_initialize(WolframLibraryData libData) { return LIBRARY_NO_ERROR; }
|
|
17
15
|
EXTERN_C DLLEXPORT void WolframLibrary_uninitialize(WolframLibraryData libData) { return; }
|
|
18
16
|
|
|
19
17
|
EXTERN_C DLLEXPORT int IndexCreate(WolframLibraryData libData, mint Argc, MArgument* Args, MArgument Res) {
|
|
20
18
|
index_config_t config;
|
|
21
|
-
char*
|
|
19
|
+
char* quantization_cstr = nullptr;
|
|
22
20
|
char* metric_cstr = nullptr;
|
|
23
21
|
try {
|
|
24
|
-
|
|
22
|
+
quantization_cstr = MArgument_getUTF8String(Args[1]);
|
|
25
23
|
metric_cstr = MArgument_getUTF8String(Args[0]);
|
|
26
24
|
std::size_t dimensions = static_cast<std::size_t>(MArgument_getInteger(Args[2]));
|
|
27
25
|
std::size_t capacity = static_cast<std::size_t>(MArgument_getInteger(Args[3]));
|
|
@@ -29,9 +27,9 @@ EXTERN_C DLLEXPORT int IndexCreate(WolframLibraryData libData, mint Argc, MArgum
|
|
|
29
27
|
config.expansion_add = static_cast<std::size_t>(MArgument_getInteger(Args[5]));
|
|
30
28
|
config.expansion_search = static_cast<std::size_t>(MArgument_getInteger(Args[6]));
|
|
31
29
|
|
|
32
|
-
scalar_kind_t
|
|
30
|
+
scalar_kind_t quantization = scalar_kind_from_name(quantization_cstr, std::strlen(quantization_cstr));
|
|
33
31
|
metric_kind_t metric_kind = metric_from_name(metric_cstr, std::strlen(metric_cstr));
|
|
34
|
-
index_t index = make_punned<index_t>(metric_kind, dimensions,
|
|
32
|
+
index_t index = make_punned<index_t>(metric_kind, dimensions, quantization, config);
|
|
35
33
|
index.reserve(capacity);
|
|
36
34
|
|
|
37
35
|
index_t* result_ptr = new index_t(std::move(index));
|
|
@@ -41,7 +39,7 @@ EXTERN_C DLLEXPORT int IndexCreate(WolframLibraryData libData, mint Argc, MArgum
|
|
|
41
39
|
return LIBRARY_FUNCTION_ERROR;
|
|
42
40
|
}
|
|
43
41
|
|
|
44
|
-
libData->UTF8String_disown(
|
|
42
|
+
libData->UTF8String_disown(quantization_cstr);
|
|
45
43
|
libData->UTF8String_disown(metric_cstr);
|
|
46
44
|
return LIBRARY_NO_ERROR;
|
|
47
45
|
}
|
|
@@ -119,12 +117,12 @@ EXTERN_C DLLEXPORT int IndexAdd(WolframLibraryData libData, mint Argc, MArgument
|
|
|
119
117
|
index_t* c_ptr = (index_t*)MArgument_getUTF8String(Args[0]);
|
|
120
118
|
float* vector_data = nullptr;
|
|
121
119
|
try {
|
|
122
|
-
int
|
|
120
|
+
int key = MArgument_getInteger(Args[1]);
|
|
123
121
|
MTensor tens = MArgument_getMTensor(Args[2]);
|
|
124
122
|
std::size_t len = libData->MTensor_getFlattenedLength(tens);
|
|
125
123
|
vector_data = (float*)libData->MTensor_getRealData(tens);
|
|
126
124
|
vector_view_t vector_span = vector_view_t{vector_data, len};
|
|
127
|
-
c_ptr->add(
|
|
125
|
+
c_ptr->add(key, vector_span);
|
|
128
126
|
} catch (...) {
|
|
129
127
|
return LIBRARY_FUNCTION_ERROR;
|
|
130
128
|
}
|
package/golang/usearch.h
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
package usearch
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"sync"
|
|
5
|
-
"testing"
|
|
6
|
-
)
|
|
7
|
-
|
|
8
|
-
func TestParallelism(t *testing.T) {
|
|
9
|
-
totalInserts := 3000
|
|
10
|
-
ind := NewIndex(DefaultConfig(128))
|
|
11
|
-
defer ind.Destroy()
|
|
12
|
-
|
|
13
|
-
var v0 = make([]float32, 128)
|
|
14
|
-
v0[0] = 4.4
|
|
15
|
-
var wg sync.WaitGroup
|
|
16
|
-
ind.Reserve(totalInserts)
|
|
17
|
-
|
|
18
|
-
// second thread
|
|
19
|
-
wg.Add(1)
|
|
20
|
-
go func() {
|
|
21
|
-
defer wg.Done()
|
|
22
|
-
var v1 = make([]float32, 128)
|
|
23
|
-
for l := 0; l < totalInserts/2; l++ {
|
|
24
|
-
ind.Add(10000+l, v1)
|
|
25
|
-
}
|
|
26
|
-
}()
|
|
27
|
-
|
|
28
|
-
for l := 0; l < totalInserts/2; l++ {
|
|
29
|
-
ind.Add(l, v0)
|
|
30
|
-
}
|
|
31
|
-
wg.Wait()
|
|
32
|
-
if ind.Len() != totalInserts {
|
|
33
|
-
t.Errorf("wrong length: expected %d, got %d", totalInserts, ind.Len())
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
res := ind.Search(v0[:], 10)
|
|
37
|
-
if len(res) != 10 {
|
|
38
|
-
t.Errorf("wrong search result length: expected %d, got %d", 10, len(res))
|
|
39
|
-
}
|
|
40
|
-
}
|