hnswlib 0.2.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 84f3ca262eddd8b331cbedc163e55e28d77eba76d2184332fef6afd0e2ddcce5
4
- data.tar.gz: 248f8ca655ae83e2f56b26910dde6e96a23771040d61bac7ee7204e294063c2a
3
+ metadata.gz: 885313b6fa45e5affa85d537d015b8276d92f361530c9bff8bbf9673990b3665
4
+ data.tar.gz: 5eb711a3a8be4fee6142a1f18fc74fd0044bacb381e8acef4ccf839df2ee005a
5
5
  SHA512:
6
- metadata.gz: 56d528a7ce9af7f96b291e74fc6a4c8ff1d3da3d66e1dbeed0dc8e884f5f5822b04e35d0fb12f83155a97ac1505a9a1db7b245cdcb2b08ba96ae6657743ccfeb
7
- data.tar.gz: 47ca9c4db92798e07576505bc0c6b2a4f3ffb8afdcfdd020226232d8fca31d4259571509ce08ccde8eeecd06bcfa7cc2a5512c29d2568fa9b5d0ec0736aaa2ea
6
+ metadata.gz: 552a7b1f43bc0743d059c97e5c57ca3abfcee3d3795619cb40599e4363da24ec832151ed3084d2134c969727d3e41f36b8e3d94c2b2fcb9e6b15752c04ece4d0
7
+ data.tar.gz: 8d3f95354ee12f6e75c2258f90c929777a3dac375a09e996c7d772cb8179c75ba816a0ab87a6c7e82250b628e0276d9da4dedc058238d8f4fd390a80b5929e33
data/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ ## [0.5.1] - 2022-02-11
2
+
3
+ - Update bundled hnswlib version to 0.6.1.
4
+ - Update documentations.
5
+ - Introduce conventional commits.
6
+
7
+ ## [0.5.0] - 2021-12-12
8
+
9
+ - Update bundled hnswlib version to 0.6.0.
10
+
11
+ ## [0.4.0] - 2021-09-12
12
+
13
+ - Add type declaration file.
14
+
15
+ ## [0.3.0] - 2021-08-08
16
+
17
+ - Rename `Hnswlib::Index` to `Hnswlib::HnswIndex` (for compatibility, `Hnswlib::Index` has been an alis for `Hnswlib::HnswIndex`).
18
+ - Update API documentation.
19
+
1
20
  ## [0.2.0] - 2021-08-02
2
21
 
3
22
  - Add binding class for the BruteforceSearch.
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/)
@@ -35,7 +49,7 @@ Or install it yourself as:
35
49
  require 'hnswlib'
36
50
 
37
51
  f = 40 # length of item vector that will be indexed.
38
- t = Hnswlib::Index.new(n_features: f, max_item: 1000)
52
+ t = Hnswlib::HnswIndex.new(n_features: f, max_item: 1000)
39
53
 
40
54
  1000.times do |i|
41
55
  v = Array.new(f) { rand }
@@ -44,7 +58,7 @@ end
44
58
 
45
59
  t.save('test.ann')
46
60
 
47
- u = Hnswlib::Index.new(n_features: f, max_item: 1000)
61
+ u = Hnswlib::HnswIndex.new(n_features: f, max_item: 1000)
48
62
  u.load('test.ann')
49
63
  p u.get_nns_by_item(0, 100) # will find the 100 nearest neighbors.
