hnswlib 0.3.0 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/README.md +15 -1
- data/ext/hnswlib/hnswlibext.cpp +1 -1
- data/ext/hnswlib/hnswlibext.hpp +1 -1
- data/ext/hnswlib/src/bruteforce.h +2 -2
- data/ext/hnswlib/src/hnswalg.h +52 -38
- data/ext/hnswlib/src/hnswlib.h +85 -1
- data/ext/hnswlib/src/space_ip.h +119 -25
- data/ext/hnswlib/src/space_l2.h +57 -8
- data/ext/hnswlib/src/visited_list_pool.h +1 -0
- data/lib/hnswlib/version.rb +2 -2
- data/sig/hnswlib.rbs +69 -0
- metadata +4 -10
- data/.github/workflows/build.yml +0 -20
- data/.gitignore +0 -19
- data/.rspec +0 -3
- data/CODE_OF_CONDUCT.md +0 -84
- data/Gemfile +0 -10
- data/Rakefile +0 -17
- data/hnswlib.gemspec +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f313f103d9a4a6e6ae92f21cc92a4aaaecd3121e3a2c5be533f87954afb5ac7d
|
4
|
+
data.tar.gz: 3b7ec61621c905759f466df1ac76c18a76186a5ef5c84517ada5c1493524ec5c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 12717a35c16fb2ef3f8884fb541559349410e43c9ba35c98de4fbf4a5faa6cd58ffba2cd26ac049ffaea855cf2c4e33f77ffefea023b901cbd8605ada3b63d5c
|
7
|
+
data.tar.gz: c134905c5c268313a8a6bfff69567a66ca62e3d2e1a9d133cd87592262994eaf3731ac3b59e1eaed212a9dfa514f2c91b9eceb11f420e6f988d15f9250dbe863
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
## [0.5.2] - 2022-02-19
|
2
|
+
|
3
|
+
- Update bundled hnswlib version to 0.6.2.
|
4
|
+
|
5
|
+
## [0.5.1] - 2022-02-11
|
6
|
+
|
7
|
+
- Update bundled hnswlib version to 0.6.1.
|
8
|
+
- Update documentations.
|
9
|
+
- Introduce conventional commits.
|
10
|
+
|
11
|
+
## [0.5.0] - 2021-12-12
|
12
|
+
|
13
|
+
- Update bundled hnswlib version to 0.6.0.
|
14
|
+
|
15
|
+
## [0.4.0] - 2021-09-12
|
16
|
+
|
17
|
+
- Add type declaration file.
|
18
|
+
|
1
19
|
## [0.3.0] - 2021-08-08
|
2
20
|
|
3
21
|
- Rename `Hnswlib::Index` to `Hnswlib::HnswIndex` (for compatibility, `Hnswlib::Index` has been an alis for `Hnswlib::HnswIndex`).
|
data/README.md
CHANGED
@@ -25,6 +25,20 @@ Or install it yourself as:
|
|
25
25
|
|
26
26
|
$ gem install hnswlib
|
27
27
|
|
28
|
+
Note: hnswlib.rb gives no optimization options when building native extensions.
|
29
|
+
If necessary, add the optimization options yourself during installation, as follows;
|
30
|
+
|
31
|
+
```
|
32
|
+
$ bundle config --local build.hnswlib "--with-cxxflags=-march=native"
|
33
|
+
$ bundle install
|
34
|
+
```
|
35
|
+
|
36
|
+
Or:
|
37
|
+
|
38
|
+
```
|
39
|
+
$ gem install hnswlib -- --with-cxxflags=-march=native
|
40
|
+
```
|
41
|
+
|
28
42
|
## Documentation
|
29
43
|
|
30
44
|
* [Hnswlib.rb API Documentation](https://yoshoku.github.io/hnswlib.rb/doc/)
|
@@ -57,4 +71,4 @@ The gem is available as open source under the terms of the [Apache-2.0 License](
|
|
57
71
|
|
58
72
|
Bug reports and pull requests are welcome on GitHub at https://github.com/yoshoku/hnswlib.rb.
|
59
73
|
This project is intended to be a safe, welcoming space for collaboration,
|
60
|
-
and contributors are expected to adhere to the [
|
74
|
+
and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
data/ext/hnswlib/hnswlibext.cpp
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
/**
|
2
2
|
* hnswlib.rb is a Ruby binding for the Hnswlib.
|
3
3
|
*
|
4
|
-
* Copyright (c) 2021 Atsushi Tatsuma
|
4
|
+
* Copyright (c) 2021-2022 Atsushi Tatsuma
|
5
5
|
*
|
6
6
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
* you may not use this file except in compliance with the License.
|
data/ext/hnswlib/hnswlibext.hpp
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
/**
|
2
2
|
* hnswlib.rb is a Ruby binding for the Hnswlib.
|
3
3
|
*
|
4
|
-
* Copyright (c) 2021 Atsushi Tatsuma
|
4
|
+
* Copyright (c) 2021-2022 Atsushi Tatsuma
|
5
5
|
*
|
6
6
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
* you may not use this file except in compliance with the License.
|
@@ -92,13 +92,13 @@ namespace hnswlib {
|
|
92
92
|
searchKnn(const void *query_data, size_t k) const {
|
93
93
|
std::priority_queue<std::pair<dist_t, labeltype >> topResults;
|
94
94
|
if (cur_element_count == 0) return topResults;
|
95
|
-
for (
|
95
|
+
for (int i = 0; i < k; i++) {
|
96
96
|
dist_t dist = fstdistfunc_(query_data, data_ + size_per_element_ * i, dist_func_param_);
|
97
97
|
topResults.push(std::pair<dist_t, labeltype>(dist, *((labeltype *) (data_ + size_per_element_ * i +
|
98
98
|
data_size_))));
|
99
99
|
}
|
100
100
|
dist_t lastdist = topResults.top().first;
|
101
|
-
for (
|
101
|
+
for (int i = k; i < cur_element_count; i++) {
|
102
102
|
dist_t dist = fstdistfunc_(query_data, data_ + size_per_element_ * i, dist_func_param_);
|
103
103
|
if (dist <= lastdist) {
|
104
104
|
topResults.push(std::pair<dist_t, labeltype>(dist, *((labeltype *) (data_ + size_per_element_ * i +
|
data/ext/hnswlib/src/hnswalg.h
CHANGED
@@ -19,7 +19,6 @@ namespace hnswlib {
|
|
19
19
|
static const tableint max_update_element_locks = 65536;
|
20
20
|
HierarchicalNSW() : visited_list_pool_(nullptr), data_level0_memory_(nullptr), linkLists_(nullptr), cur_element_count(0) { }
|
21
21
|
HierarchicalNSW(SpaceInterface<dist_t> *s) {
|
22
|
-
|
23
22
|
}
|
24
23
|
|
25
24
|
HierarchicalNSW(SpaceInterface<dist_t> *s, const std::string &location, bool nmslib = false, size_t max_elements=0) {
|
@@ -30,7 +29,7 @@ namespace hnswlib {
|
|
30
29
|
link_list_locks_(max_elements), link_list_update_locks_(max_update_element_locks), element_levels_(max_elements) {
|
31
30
|
max_elements_ = max_elements;
|
32
31
|
|
33
|
-
|
32
|
+
num_deleted_ = 0;
|
34
33
|
data_size_ = s->get_data_size();
|
35
34
|
fstdistfunc_ = s->get_dist_func();
|
36
35
|
dist_func_param_ = s->get_dist_func_param();
|
@@ -57,8 +56,6 @@ namespace hnswlib {
|
|
57
56
|
|
58
57
|
visited_list_pool_ = new VisitedListPool(1, max_elements);
|
59
58
|
|
60
|
-
|
61
|
-
|
62
59
|
//initializations for special treatment of the first node
|
63
60
|
enterpoint_node_ = -1;
|
64
61
|
maxlevel_ = -1;
|
@@ -93,6 +90,7 @@ namespace hnswlib {
|
|
93
90
|
size_t cur_element_count;
|
94
91
|
size_t size_data_per_element_;
|
95
92
|
size_t size_links_per_element_;
|
93
|
+
size_t num_deleted_;
|
96
94
|
|
97
95
|
size_t M_;
|
98
96
|
size_t maxM_;
|
@@ -113,20 +111,15 @@ namespace hnswlib {
|
|
113
111
|
std::vector<std::mutex> link_list_update_locks_;
|
114
112
|
tableint enterpoint_node_;
|
115
113
|
|
116
|
-
|
117
114
|
size_t size_links_level0_;
|
118
115
|
size_t offsetData_, offsetLevel0_;
|
119
116
|
|
120
|
-
|
121
117
|
char *data_level0_memory_;
|
122
118
|
char **linkLists_;
|
123
119
|
std::vector<int> element_levels_;
|
124
120
|
|
125
121
|
size_t data_size_;
|
126
122
|
|
127
|
-
bool has_deletions_;
|
128
|
-
|
129
|
-
|
130
123
|
size_t label_offset_;
|
131
124
|
DISTFUNC<dist_t> fstdistfunc_;
|
132
125
|
void *dist_func_param_;
|
@@ -183,7 +176,7 @@ namespace hnswlib {
|
|
183
176
|
|
184
177
|
while (!candidateSet.empty()) {
|
185
178
|
std::pair<dist_t, tableint> curr_el_pair = candidateSet.top();
|
186
|
-
if ((-curr_el_pair.first) > lowerBound) {
|
179
|
+
if ((-curr_el_pair.first) > lowerBound && top_candidates.size() == ef_construction_) {
|
187
180
|
break;
|
188
181
|
}
|
189
182
|
candidateSet.pop();
|
@@ -272,7 +265,7 @@ namespace hnswlib {
|
|
272
265
|
|
273
266
|
std::pair<dist_t, tableint> current_node_pair = candidate_set.top();
|
274
267
|
|
275
|
-
if ((-current_node_pair.first) > lowerBound) {
|
268
|
+
if ((-current_node_pair.first) > lowerBound && (top_candidates.size() == ef || has_deletions == false)) {
|
276
269
|
break;
|
277
270
|
}
|
278
271
|
candidate_set.pop();
|
@@ -548,7 +541,7 @@ namespace hnswlib {
|
|
548
541
|
}
|
549
542
|
}
|
550
543
|
|
551
|
-
if (
|
544
|
+
if (num_deleted_) {
|
552
545
|
std::priority_queue<std::pair<dist_t, tableint >> top_candidates1=searchBaseLayerST<true>(currObj, query_data,
|
553
546
|
ef_);
|
554
547
|
top_candidates.swap(top_candidates1);
|
@@ -624,8 +617,6 @@ namespace hnswlib {
|
|
624
617
|
}
|
625
618
|
|
626
619
|
void loadIndex(const std::string &location, SpaceInterface<dist_t> *s, size_t max_elements_i=0) {
|
627
|
-
|
628
|
-
|
629
620
|
std::ifstream input(location, std::ios::binary);
|
630
621
|
|
631
622
|
if (!input.is_open())
|
@@ -640,7 +631,7 @@ namespace hnswlib {
|
|
640
631
|
readBinaryPOD(input, max_elements_);
|
641
632
|
readBinaryPOD(input, cur_element_count);
|
642
633
|
|
643
|
-
size_t max_elements=max_elements_i;
|
634
|
+
size_t max_elements = max_elements_i;
|
644
635
|
if(max_elements < cur_element_count)
|
645
636
|
max_elements = max_elements_;
|
646
637
|
max_elements_ = max_elements;
|
@@ -689,26 +680,19 @@ namespace hnswlib {
|
|
689
680
|
|
690
681
|
input.seekg(pos,input.beg);
|
691
682
|
|
692
|
-
|
693
683
|
data_level0_memory_ = (char *) malloc(max_elements * size_data_per_element_);
|
694
684
|
if (data_level0_memory_ == nullptr)
|
695
685
|
throw std::runtime_error("Not enough memory: loadIndex failed to allocate level0");
|
696
686
|
input.read(data_level0_memory_, cur_element_count * size_data_per_element_);
|
697
687
|
|
698
|
-
|
699
|
-
|
700
|
-
|
701
688
|
size_links_per_element_ = maxM_ * sizeof(tableint) + sizeof(linklistsizeint);
|
702
689
|
|
703
|
-
|
704
690
|
size_links_level0_ = maxM0_ * sizeof(tableint) + sizeof(linklistsizeint);
|
705
691
|
std::vector<std::mutex>(max_elements).swap(link_list_locks_);
|
706
692
|
std::vector<std::mutex>(max_update_element_locks).swap(link_list_update_locks_);
|
707
693
|
|
708
|
-
|
709
694
|
visited_list_pool_ = new VisitedListPool(1, max_elements);
|
710
695
|
|
711
|
-
|
712
696
|
linkLists_ = (char **) malloc(sizeof(void *) * max_elements);
|
713
697
|
if (linkLists_ == nullptr)
|
714
698
|
throw std::runtime_error("Not enough memory: loadIndex failed to allocate linklists");
|
@@ -732,11 +716,9 @@ namespace hnswlib {
|
|
732
716
|
}
|
733
717
|
}
|
734
718
|
|
735
|
-
has_deletions_=false;
|
736
|
-
|
737
719
|
for (size_t i = 0; i < cur_element_count; i++) {
|
738
720
|
if(isMarkedDeleted(i))
|
739
|
-
|
721
|
+
num_deleted_ += 1;
|
740
722
|
}
|
741
723
|
|
742
724
|
input.close();
|
@@ -745,7 +727,7 @@ namespace hnswlib {
|
|
745
727
|
}
|
746
728
|
|
747
729
|
template<typename data_t>
|
748
|
-
std::vector<data_t> getDataByLabel(labeltype label)
|
730
|
+
std::vector<data_t> getDataByLabel(labeltype label) const
|
749
731
|
{
|
750
732
|
tableint label_c;
|
751
733
|
auto search = label_lookup_.find(label);
|
@@ -758,7 +740,7 @@ namespace hnswlib {
|
|
758
740
|
size_t dim = *((size_t *) dist_func_param_);
|
759
741
|
std::vector<data_t> data;
|
760
742
|
data_t* data_ptr = (data_t*) data_ptrv;
|
761
|
-
for (
|
743
|
+
for (int i = 0; i < dim; i++) {
|
762
744
|
data.push_back(*data_ptr);
|
763
745
|
data_ptr += 1;
|
764
746
|
}
|
@@ -766,19 +748,19 @@ namespace hnswlib {
|
|
766
748
|
}
|
767
749
|
|
768
750
|
static const unsigned char DELETE_MARK = 0x01;
|
769
|
-
//
|
751
|
+
// static const unsigned char REUSE_MARK = 0x10;
|
770
752
|
/**
|
771
753
|
* Marks an element with the given label deleted, does NOT really change the current graph.
|
772
754
|
* @param label
|
773
755
|
*/
|
774
756
|
void markDelete(labeltype label)
|
775
757
|
{
|
776
|
-
has_deletions_=true;
|
777
758
|
auto search = label_lookup_.find(label);
|
778
759
|
if (search == label_lookup_.end()) {
|
779
760
|
throw std::runtime_error("Label not found");
|
780
761
|
}
|
781
|
-
|
762
|
+
tableint internalId = search->second;
|
763
|
+
markDeletedInternal(internalId);
|
782
764
|
}
|
783
765
|
|
784
766
|
/**
|
@@ -787,8 +769,31 @@ namespace hnswlib {
|
|
787
769
|
* @param internalId
|
788
770
|
*/
|
789
771
|
void markDeletedInternal(tableint internalId) {
|
790
|
-
|
791
|
-
|
772
|
+
assert(internalId < cur_element_count);
|
773
|
+
if (!isMarkedDeleted(internalId))
|
774
|
+
{
|
775
|
+
unsigned char *ll_cur = ((unsigned char *)get_linklist0(internalId))+2;
|
776
|
+
*ll_cur |= DELETE_MARK;
|
777
|
+
num_deleted_ += 1;
|
778
|
+
}
|
779
|
+
else
|
780
|
+
{
|
781
|
+
throw std::runtime_error("The requested to delete element is already deleted");
|
782
|
+
}
|
783
|
+
}
|
784
|
+
|
785
|
+
/**
|
786
|
+
* Remove the deleted mark of the node, does NOT really change the current graph.
|
787
|
+
* @param label
|
788
|
+
*/
|
789
|
+
void unmarkDelete(labeltype label)
|
790
|
+
{
|
791
|
+
auto search = label_lookup_.find(label);
|
792
|
+
if (search == label_lookup_.end()) {
|
793
|
+
throw std::runtime_error("Label not found");
|
794
|
+
}
|
795
|
+
tableint internalId = search->second;
|
796
|
+
unmarkDeletedInternal(internalId);
|
792
797
|
}
|
793
798
|
|
794
799
|
/**
|
@@ -796,8 +801,17 @@ namespace hnswlib {
|
|
796
801
|
* @param internalId
|
797
802
|
*/
|
798
803
|
void unmarkDeletedInternal(tableint internalId) {
|
799
|
-
|
800
|
-
|
804
|
+
assert(internalId < cur_element_count);
|
805
|
+
if (isMarkedDeleted(internalId))
|
806
|
+
{
|
807
|
+
unsigned char *ll_cur = ((unsigned char *)get_linklist0(internalId))+2;
|
808
|
+
*ll_cur &= ~DELETE_MARK;
|
809
|
+
num_deleted_ -= 1;
|
810
|
+
}
|
811
|
+
else
|
812
|
+
{
|
813
|
+
throw std::runtime_error("The requested to undelete element is not deleted");
|
814
|
+
}
|
801
815
|
}
|
802
816
|
|
803
817
|
/**
|
@@ -858,8 +872,8 @@ namespace hnswlib {
|
|
858
872
|
}
|
859
873
|
|
860
874
|
for (auto&& neigh : sNeigh) {
|
861
|
-
//
|
862
|
-
//
|
875
|
+
// if (neigh == internalId)
|
876
|
+
// continue;
|
863
877
|
|
864
878
|
std::priority_queue<std::pair<dist_t, tableint>, std::vector<std::pair<dist_t, tableint>>, CompareByFirst> candidates;
|
865
879
|
size_t size = sCand.find(neigh) == sCand.end() ? sCand.size() : sCand.size() - 1; // sCand guaranteed to have size >= 1
|
@@ -990,7 +1004,7 @@ namespace hnswlib {
|
|
990
1004
|
unmarkDeletedInternal(existingInternalId);
|
991
1005
|
}
|
992
1006
|
updatePoint(data_point, existingInternalId, 1.0);
|
993
|
-
|
1007
|
+
|
994
1008
|
return existingInternalId;
|
995
1009
|
}
|
996
1010
|
|
@@ -1134,7 +1148,7 @@ namespace hnswlib {
|
|
1134
1148
|
}
|
1135
1149
|
|
1136
1150
|
std::priority_queue<std::pair<dist_t, tableint>, std::vector<std::pair<dist_t, tableint>>, CompareByFirst> top_candidates;
|
1137
|
-
if (
|
1151
|
+
if (num_deleted_) {
|
1138
1152
|
top_candidates=searchBaseLayerST<true,true>(
|
1139
1153
|
currObj, query_data, std::max(ef_, k));
|
1140
1154
|
}
|
data/ext/hnswlib/src/hnswlib.h
CHANGED
@@ -4,6 +4,9 @@
|
|
4
4
|
#define USE_SSE
|
5
5
|
#ifdef __AVX__
|
6
6
|
#define USE_AVX
|
7
|
+
#ifdef __AVX512F__
|
8
|
+
#define USE_AVX512
|
9
|
+
#endif
|
7
10
|
#endif
|
8
11
|
#endif
|
9
12
|
#endif
|
@@ -12,15 +15,97 @@
|
|
12
15
|
#ifdef _MSC_VER
|
13
16
|
#include <intrin.h>
|
14
17
|
#include <stdexcept>
|
18
|
+
#include "cpu_x86.h"
|
19
|
+
void cpu_x86::cpuid(int32_t out[4], int32_t eax, int32_t ecx) {
|
20
|
+
__cpuidex(out, eax, ecx);
|
21
|
+
}
|
22
|
+
__int64 xgetbv(unsigned int x) {
|
23
|
+
return _xgetbv(x);
|
24
|
+
}
|
15
25
|
#else
|
16
26
|
#include <x86intrin.h>
|
27
|
+
#include <cpuid.h>
|
28
|
+
#include <stdint.h>
|
29
|
+
void cpuid(int32_t cpuInfo[4], int32_t eax, int32_t ecx) {
|
30
|
+
__cpuid_count(eax, ecx, cpuInfo[0], cpuInfo[1], cpuInfo[2], cpuInfo[3]);
|
31
|
+
}
|
32
|
+
uint64_t xgetbv(unsigned int index) {
|
33
|
+
uint32_t eax, edx;
|
34
|
+
__asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));
|
35
|
+
return ((uint64_t)edx << 32) | eax;
|
36
|
+
}
|
37
|
+
#endif
|
38
|
+
|
39
|
+
#if defined(USE_AVX512)
|
40
|
+
#include <immintrin.h>
|
17
41
|
#endif
|
18
42
|
|
19
43
|
#if defined(__GNUC__)
|
20
44
|
#define PORTABLE_ALIGN32 __attribute__((aligned(32)))
|
45
|
+
#define PORTABLE_ALIGN64 __attribute__((aligned(64)))
|
21
46
|
#else
|
22
47
|
#define PORTABLE_ALIGN32 __declspec(align(32))
|
48
|
+
#define PORTABLE_ALIGN64 __declspec(align(64))
|
23
49
|
#endif
|
50
|
+
|
51
|
+
// Adapted from https://github.com/Mysticial/FeatureDetector
|
52
|
+
#define _XCR_XFEATURE_ENABLED_MASK 0
|
53
|
+
|
54
|
+
bool AVXCapable() {
|
55
|
+
int cpuInfo[4];
|
56
|
+
|
57
|
+
// CPU support
|
58
|
+
cpuid(cpuInfo, 0, 0);
|
59
|
+
int nIds = cpuInfo[0];
|
60
|
+
|
61
|
+
bool HW_AVX = false;
|
62
|
+
if (nIds >= 0x00000001) {
|
63
|
+
cpuid(cpuInfo, 0x00000001, 0);
|
64
|
+
HW_AVX = (cpuInfo[2] & ((int)1 << 28)) != 0;
|
65
|
+
}
|
66
|
+
|
67
|
+
// OS support
|
68
|
+
cpuid(cpuInfo, 1, 0);
|
69
|
+
|
70
|
+
bool osUsesXSAVE_XRSTORE = (cpuInfo[2] & (1 << 27)) != 0;
|
71
|
+
bool cpuAVXSuport = (cpuInfo[2] & (1 << 28)) != 0;
|
72
|
+
|
73
|
+
bool avxSupported = false;
|
74
|
+
if (osUsesXSAVE_XRSTORE && cpuAVXSuport) {
|
75
|
+
uint64_t xcrFeatureMask = xgetbv(_XCR_XFEATURE_ENABLED_MASK);
|
76
|
+
avxSupported = (xcrFeatureMask & 0x6) == 0x6;
|
77
|
+
}
|
78
|
+
return HW_AVX && avxSupported;
|
79
|
+
}
|
80
|
+
|
81
|
+
bool AVX512Capable() {
|
82
|
+
if (!AVXCapable()) return false;
|
83
|
+
|
84
|
+
int cpuInfo[4];
|
85
|
+
|
86
|
+
// CPU support
|
87
|
+
cpuid(cpuInfo, 0, 0);
|
88
|
+
int nIds = cpuInfo[0];
|
89
|
+
|
90
|
+
bool HW_AVX512F = false;
|
91
|
+
if (nIds >= 0x00000007) { // AVX512 Foundation
|
92
|
+
cpuid(cpuInfo, 0x00000007, 0);
|
93
|
+
HW_AVX512F = (cpuInfo[1] & ((int)1 << 16)) != 0;
|
94
|
+
}
|
95
|
+
|
96
|
+
// OS support
|
97
|
+
cpuid(cpuInfo, 1, 0);
|
98
|
+
|
99
|
+
bool osUsesXSAVE_XRSTORE = (cpuInfo[2] & (1 << 27)) != 0;
|
100
|
+
bool cpuAVXSuport = (cpuInfo[2] & (1 << 28)) != 0;
|
101
|
+
|
102
|
+
bool avx512Supported = false;
|
103
|
+
if (osUsesXSAVE_XRSTORE && cpuAVXSuport) {
|
104
|
+
uint64_t xcrFeatureMask = xgetbv(_XCR_XFEATURE_ENABLED_MASK);
|
105
|
+
avx512Supported = (xcrFeatureMask & 0xe6) == 0xe6;
|
106
|
+
}
|
107
|
+
return HW_AVX512F && avx512Supported;
|
108
|
+
}
|
24
109
|
#endif
|
25
110
|
|
26
111
|
#include <queue>
|
@@ -99,7 +184,6 @@ namespace hnswlib {
|
|
99
184
|
|
100
185
|
return result;
|
101
186
|
}
|
102
|
-
|
103
187
|
}
|
104
188
|
|
105
189
|
#include "space_l2.h"
|
data/ext/hnswlib/src/space_ip.h
CHANGED
@@ -10,15 +10,20 @@ namespace hnswlib {
|
|
10
10
|
for (unsigned i = 0; i < qty; i++) {
|
11
11
|
res += ((float *) pVect1)[i] * ((float *) pVect2)[i];
|
12
12
|
}
|
13
|
-
return
|
13
|
+
return res;
|
14
14
|
|
15
15
|
}
|
16
16
|
|
17
|
+
static float
|
18
|
+
InnerProductDistance(const void *pVect1, const void *pVect2, const void *qty_ptr) {
|
19
|
+
return 1.0f - InnerProduct(pVect1, pVect2, qty_ptr);
|
20
|
+
}
|
21
|
+
|
17
22
|
#if defined(USE_AVX)
|
18
23
|
|
19
24
|
// Favor using AVX if available.
|
20
25
|
static float
|
21
|
-
|
26
|
+
InnerProductSIMD4ExtAVX(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
|
22
27
|
float PORTABLE_ALIGN32 TmpRes[8];
|
23
28
|
float *pVect1 = (float *) pVect1v;
|
24
29
|
float *pVect2 = (float *) pVect2v;
|
@@ -61,13 +66,20 @@ namespace hnswlib {
|
|
61
66
|
|
62
67
|
_mm_store_ps(TmpRes, sum_prod);
|
63
68
|
float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3];;
|
64
|
-
return
|
65
|
-
}
|
69
|
+
return sum;
|
70
|
+
}
|
66
71
|
|
67
|
-
|
72
|
+
static float
|
73
|
+
InnerProductDistanceSIMD4ExtAVX(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
|
74
|
+
return 1.0f - InnerProductSIMD4ExtAVX(pVect1v, pVect2v, qty_ptr);
|
75
|
+
}
|
76
|
+
|
77
|
+
#endif
|
78
|
+
|
79
|
+
#if defined(USE_SSE)
|
68
80
|
|
69
81
|
static float
|
70
|
-
|
82
|
+
InnerProductSIMD4ExtSSE(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
|
71
83
|
float PORTABLE_ALIGN32 TmpRes[8];
|
72
84
|
float *pVect1 = (float *) pVect1v;
|
73
85
|
float *pVect2 = (float *) pVect2v;
|
@@ -119,7 +131,52 @@ namespace hnswlib {
|
|
119
131
|
_mm_store_ps(TmpRes, sum_prod);
|
120
132
|
float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3];
|
121
133
|
|
122
|
-
return
|
134
|
+
return sum;
|
135
|
+
}
|
136
|
+
|
137
|
+
static float
|
138
|
+
InnerProductDistanceSIMD4ExtSSE(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
|
139
|
+
return 1.0f - InnerProductSIMD4ExtSSE(pVect1v, pVect2v, qty_ptr);
|
140
|
+
}
|
141
|
+
|
142
|
+
#endif
|
143
|
+
|
144
|
+
|
145
|
+
#if defined(USE_AVX512)
|
146
|
+
|
147
|
+
static float
|
148
|
+
InnerProductSIMD16ExtAVX512(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
|
149
|
+
float PORTABLE_ALIGN64 TmpRes[16];
|
150
|
+
float *pVect1 = (float *) pVect1v;
|
151
|
+
float *pVect2 = (float *) pVect2v;
|
152
|
+
size_t qty = *((size_t *) qty_ptr);
|
153
|
+
|
154
|
+
size_t qty16 = qty / 16;
|
155
|
+
|
156
|
+
|
157
|
+
const float *pEnd1 = pVect1 + 16 * qty16;
|
158
|
+
|
159
|
+
__m512 sum512 = _mm512_set1_ps(0);
|
160
|
+
|
161
|
+
while (pVect1 < pEnd1) {
|
162
|
+
//_mm_prefetch((char*)(pVect2 + 16), _MM_HINT_T0);
|
163
|
+
|
164
|
+
__m512 v1 = _mm512_loadu_ps(pVect1);
|
165
|
+
pVect1 += 16;
|
166
|
+
__m512 v2 = _mm512_loadu_ps(pVect2);
|
167
|
+
pVect2 += 16;
|
168
|
+
sum512 = _mm512_add_ps(sum512, _mm512_mul_ps(v1, v2));
|
169
|
+
}
|
170
|
+
|
171
|
+
_mm512_store_ps(TmpRes, sum512);
|
172
|
+
float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3] + TmpRes[4] + TmpRes[5] + TmpRes[6] + TmpRes[7] + TmpRes[8] + TmpRes[9] + TmpRes[10] + TmpRes[11] + TmpRes[12] + TmpRes[13] + TmpRes[14] + TmpRes[15];
|
173
|
+
|
174
|
+
return sum;
|
175
|
+
}
|
176
|
+
|
177
|
+
static float
|
178
|
+
InnerProductDistanceSIMD16ExtAVX512(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
|
179
|
+
return 1.0f - InnerProductSIMD16ExtAVX512(pVect1v, pVect2v, qty_ptr);
|
123
180
|
}
|
124
181
|
|
125
182
|
#endif
|
@@ -127,7 +184,7 @@ namespace hnswlib {
|
|
127
184
|
#if defined(USE_AVX)
|
128
185
|
|
129
186
|
static float
|
130
|
-
|
187
|
+
InnerProductSIMD16ExtAVX(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
|
131
188
|
float PORTABLE_ALIGN32 TmpRes[8];
|
132
189
|
float *pVect1 = (float *) pVect1v;
|
133
190
|
float *pVect2 = (float *) pVect2v;
|
@@ -159,13 +216,20 @@ namespace hnswlib {
|
|
159
216
|
_mm256_store_ps(TmpRes, sum256);
|
160
217
|
float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3] + TmpRes[4] + TmpRes[5] + TmpRes[6] + TmpRes[7];
|
161
218
|
|
162
|
-
return
|
219
|
+
return sum;
|
163
220
|
}
|
164
221
|
|
165
|
-
|
222
|
+
static float
|
223
|
+
InnerProductDistanceSIMD16ExtAVX(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
|
224
|
+
return 1.0f - InnerProductSIMD16ExtAVX(pVect1v, pVect2v, qty_ptr);
|
225
|
+
}
|
226
|
+
|
227
|
+
#endif
|
228
|
+
|
229
|
+
#if defined(USE_SSE)
|
166
230
|
|
167
|
-
|
168
|
-
|
231
|
+
static float
|
232
|
+
InnerProductSIMD16ExtSSE(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
|
169
233
|
float PORTABLE_ALIGN32 TmpRes[8];
|
170
234
|
float *pVect1 = (float *) pVect1v;
|
171
235
|
float *pVect2 = (float *) pVect2v;
|
@@ -206,14 +270,24 @@ namespace hnswlib {
|
|
206
270
|
_mm_store_ps(TmpRes, sum_prod);
|
207
271
|
float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3];
|
208
272
|
|
209
|
-
return
|
273
|
+
return sum;
|
274
|
+
}
|
275
|
+
|
276
|
+
static float
|
277
|
+
InnerProductDistanceSIMD16ExtSSE(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
|
278
|
+
return 1.0f - InnerProductSIMD16ExtSSE(pVect1v, pVect2v, qty_ptr);
|
210
279
|
}
|
211
280
|
|
212
281
|
#endif
|
213
282
|
|
214
|
-
#if defined(USE_SSE) || defined(USE_AVX)
|
283
|
+
#if defined(USE_SSE) || defined(USE_AVX) || defined(USE_AVX512)
|
284
|
+
DISTFUNC<float> InnerProductSIMD16Ext = InnerProductSIMD16ExtSSE;
|
285
|
+
DISTFUNC<float> InnerProductSIMD4Ext = InnerProductSIMD4ExtSSE;
|
286
|
+
DISTFUNC<float> InnerProductDistanceSIMD16Ext = InnerProductDistanceSIMD16ExtSSE;
|
287
|
+
DISTFUNC<float> InnerProductDistanceSIMD4Ext = InnerProductDistanceSIMD4ExtSSE;
|
288
|
+
|
215
289
|
static float
|
216
|
-
|
290
|
+
InnerProductDistanceSIMD16ExtResiduals(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
|
217
291
|
size_t qty = *((size_t *) qty_ptr);
|
218
292
|
size_t qty16 = qty >> 4 << 4;
|
219
293
|
float res = InnerProductSIMD16Ext(pVect1v, pVect2v, &qty16);
|
@@ -222,11 +296,11 @@ namespace hnswlib {
|
|
222
296
|
|
223
297
|
size_t qty_left = qty - qty16;
|
224
298
|
float res_tail = InnerProduct(pVect1, pVect2, &qty_left);
|
225
|
-
return res + res_tail
|
299
|
+
return 1.0f - (res + res_tail);
|
226
300
|
}
|
227
301
|
|
228
302
|
static float
|
229
|
-
|
303
|
+
InnerProductDistanceSIMD4ExtResiduals(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
|
230
304
|
size_t qty = *((size_t *) qty_ptr);
|
231
305
|
size_t qty4 = qty >> 2 << 2;
|
232
306
|
|
@@ -237,7 +311,7 @@ namespace hnswlib {
|
|
237
311
|
float *pVect2 = (float *) pVect2v + qty4;
|
238
312
|
float res_tail = InnerProduct(pVect1, pVect2, &qty_left);
|
239
313
|
|
240
|
-
return res + res_tail
|
314
|
+
return 1.0f - (res + res_tail);
|
241
315
|
}
|
242
316
|
#endif
|
243
317
|
|
@@ -249,16 +323,37 @@ namespace hnswlib {
|
|
249
323
|
public:
|
250
324
|
InnerProductSpace() : data_size_(0), dim_(0) { }
|
251
325
|
InnerProductSpace(size_t dim) {
|
252
|
-
fstdistfunc_ =
|
253
|
-
#if defined(USE_AVX) || defined(USE_SSE)
|
326
|
+
fstdistfunc_ = InnerProductDistance;
|
327
|
+
#if defined(USE_AVX) || defined(USE_SSE) || defined(USE_AVX512)
|
328
|
+
#if defined(USE_AVX512)
|
329
|
+
if (AVX512Capable()) {
|
330
|
+
InnerProductSIMD16Ext = InnerProductSIMD16ExtAVX512;
|
331
|
+
InnerProductDistanceSIMD16Ext = InnerProductDistanceSIMD16ExtAVX512;
|
332
|
+
} else if (AVXCapable()) {
|
333
|
+
InnerProductSIMD16Ext = InnerProductSIMD16ExtAVX;
|
334
|
+
InnerProductDistanceSIMD16Ext = InnerProductDistanceSIMD16ExtAVX;
|
335
|
+
}
|
336
|
+
#elif defined(USE_AVX)
|
337
|
+
if (AVXCapable()) {
|
338
|
+
InnerProductSIMD16Ext = InnerProductSIMD16ExtAVX;
|
339
|
+
InnerProductDistanceSIMD16Ext = InnerProductDistanceSIMD16ExtAVX;
|
340
|
+
}
|
341
|
+
#endif
|
342
|
+
#if defined(USE_AVX)
|
343
|
+
if (AVXCapable()) {
|
344
|
+
InnerProductSIMD4Ext = InnerProductSIMD4ExtAVX;
|
345
|
+
InnerProductDistanceSIMD4Ext = InnerProductDistanceSIMD4ExtAVX;
|
346
|
+
}
|
347
|
+
#endif
|
348
|
+
|
254
349
|
if (dim % 16 == 0)
|
255
|
-
fstdistfunc_ =
|
350
|
+
fstdistfunc_ = InnerProductDistanceSIMD16Ext;
|
256
351
|
else if (dim % 4 == 0)
|
257
|
-
fstdistfunc_ =
|
352
|
+
fstdistfunc_ = InnerProductDistanceSIMD4Ext;
|
258
353
|
else if (dim > 16)
|
259
|
-
fstdistfunc_ =
|
354
|
+
fstdistfunc_ = InnerProductDistanceSIMD16ExtResiduals;
|
260
355
|
else if (dim > 4)
|
261
|
-
fstdistfunc_ =
|
356
|
+
fstdistfunc_ = InnerProductDistanceSIMD4ExtResiduals;
|
262
357
|
#endif
|
263
358
|
dim_ = dim;
|
264
359
|
data_size_ = dim * sizeof(float);
|
@@ -279,5 +374,4 @@ namespace hnswlib {
|
|
279
374
|
~InnerProductSpace() {}
|
280
375
|
};
|
281
376
|
|
282
|
-
|
283
377
|
}
|
data/ext/hnswlib/src/space_l2.h
CHANGED
@@ -19,11 +19,46 @@ namespace hnswlib {
|
|
19
19
|
return (res);
|
20
20
|
}
|
21
21
|
|
22
|
+
#if defined(USE_AVX512)
|
23
|
+
|
24
|
+
// Favor using AVX512 if available.
|
25
|
+
static float
|
26
|
+
L2SqrSIMD16ExtAVX512(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
|
27
|
+
float *pVect1 = (float *) pVect1v;
|
28
|
+
float *pVect2 = (float *) pVect2v;
|
29
|
+
size_t qty = *((size_t *) qty_ptr);
|
30
|
+
float PORTABLE_ALIGN64 TmpRes[16];
|
31
|
+
size_t qty16 = qty >> 4;
|
32
|
+
|
33
|
+
const float *pEnd1 = pVect1 + (qty16 << 4);
|
34
|
+
|
35
|
+
__m512 diff, v1, v2;
|
36
|
+
__m512 sum = _mm512_set1_ps(0);
|
37
|
+
|
38
|
+
while (pVect1 < pEnd1) {
|
39
|
+
v1 = _mm512_loadu_ps(pVect1);
|
40
|
+
pVect1 += 16;
|
41
|
+
v2 = _mm512_loadu_ps(pVect2);
|
42
|
+
pVect2 += 16;
|
43
|
+
diff = _mm512_sub_ps(v1, v2);
|
44
|
+
// sum = _mm512_fmadd_ps(diff, diff, sum);
|
45
|
+
sum = _mm512_add_ps(sum, _mm512_mul_ps(diff, diff));
|
46
|
+
}
|
47
|
+
|
48
|
+
_mm512_store_ps(TmpRes, sum);
|
49
|
+
float res = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3] + TmpRes[4] + TmpRes[5] + TmpRes[6] +
|
50
|
+
TmpRes[7] + TmpRes[8] + TmpRes[9] + TmpRes[10] + TmpRes[11] + TmpRes[12] +
|
51
|
+
TmpRes[13] + TmpRes[14] + TmpRes[15];
|
52
|
+
|
53
|
+
return (res);
|
54
|
+
}
|
55
|
+
#endif
|
56
|
+
|
22
57
|
#if defined(USE_AVX)
|
23
58
|
|
24
59
|
// Favor using AVX if available.
|
25
60
|
static float
|
26
|
-
|
61
|
+
L2SqrSIMD16ExtAVX(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
|
27
62
|
float *pVect1 = (float *) pVect1v;
|
28
63
|
float *pVect2 = (float *) pVect2v;
|
29
64
|
size_t qty = *((size_t *) qty_ptr);
|
@@ -55,10 +90,12 @@ namespace hnswlib {
|
|
55
90
|
return TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3] + TmpRes[4] + TmpRes[5] + TmpRes[6] + TmpRes[7];
|
56
91
|
}
|
57
92
|
|
58
|
-
#
|
93
|
+
#endif
|
94
|
+
|
95
|
+
#if defined(USE_SSE)
|
59
96
|
|
60
97
|
static float
|
61
|
-
|
98
|
+
L2SqrSIMD16ExtSSE(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
|
62
99
|
float *pVect1 = (float *) pVect1v;
|
63
100
|
float *pVect2 = (float *) pVect2v;
|
64
101
|
size_t qty = *((size_t *) qty_ptr);
|
@@ -106,7 +143,9 @@ namespace hnswlib {
|
|
106
143
|
}
|
107
144
|
#endif
|
108
145
|
|
109
|
-
#if defined(USE_SSE) || defined(USE_AVX)
|
146
|
+
#if defined(USE_SSE) || defined(USE_AVX) || defined(USE_AVX512)
|
147
|
+
DISTFUNC<float> L2SqrSIMD16Ext = L2SqrSIMD16ExtSSE;
|
148
|
+
|
110
149
|
static float
|
111
150
|
L2SqrSIMD16ExtResiduals(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
|
112
151
|
size_t qty = *((size_t *) qty_ptr);
|
@@ -122,7 +161,7 @@ namespace hnswlib {
|
|
122
161
|
#endif
|
123
162
|
|
124
163
|
|
125
|
-
#
|
164
|
+
#if defined(USE_SSE)
|
126
165
|
static float
|
127
166
|
L2SqrSIMD4Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
|
128
167
|
float PORTABLE_ALIGN32 TmpRes[8];
|
@@ -175,7 +214,17 @@ namespace hnswlib {
|
|
175
214
|
L2Space() : data_size_(0), dim_(0) { }
|
176
215
|
L2Space(size_t dim) {
|
177
216
|
fstdistfunc_ = L2Sqr;
|
178
|
-
|
217
|
+
#if defined(USE_SSE) || defined(USE_AVX) || defined(USE_AVX512)
|
218
|
+
#if defined(USE_AVX512)
|
219
|
+
if (AVX512Capable())
|
220
|
+
L2SqrSIMD16Ext = L2SqrSIMD16ExtAVX512;
|
221
|
+
else if (AVXCapable())
|
222
|
+
L2SqrSIMD16Ext = L2SqrSIMD16ExtAVX;
|
223
|
+
#elif defined(USE_AVX)
|
224
|
+
if (AVXCapable())
|
225
|
+
L2SqrSIMD16Ext = L2SqrSIMD16ExtAVX;
|
226
|
+
#endif
|
227
|
+
|
179
228
|
if (dim % 16 == 0)
|
180
229
|
fstdistfunc_ = L2SqrSIMD16Ext;
|
181
230
|
else if (dim % 4 == 0)
|
@@ -184,7 +233,7 @@ namespace hnswlib {
|
|
184
233
|
fstdistfunc_ = L2SqrSIMD16ExtResiduals;
|
185
234
|
else if (dim > 4)
|
186
235
|
fstdistfunc_ = L2SqrSIMD4ExtResiduals;
|
187
|
-
|
236
|
+
#endif
|
188
237
|
dim_ = dim;
|
189
238
|
data_size_ = dim * sizeof(float);
|
190
239
|
}
|
@@ -279,4 +328,4 @@ namespace hnswlib {
|
|
279
328
|
};
|
280
329
|
|
281
330
|
|
282
|
-
}
|
331
|
+
}
|
data/lib/hnswlib/version.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
# Hnswlib.rb provides Ruby bindings for the Hnswlib.
|
4
4
|
module Hnswlib
|
5
5
|
# The version of Hnswlib.rb you install.
|
6
|
-
VERSION = '0.
|
6
|
+
VERSION = '0.5.2'
|
7
7
|
|
8
8
|
# The version of Hnswlib included with gem.
|
9
|
-
HSWLIB_VERSION = '0.
|
9
|
+
HSWLIB_VERSION = '0.6.2'
|
10
10
|
end
|
data/sig/hnswlib.rbs
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
module Hnswlib
|
2
|
+
VERSION: ::String
|
3
|
+
HSWLIB_VERSION: ::String
|
4
|
+
|
5
|
+
class HnswIndex
|
6
|
+
attr_reader metric: String
|
7
|
+
|
8
|
+
def initialize: (n_features: Integer n_features, max_item: Integer max_item, ?metric: ::String metric, ?m: ::Integer m, ?ef_construction: ::Integer ef_construction, ?random_seed: ::Integer random_seed) -> void
|
9
|
+
def add_item: (Integer i, Array[Float] v) -> bool
|
10
|
+
def get_item: (Integer i) -> Array[Float]
|
11
|
+
def remove_item: (Integer i) -> void
|
12
|
+
def get_nns_by_item: (Integer i, Integer n, ?include_distances: (true | false) include_distances) -> ([Array[Integer], Array[Float]] | Array[Integer])
|
13
|
+
def get_nns_by_vector: (Array[Float] v, Integer n, ?include_distances: (true | false) include_distances) -> ([Array[Integer], Array[Float]] | Array[Integer])
|
14
|
+
def resize_index: (Integer new_max_item) -> void
|
15
|
+
def set_ef: (Integer ef) -> void
|
16
|
+
def save: (String filename) -> void
|
17
|
+
def load: (String filename) -> void
|
18
|
+
def get_distance: (Integer i, Integer j) -> Float
|
19
|
+
def n_items: () -> Integer
|
20
|
+
def n_features: () -> Integer
|
21
|
+
def max_item: () -> Integer
|
22
|
+
end
|
23
|
+
|
24
|
+
Index: untyped
|
25
|
+
|
26
|
+
class L2Space
|
27
|
+
attr_accessor dim: Integer
|
28
|
+
|
29
|
+
def initialize: (Integer dim) -> void
|
30
|
+
def distance: (Array[Float] a, Array[Float] b) -> Float
|
31
|
+
end
|
32
|
+
|
33
|
+
class InnerProductSpace
|
34
|
+
attr_accessor dim: Integer
|
35
|
+
|
36
|
+
def initialize: (Integer dim) -> void
|
37
|
+
def distance: (Array[Float] a, Array[Float] b) -> Float
|
38
|
+
end
|
39
|
+
|
40
|
+
class BruteforceSearch
|
41
|
+
attr_accessor space: (::Hnswlib::L2Space | ::Hnswlib::InnerProductSpace)
|
42
|
+
|
43
|
+
def initialize: (space: (::Hnswlib::L2Space | ::Hnswlib::InnerProductSpace) space, max_elements: Integer max_elements) -> void
|
44
|
+
def add_point: (Array[Float] arr, Integer idx) -> bool
|
45
|
+
def current_count: () -> Integer
|
46
|
+
def load_index: (String filename) -> void
|
47
|
+
def max_elements: () -> Integer
|
48
|
+
def remove_point: (Integer idx) -> void
|
49
|
+
def save_index: (String filename) -> void
|
50
|
+
def search_knn: (Array[Float] arr, Integer k) -> [Array[Integer], Array[Float]]
|
51
|
+
end
|
52
|
+
|
53
|
+
class HierarchicalNSW
|
54
|
+
attr_accessor space: (::Hnswlib::L2Space | ::Hnswlib::InnerProductSpace)
|
55
|
+
|
56
|
+
def initialize: (space: (::Hnswlib::L2Space | ::Hnswlib::InnerProductSpace) space, max_elements: Integer max_elements, ?m: Integer m, ?ef_construction: Integer ef_construction, ?random_seed: Integer random_seed) -> void
|
57
|
+
def add_point: (Array[Float] arr, Integer idx) -> bool
|
58
|
+
def current_count: () -> Integer
|
59
|
+
def get_ids: () -> Array[Integer]
|
60
|
+
def get_point: (Integer idx) -> Array[Float]
|
61
|
+
def load_index: (String filename) -> void
|
62
|
+
def mark_deleted: (Integer idx) -> void
|
63
|
+
def max_elements: () -> Integer
|
64
|
+
def resize_index: (Integer new_max_elements) -> void
|
65
|
+
def save_index: (String filename) -> void
|
66
|
+
def search_knn: (Array[Float] arr, Integer k) -> [Array[Integer], Array[Float]]
|
67
|
+
def set_ef: (Integer ef) -> void
|
68
|
+
end
|
69
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hnswlib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- yoshoku
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-19 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Hnswlib.rb provides Ruby bindings for the Hnswlib.
|
14
14
|
email:
|
@@ -18,15 +18,9 @@ extensions:
|
|
18
18
|
- ext/hnswlib/extconf.rb
|
19
19
|
extra_rdoc_files: []
|
20
20
|
files:
|
21
|
-
- ".github/workflows/build.yml"
|
22
|
-
- ".gitignore"
|
23
|
-
- ".rspec"
|
24
21
|
- CHANGELOG.md
|
25
|
-
- CODE_OF_CONDUCT.md
|
26
|
-
- Gemfile
|
27
22
|
- LICENSE.txt
|
28
23
|
- README.md
|
29
|
-
- Rakefile
|
30
24
|
- ext/hnswlib/extconf.rb
|
31
25
|
- ext/hnswlib/hnswlibext.cpp
|
32
26
|
- ext/hnswlib/hnswlibext.hpp
|
@@ -37,9 +31,9 @@ files:
|
|
37
31
|
- ext/hnswlib/src/space_ip.h
|
38
32
|
- ext/hnswlib/src/space_l2.h
|
39
33
|
- ext/hnswlib/src/visited_list_pool.h
|
40
|
-
- hnswlib.gemspec
|
41
34
|
- lib/hnswlib.rb
|
42
35
|
- lib/hnswlib/version.rb
|
36
|
+
- sig/hnswlib.rbs
|
43
37
|
homepage: https://github.com/yoshoku/hnswlib.rb
|
44
38
|
licenses:
|
45
39
|
- Apache-2.0
|
@@ -62,7 +56,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
56
|
- !ruby/object:Gem::Version
|
63
57
|
version: '0'
|
64
58
|
requirements: []
|
65
|
-
rubygems_version: 3.
|
59
|
+
rubygems_version: 3.3.7
|
66
60
|
signing_key:
|
67
61
|
specification_version: 4
|
68
62
|
summary: Ruby bindings for the Hnswlib.
|
data/.github/workflows/build.yml
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
name: build
|
2
|
-
|
3
|
-
on: [push, pull_request]
|
4
|
-
|
5
|
-
jobs:
|
6
|
-
build:
|
7
|
-
runs-on: ubuntu-latest
|
8
|
-
strategy:
|
9
|
-
fail-fast: false
|
10
|
-
matrix:
|
11
|
-
ruby: [ '2.6', '2.7', '3.0' ]
|
12
|
-
steps:
|
13
|
-
- uses: actions/checkout@v2
|
14
|
-
- name: Set up Ruby ${{ matrix.ruby }}
|
15
|
-
uses: ruby/setup-ruby@v1
|
16
|
-
with:
|
17
|
-
ruby-version: ${{ matrix.ruby }}
|
18
|
-
bundler-cache: true
|
19
|
-
- name: Build and test with Rake
|
20
|
-
run: bundle exec rake
|
data/.gitignore
DELETED
data/.rspec
DELETED
data/CODE_OF_CONDUCT.md
DELETED
@@ -1,84 +0,0 @@
|
|
1
|
-
# Contributor Covenant Code of Conduct
|
2
|
-
|
3
|
-
## Our Pledge
|
4
|
-
|
5
|
-
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
6
|
-
|
7
|
-
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
|
8
|
-
|
9
|
-
## Our Standards
|
10
|
-
|
11
|
-
Examples of behavior that contributes to a positive environment for our community include:
|
12
|
-
|
13
|
-
* Demonstrating empathy and kindness toward other people
|
14
|
-
* Being respectful of differing opinions, viewpoints, and experiences
|
15
|
-
* Giving and gracefully accepting constructive feedback
|
16
|
-
* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
|
17
|
-
* Focusing on what is best not just for us as individuals, but for the overall community
|
18
|
-
|
19
|
-
Examples of unacceptable behavior include:
|
20
|
-
|
21
|
-
* The use of sexualized language or imagery, and sexual attention or
|
22
|
-
advances of any kind
|
23
|
-
* Trolling, insulting or derogatory comments, and personal or political attacks
|
24
|
-
* Public or private harassment
|
25
|
-
* Publishing others' private information, such as a physical or email
|
26
|
-
address, without their explicit permission
|
27
|
-
* Other conduct which could reasonably be considered inappropriate in a
|
28
|
-
professional setting
|
29
|
-
|
30
|
-
## Enforcement Responsibilities
|
31
|
-
|
32
|
-
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
|
33
|
-
|
34
|
-
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
|
35
|
-
|
36
|
-
## Scope
|
37
|
-
|
38
|
-
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
|
39
|
-
|
40
|
-
## Enforcement
|
41
|
-
|
42
|
-
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at yoshoku@outlook.com. All complaints will be reviewed and investigated promptly and fairly.
|
43
|
-
|
44
|
-
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
|
45
|
-
|
46
|
-
## Enforcement Guidelines
|
47
|
-
|
48
|
-
Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
|
49
|
-
|
50
|
-
### 1. Correction
|
51
|
-
|
52
|
-
**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
|
53
|
-
|
54
|
-
**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
|
55
|
-
|
56
|
-
### 2. Warning
|
57
|
-
|
58
|
-
**Community Impact**: A violation through a single incident or series of actions.
|
59
|
-
|
60
|
-
**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
|
61
|
-
|
62
|
-
### 3. Temporary Ban
|
63
|
-
|
64
|
-
**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
|
65
|
-
|
66
|
-
**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
|
67
|
-
|
68
|
-
### 4. Permanent Ban
|
69
|
-
|
70
|
-
**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
|
71
|
-
|
72
|
-
**Consequence**: A permanent ban from any sort of public interaction within the community.
|
73
|
-
|
74
|
-
## Attribution
|
75
|
-
|
76
|
-
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
|
77
|
-
available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
78
|
-
|
79
|
-
Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
|
80
|
-
|
81
|
-
[homepage]: https://www.contributor-covenant.org
|
82
|
-
|
83
|
-
For answers to common questions about this code of conduct, see the FAQ at
|
84
|
-
https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
|
data/Gemfile
DELETED
data/Rakefile
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'bundler/gem_tasks'
|
4
|
-
require 'rspec/core/rake_task'
|
5
|
-
|
6
|
-
RSpec::Core::RakeTask.new(:spec)
|
7
|
-
|
8
|
-
require 'rake/extensiontask'
|
9
|
-
|
10
|
-
task build: :compile
|
11
|
-
|
12
|
-
Rake::ExtensionTask.new('hnswlibext') do |ext|
|
13
|
-
ext.ext_dir = 'ext/hnswlib'
|
14
|
-
ext.lib_dir = 'lib/hnswlib'
|
15
|
-
end
|
16
|
-
|
17
|
-
task default: %i[clobber compile spec]
|
data/hnswlib.gemspec
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'lib/hnswlib/version'
|
4
|
-
|
5
|
-
Gem::Specification.new do |spec|
|
6
|
-
spec.name = 'hnswlib'
|
7
|
-
spec.version = Hnswlib::VERSION
|
8
|
-
spec.authors = ['yoshoku']
|
9
|
-
spec.email = ['yoshoku@outlook.com']
|
10
|
-
|
11
|
-
spec.summary = 'Ruby bindings for the Hnswlib.'
|
12
|
-
spec.description = 'Hnswlib.rb provides Ruby bindings for the Hnswlib.'
|
13
|
-
spec.homepage = 'https://github.com/yoshoku/hnswlib.rb'
|
14
|
-
spec.license = 'Apache-2.0'
|
15
|
-
|
16
|
-
spec.metadata['homepage_uri'] = spec.homepage
|
17
|
-
spec.metadata['source_code_uri'] = spec.homepage
|
18
|
-
spec.metadata['changelog_uri'] = 'https://github.com/yoshoku/hnswlib.rb/blob/main/CHANGELOG.md'
|
19
|
-
|
20
|
-
# Specify which files should be added to the gem when it is released.
|
21
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
22
|
-
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
23
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }.reject { |f| f.include?('dummy.rb') }
|
24
|
-
end
|
25
|
-
spec.bindir = 'exe'
|
26
|
-
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
27
|
-
spec.require_paths = ['lib']
|
28
|
-
spec.extensions = ['ext/hnswlib/extconf.rb']
|
29
|
-
|
30
|
-
# Uncomment to register a new dependency of your gem
|
31
|
-
# spec.add_dependency "example-gem", "~> 1.0"
|
32
|
-
|
33
|
-
# For more information and examples about making a new gem, checkout our
|
34
|
-
# guide at: https://bundler.io/guides/creating_gem.html
|
35
|
-
end
|