Skip to content

Commit b1de698

Browse files
committed
Fixed a few more problems that cropped up while revising manual and
testing manual code.
1 parent e7eda8e commit b1de698

File tree

7 files changed

+109
-33
lines changed

7 files changed

+109
-33
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ FLANN - Fast Library for Approximate Nearest Neighbors
22
======================================================
33

44
FLANN is a library for performing fast approximate nearest neighbor searches in high dimensional spaces. It contains a collection of algorithms we found to work best for nearest neighbor search and a system for automatically choosing the best algorithm and optimum parameters depending on the dataset.
5-
FLANN is written in C++ and contains bindings for the following languages: C, MATLAB and Python.
5+
FLANN is written in C++ and contains bindings for the following languages: C, MATLAB, Python, and Ruby.
66

77

88
Documentation

doc/manual.pdf

1.04 KB
Binary file not shown.

doc/manual.tex

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ \section{Introduction}
5050
FLANN (Fast Library for Approximate Nearest Neighbors) is a library for
5151
performing fast approximate nearest neighbor searches. FLANN is written in
5252
the C++ programming language. FLANN can be easily used in many contexts
53-
through the C, MATLAB and Python bindings provided with the library.
53+
through the C, MATLAB, Python, and Ruby bindings provided with the library.
5454

5555

5656

@@ -59,7 +59,7 @@ \subsection{Quick Start}
5959

6060

6161
This section contains small examples of how to use the FLANN library from
62-
different programming languages (C++, C, MATLAB and Python).
62+
different programming languages (C++, C, MATLAB, Python, and Ruby).
6363

6464
\begin{itemize}
6565

@@ -187,6 +187,31 @@ \subsection{Quick Start}
187187
branching=32, iterations=7, checks=16);
188188
\end{Verbatim}
189189

190+
\item \textbf{Ruby}
191+
\begin{Verbatim}[fontsize=\scriptsize,frame=single]
192+
require 'flann' # also requires NMatrix
193+
194+
dataset = NMatrix.random([10000,128])
195+
testset = NMatrix.random([1000,128])
196+
197+
index = Flann::Index.new(dataset) do |params|
198+
params[:algorithm] = :kmeans
199+
params[:branching] = 32
200+
params[:iterations] = 7
201+
params[:checks] = 16
202+
end
203+
speedup = index.build! # this is optional
204+
205+
results, distances = index.nearest_neighbors(testset, 5)
206+
207+
index.save "my_index.save"
208+
209+
# Alternatively, without an index:
210+
results, distances = Flann.nearest_neighbors(dataset, testset, 5,
211+
algorithm: :kmeans, branching: 32,
212+
iterations: 7, checks: 16)
213+
214+
\end{Verbatim}
190215
\end{itemize}
191216

192217

@@ -1541,7 +1566,7 @@ \subsection{Using FLANN from python}
15411566

15421567

15431568

1544-
See section \ref{sec:quickstart} for an example of how to use the Python
1569+
See section \ref{sec:quickstart} for an example of how to use the Python and Ruby
15451570
bindings.
15461571

15471572

src/ruby/lib/flann.rb

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ def dtype_to_c d #:nodoc:
9696
# Allocates index space and distance space for storing results from various searches. For a k-nearest neighbors
9797
# search, for example, you want trows (the number of rows in the testset) times k (the number of nearest neighbors
9898
# being searched for).
99-
def allocate_results_space result_size #:nodoc:
100-
[FFI::MemoryPointer.new(:int, result_size), FFI::MemoryPointer.new(:float, result_size)]
99+
def allocate_results_space result_size, c_type #:nodoc:
100+
[FFI::MemoryPointer.new(:int, result_size), FFI::MemoryPointer.new(c_type, result_size)]
101101
end
102102

103103