50
64
  ```
@@ -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 [code of conduct](https://github.com/yoshoku/hnswlib.rb/blob/main/CODE_OF_CONDUCT.md).
74
+ and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
@@ -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.
@@ -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 (size_t i = 0; i < k; i++) {
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 (size_t i = k; i < cur_element_count; i++) {
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 +
@@ -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
- has_deletions_=false;
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 (has_deletions_) {
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
- has_deletions_=true;
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 (size_t i = 0; i < dim; i++) {
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
- // static const unsigned char REUSE_MARK = 0x10;
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
- markDeletedInternal(search->second);
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
- unsigned char *ll_cur = ((unsigned char *)get_linklist0(internalId))+2;
791
- *ll_cur |= DELETE_MARK;
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
- unsigned char *ll_cur = ((unsigned char *)get_linklist0(internalId))+2;
800
- *ll_cur &= ~DELETE_MARK;
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
- // if (neigh == internalId)
862
- // continue;
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 (has_deletions_) {
1151
+ if (num_deleted_) {
1138
1152
  top_candidates=searchBaseLayerST<true,true>(
1139
1153
  currObj, query_data, std::max(ef_, k));
1140
1154
  }
@@ -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"
@@ -18,7 +18,7 @@ namespace hnswlib {
18
18
 
19
19
  // Favor using AVX if available.
20
20
  static float
21
- InnerProductSIMD4Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
21
+ InnerProductSIMD4ExtAVX(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
22
22
  float PORTABLE_ALIGN32 TmpRes[8];
23
23
  float *pVect1 = (float *) pVect1v;
24
24
  float *pVect2 = (float *) pVect2v;
@@ -64,10 +64,12 @@ namespace hnswlib {
64
64
  return 1.0f - sum;
65
65
  }
66
66
 
67
- #elif defined(USE_SSE)
67
+ #endif
68
+
69
+ #if defined(USE_SSE)
68
70
 
69
71
  static float
70
- InnerProductSIMD4Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
72
+ InnerProductSIMD4ExtSSE(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
71
73
  float PORTABLE_ALIGN32 TmpRes[8];
72
74
  float *pVect1 = (float *) pVect1v;
73
75
  float *pVect2 = (float *) pVect2v;
@@ -124,10 +126,45 @@ namespace hnswlib {
124
126
 
125
127
  #endif
126
128
 
129
+
130
+ #if defined(USE_AVX512)
131
+
132
+ static float
133
+ InnerProductSIMD16ExtAVX512(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
134
+ float PORTABLE_ALIGN64 TmpRes[16];
135
+ float *pVect1 = (float *) pVect1v;
136
+ float *pVect2 = (float *) pVect2v;
137
+ size_t qty = *((size_t *) qty_ptr);
138
+
139
+ size_t qty16 = qty / 16;
140
+
141
+
142
+ const float *pEnd1 = pVect1 + 16 * qty16;
143
+
144
+ __m512 sum512 = _mm512_set1_ps(0);
145
+
146
+ while (pVect1 < pEnd1) {
147
+ //_mm_prefetch((char*)(pVect2 + 16), _MM_HINT_T0);
148
+
149
+ __m512 v1 = _mm512_loadu_ps(pVect1);
150
+ pVect1 += 16;
151
+ __m512 v2 = _mm512_loadu_ps(pVect2);
152
+ pVect2 += 16;
153
+ sum512 = _mm512_add_ps(sum512, _mm512_mul_ps(v1, v2));
154
+ }
155
+
156
+ _mm512_store_ps(TmpRes, sum512);
157
+ 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];
158
+
159
+ return 1.0f - sum;
160
+ }
161
+
162
+ #endif
163
+
127
164
  #if defined(USE_AVX)
128
165
 
129
166
  static float
130
- InnerProductSIMD16Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
167
+ InnerProductSIMD16ExtAVX(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
131
168
  float PORTABLE_ALIGN32 TmpRes[8];
132
169
  float *pVect1 = (float *) pVect1v;
133
170
  float *pVect2 = (float *) pVect2v;
@@ -162,10 +199,12 @@ namespace hnswlib {
162
199
  return 1.0f - sum;
163
200
  }
164
201
 
165
- #elif defined(USE_SSE)
202
+ #endif
203
+
204
+ #if defined(USE_SSE)
166
205
 
167
206
  static float
168
- InnerProductSIMD16Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
207
+ InnerProductSIMD16ExtSSE(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
169
208
  float PORTABLE_ALIGN32 TmpRes[8];
170
209
  float *pVect1 = (float *) pVect1v;
171
210
  float *pVect2 = (float *) pVect2v;
@@ -211,7 +250,10 @@ namespace hnswlib {
211
250
 
212
251
  #endif
213
252
 
214
- #if defined(USE_SSE) || defined(USE_AVX)
253
+ #if defined(USE_SSE) || defined(USE_AVX) || defined(USE_AVX512)
254
+ DISTFUNC<float> InnerProductSIMD16Ext = InnerProductSIMD16ExtSSE;
255
+ DISTFUNC<float> InnerProductSIMD4Ext = InnerProductSIMD4ExtSSE;
256
+
215
257
  static float
216
258
  InnerProductSIMD16ExtResiduals(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
217
259
  size_t qty = *((size_t *) qty_ptr);
@@ -250,7 +292,21 @@ namespace hnswlib {
250
292
  InnerProductSpace() : data_size_(0), dim_(0) { }
251
293
  InnerProductSpace(size_t dim) {
252
294
  fstdistfunc_ = InnerProduct;
253
- #if defined(USE_AVX) || defined(USE_SSE)
295
+ #if defined(USE_AVX) || defined(USE_SSE) || defined(USE_AVX512)
296
+ #if defined(USE_AVX512)
297
+ if (AVX512Capable())
298
+ InnerProductSIMD16Ext = InnerProductSIMD16ExtAVX512;
299
+ else if (AVXCapable())
300
+ InnerProductSIMD16Ext = InnerProductSIMD16ExtAVX;
301
+ #elif defined(USE_AVX)
302
+ if (AVXCapable())
303
+ InnerProductSIMD16Ext = InnerProductSIMD16ExtAVX;
304
+ #endif
305
+ #if defined(USE_AVX)
306
+ if (AVXCapable())
307
+ InnerProductSIMD4Ext = InnerProductSIMD4ExtAVX;
308
+ #endif
309
+
254
310
  if (dim % 16 == 0)
255
311
  fstdistfunc_ = InnerProductSIMD16Ext;
256
312
  else if (dim % 4 == 0)
@@ -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
- L2SqrSIMD16Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
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
- #elif defined(USE_SSE)
93
+ #endif
94
+
95
+ #if defined(USE_SSE)
59
96
 
60
97
  static float
61
- L2SqrSIMD16Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
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
- #ifdef USE_SSE
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
- #if defined(USE_SSE) || defined(USE_AVX)
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
- #endif
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
+ }
@@ -2,6 +2,7 @@
2
2
 
3
3
  #include <mutex>
4
4
  #include <string.h>
5
+ #include <deque>
5
6
 
6
7
  namespace hnswlib {
7
8
  typedef unsigned short int vl_type;
@@ -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.2.0'
6
+ VERSION = '0.5.1'
7
7
 
8
8
  # The version of Hnswlib included with gem.
9
- HSWLIB_VERSION = '0.5.2'
9
+ HSWLIB_VERSION = '0.6.1'
10
10
  end
data/lib/hnswlib.rb CHANGED
@@ -4,12 +4,12 @@ require_relative 'hnswlib/version'
4
4
  require_relative 'hnswlib/hnswlibext'
5
5
 
6
6
  module Hnswlib
7
- # Index is a class that provides functions for k-nearest eighbors search.
7
+ # HnswIndex is a class that provides functions for k-nearest eighbors search.
8
8
  #
9
9
  # @example
10
10
  # require 'hnswlib'
11
11
  #
12
- # index = Hnswlib::Index.new(n_features: 100, max_item: 10000)
12
+ # index = Hnswlib::HnswIndex.new(n_features: 100, max_item: 10000)
13
13
  #
14
14
  # 5000.times do |item_id|
15
15
  # item_vec = Array.new(100) { rand - 0.5 }
@@ -18,7 +18,7 @@ module Hnswlib
18
18
  #
19
19
  # index.get_nns_by_item(0, 100)
20
20
  #
21
- class Index
21
+ class HnswIndex
22
22
  # Returns the metric of index.
23
23
  # @return [String]
24
24
  attr_reader :metric
@@ -150,4 +150,7 @@ module Hnswlib
150
150
  @index.max_elements
151
151
  end
152
152
  end
153
+
154
+ # Index is alias of HnswIndex
155
+ Index = ::Hnswlib::HnswIndex
153
156
  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.2.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - yoshoku
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-08-02 00:00:00.000000000 Z
11
+ date: 2022-02-11 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.2.22
59
+ rubygems_version: 3.3.3
66
60
  signing_key:
67
61
  specification_version: 4
68
62
  summary: Ruby bindings for the Hnswlib.
@@ -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
@@ -1,19 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
- /tmp/
9
- *.bundle
10
- *.so
11
- *.o
12
- *.a
13
- mkmf.log
14
-
15
- # rspec failure tracking
16
- .rspec_status
17
-
18
- *.ann
19
- /bin/
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
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
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
-
5
- # Specify your gem's dependencies in hnswlib.gemspec
6
- gemspec
7
-
8
- gem 'rake', '~> 13.0'
9
- gem 'rake-compiler', '~> 1.1'
10
- gem 'rspec', '~> 3.0'
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