affinity_propagation 0.1.0 → 0.2.0

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
- SHA1:
3
- metadata.gz: 26f8c5ca0ecef4d7c7e89341a5039eadefef5871
4
- data.tar.gz: cd466acdd9604ca6e547558438203b88283d21c9
2
+ SHA256:
3
+ metadata.gz: 43c3b9be3a0ad3d3ac9a4c428194f66af06d9add87d9390fc70267d92631f04d
4
+ data.tar.gz: 91c8248f1dce46cd08a220114d5f74d01e2dad90e4ac4e5469c326f0cf60f0bb
5
5
  SHA512:
6
- metadata.gz: 72c71cf377b691208ffeb714022216bb3d4b259ec09157feded788f49858d8e5260538ab73dec78e1a126b9914c9eb8c251a0c44818235ba35c366b81f24cd28
7
- data.tar.gz: 4fdcd1c0742094f837c95ebcd4366045006deb97dd358fe55a6575244ad07eb9207a3b5331cf9f8035fb2ec9dca2ad0b46d68188eb23d79b0a87646e79542ab5
6
+ metadata.gz: 598aecb0dc0f1593dbe032527cb22e72903b75a1e5c9721a99145ce490a175e54ff6817db729328652aefbf6b93f1cd460fa6358a2cda167c5f0bdebfc607a89
7
+ data.tar.gz: fed4c922d48fd640889b98c1434cf39006b43628429406bd369a856e0b9c75df086b6b71d84f802753e7ed879fb451e0d8334ffde81914f65fb64a06f057cc9e
data/.gitignore CHANGED
@@ -42,7 +42,7 @@ build-iPhoneSimulator/
42
42
 
43
43
  # for a library or gem, you might want to ignore these files since the code is
44
44
  # intended to run in multiple environments; otherwise, check them in:
45
- # Gemfile.lock
45
+ Gemfile.lock
46
46
  # .ruby-version
47
47
  # .ruby-gemset
48
48
 
@@ -52,3 +52,4 @@ build-iPhoneSimulator/
52
52
  # IDEs
53
53
  .idea
54
54
 
55
+ .byebug_history
@@ -28,5 +28,6 @@ Gem::Specification.new do |spec|
28
28
  spec.add_development_dependency "bundler", "~> 1.15"
29
29
  spec.add_development_dependency "rake", "~> 10.0"
30
30
  spec.add_development_dependency "rspec", "~> 3.0"
31
- spec.add_development_dependency "activesupport"
31
+
32
+ spec.add_runtime_dependency "concurrent-ruby", "~> 1.1.5"
32
33
  end
@@ -1,27 +1,8 @@
1
1
  require 'matrix'
2
+ require 'concurrent'
3
+ require 'thread'
2
4
 
3
5
  module AffinityPropagation
4
- refine Array do
5
- def median
6
- relevant_elements = if size % 2 == 0
7
- # Even number of items in this list => let's get the middle two and return their mean
8
- slice(size / 2, 2)
9
- else
10
- slice(size / 2, 1)
11
- end
12
-
13
- relevant_elements.sum / relevant_elements.size
14
- end
15
- end
16
-
17
- refine Matrix do
18
- def to_matlab
19
- output = row_vectors.map(&:to_a).map { |row| row.join(', ') }.join('; ')
20
-
21
- "[#{output}]"
22
- end
23
- end
24
-
25
6
  class Calculator
26
7
  using AffinityPropagation
27
8
 
@@ -33,7 +14,7 @@ module AffinityPropagation
33
14
  @data = data
34
15
  @lambda = lambda
35
16
 
36
- raise 'no block provide to calculate similarities within data!' unless block_given?
17
+ raise 'no block provided to calculate similarities within data!' unless block_given?
37
18
 
38
19
  @similarities = similarity_matrix(&block)
39
20
  reset
@@ -70,25 +51,47 @@ module AffinityPropagation
70
51
  end
71
52
 
72
53
  def run(iterations: 100, stable_iterations: 10)
73
- iterate while @total_iterations < iterations && @stable_cluster_iterations < stable_iterations
54
+ while @total_iterations < iterations && @stable_cluster_iterations < stable_iterations
55
+ iterate
56
+
57
+ yield(@total_iterations, @stable_cluster_iterations) if block_given?
58
+ end
74
59
  end
75
60
 
76
61
  private
77
62
 
