bribera-rubyvor 0.0.5 → 0.0.6
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.
- data/History.txt +4 -0
- data/ext/ruby_vor.c +3 -0
- data/lib/ruby_vor/computation.rb +59 -20
- data/lib/ruby_vor/priority_queue.rb +26 -1
- data/lib/ruby_vor/version.rb +1 -1
- data/rubyvor.gemspec +1 -1
- data/test/test_computation.rb +73 -33
- data/test/test_priority_queue.rb +17 -1
- metadata +1 -1
data/History.txt
CHANGED
data/ext/ruby_vor.c
CHANGED
data/lib/ruby_vor/computation.rb
CHANGED
@@ -3,6 +3,8 @@ module RubyVor
|
|
3
3
|
class Computation
|
4
4
|
attr_reader :points, :voronoi_diagram_raw, :delaunay_triangulation_raw
|
5
5
|
|
6
|
+
DIST_PROC = lambda{|a,b| a.distance_from(b)}
|
7
|
+
|
6
8
|
# Create a computation from an existing set of raw data.
|
7
9
|
def initialize(points=[], vd_raw=[], dt_raw=[])
|
8
10
|
@points = points
|
@@ -16,8 +18,7 @@ module RubyVor
|
|
16
18
|
#
|
17
19
|
# This method allows the caller to pass in a lambda for customizing distance calculations. For instance, to use a GeoRuby::SimpleFeatures::Point, one would:
|
18
20
|
# > cluster_by_distance(50 lambda{|a,b| a.spherical_distance(b, 3958.754)}) # this rejects edges greater than 50 miles, using spherical distance as a measure
|
19
|
-
def cluster_by_distance(max_distance, dist_proc=
|
20
|
-
|
21
|
+
def cluster_by_distance(max_distance, dist_proc=DIST_PROC)
|
21
22
|
clusters = []
|
22
23
|
nodes = (0..points.length-1).to_a
|
23
24
|
visited = [false] * points.length
|
@@ -58,7 +59,7 @@ module RubyVor
|
|
58
59
|
# Computes the minimum spanning tree for given points, using the Delaunay triangulation as a seed.
|
59
60
|
#
|
60
61
|
# For points on a Euclidean plane, the MST is always comprised of a subset of the edges in a Delaunay triangulation. This makes computation of the tree very efficient: simply compute the Delaunay triangulation, and then run Prim's algorithm on the resulting edges.
|
61
|
-
def minimum_spanning_tree(dist_proc=
|
62
|
+
def minimum_spanning_tree(dist_proc=DIST_PROC)
|
62
63
|
nodes = []
|
63
64
|
q = RubyVor::PriorityQueue.new
|
64
65
|
(0..points.length-1).to_a.map do |n|
|
@@ -112,16 +113,16 @@ module RubyVor
|
|
112
113
|
|
113
114
|
end
|
114
115
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
h[v.data[0]]
|
119
|
-
h
|
116
|
+
nodes.inject({}) do |h,adjacency_list|
|
117
|
+
v = adjacency_list.data[0]
|
118
|
+
adjacency_list.data[3].each do |node|
|
119
|
+
h[ (v < node.data[0]) ? [v,node.data[0]] : [node.data[0],v] ] ||= node.priority
|
120
120
|
end
|
121
|
+
h
|
121
122
|
end
|
122
123
|
end
|
123
124
|
|
124
|
-
def cluster_by_size(sizes=[])
|
125
|
+
def cluster_by_size(sizes=[], dist_proc=DIST_PROC)
|
125
126
|
# TODO
|
126
127
|
# * (DONE) Create a minimum_spanning_tree routine to:
|
127
128
|
# 1. (DONE) compute weights (should be done for us in C?)
|
@@ -133,22 +134,60 @@ module RubyVor
|
|
133
134
|
# 2. Determine remaining connectivity using BFS as above?
|
134
135
|
# 3. Some other more efficient connectivity test?
|
135
136
|
# 4. Return {n1 => cluster, n2 => cluster} for all n.
|
136
|
-
|
137
|
+
|
138
|
+
sized_clusters = sizes.inject({}) {|h,s| h[s] = []; h}
|
139
|
+
|
140
|
+
|
141
|
+
mst = minimum_spanning_tree(dist_proc).to_a
|
142
|
+
mst.sort!{|a,b|a.last <=> b.last}
|
143
|
+
|
144
|
+
sizes = sizes.sort
|
145
|
+
last_size = 0
|
146
|
+
while current_size = sizes.shift
|
147
|
+
current_size -= 1
|
148
|
+
|
149
|
+
# Remove edge count delta
|
150
|
+
delta = current_size - last_size
|
151
|
+
mst.slice!(-delta,delta)
|
152
|
+
|
153
|
+
visited = [nil] * points.length
|
137
154
|
|
138
|
-
protected
|
139
155
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
156
|
+
|
157
|
+
for edge in mst
|
158
|
+
i, j = edge.first
|
159
|
+
|
160
|
+
next if !visited[i].nil? && visited[i] == visited[j]
|
161
|
+
|
162
|
+
if visited[i] && visited[j]
|
163
|
+
visited[i].concat(visited[j])
|
164
|
+
visited[i].uniq!
|
165
|
+
visited[j].each{|v| visited[v] = visited[i]}
|
166
|
+
visited[j] = visited[i]
|
167
|
+
else
|
168
|
+
cluster = visited[i] || visited[j] || []
|
169
|
+
cluster.push(i) unless visited[i]
|
170
|
+
cluster.push(j) unless visited[j]
|
171
|
+
visited[i] = visited[j] = cluster
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
visited.each_with_index do |visits, v|
|
177
|
+
if visits.nil?
|
178
|
+
visited[v] = [v]
|
179
|
+
end
|
146
180
|
end
|
147
|
-
|
148
|
-
|
149
|
-
|
181
|
+
|
182
|
+
sized_clusters[current_size + 1] = visited.uniq
|
183
|
+
|
184
|
+
last_size = current_size
|
185
|
+
end
|
186
|
+
|
187
|
+
sized_clusters
|
150
188
|
end
|
151
189
|
|
190
|
+
|
152
191
|
end
|
153
192
|
end
|
154
193
|
end
|
@@ -2,8 +2,33 @@ module RubyVor
|
|
2
2
|
class PriorityQueue
|
3
3
|
|
4
4
|
attr_reader :data, :size
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def build_queue(max_index=-1,&block)
|
8
|
+
data = []
|
9
|
+
|
10
|
+
index = 0
|
11
|
+
loop do
|
12
|
+
x = QueueItem.new(nil, nil, nil)
|
13
|
+
|
14
|
+
yield(x)
|
15
|
+
break if !(max_index < 0 || index < max_index) || x.priority.nil?
|
16
|
+
|
17
|
+
x.index = index
|
18
|
+
data.push(x)
|
19
|
+
index += 1
|
20
|
+
end
|
21
|
+
|
22
|
+
q = new
|
23
|
+
q.instance_variable_set(:@data, data)
|
24
|
+
q.instance_variable_set(:@size, data.length)
|
25
|
+
q.heapify()
|
26
|
+
|
27
|
+
return q
|
28
|
+
end
|
29
|
+
end
|
5
30
|
|
6
|
-
def initialize
|
31
|
+
def initialize
|
7
32
|
@data = []
|
8
33
|
@size = 0
|
9
34
|
heapify()
|
data/lib/ruby_vor/version.rb
CHANGED
data/rubyvor.gemspec
CHANGED
data/test/test_computation.rb
CHANGED
@@ -47,17 +47,15 @@ class TestComputation < MiniTest::Unit::TestCase
|
|
47
47
|
]
|
48
48
|
|
49
49
|
comp = RubyVor::VDDT::Computation.from_points(points)
|
50
|
-
|
51
50
|
expected_mst = [
|
52
|
-
[1],
|
53
|
-
[
|
54
|
-
[1]
|
51
|
+
[0,1],
|
52
|
+
[1,2]
|
55
53
|
]
|
56
54
|
computed_mst = comp.minimum_spanning_tree
|
57
55
|
|
58
56
|
# Assert nodes are correct
|
59
|
-
assert_equal expected_mst.
|
60
|
-
computed_mst.
|
57
|
+
assert_equal expected_mst.sort, \
|
58
|
+
computed_mst.keys.sort
|
61
59
|
end
|
62
60
|
|
63
61
|
def test_advanced_mst
|
@@ -95,36 +93,35 @@ class TestComputation < MiniTest::Unit::TestCase
|
|
95
93
|
comp = RubyVor::VDDT::Computation.from_points(points)
|
96
94
|
|
97
95
|
expected_mst = [
|
98
|
-
[1
|
99
|
-
[0,
|
100
|
-
[1,
|
101
|
-
[
|
102
|
-
[3],
|
103
|
-
[
|
104
|
-
[5,
|
105
|
-
[
|
106
|
-
[7
|
107
|
-
[8],
|
108
|
-
[
|
109
|
-
[10,
|
110
|
-
[11],
|
111
|
-
[
|
112
|
-
[13,
|
113
|
-
[14],
|
114
|
-
[
|
115
|
-
[
|
116
|
-
[
|
117
|
-
[
|
118
|
-
[
|
119
|
-
[
|
120
|
-
[
|
121
|
-
[
|
122
|
-
[23] # 24
|
96
|
+
[0,1],
|
97
|
+
[0,13],
|
98
|
+
[1,2],
|
99
|
+
[1,5],
|
100
|
+
[2,3],
|
101
|
+
[3,4],
|
102
|
+
[5,6],
|
103
|
+
[5,10],
|
104
|
+
[6,7],
|
105
|
+
[7,8],
|
106
|
+
[8,9],
|
107
|
+
[10,11],
|
108
|
+
[11,12],
|
109
|
+
[13,14],
|
110
|
+
[13,16],
|
111
|
+
[14,15],
|
112
|
+
[16,17],
|
113
|
+
[17,18],
|
114
|
+
[18,19],
|
115
|
+
[19,20],
|
116
|
+
[20,21],
|
117
|
+
[21,22],
|
118
|
+
[22,23],
|
119
|
+
[23,24]
|
123
120
|
]
|
124
121
|
computed_mst = comp.minimum_spanning_tree
|
125
122
|
|
126
|
-
assert_equal expected_mst.
|
127
|
-
computed_mst.
|
123
|
+
assert_equal expected_mst.sort, \
|
124
|
+
computed_mst.keys.sort
|
128
125
|
end
|
129
126
|
|
130
127
|
def test_cluster_by_distance
|
@@ -150,6 +147,16 @@ class TestComputation < MiniTest::Unit::TestCase
|
|
150
147
|
|
151
148
|
end
|
152
149
|
|
150
|
+
|
151
|
+
def test_duplicate_points
|
152
|
+
comp = RubyVor::VDDT::Computation.from_points([RubyVor::Point.new(1,1), RubyVor::Point.new(1,1), RubyVor::Point.new(2,3), RubyVor::Point.new(1,1), RubyVor::Point.new(4,0)])
|
153
|
+
refute_equal comp.nn_graph, nil
|
154
|
+
refute_equal comp.minimum_spanning_tree, nil
|
155
|
+
refute_equal comp.cluster_by_distance(3), nil
|
156
|
+
refute_equal comp.cluster_by_size([2]), nil
|
157
|
+
end
|
158
|
+
|
159
|
+
|
153
160
|
def test_bad_data
|
154
161
|
assert_raises TypeError do
|
155
162
|
comp = RubyVor::VDDT::Computation.from_points([RubyVor::Point.new(1,1), RubyVor::Point.new(21,3), RubyVor::Point.new(2,:s)])
|
@@ -173,6 +180,39 @@ class TestComputation < MiniTest::Unit::TestCase
|
|
173
180
|
end
|
174
181
|
end
|
175
182
|
|
183
|
+
|
184
|
+
def test_cluster_by_size
|
185
|
+
comp = RubyVor::VDDT::Computation.from_points([
|
186
|
+
RubyVor::Point.new(0.25, 0.25),
|
187
|
+
RubyVor::Point.new(1, 0.25),
|
188
|
+
RubyVor::Point.new(0.5, 1),
|
189
|
+
RubyVor::Point.new(5, 5),
|
190
|
+
RubyVor::Point.new(10.25, 10.25),
|
191
|
+
RubyVor::Point.new(13, 9),
|
192
|
+
RubyVor::Point.new(9, 9)
|
193
|
+
])
|
194
|
+
|
195
|
+
sizes = [1, 3, 5, 7]
|
196
|
+
|
197
|
+
computed_sized_clusters = comp.cluster_by_size(sizes)
|
198
|
+
|
199
|
+
# Check that we got clusters for each size requested
|
200
|
+
assert_equal sizes, \
|
201
|
+
computed_sized_clusters.keys.sort
|
202
|
+
|
203
|
+
assert_equal [[0,1,2,3,4,5,6]], \
|
204
|
+
computed_sized_clusters[1].map{|cl| cl.sort}.sort
|
205
|
+
|
206
|
+
assert_equal [[0,1,2], [3], [4,5,6]], \
|
207
|
+
computed_sized_clusters[3].map{|cl| cl.sort}.sort
|
208
|
+
|
209
|
+
assert_equal [[0,1,2], [3], [4], [5], [6]], \
|
210
|
+
computed_sized_clusters[5].map{|cl| cl.sort}.sort
|
211
|
+
|
212
|
+
assert_equal [[0], [1], [2], [3], [4], [5], [6]], \
|
213
|
+
computed_sized_clusters[7].sort
|
214
|
+
end
|
215
|
+
|
176
216
|
#
|
177
217
|
# A few helper methods
|
178
218
|
#
|
data/test/test_priority_queue.rb
CHANGED
@@ -44,7 +44,7 @@ class TestPriorityQueue < MiniTest::Unit::TestCase
|
|
44
44
|
|
45
45
|
old_n = -1.0 * Float::MAX
|
46
46
|
while n = q.pop
|
47
|
-
assert n.priority >= old_n
|
47
|
+
assert n.priority >= old_n, 'Heap-order property violated'
|
48
48
|
old_n = n.priority
|
49
49
|
end
|
50
50
|
end
|
@@ -96,6 +96,22 @@ class TestPriorityQueue < MiniTest::Unit::TestCase
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
+
def test_build_queue
|
100
|
+
n = 100
|
101
|
+
|
102
|
+
q = RubyVor::PriorityQueue.build_queue(n) do |queue_item|
|
103
|
+
queue_item.priority = rand * 10000.0 - 5000.0
|
104
|
+
end
|
105
|
+
|
106
|
+
assert_equal n, q.data.length
|
107
|
+
assert_equal n, q.size
|
108
|
+
|
109
|
+
old_n = -1.0 * Float::MAX
|
110
|
+
while n = q.pop
|
111
|
+
assert n.priority >= old_n, 'Heap-order property violated'
|
112
|
+
old_n = n.priority
|
113
|
+
end
|
114
|
+
end
|
99
115
|
|
100
116
|
private
|
101
117
|
|