ngt 0.2.4 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d00cfd25aa3d93228994f1f490c27e5c7bd0566013c6d4af9c9ec22157cd5bf0
4
- data.tar.gz: a33f524d222fc6b9c7733039217349e9897926b19854faff58f088a80bb55b58
3
+ metadata.gz: e6b9362e0da232f074b026b2545275061b9bac6e6124d6927e1bb7d798ac61e1
4
+ data.tar.gz: afa434689a718d77e944e12c8322258d6e5c064e99545f872c5d6cb1cffa614e
5
5
  SHA512:
6
- metadata.gz: 1e1431ab3453ce24ab8d3886fc4ce201cc48ec239eb8eb083bb72d9880d8bf554e2e45ce7252e207d7e7da7f10323023364ff1b33ca95ab6bd6795fdbf79e5c0
7
- data.tar.gz: 3a932a1da2b75baac08ac477f9e92d1d6f203237f3b94e6962c749d906eb7c63a4a5f0162395f39db21734338ba147a10acb4210cb37d5397d024aef00f32795
6
+ metadata.gz: 2e676a72eda431ab954a1dcd77e6e7ae2ed843eb3bff1dcc8ae479b1c176a99bfaaa1dd27a6f79ef2f7eca0f6ceb6a35a5bf74a58b38cdda46b676aa0b5adc2d
7
+ data.tar.gz: ee1b5d27e178712b42ba9d5a6380e0c1d0ea9a9ac6e5941fca60fdd3a4e2ea5d9266f31dfdd79f117fe7fba08bbb92a21d93a363168ca36aa839d303c9e9deae
@@ -1,3 +1,10 @@
1
+ ## 0.3.0 (2020-03-25)
2
+
3
+ - Updated NGT to 1.10.0
4
+ - Added support for OpenMP on Mac
5
+ - Create index in memory if no path specified
6
+ - Added `normalized_angle` and `normalized_cosine`
7
+
1
8
  ## 0.2.4 (2020-03-09)
2
9
 
3
10
  - Updated NGT to 1.9.1
data/README.md CHANGED
@@ -12,6 +12,12 @@ Add this line to your application’s Gemfile:
12
12
  gem 'ngt'
13
13
  ```
14
14
 
15
+ On Mac, also install OpenMP:
16
+
17
+ ```sh
18
+ brew install libomp
19
+ ```
20
+
15
21
  NGT is not available for Windows
16
22
 
17
23
  ## Getting Started
@@ -118,7 +124,7 @@ Ngt::Index.new(dimensions,
118
124
  edge_size_for_creation: 10,
119
125
  edge_size_for_search: 40,
120
126
  object_type: :float, # :float, :integer
121
- distance_type: :l2, # :l1, :l2, :hamming, :angle, :cosine, or :jaccard
127
+ distance_type: :l2, # :l1, :l2, :hamming, :angle, :cosine, :normalized_angle, :normalized_cosine, :jaccard
122
128
  path: nil
123
129
  )
124
130
  ```
@@ -15,8 +15,11 @@ module Ngt
15
15
  :distance, :float
16
16
  end
17
17
 
18
+ enum :distance_type, [:l1, :l2, :hamming, :angle, :cosine, :normalized_angle, :normalized_cosine, :jaccard]
19
+
18
20
  attach_function :ngt_open_index, %i[string pointer], :pointer
19
21
  attach_function :ngt_create_graph_and_tree, %i[string pointer pointer], :pointer
22
+ attach_function :ngt_create_graph_and_tree_in_memory, %i[pointer pointer], :pointer
20
23
  attach_function :ngt_create_property, %i[pointer], :pointer
21
24
  attach_function :ngt_save_index, %i[pointer string pointer], :bool
22
25
  attach_function :ngt_get_property, %i[pointer pointer pointer], :bool
@@ -34,23 +37,26 @@ module Ngt
34
37
  attach_function :ngt_set_property_distance_type_hamming, %i[pointer pointer], :bool
35
38
  attach_function :ngt_set_property_distance_type_jaccard, %i[pointer pointer], :bool
36
39
  attach_function :ngt_set_property_distance_type_cosine, %i[pointer pointer], :bool
37
- attach_function :ngt_batch_insert_index, %i[pointer pointer uint32 pointer pointer], :bool
38
- attach_function :ngt_create_index, %i[pointer uint32 pointer], :bool
39
- attach_function :ngt_remove_index, %i[pointer int pointer], :bool
40
+ attach_function :ngt_set_property_distance_type_normalized_angle, %i[pointer pointer], :bool
41
+ attach_function :ngt_set_property_distance_type_normalized_cosine, %i[pointer pointer], :bool
40
42
  attach_function :ngt_insert_index, %i[pointer pointer uint32 pointer], :int