@@ -132,18 +132,23 @@ def handle_parameters parameters #:nodoc:
132132
# Find the k nearest neighbors.
133133
#
134134
# If no index parameters are given, FLANN_Parameters::DEFAULT are used. A block is accepted as well.
135-
def nearest_neighbors dataset, testset, k, parameters: Parameters.new(Parameters::DEFAULT)
135+
def nearest_neighbors dataset, testset, k, parameters = {}
136+
parameters = Parameters.new(Flann::Parameters::DEFAULT.merge(parameters))
136137
# Get a pointer and a struct regardless of how the arguments are supplied.
137138
parameters_ptr, parameters = handle_parameters(parameters)
138139
result_size = testset.shape[0] * k
139-
indices_int_ptr, distances_float_ptr = allocate_results_space(result_size)
140140

141-
Flann.flann_find_nearest_neighbors FFI::Pointer.new_from_nmatrix(dataset), dataset.shape[0], dataset.shape[1],
142-
FFI::Pointer.new_from_nmatrix(testset), testset.shape[0],
143-
indices_int_ptr, distances_float_ptr, k, parameters_ptr
141+
c_type = Flann::dtype_to_c(dataset.dtype)
142+
c_method = "flann_find_nearest_neighbors_#{c_type}".to_sym
143+
indices_int_ptr, distances_t_ptr = allocate_results_space(result_size, c_type)
144+
145+
# dataset, rows, cols, testset, trows, indices, dists, nn, flann_params
146+
Flann.send c_method, FFI::Pointer.new_from_nmatrix(dataset), dataset.shape[0], dataset.shape[1],
147+
FFI::Pointer.new_from_nmatrix(testset), testset.shape[0],
148+
indices_int_ptr, distances_t_ptr, k, parameters_ptr
144149

145150
# Return results: two arrays, one of indices and one of distances.
146-
[indices_int_ptr.read_array_of_int(result_size), distances_float_ptr.read_array_of_float(result_size)]
151+
[indices_int_ptr.read_array_of_int(result_size), distances_t_ptr.read_array_of_float(result_size)]
147152
end
148153
alias :nn :nearest_neighbors
149154

@@ -158,7 +163,8 @@ def set_distance_type! distance_function, order = 0
158163
# Arguments:
159164
# * dataset: NMatrix of points
160165
# * parameters:
161-
def cluster dataset, clusters, parameters: Parameters.new(Parameters::DEFAULT)
166+
def cluster dataset, clusters, parameters = {}
167+
parameters = Parameters.new(Flann::Parameters::DEFAULT.merge(parameters))
162168
c_method = "flann_compute_cluster_centers_#{Flann::dtype_to_c(dataset.dtype)}".to_sym
163169

164170
result = dataset.clone_structure
@@ -184,10 +190,16 @@ def cluster dataset, clusters, parameters: Parameters.new(Parameters::DEFAULT)
184190
attach_function :flann_build_index_double, [:pointer, :int, :int, :pointer, :index_params_ptr], :index_ptr
185191

186192
# index, testset, trows, indices, dists, nn, flann_params
187-
attach_function :flann_find_nearest_neighbors_index, [:index_ptr, :pointer, :int, :pointer, :pointer, :int, :index_params_ptr], :int
193+
attach_function :flann_find_nearest_neighbors_index_byte, [:index_ptr, :pointer, :int, :pointer, :pointer, :int, :index_params_ptr], :int
194+
attach_function :flann_find_nearest_neighbors_index_int, [:index_ptr, :pointer, :int, :pointer, :pointer, :int, :index_params_ptr], :int
195+
attach_function :flann_find_nearest_neighbors_index_float, [:index_ptr, :pointer, :int, :pointer, :pointer, :int, :index_params_ptr], :int
196+
attach_function :flann_find_nearest_neighbors_index_double, [:index_ptr, :pointer, :int, :pointer, :pointer, :int, :index_params_ptr], :int
188197

