affinity_propagation 0.1.0 → 0.2.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
- 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