flann 1.8.4.2

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.
@@ -0,0 +1,162 @@
1
+ # Copyright 2014 John O. Woods (john.o.woods@gmail.com), West Virginia
2
+ # University's Applied Space Exploration Lab, and West Virginia Robotic
3
+ # Technology Center. All rights reserved.
4
+ #
5
+ # THE BSD LICENSE
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions
9
+ # are met:
10
+ #
11
+ # 1. Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ # 2. Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
+ # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
+ # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
+ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ class FFI::Pointer
29
+ class << self
30
+ def new_from_nmatrix nm
31
+ raise(StorageError, "dense storage expected") unless nm.dense?
32
+ c_type = Flann::dtype_to_c(nm.dtype)
33
+ c_type = :uchar if c_type == :byte
34
+ ::FFI::Pointer.new(c_type, nm.data_pointer).tap { |p| p.autorelease = false }
35
+ end
36
+ end
37
+ end
38
+
39
+ module Flann
40
+ class Index
41
+
42
+ # Constructor takes a block where we set each of the parameters. We need to be careful to do this since
43
+ # we're using the C API and not C++; so everything important needs to be initialized or there could be
44
+ # a segfault. For reasonable default definitions, see:
45
+ #
46
+ # * https://github.com/mariusmuja/flann/tree/master/src/cpp/flann/algorithms
47
+ #
48
+ def initialize index_dataset = nil, dtype: :float64, parameters: Flann::Parameters::DEFAULT
49
+ @dataset = index_dataset
50
+ #require 'pry'
51
+ #binding.pry if @dataset.nil?
52
+ @dtype = (!index_dataset.nil? && index_dataset.is_a?(NMatrix)) ? index_dataset.dtype : dtype
53
+ @index_ptr = nil
54
+
55
+ @parameters_ptr, @parameters = Flann::handle_parameters(parameters)
56
+
57
+ yield @parameters if block_given?
58
+ end
59
+ attr_reader :dtype, :dataset, :parameters, :parameters_ptr, :index_ptr
60
+
61
+ # Assign a new dataset. Requires that the old index be freed.
62
+ def dataset= index_dataset
63
+ free!
64
+ @dataset = index_dataset
65
+ end
66
+
67
+ # Build an index
68
+ def build!
69
+ raise("no dataset specified") if dataset.nil?
70
+ c_type = Flann::dtype_to_c(dtype)
71
+ c_method = "flann_build_index_#{c_type}".to_sym
72
+ speedup_float_ptr = FFI::MemoryPointer.new(:float)
73
+ @index_ptr = Flann.send(c_method, FFI::Pointer.new_from_nmatrix(dataset), dataset.shape[0], dataset.shape[1], speedup_float_ptr, parameters_ptr)
74
+ if index_ptr.address == 0
75
+ require 'pry'
76
+ binding.pry
77
+ raise("failed to allocate index_ptr")
78
+ end
79
+
80
+
81
+ # Return the speedup
82
+ speedup_float_ptr.read_float
83
+ end
84
+
85
+ # Get the nearest neighbors based on this index. Forces a build of the index if one hasn't been done yet.
86
+ def nearest_neighbors testset, k, parameters = {}
87
+ parameters = Parameters.new(Flann::Parameters::DEFAULT.merge(parameters))
88
+
89
+ self.build! if index_ptr.nil?
90
+
91
+ parameters_ptr, parameters = Flann::handle_parameters(parameters)
92
+ result_size = testset.shape[0] * k
93
+
94
+ c_type = Flann::dtype_to_c(dataset.dtype)
95
+ c_method = "flann_find_nearest_neighbors_index_#{c_type}".to_sym
96
+ indices_int_ptr, distances_t_ptr = Flann::allocate_results_space(result_size, c_type)
97
+
98
+ Flann.send c_method, index_ptr,
99
+ FFI::Pointer.new_from_nmatrix(testset),
100
+ testset.shape[0],
101
+ indices_int_ptr, distances_t_ptr,
102
+ k,
103
+ parameters_ptr
104
+
105
+
106
+ [indices_int_ptr.read_array_of_int(result_size),
107
+ c_type == :double ? distances_t_ptr.read_array_of_double(result_size) : distances_t_ptr.read_array_of_float(result_size)]
108
+ end
109
+
110
+ # Perform a radius search on a single query point
111
+ def radius_search query, radius, max_k=nil, parameters = {}
112
+ max_k ||= dataset.shape[0]
113
+ parameters = Parameters.new(Flann::Parameters::DEFAULT.merge(parameters))
114
+
115
+ self.build! if index_ptr.nil?
116
+ parameters_ptr, parameters = Flann::handle_parameters(parameters)
117
+
118
+ c_type = Flann::dtype_to_c(dataset.dtype)
119
+ c_method = "flann_radius_search_#{c_type}".to_sym
120
+ indices_int_ptr, distances_t_ptr = Flann::allocate_results_space(max_k, c_type)
121
+
122
+ Flann.send(c_method, index_ptr, FFI::Pointer.new_from_nmatrix(query), indices_int_ptr, distances_t_ptr, max_k, radius, parameters_ptr)
123
+
124
+ # Return results: two arrays, one of indices and one of distances.
125
+ indices = indices_int_ptr.read_array_of_int(max_k)
126
+ distances = c_type == :double ? distances_t_ptr.read_array_of_double(max_k) : distances_t_ptr.read_array_of_float(max_k)
127
+
128
+ # Stop where indices == -1
129
+ cutoff = indices.find_index(-1)
130
+ cutoff.nil? ? [indices, distances] : [indices[0...cutoff], distances[0...cutoff]]
131
+ end
132
+
133
+ # Save an index to a file (without the dataset).
134
+ def save filename
135
+ raise(IOError, "Cannot write an unbuilt index") if index_ptr.nil? # FIXME: This should probably have its own exception type.
136
+ c_method = "flann_save_index_#{Flann::dtype_to_c(dtype)}".to_sym
137
+ Flann.send(c_method, index_ptr, filename)
138
+ self
139
+ end
140
+
141
+ # Load an index from a file (with the dataset already known!).
142
+ #
143
+ # FIXME: This needs to free the previous dataset first.
144
+ def load! filename
145
+ c_method = "flann_load_index_#{Flann::dtype_to_c(dtype)}".to_sym
146
+
147
+ @index_ptr = Flann.send(c_method, filename, FFI::Pointer.new_from_nmatrix(dataset), dataset.shape[0], dataset.shape[1])
148
+ self
149
+ end
150
+
151
+ # Free an index
152
+ def free! parameters = {}
153
+ parameters = Parameters.new(Flann::Parameters::DEFAULT.merge(parameters))
154
+ c_method = "flann_free_index_#{Flann::dtype_to_c(dtype)}".to_sym
155
+ parameters_ptr, parameters = Flann::handle_parameters(parameters)
156
+ Flann.send(c_method, index_ptr, parameters_ptr)
157
+ @index_ptr = nil
158
+ self
159
+ end
160
+
161
+ end
162
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright 2014 John O. Woods (john.o.woods@gmail.com), West Virginia
2
+ # University's Applied Space Exploration Lab, and West Virginia Robotic
3
+ # Technology Center. All rights reserved.
4
+ #
5
+ # THE BSD LICENSE
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions
9
+ # are met:
10
+ #
11
+ # 1. Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ # 2. Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
+ # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
+ # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
+ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ module Flann
29
+ module VERSION
30
+ MAJOR = 1
31
+ MINOR = 8
32
+ TINY = 4
33
+ MINISCULE = 2
34
+
35
+ STRING = [MAJOR, MINOR, TINY, MINISCULE].compact.join('.')
36
+ end
37
+ end
@@ -0,0 +1,104 @@
1
+ # Copyright 2014 John O. Woods (john.o.woods@gmail.com), West Virginia
2
+ # University's Applied Space Exploration Lab, and West Virginia Robotic
3
+ # Technology Center. All rights reserved.
4
+ #
5
+ # THE BSD LICENSE
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions
9
+ # are met:
10
+ #
11
+ # 1. Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ # 2. Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
+ # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
+ # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
+ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ require File.dirname(__FILE__) + "/spec_helper.rb"
29
+
30
+ describe Flann do
31
+
32
+ it "::VERSION::STRING matches #define FLANN_VERSION_ in config.h" do
33
+ found = false
34
+ File.open(File.dirname(__FILE__) + "/../../cpp/flann/config.h", "r") do |f|
35
+ while line = f.gets
36
+ next unless line =~ /#[\s]*define[\s]+FLANN_VERSION_[\s]+"\d.\d.\d"/
37
+ fields = line.split
38
+ found = true
39
+ expect(fields.last[1...-1]).to eq(Flann::VERSION::STRING.split('.')[0...-1].join('.'))
40
+ end
41
+ end
42
+
43
+ raise("could not find version string in config.h") unless found
44
+ end
45
+
46
+ it "works on the example given in the manual" do
47
+ dataset = NMatrix.random([10000,128])
48
+ testset = NMatrix.random([1000,128])
49
+
50
+ index = Flann::Index.new(dataset) do |params|
51
+ params[:algorithm] = :kmeans
52
+ params[:branching] = 32
53
+ params[:iterations] = 7
54
+ params[:checks] = 16
55
+ end
56
+ speedup = index.build! # this is optional
57
+
58
+ results, distances = index.nearest_neighbors(testset, 5)
59
+
60
+ # Skip saving, as that's tested elsewhere, and I don't feel like cleaning up.
61
+ # index.save "my_index.save"
62
+
63
+ # Alternatively, without an index:
64
+ results, distances = Flann.nearest_neighbors(dataset, testset, 5,
65
+ algorithm: :kmeans, branching: 32,
66
+ iterations: 7, checks: 16)
67
+ end
68
+
69
+ context "#set_distance_type!" do
70
+ it "sets the distance functor without error" do
71
+ Flann.set_distance_type! :euclidean
72
+
73
+ # Version check needed before attempting get_distance_type
74
+ if Flann.respond_to?(:flann_get_distance_type)
75
+ d = Flann.get_distance_type
76
+ expect(d).to eq(:euclidean)
77
+ end
78
+ end
79
+ end
80
+
81
+ [:byte, :int32, :float32, :float64].each do |dtype|
82
+ before :each do
83
+ scale = [:byte, :int32, :int64].include?(dtype) ? 255 : 1.0
84
+ @dataset = NMatrix.random([1000,128], dtype: dtype, scale: scale)
85
+ @testset = NMatrix.random([100,128], dtype: dtype, scale: scale)
86
+ end
87
+
88
+ context "#nearest_neighbors" do
89
+ it "computes the nearest neighbors without an index" do
90
+ Flann.nearest_neighbors @dataset, @testset, 5
91
+ end
92
+ end
93
+
94
+ context "#cluster" do
95
+ it "calls flann_compute_cluster_centers_... properly" do
96
+ Flann.cluster(@dataset, 5)
97
+ end
98
+ end
99
+
100
+
101
+ end
102
+
103
+
104
+ end
@@ -0,0 +1,114 @@
1
+ # Copyright 2014 John O. Woods (john.o.woods@gmail.com), West Virginia
2
+ # University's Applied Space Exploration Lab, and West Virginia Robotic
3
+ # Technology Center. All rights reserved.
4
+ #
5
+ # THE BSD LICENSE
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions
9
+ # are met:
10
+ #
11
+ # 1. Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ # 2. Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
+ # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
+ # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
+ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ require File.dirname(__FILE__) + "/spec_helper.rb"
29
+
30
+
31
+ describe Flann::Index do
32
+ [:byte, :int32, :float32, :float64].each do |dtype|
33
+ context dtype.inspect do
34
+ before :each do
35
+ scale = [:byte, :int32, :int64].include?(dtype) ? 255 : 1.0
36
+
37
+ @dataset = NMatrix.random([1000,128], dtype: dtype, scale: scale)
38
+ @testset = NMatrix.random([100,128], dtype: dtype, scale: scale)
39
+ @index = Flann::Index.new(@dataset) do |t|
40
+ t[:algorithm] = :kdtree
41
+ t[:trees] = 4
42
+ end
43
+ @index.build!
44
+ end
45
+
46
+
47
+ context "#nearest_neighbors" do
48
+ it "runs without error" do
49
+ @index.nearest_neighbors @testset, 5
50
+ end
51
+
52
+ if dtype == :float32
53
+ it "runs a :kdtree_single search correctly" do
54
+ @dataset = read_dataset "test.dataset"
55
+ @testset = @dataset[0,:*].clone
56
+ @index = Flann::Index.new(@dataset) do |t|
57
+ t[:algorithm] = :kdtree_single
58
+ end
59
+ @index.build!
60
+ indices, distances = @index.nearest_neighbors @testset, 11
61
+ #expect(indices).to eq([0,1,256,257,2,512,258,513,514,3,768])
62
+ expect(indices[0]).to eq(0)
63
+ expect(indices[1..2].sort).to eq([1,256])
64
+ expect(indices[3]).to eq(257)
65
+ expect(indices[4..5].sort).to eq([2,512])
66
+ expect(indices[6..7].sort).to eq([258,513])
67
+ expect(indices[8]).to eq(514)
68
+ expect(indices[9..10].sort).to eq([3,768])
69
+
70
+ expect(distances[0]).to be_within(1E-16).of(0.0)
71
+ expect(distances[1]).to be_within(1E-4).of(2.614689)
72
+ expect(distances[2]).to be_within(1E-4).of(2.614689)
73
+ expect(distances[3]).to be_within(1E-4).of(5.229378)
74
+ expect(distances[4]).to be_within(1E-4).of(10.465225)
75
+ expect(distances[5]).to be_within(1E-4).of(10.465225)
76
+ expect(distances[6]).to be_within(1E-4).of(13.079914)
77
+ expect(distances[7]).to be_within(1E-4).of(13.079914)
78
+ expect(distances[8]).to be_within(1E-4).of(20.93045)
79
+ expect(distances[9]).to be_within(1E-4).of(23.541904)
80
+ end
81
+ end
82
+ end
83
+
84
+
85
+ context "#radius_search" do
86
+ it "runs without error" do
87
+ query = NMatrix.random([1,128])
88
+ @index.radius_search query, 0.4
89
+ end
90
+ end
91
+
92
+
93
+ context "#save" do
94
+ it "saves an index to a file which can be loaded again" do
95
+ FileUtils.rm("temp_index.save_file", :force => true)
96
+ @index.save("temp_index.save_file")
97
+
98
+ raise(IOError, "save failed") unless File.exists?("temp_index.save_file")
99
+
100
+ post_index = Flann::Index.new(@dataset)
101
+ post_index.load!("temp_index.save_file")
102
+ FileUtils.rm("temp_index.save_file", :force => true)
103
+ end
104
+ end
105
+
106
+
107
+ context "#free!" do
108
+ it "frees an index" do
109
+ @index.free!
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,50 @@
1
+ # Copyright 2014 John O. Woods (john.o.woods@gmail.com), West Virginia
2
+ # University's Applied Space Exploration Lab, and West Virginia Robotic
3
+ # Technology Center. All rights reserved.
4
+ #
5
+ # THE BSD LICENSE
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions
9
+ # are met:
10
+ #
11
+ # 1. Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ # 2. Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
+ # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
+ # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
+ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ require 'rspec'
29
+ require 'rspec/longrun'
30
+
31
+ require "./lib/flann"
32
+
33
+ # Helper function for reading a test dataset so we can test nearest neighbors
34
+ # and radius search and such.
35
+ def read_dataset filename
36
+ Dir.chdir("spec") do
37
+ f = File.new(filename, 'r')
38
+ n = NMatrix.new([65536, 3], dtype: :float32)
39
+ i = 0
40
+ while line = f.gets
41
+ line.chomp!
42
+ fields = line.split
43
+ n[i,:*] = fields.map { |field| field.to_f }
44
+
45
+ i += 1
46
+ end
47
+
48
+ n
49
+ end
50
+ end