189198
# dataset, rows, cols, testset, trows, indices, dists, nn, flann_params
190-
attach_function :flann_find_nearest_neighbors, [:pointer, :int, :int, :pointer, :int, :pointer, :pointer, :int, :index_params_ptr], :int
199+
attach_function :flann_find_nearest_neighbors_byte, [:pointer, :int, :int, :pointer, :int, :pointer, :pointer, :int, :index_params_ptr], :int
200+
attach_function :flann_find_nearest_neighbors_int, [:pointer, :int, :int, :pointer, :int, :pointer, :pointer, :int, :index_params_ptr], :int
201+
attach_function :flann_find_nearest_neighbors_float, [:pointer, :int, :int, :pointer, :int, :pointer, :pointer, :int, :index_params_ptr], :int
202+
attach_function :flann_find_nearest_neighbors_double, [:pointer, :int, :int, :pointer, :int, :pointer, :pointer, :int, :index_params_ptr], :int
191203

192204
# index, query point, result indices, result distances, max_nn, radius, flann_params
193205
attach_function :flann_radius_search_byte, [:index_ptr, :pointer, :pointer, :pointer, :int, :float, :index_params_ptr], :int

src/ruby/lib/flann/index.rb

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class Index
1616
#
1717
# * https://github.com/mariusmuja/flann/tree/master/src/cpp/flann/algorithms
1818
#
19-
def initialize dataset: nil, dtype: :float64, parameters: Flann::Parameters::DEFAULT
19+
def initialize dataset = nil, dtype: :float64, parameters: Flann::Parameters::DEFAULT
2020
@dataset = dataset
2121
@dtype = (!dataset.nil? && dataset.is_a?(NMatrix)) ? dataset.dtype : dtype
2222
@index_ptr = nil
@@ -27,6 +27,11 @@ def initialize dataset: nil, dtype: :float64, parameters: Flann::Parameters::DEF
2727
end
2828
attr_reader :dtype, :dataset, :parameters, :parameters_ptr, :index_ptr
2929

30+
# Assign a new dataset. Requires that the old index be freed.
31+
def dataset= new_dataset
32+
free!
33+
end
34+
3035
# Build an index
3136
def build!
3237
raise("no dataset specified") if dataset.nil?
@@ -40,34 +45,44 @@ def build!
4045
end
4146

4247
# Get the nearest neighbors based on this index. Forces a build of the index if one hasn't been done yet.
43-
def nearest_neighbors testset, k, parameters: Parameters.new(Parameters::DEFAULT)
48+
def nearest_neighbors testset, k, parameters = {}
49+
parameters = Parameters.new(Flann::Parameters::DEFAULT.merge(parameters))
50+
4451
self.build! if index_ptr.nil?
4552

4653
parameters_ptr, parameters = Flann::handle_parameters(parameters)
4754
result_size = testset.shape[0] * k
48-
indices_int_ptr, distances_float_ptr = Flann::allocate_results_space(result_size)
4955

50-
Flann.flann_find_nearest_neighbors_index index_ptr,
51-
FFI::Pointer.new_from_nmatrix(testset),
52-
testset.shape[0],
53-
indices_int_ptr, distances_float_ptr,
54-
k,
55-
parameters_ptr
56+
c_type = Flann::dtype_to_c(dataset.dtype)
57+
c_method = "flann_find_nearest_neighbors_index_#{c_type}".to_sym
58+
indices_int_ptr, distances_t_ptr = Flann::allocate_results_space(result_size, c_type)
5659

57-
[indices_int_ptr.read_array_of_int(result_size), distances_float_ptr.read_array_of_float(result_size)]
60+
Flann.send c_method, index_ptr,
61+
FFI::Pointer.new_from_nmatrix(testset),
62+
testset.shape[0],
63+
indices_int_ptr, distances_t_ptr,
64+
k,
65+
parameters_ptr
66+
67+
[indices_int_ptr.read_array_of_int(result_size), distances_t_ptr.read_array_of_float(result_size)]
5868
end
5969