41
43
  attach_function :ngt_insert_index_as_float, %i[pointer pointer uint32 pointer], :int
42
44
  attach_function :ngt_create_empty_results, %i[pointer], :pointer
43
45
  attach_function :ngt_search_index, %i[pointer pointer int32 size_t float float pointer pointer], :bool
44
46
  attach_function :ngt_get_result_size, %i[pointer pointer], :uint32
45
47
  attach_function :ngt_get_result, %i[pointer uint32 pointer], ObjectDistance.by_value
48
+ attach_function :ngt_batch_insert_index, %i[pointer pointer uint32 pointer pointer], :bool
49
+ attach_function :ngt_create_index, %i[pointer uint32 pointer], :bool
50
+ attach_function :ngt_remove_index, %i[pointer int pointer], :bool
46
51
  attach_function :ngt_get_object_space, %i[pointer pointer], :pointer
47
52
  attach_function :ngt_get_object_as_float, %i[pointer int pointer], :pointer
48
53
  attach_function :ngt_get_object_as_integer, %i[pointer int pointer], :pointer
54
+ attach_function :ngt_destroy_results, %i[pointer], :void
49
55
  attach_function :ngt_destroy_property, %i[pointer], :void
50
56
  attach_function :ngt_close_index, %i[pointer], :void
51
57
  attach_function :ngt_get_property_edge_size_for_creation, %i[pointer pointer], :int16
52
58
  attach_function :ngt_get_property_edge_size_for_search, %i[pointer pointer], :int16
53
- attach_function :ngt_get_property_distance_type, %i[pointer pointer], :int32
59
+ attach_function :ngt_get_property_distance_type, %i[pointer pointer], :distance_type
54
60
  attach_function :ngt_create_error_object, %i[], :pointer
55
61
  attach_function :ngt_get_error_string, %i[pointer], :string
56
62
  attach_function :ngt_destroy_error_object, %i[pointer], :void
@@ -2,34 +2,44 @@ module Ngt
2
2
  class Index
3
3
  include Utils
4
4
 
5
- DISTANCE_TYPES = [:l1, :l2, :hamming, :angle, :cosine, :normalized_angle, :normalized_cosine, :jaccard]
5
+ attr_reader :path
6
6
 
7
- attr_reader :dimensions, :distance_type, :edge_size_for_creation, :edge_size_for_search, :object_type, :path
8
-
9
- def initialize(path)
7
+ def initialize(index, path)
8
+ @index = index
10
9
  @path = path
10
+
11
11
  @error = FFI.ngt_create_error_object
12
- @index = ffi(:ngt_open_index, path)
12
+ @property = ffi(:ngt_create_property)
13
+ ffi(:ngt_get_property, @index, @property)
13
14
 
14
- property = ffi(:ngt_create_property)
15
- ffi(:ngt_get_property, @index, property)
15
+ ObjectSpace.define_finalizer(self, self.class.finalize(@error, @index, @property))
16
+ end
16
17
 
17
- @dimensions = ffi(:ngt_get_property_dimension, property)
18
- @distance_type = DISTANCE_TYPES[ffi(:ngt_get_property_distance_type, property)]
19
- @edge_size_for_creation = ffi(:ngt_get_property_edge_size_for_creation, property)
20
- @edge_size_for_search = ffi(:ngt_get_property_edge_size_for_search, property)
18
+ def dimensions
19
+ @dimensions ||= ffi(:ngt_get_property_dimension, @property)
20
+ end
21
21
 
22
- object_type = ffi(:ngt_get_property_object_type, property)
23
- @float = FFI.ngt_is_property_object_type_float(object_type)
24
- @object_type = @float ? :float : :integer
22
+ def distance_type
23
+ @distance_type ||= ffi(:ngt_get_property_distance_type, @property)
24
+ end
25
25
 
26
- @object_space = ffi(:ngt_get_object_space, @index)
26
+ def edge_size_for_creation
27
+ @edge_size_for_creation ||= ffi(:ngt_get_property_edge_size_for_creation, @property)
28
+ end
29
+
30
+ def edge_size_for_search
31
+ @edge_size_for_search ||= ffi(:ngt_get_property_edge_size_for_search, @property)
32
+ end
27
33
 