63
+ def median(array)
64
+ relevant_elements = if array.size % 2 == 0
65
+ # Even number of items in this list => let's get the middle two and return their mean
66
+ array.sort.slice(array.size / 2, 2)
67
+ else
68
+ array.sort.slice(array.size / 2, 1)
69
+ end
70
+
71
+ relevant_elements.sum / relevant_elements.size
72
+ end
73
+
78
74
  def similarity_matrix(&block)
79
75
  similarity_array = []
80
- similarities = Matrix.build(@data.size, @data.size) do |row_idx, col_idx|
76
+ similarities_future = Matrix.build(@data.size, @data.size) do |row_idx, col_idx|
81
77
  exemplar = @data[row_idx]
82
78
  datum = @data[col_idx]
83
79
 
84
- similarity = block.call(datum, exemplar)
85
- similarity_array << similarity
86
- similarity
80
+ similarity_future = Concurrent::Future.execute(executor: :fast) { block.call(datum, exemplar)}
81
+ similarity_array << similarity_future
82
+
83
+ similarity_future
87
84
  end
88
85
 
89
- median_similarity = similarity_array.median
86
+ while similarity_array.any?(&:pending?)
87
+ sleep 0.1
88
+ end
89
+
90
+ similarity_array.map!(&:value)
91
+ similarities = similarities_future.map(&:value)
92
+
93
+ median_similarity = median(similarity_array)
90
94
 
91
- # Matrices can't be modified once created so we have to use this hack
92
95
  (0...@data.size).each { |idx| similarities.send(:[]=, idx, idx, median_similarity) }
93
96
 
94
97
  similarities
@@ -99,49 +102,82 @@ module AffinityPropagation
99
102
  end
100
103
 
101
104
  def responsibility_matrix
102
- Matrix.build(@similarities.row_count, @similarities.column_count) do |row_idx, col_idx|
105
+ responsibility_futures = []
106
+
107
+ responsibilities_future = Matrix.build(@similarities.row_count, @similarities.column_count) do |row_idx, col_idx|
103
108
  exemplar_idx = row_idx
104
109
  datum_idx = col_idx
105
110
 
111
+ current_similarity = @similarities[row_idx, col_idx]
112
+ current_responsibility = @responsibilities[exemplar_idx, datum_idx]
113
+
106
114
  availability_column = @availabilities.column(col_idx).to_a
107
- availability_column.slice!(exemplar_idx)
108
115
  similarity_column = @similarities.column(col_idx).to_a
109
- similarity_column.slice!(exemplar_idx)
110
116
 
111
- availability_plus_similarity = []
112
- availability_column.zip(similarity_column) { |data| availability_plus_similarity << data.sum }
117
+ responsibility_future = Concurrent::Future.execute(executor: :fast) do
118
+ availability_column.slice!(exemplar_idx)
119
+ similarity_column.slice!(exemplar_idx)
120
+
121
+ availability_plus_similarity = []
122
+ availability_column.zip(similarity_column) { |data| availability_plus_similarity << data.sum }
123
+
124
+ dampen(current_similarity - availability_plus_similarity.max, current_responsibility)
125
+ end
126
+
127
+ responsibility_futures << responsibility_future
128
+ responsibility_future
129
+ end
113
130
 
114
- dampen(@similarities[row_idx, col_idx] - availability_plus_similarity.max, @responsibilities[exemplar_idx, datum_idx])
131
+ while responsibility_futures.any?(&:pending?)
132
+ sleep 0.1
115
133
  end
134
+
135
+ responsibilities_future.map(&:value)
116
136
  end
117
137
 
118
138
  def availability_matrix
119
- Matrix.build(@responsibilities.row_count, @responsibilities.column_count) do |row_idx, col_idx|
139
+ availability_futures = []
140
+
141
+ availabilities_future = Matrix.build(@responsibilities.row_count, @responsibilities.column_count) do |row_idx, col_idx|
120
142
  exemplar_idx = row_idx
121
143
  datum_idx = col_idx
122
144
  responsibility_column = @responsibilities.row(exemplar_idx).to_a
123
145
 
124
- if exemplar_idx == datum_idx
125
- # self-availability
126
- responsibility_column.slice!(exemplar_idx)
146
+ current_availability = @availabilities[exemplar_idx, datum_idx]
147
+ current_responsibility = @responsibilities[exemplar_idx, exemplar_idx]
127
148
 
128
- dampen(responsibility_column.inject(0) { |sum, item| sum += [0, item].max },@availabilities[exemplar_idx, datum_idx])
129
- else
130
- self_responsibility = @responsibilities[exemplar_idx, exemplar_idx]
131
- if datum_idx > exemplar_idx
132
- # Slice out the datum index first since in this case it won't affect the exemplar index
133
- responsibility_column.slice!(datum_idx)
149
+ availability_future = Concurrent::Future.execute(executor: :fast) do
150
+ if exemplar_idx == datum_idx
151
+ # self-availability
134
152
  responsibility_column.slice!(exemplar_idx)