6070
# Perform a radius search on a single query point
61-
def radius_search query, radius, max_k: dataset.shape[1], parameters: Parameters.new(Parameters::DEFAULT)
71+
def radius_search query, radius, parameters = {}
72+
max_k = parameters[:max_neighbors] || dataset.shape[1]
73+
parameters = Parameters.new(Flann::Parameters::DEFAULT.merge(parameters))
74+
6275
self.build! if index_ptr.nil?
6376
parameters_ptr, parameters = Flann::handle_parameters(parameters)
64-
indices_int_ptr, distances_float_ptr = Flann::allocate_results_space(max_k)
6577

66-
c_method = "flann_radius_search_#{Flann::dtype_to_c(dtype)}".to_sym
67-
Flann.send(c_method, index_ptr, FFI::Pointer.new_from_nmatrix(query), indices_int_ptr, distances_float_ptr, max_k, radius, parameters_ptr)
78+
c_type = Flann::dtype_to_c(dataset.dtype)
79+
c_method = "flann_radius_search_#{c_type}".to_sym
80+
indices_int_ptr, distances_t_ptr = Flann::allocate_results_space(max_k, c_type)
81+
82+
Flann.send(c_method, index_ptr, FFI::Pointer.new_from_nmatrix(query), indices_int_ptr, distances_t_ptr, max_k, radius, parameters_ptr)
6883

6984
# Return results: two arrays, one of indices and one of distances.
70-
[indices_int_ptr.read_array_of_int(max_k), distances_float_ptr.read_array_of_float(max_k)]
85+
[indices_int_ptr.read_array_of_int(max_k), distances_t_ptr.read_array_of_float(max_k)]
7186
end
7287

7388
# Save an index to a file (without the dataset).
@@ -89,7 +104,8 @@ def load! filename
89104
end
90105

91106
# Free an index
92-
def free! parameters = Parameters.new(Parameters::DEFAULT)
107+
def free! parameters = {}
108+
parameters = Parameters.new(Flann::Parameters::DEFAULT.merge(parameters))
93109
c_method = "flann_free_index_#{Flann::dtype_to_c(dtype)}".to_sym
94110
parameters_ptr, parameters = Flann::handle_parameters(parameters)
95111
Flann.send(c_method, index_ptr, parameters_ptr)

src/ruby/spec/flann_spec.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,29 @@
1616
raise("could not find version string in config.h") unless found
1717
end
1818

19+
it "works on the example given in the manual" do
20+
dataset = NMatrix.random([10000,128])
21+
testset = NMatrix.random([1000,128])
22+
23+
index = Flann::Index.new(dataset) do |params|
24+
params[:algorithm] = :kmeans
25+
params[:branching] = 32
26+
params[:iterations] = 7
27+
params[:checks] = 16
28+
end
29+
speedup = index.build! # this is optional
30+
31+
results, distances = index.nearest_neighbors(testset, 5)
32+
33+
# Skip saving, as that's tested elsewhere, and I don't feel like cleaning up.
34+
# index.save "my_index.save"
35+
36+
# Alternatively, without an index:
37+
results, distances = Flann.nearest_neighbors(dataset, testset, 5,
38+
algorithm: :kmeans, branching: 32,
39+
iterations: 7, checks: 16)
40+
end
41+
1942
context "#set_distance_type!" do
2043
it "sets the distance functor without error" do
2144
pending "distance type unsupported in the C bindings, use the C++ bindings instead"

src/ruby/spec/index_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
before :each do
77
@dataset = NMatrix.random([1000,128])
88
@testset = NMatrix.random([100,128])
9-
@index = Flann::Index.new(dataset: @dataset) do |t|
9+
@index = Flann::Index.new(@dataset) do |t|
1010
t[:algorithm] = :kdtree
1111
t[:trees] = 4
1212
end
@@ -43,7 +43,7 @@
4343

4444
raise(IOError, "save failed") unless File.exists?("temp_index.save_file")
4545

46-
post_index = Flann::Index.new(dataset: @dataset)
46+
post_index = Flann::Index.new(@dataset)
4747
post_index.load!("temp_index.save_file")
4848
FileUtils.rm("temp_index.save_file", :force => true)
4949
end

0 commit comments

Comments
 (0)