28
- ObjectSpace.define_finalizer(self, self.class.finalize(@error))
34
+ def object_type
35
+ @object_type ||= begin
36
+ object_type = ffi(:ngt_get_property_object_type, @property)
37
+ FFI.ngt_is_property_object_type_float(object_type) ? :float : :integer
38
+ end
29
39
  end
30
40
 
31
41
  def insert(object)
32
- ffi(:ngt_insert_index, @index, c_object(object.to_a), @dimensions)
42
+ ffi(:ngt_insert_index, @index, c_object(object.to_a), dimensions)
33
43
  end
34
44
 
35
45
  def batch_insert(objects, num_threads: 8)
@@ -59,12 +69,12 @@ module Ngt
59
69
  end
60
70
 
61
71
  def object(id)
62
- if float?
72
+ if object_type == :float
63
73
  res = ffi(:ngt_get_object_as_float, @object_space, id)
64
- res.read_array_of_float(@dimensions)
74
+ res.read_array_of_float(dimensions)
65
75
  else
66
76
  res = ffi(:ngt_get_object_as_integer, @object_space, id)
67
- res.read_array_of_uint8(@dimensions)
77
+ res.read_array_of_uint8(dimensions)
68
78
  end
69
79
  end
70
80
 
@@ -75,7 +85,7 @@ module Ngt
75
85
  def search(query, size: 20, epsilon: 0.1, radius: nil)
76
86
  radius ||= -1.0
77
87
  results = ffi(:ngt_create_empty_results)
78
- ffi(:ngt_search_index, @index, c_object(query.to_a), @dimensions, size, epsilon, radius, results)
88
+ ffi(:ngt_search_index, @index, c_object(query.to_a), dimensions, size, epsilon, radius, results)
79
89
  result_size = ffi(:ngt_get_result_size, results)
80
90
  ret = []
81
91
  result_size.times do |i|
@@ -86,12 +96,14 @@ module Ngt
86
96
  }
87
97
  end
88
98
  ret
99
+ ensure
100
+ FFI.ngt_destroy_results(results) if results
89
101
  end
90
102
 
91
103
  def save(path2 = nil, path: nil)
92
104
  warn "[ngt] Passing path as an option is deprecated - use an argument instead" if path
93
- path ||= path2 || @path
94
- ffi(:ngt_save_index, @index, path)
105
+ @path = path || path2 || @path || Dir.mktmpdir
106
+ ffi(:ngt_save_index, @index, @path)
95
107
  end
96
108
 
97
109
  def close
@@ -101,58 +113,65 @@ module Ngt
101
113
  def self.new(dimensions, path: nil, edge_size_for_creation: 10,
102
114
  edge_size_for_search: 40, object_type: :float, distance_type: :l2)
103
115
 
104
- # called from load
105
- return super(path) if path && dimensions.nil?
116
+ error = FFI.ngt_create_error_object
106
117
 
107
- # TODO remove in 0.3.0
108
- create = dimensions.is_a?(Integer) || path
109
- unless create
118
+ # TODO remove in 0.4.0
119
+ if !dimensions.is_a?(Integer) && !path
110
120
  warn "[ngt] Passing a path to new is deprecated - use load instead"
111
- return super(dimensions)
121
+ path = dimensions
122
+ dimensions = nil
112
123
  end
113
124
 
114
- path ||= Dir.mktmpdir
115
- error = FFI.ngt_create_error_object
116
- property = ffi(:ngt_create_property, error)
117
- ffi(:ngt_set_property_dimension, property, dimensions, error)
118
- ffi(:ngt_set_property_edge_size_for_creation, property, edge_size_for_creation, error)
119
- ffi(:ngt_set_property_edge_size_for_search, property, edge_size_for_search, error)
120
-
121
- case object_type.to_s.downcase
122
- when "float"
123
- ffi(:ngt_set_property_object_type_float, property, error)
124
- when "integer"
125
- ffi(:ngt_set_property_object_type_integer, property, error)
125
+ if path && dimensions.nil?
126
+ index = ffi(:ngt_open_index, path, error)
126
127
  else