153
+
154
+ dampen(responsibility_column.inject(0) { |sum, item| sum += [0, item].max }, current_availability)
135
155
  else
136
- responsibility_column.slice!(exemplar_idx)
137
- responsibility_column.slice!(datum_idx)
156
+ self_responsibility = current_responsibility
157
+ if datum_idx > exemplar_idx
158
+ # Slice out the datum index first since in this case it won't affect the exemplar index
159
+ responsibility_column.slice!(datum_idx)
160
+ responsibility_column.slice!(exemplar_idx)
161
+ else
162
+ responsibility_column.slice!(exemplar_idx)
163
+ responsibility_column.slice!(datum_idx)
164
+ end
165
+
166
+ responsibility_column_sum = responsibility_column.inject(0) { |sum, item| sum += [0, item].max }
167
+
168
+ dampen([0, self_responsibility + responsibility_column_sum].min, current_availability)
138
169
  end
170
+ end
139
171
 
140
- responsibility_column_sum = responsibility_column.inject(0) { |sum, item| sum += [0, item].max }
172
+ availability_futures << availability_future
173
+ availability_future
174
+ end
141
175
 
142
- dampen([0, self_responsibility + responsibility_column_sum].min, @availabilities[exemplar_idx, datum_idx])
143
- end
176
+ while availability_futures.any?(&:pending?)
177
+ sleep 0.1
144
178
  end
179
+
180
+ availabilities_future.map(&:value)
145
181
  end
146
182
 
147
183
  def identify_raw_clusters
@@ -1,3 +1,3 @@
1
1
  module AffinityPropagation
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: affinity_propagation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shahbaz Javeed
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-09-10 00:00:00.000000000 Z
11
+ date: 2019-08-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -53,19 +53,19 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: activesupport
56
+ name: concurrent-ruby
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
61
+ version: 1.1.5
62
+ type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: 1.1.5
69
69
  description: "\n Affinity Propagation is a clustering algorithm that does not require
70
70
  pre-specifying the number of clusters like\n k-means and other similar algorithms
71
71
  do. This is a ruby implementation of the original version defined by Frey and\n
@@ -83,7 +83,6 @@ files:
83
83
  - ".ruby-version"
84
84
  - ".travis.yml"
85
85
  - Gemfile
86
- - Gemfile.lock
87
86
  - LICENSE.txt
88
87
  - README.md
89
88
  - Rakefile
@@ -112,8 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
111
  - !ruby/object:Gem::Version
113
112
  version: '0'
114
113
  requirements: []
115
- rubyforge_project:
116
- rubygems_version: 2.6.11
114
+ rubygems_version: 3.0.6
117
115
  signing_key:
118
116
  specification_version: 4
119
117
  summary: An implementation of the affinity propagation clustering algorithm by Frey
@@ -1,47 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- affinity_propagation (0.1.0)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- activesupport (5.1.4)
10
- concurrent-ruby (~> 1.0, >= 1.0.2)
11
- i18n (~> 0.7)
12
- minitest (~> 5.1)
13
- tzinfo (~> 1.1)
14
- concurrent-ruby (1.0.5)
15
- diff-lcs (1.3)
16
- i18n (0.8.6)
17
- minitest (5.10.3)
18
- rake (10.5.0)
19
- rspec (3.6.0)
20
- rspec-core (~> 3.6.0)
21
- rspec-expectations (~> 3.6.0)
22
- rspec-mocks (~> 3.6.0)
23
- rspec-core (3.6.0)
24
- rspec-support (~> 3.6.0)
25
- rspec-expectations (3.6.0)
26
- diff-lcs (>= 1.2.0, < 2.0)
27
- rspec-support (~> 3.6.0)
28
- rspec-mocks (3.6.0)
29
- diff-lcs (>= 1.2.0, < 2.0)
30
- rspec-support (~> 3.6.0)
31
- rspec-support (3.6.0)
32
- thread_safe (0.3.6)
33
- tzinfo (1.2.3)
34
- thread_safe (~> 0.1)
35
-
36
- PLATFORMS
37
- ruby
38
-
39
- DEPENDENCIES
40
- activesupport
41
- affinity_propagation!
42
- bundler (~> 1.15)
43
- rake (~> 10.0)
44
- rspec (~> 3.0)
45
-
46
- BUNDLED WITH
47
- 1.15.4