127
- raise ArgumentError, "Unknown object type: #{object_type}"
128
+ property = ffi(:ngt_create_property, error)
129
+ ffi(:ngt_set_property_dimension, property, dimensions, error)
130
+ ffi(:ngt_set_property_edge_size_for_creation, property, edge_size_for_creation, error)
131
+ ffi(:ngt_set_property_edge_size_for_search, property, edge_size_for_search, error)
132
+
133
+ case object_type.to_s.downcase
134
+ when "float"
135
+ ffi(:ngt_set_property_object_type_float, property, error)
136
+ when "integer"
137
+ ffi(:ngt_set_property_object_type_integer, property, error)
138
+ else
139
+ raise ArgumentError, "Unknown object type: #{object_type}"
140
+ end
141
+
142
+ case distance_type.to_s.downcase
143
+ when "l1"
144
+ ffi(:ngt_set_property_distance_type_l1, property, error)
145
+ when "l2"
146
+ ffi(:ngt_set_property_distance_type_l2, property, error)
147
+ when "angle"
148
+ ffi(:ngt_set_property_distance_type_angle, property, error)
149
+ when "hamming"
150
+ ffi(:ngt_set_property_distance_type_hamming, property, error)
151
+ when "jaccard"
152
+ ffi(:ngt_set_property_distance_type_jaccard, property, error)
153
+ when "cosine"
154
+ ffi(:ngt_set_property_distance_type_cosine, property, error)
155
+ when "normalized_angle"
156
+ ffi(:ngt_set_property_distance_type_normalized_angle, property, error)
157
+ when "normalized_cosine"
158
+ ffi(:ngt_set_property_distance_type_normalized_cosine, property, error)
159
+ else
160
+ raise ArgumentError, "Unknown distance type: #{distance_type}"
161
+ end
162
+
163
+ index =
164
+ if path
165
+ ffi(:ngt_create_graph_and_tree, path, property, error)
166
+ else
167
+ ffi(:ngt_create_graph_and_tree_in_memory, property, error)
168
+ end
128
169
  end
129
170
 
130
- case distance_type.to_s.downcase
131
- when "l1"
132
- ffi(:ngt_set_property_distance_type_l1, property, error)
133
- when "l2"
134
- ffi(:ngt_set_property_distance_type_l2, property, error)
135
- when "angle"
136
- ffi(:ngt_set_property_distance_type_angle, property, error)
137
- when "hamming"
138
- ffi(:ngt_set_property_distance_type_hamming, property, error)
139
- when "jaccard"
140
- ffi(:ngt_set_property_distance_type_jaccard, property, error)
141
- when "cosine"
142
- ffi(:ngt_set_property_distance_type_cosine, property, error)
143
- else
144
- raise ArgumentError, "Unknown distance type: #{distance_type}"
145
- end
146
-
147
- index = ffi(:ngt_create_graph_and_tree, path, property, error)
148
- FFI.ngt_close_index(index)
149
- index = nil
150
-
151
- super(path)
171
+ super(index, path)
152
172
  ensure
153
173
  FFI.ngt_destroy_error_object(error) if error
154
174
  FFI.ngt_destroy_property(property) if property
155
- FFI.ngt_close_index(index) if index
156
175
  end
157
176
 
158
177
  def self.load(path)
@@ -169,11 +188,12 @@ module Ngt
169
188
  Utils.ffi(*args)
170
189
  end
171
190
 
172
- def self.finalize(error)
191
+ def self.finalize(error, index, property)
173
192
  # must use proc instead of stabby lambda
174
193
  proc do
175
- # TODO clean-up more objects
176
194
  FFI.ngt_destroy_error_object(error)
195
+ FFI.ngt_close_index(index)
196
+ FFI.ngt_destroy_property(property)
177
197
  end
178
198
  end
179
199
 
@@ -183,10 +203,6 @@ module Ngt
183
203
  defined?(Numo::NArray) && data.is_a?(Numo::NArray)
184
204
  end
185
205
 
186
- def float?
187
- @float
188
- end
189
-
190
206
  def c_object(object)
191
207
  c_object = ::FFI::MemoryPointer.new(:double, object.size)
192
208
  c_object.write_array_of_double(object)
@@ -33,7 +33,12 @@ module Ngt
33
33
  private
34
34
 
35
35
  def path(obj)
36
- obj.is_a?(Ngt::Index) ? obj.path : obj
36
+ if obj.is_a?(Ngt::Index)
37
+ raise ArgumentError, "Index not saved" unless obj.path
38
+ obj.path
39
+ else
40
+ obj
41
+ end
37
42
  end
38
43
  end
39
44
  end
@@ -1,3 +1,3 @@
1
1
  module Ngt
2
- VERSION = "0.2.4"
2
+ VERSION = "0.3.0"
3
3
  end
Binary file
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ngt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-09 00:00:00.000000000 Z
11
+ date: 2020-03-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi