machine_learning_workbench 0.4.4 → 0.4.5

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
2
  SHA1:
3
- metadata.gz: c0dbf5e005de67e89c1df904d9811da1f66deb20
4
- data.tar.gz: 34eb691f9d7689e7b414592f58bc735271a0f0ac
3
+ metadata.gz: 59f6c7b552e7165e67222a70e5ceb231bf482d69
4
+ data.tar.gz: '09e9df7a6e045fadec71456f327f1a8118e31a5d'
5
5
  SHA512:
6
- metadata.gz: 48a85943f1f734fa0fe2ce95f9e053bf9059f886b99788c22f04d944bb3cc4202cb227ea23699c545b643f6d022e042d7ff50cc02a3914f853b6e3e14c5a936e
7
- data.tar.gz: 264b9f60818a2909df354fe71390f54b8040af69de3f223679832387a1d32947d2c32e83c5efbf2f49db47c0d6792a8b2cc836229cdd5a87b78397fa2f9eb7b1
6
+ metadata.gz: 5eae025849e2640b3137128a912cb54ce6f48fad9c27c63462dc08cd787c1a53527c2bcdcd06f0230bc0e7b1b50d8699f3778878ea7331f00a66bbc7ec114baf
7
+ data.tar.gz: 6aced52a85fd32650bdc88810c596d3305d2f3efeab2034a0ac73e58f3691dda5a4013e89a79546da033cb5ed81833bb090b0b69b7d7ed32e94d46b900bab165
@@ -26,157 +26,157 @@ module MachineLearningWorkbench::Monkey
26
26
  end
27
27
  end
28
28
 
29
- module AdvancelyOperationable # how am I supposed to name these things??
30
-
31
- # Outer matrix relationship generalization.
32
- # Make a matrix the same shape as `self`; each element is a matrix,
33
- # with the same shape as `other`, resulting from the interaction of
34
- # the corresponding element in `self` and all the elements in `other`.
35
- # @param other [NMatrix] other matrix
36
- # @note This implementation works only for 2D matrices (same as most
37
- # other methods here). It's a quick hack, a proof of concept barely
38
- # sufficient for my urgent needs.
39
- # @note Output size is fixed! Since NMatrix does not graciously yield to
40
- # being composed of other NMatrices (by adapting the shape of the root
41
- # matrix), the block cannot return matrices in there.
42
- # @return [NMatrix]
43
- def outer other
44
- # NOTE: Map of map in NMatrix does not work as expected!
45
- # self.map { |v1| other.map { |v2| yield(v1,v2) } }
46
- # NOTE: this doesn't cut it either... can't capture the structure
47
- # NMatrix[ *self.collect { |v1| other.collect { |v2| yield(v1,v2) } } ]
48
- raise ArgumentError unless block_given?
49
- NMatrix.new(self.shape+other.shape).tap do |m|
50
- each_stored_with_indices do |v1,r1,c1|
51
- other.each_stored_with_indices do |v2,r2,c2|
52
- m[r1,c1,r2,c2] = yield(v1,v2)
53
- end
54
- end
55
- end
56
- end
57
-
58
- # Flat-output generalized outer relationship. Same as `#outer`, but the
59
- # result is a 2-dim matrix of the interactions between all the elements
60
- # in `self` (as rows) and all the elements in `other` (as columns)
61
- # @param other [NMatrix] other matrix
62
- # @return [NMatrix]
63
- def outer_flat other
64
- raise ArgumentError unless block_given?
65
- data = collect { |v1| other.collect { |v2| yield(v1, v2) } }
66
- self.class[*data, dtype: dtype]
67
- end
68
-
69
- # Matrix exponential: `e^self` (not to be confused with `self^n`!)
70
- # @return [NMatrix]
71
- def exponential
72
- # special case: one-dimensional matrix: just exponentiate the values
73
- if (dim == 1) || (dim == 2 && shape.include?(1))
74
- return NMatrix.new shape, collect(&Math.method(:exp)), dtype: dtype
75
- end
76
-
77
- # Eigenvalue decomposition method from scipy/linalg/matfuncs.py#expm2
78
-
79
- # TODO: find out why can't I get away without double transpose!
80
- e_values, e_vectors = eigen_symm
81
-
82
- e_vals_exp_dmat = NMatrix.diagonal e_values.collect(&Math.method(:exp))
83
- # ASSUMING WE'RE ONLY USING THIS TO EXPONENTIATE LOG_SIGMA IN XNES
84
- # Theoretically we need the right eigenvectors, which for a symmetric
85
- # matrix should be just transposes of the eigenvectors.
86
- # But we have a positive definite matrix, so the final composition
87
- # below holds without transposing
88
- # BUT, strangely, I can't seem to get eigen_symm to green the tests
89
- # ...with or without transpose
90
- # e_vectors = e_vectors.transpose
91
- e_vectors.dot(e_vals_exp_dmat).dot(e_vectors.invert)#.transpose
92
- end
93
-
94
- # Calculate matrix eigenvalues and eigenvectors using LAPACK
95
- # @param which [:both, :left, :right] which eigenvectors do you want?
96
- # @return [Array<NMatrix, NMatrix[, NMatrix]>]
97
- # eigenvalues (as column vector), left eigenvectors, right eigenvectors.
98
- # A value different than `:both` for param `which` reduces the return size.
99
- # @note requires LAPACK
100
- # @note WARNING! a param `which` different than :both alters the returns
101
- # @note WARNING! machine-precision-error imaginary part Complex
102
- # often returned! For symmetric matrices use #eigen_symm_right below
103
- def eigen which=:both
104
- raise ArgumentError unless [:both, :left, :right].include? which
105
- NMatrix::LAPACK.geev(self, which)
106
- end
107
-
108
- # Eigenvalues and right eigenvectors for symmetric matrices using LAPACK
109
- # @note code taken from gem `nmatrix-atlas` NMatrix::LAPACK#geev
110
- # @note FOR SYMMETRIC MATRICES ONLY!!
111
- # @note WARNING: will return real matrices, imaginary parts are discarded!
112
- # @note WARNING: only left eigenvectors will be returned!
113
- # @todo could it be possible to save some of the transpositions?
114
- # @return [Array<NMatrix, NMatrix>] eigenvalues and (left) eigenvectors
115
- def eigen_symm
116
- # TODO: check for symmetry if not too slow
117
- raise TypeError, "Only real-valued matrices" if complex_dtype?
118
- raise StorageTypeError, "Only dense matrices (because LAPACK)" unless dense?
119
- raise ShapeError, "Only square matrices" unless dim == 2 && shape[0] == shape[1]
120
-
121
- n = shape[0]
122
-
123
- # Outputs
124
- e_values = NMatrix.new([n, 1], dtype: dtype)
125
- e_values_img = NMatrix.new([n, 1], dtype: dtype) # to satisfy C alloc
126
- e_vectors = clone_structure
127
-
128
- NMatrix::LAPACK::lapack_geev(
129
- false, # compute left eigenvectors of A?
130
- :t, # compute right eigenvectors of A? (left eigenvectors of A**T)
131
- n, # order of the matrix
132
- transpose, # input matrix => needs to be column-wise # self,
133
- n, # leading dimension of matrix
134
- e_values, # real part of computed eigenvalues
135
- e_values_img, # imaginary part of computed eigenvalues (will be discarded)
136
- nil, # left eigenvectors, if applicable
137
- n, # leading dimension of left_output
138
- e_vectors, # right eigenvectors, if applicable
139
- n, # leading dimension of right_output
140
- 2*n # no clue what's this
141
- )
142
-
143
- raise "Uhm why complex eigenvalues?" if e_values_img.any? {|v| v>1e-10}
144
- return [e_values, e_vectors.transpose]
145
- end
146
-
147
-
148
- # The NMatrix documentation refers to a function `#nrm2` (aliased to `#norm2`)
149
- # to compute the norm of a matrix. Fun fact: that is the implementation for vectors,
150
- # and calling it on a matrix returns NotImplementedError :) you have to toggle the
151
- # source to understand why:
152
- # http://sciruby.com/nmatrix/docs/NMatrix.html#method-i-norm2 .
153
- # A search for the actual source on GitHub reveals a (I guess new?) method
154
- # `#matrix_norm`, with a decent choice of norms to choose from. Unfortunately, as the
155
- # name says, it is stuck to compute full-matrix norms.
156
- # So I resigned to dance to `Array`s and back, and implemented it with `#each_rank`.
157
- # Unexplicably, I get a list of constant values as the return value; same with
158
- # `#each_row`.
159
- # What can I say, we're back to referencing rows by index. I am just wasting too much
160
- # time figuring out these details to write a generalized version with an optional
161
- # `dimension` to go along.
162
- # @return [NMatrix] the vector norm along the rows
163
- def row_norms
164
- norms = rows.times.map { |i| row(i).norm2 }
165
- NMatrix.new [rows, 1], norms, dtype: dtype
166
- end
167
-
168
- # `NMatrix#to_a` has inconsistent behavior: single-row matrices are
169
- # converted to one-dimensional Arrays rather than a 2D Array with
170
- # only one row. Patching `#to_a` directly is not feasible as the
171
- # constructor seems to depend on it, and I have little interest in
172
- # investigating further.
173
- # @return [Array<Array>] a consistent array representation, such that
174
- # `nmat.to_consistent_a.to_nm == nmat` holds for single-row matrices
175
- def to_consistent_a
176
- dim == 2 && shape[0] == 1 ? [to_a] : to_a
177
- end
178
- alias :to_ca :to_consistent_a
179
- end
29
+ # module AdvancelyOperationable # how am I supposed to name these things??
30
+
31
+ # # Outer matrix relationship generalization.
32
+ # # Make a matrix the same shape as `self`; each element is a matrix,
33
+ # # with the same shape as `other`, resulting from the interaction of
34
+ # # the corresponding element in `self` and all the elements in `other`.
35
+ # # @param other [NMatrix] other matrix
36
+ # # @note This implementation works only for 2D matrices (same as most
37
+ # # other methods here). It's a quick hack, a proof of concept barely
38
+ # # sufficient for my urgent needs.
39
+ # # @note Output size is fixed! Since NMatrix does not graciously yield to
40
+ # # being composed of other NMatrices (by adapting the shape of the root
41
+ # # matrix), the block cannot return matrices in there.
42
+ # # @return [NMatrix]
43
+ # def outer other
44
+ # # NOTE: Map of map in NMatrix does not work as expected!
45
+ # # self.map { |v1| other.map { |v2| yield(v1,v2) } }
46
+ # # NOTE: this doesn't cut it either... can't capture the structure
47
+ # # NMatrix[ *self.collect { |v1| other.collect { |v2| yield(v1,v2) } } ]
48
+ # raise ArgumentError unless block_given?
49
+ # NMatrix.new(self.shape+other.shape).tap do |m|
50
+ # each_stored_with_indices do |v1,r1,c1|
51
+ # other.each_stored_with_indices do |v2,r2,c2|
52
+ # m[r1,c1,r2,c2] = yield(v1,v2)
53
+ # end
54
+ # end
55
+ # end
56
+ # end
57
+
58
+ # # Flat-output generalized outer relationship. Same as `#outer`, but the
59
+ # # result is a 2-dim matrix of the interactions between all the elements
60
+ # # in `self` (as rows) and all the elements in `other` (as columns)
61
+ # # @param other [NMatrix] other matrix
62
+ # # @return [NMatrix]
63
+ # def outer_flat other
64
+ # raise ArgumentError unless block_given?
65
+ # data = collect { |v1| other.collect { |v2| yield(v1, v2) } }
66
+ # self.class[*data, dtype: dtype]
67
+ # end
68
+
69
+ # # Matrix exponential: `e^self` (not to be confused with `self^n`!)
70
+ # # @return [NMatrix]
71
+ # def exponential
72
+ # # special case: one-dimensional matrix: just exponentiate the values
73
+ # if (dim == 1) || (dim == 2 && shape.include?(1))
74
+ # return NMatrix.new shape, collect(&Math.method(:exp)), dtype: dtype
75
+ # end
76
+
77
+ # # Eigenvalue decomposition method from scipy/linalg/matfuncs.py#expm2
78
+
79
+ # # TODO: find out why can't I get away without double transpose!
80
+ # e_values, e_vectors = eigen_symm
81
+
82
+ # e_vals_exp_dmat = NMatrix.diagonal e_values.collect(&Math.method(:exp))
83
+ # # ASSUMING WE'RE ONLY USING THIS TO EXPONENTIATE LOG_SIGMA IN XNES
84
+ # # Theoretically we need the right eigenvectors, which for a symmetric
85
+ # # matrix should be just transposes of the eigenvectors.
86
+ # # But we have a positive definite matrix, so the final composition
87
+ # # below holds without transposing
88
+ # # BUT, strangely, I can't seem to get eigen_symm to green the tests
89
+ # # ...with or without transpose
90
+ # # e_vectors = e_vectors.transpose
91
+ # e_vectors.dot(e_vals_exp_dmat).dot(e_vectors.invert)#.transpose
92
+ # end
93
+
94
+ # # Calculate matrix eigenvalues and eigenvectors using LAPACK
95
+ # # @param which [:both, :left, :right] which eigenvectors do you want?
96
+ # # @return [Array<NMatrix, NMatrix[, NMatrix]>]
97
+ # # eigenvalues (as column vector), left eigenvectors, right eigenvectors.
98
+ # # A value different than `:both` for param `which` reduces the return size.
99
+ # # @note requires LAPACK
100
+ # # @note WARNING! a param `which` different than :both alters the returns
101
+ # # @note WARNING! machine-precision-error imaginary part Complex
102
+ # # often returned! For symmetric matrices use #eigen_symm_right below
103
+ # def eigen which=:both
104
+ # raise ArgumentError unless [:both, :left, :right].include? which
105
+ # NMatrix::LAPACK.geev(self, which)
106
+ # end
107
+
108
+ # # Eigenvalues and right eigenvectors for symmetric matrices using LAPACK
109
+ # # @note code taken from gem `nmatrix-atlas` NMatrix::LAPACK#geev
110
+ # # @note FOR SYMMETRIC MATRICES ONLY!!
111
+ # # @note WARNING: will return real matrices, imaginary parts are discarded!
112
+ # # @note WARNING: only left eigenvectors will be returned!
113
+ # # @todo could it be possible to save some of the transpositions?
114
+ # # @return [Array<NMatrix, NMatrix>] eigenvalues and (left) eigenvectors
115
+ # def eigen_symm
116
+ # # TODO: check for symmetry if not too slow
117
+ # raise TypeError, "Only real-valued matrices" if complex_dtype?
118
+ # raise StorageTypeError, "Only dense matrices (because LAPACK)" unless dense?
119
+ # raise ShapeError, "Only square matrices" unless dim == 2 && shape[0] == shape[1]
120
+
121
+ # n = shape[0]
122
+
123
+ # # Outputs
124
+ # e_values = NMatrix.new([n, 1], dtype: dtype)
125
+ # e_values_img = NMatrix.new([n, 1], dtype: dtype) # to satisfy C alloc
126
+ # e_vectors = clone_structure
127
+
128
+ # NMatrix::LAPACK::lapack_geev(
129
+ # false, # compute left eigenvectors of A?
130
+ # :t, # compute right eigenvectors of A? (left eigenvectors of A**T)
131
+ # n, # order of the matrix
132
+ # transpose, # input matrix => needs to be column-wise # self,
133
+ # n, # leading dimension of matrix
134
+ # e_values, # real part of computed eigenvalues
135
+ # e_values_img, # imaginary part of computed eigenvalues (will be discarded)
136
+ # nil, # left eigenvectors, if applicable
137
+ # n, # leading dimension of left_output
138
+ # e_vectors, # right eigenvectors, if applicable
139
+ # n, # leading dimension of right_output
140
+ # 2*n # no clue what's this
141
+ # )
142
+
143
+ # raise "Uhm why complex eigenvalues?" if e_values_img.any? {|v| v>1e-10}
144
+ # return [e_values, e_vectors.transpose]
145
+ # end
146
+
147
+
148
+ # # The NMatrix documentation refers to a function `#nrm2` (aliased to `#norm2`)
149
+ # # to compute the norm of a matrix. Fun fact: that is the implementation for vectors,
150
+ # # and calling it on a matrix returns NotImplementedError :) you have to toggle the
151
+ # # source to understand why:
152
+ # # http://sciruby.com/nmatrix/docs/NMatrix.html#method-i-norm2 .
153
+ # # A search for the actual source on GitHub reveals a (I guess new?) method
154
+ # # `#matrix_norm`, with a decent choice of norms to choose from. Unfortunately, as the
155
+ # # name says, it is stuck to compute full-matrix norms.
156
+ # # So I resigned to dance to `Array`s and back, and implemented it with `#each_rank`.
157
+ # # Unexplicably, I get a list of constant values as the return value; same with
158
+ # # `#each_row`.
159
+ # # What can I say, we're back to referencing rows by index. I am just wasting too much
160
+ # # time figuring out these details to write a generalized version with an optional
161
+ # # `dimension` to go along.
162
+ # # @return [NMatrix] the vector norm along the rows
163
+ # def row_norms
164
+ # norms = rows.times.map { |i| row(i).norm2 }
165
+ # NMatrix.new [rows, 1], norms, dtype: dtype
166
+ # end
167
+
168
+ # # `NMatrix#to_a` has inconsistent behavior: single-row matrices are
169
+ # # converted to one-dimensional Arrays rather than a 2D Array with
170
+ # # only one row. Patching `#to_a` directly is not feasible as the
171
+ # # constructor seems to depend on it, and I have little interest in
172
+ # # investigating further.
173
+ # # @return [Array<Array>] a consistent array representation, such that
174
+ # # `nmat.to_consistent_a.to_nm == nmat` holds for single-row matrices
175
+ # def to_consistent_a
176
+ # dim == 2 && shape[0] == 1 ? [to_a] : to_a
177
+ # end
178
+ # alias :to_ca :to_consistent_a
179
+ # end
180
180
 
181
181
  module NumericallyApproximatable
182
182
  # Verifies if `self` and `other` are withing `epsilon` of each other.
@@ -189,34 +189,34 @@ module MachineLearningWorkbench::Monkey
189
189
  end
190
190
  end
191
191
 
192
- module MatrixApproximatable
193
- # Verifies if all values at corresponding indices approximate each other.
194
- # @param other [NMatrix]
195
- # @param epsilon [Float]
196
- def approximates? other, epsilon=1e-5
197
- return false unless self.shape == other.shape
198
- # two ways to go here:
199
- # - epsilon is aggregated: total cumulative accepted error
200
- # => `(self - other).reduce(:+) < epsilon`
201
- # - epsilon is local: per element accepted error
202
- # => `v.approximates? other[*idxs], epsilon`
203
- # Given the use I make (near-equality), I choose the first interpretation
204
- # Note the second is sensitive to opposite signs balancing up
205
- self.each_stored_with_indices.all? do |v,*idxs|
206
- v.approximates? other[*idxs], epsilon
207
- end
208
- end
209
- end
210
-
211
- module CPtrDumpable
212
- def marshall_dump
213
- [shape, dtype, data_pointer]
214
- end
215
-
216
- def marshall_load
217
- raise NotImplementedError, "There's no setter for the data pointer!"
218
- end
219
- end
192
+ # module MatrixApproximatable
193
+ # # Verifies if all values at corresponding indices approximate each other.
194
+ # # @param other [NMatrix]
195
+ # # @param epsilon [Float]
196
+ # def approximates? other, epsilon=1e-5
197
+ # return false unless self.shape == other.shape
198
+ # # two ways to go here:
199
+ # # - epsilon is aggregated: total cumulative accepted error
200
+ # # => `(self - other).reduce(:+) < epsilon`
201
+ # # - epsilon is local: per element accepted error
202
+ # # => `v.approximates? other[*idxs], epsilon`
203
+ # # Given the use I make (near-equality), I choose the first interpretation
204
+ # # Note the second is sensitive to opposite signs balancing up
205
+ # self.each_stored_with_indices.all? do |v,*idxs|
206
+ # v.approximates? other[*idxs], epsilon
207
+ # end
208
+ # end
209
+ # end
210
+
211
+ # module CPtrDumpable
212
+ # def marshall_dump
213
+ # [shape, dtype, data_pointer]
214
+ # end
215
+
216
+ # def marshall_load
217
+ # raise NotImplementedError, "There's no setter for the data pointer!"
218
+ # end
219
+ # end
220
220
 
221
221
  module ToNArrayConvertible
222
222
  def to_na
@@ -1,20 +1,22 @@
1
1
  module MachineLearningWorkbench::Tools
2
2
  module Verification
3
- def self.in_range! nmat, vrange
4
- # Raise if values not in range
5
- vmin, vmax = vrange.to_a
6
- nmat.each_with_indices do |v, *idxs|
7
- raise "Value not in range" unless v&.between? vmin, vmax
8
- end
9
- end
3
+ # TODO: switch to NArray
10
4
 
11
- # Fix if values not in range
12
- def self.in_range nmat, vrange
13
- vmin, vmax = vrange.to_a
14
- nmat.each_with_indices do |v, *idxs|
15
- nmat[*idxs] = vmin if v < vmin
16
- nmat[*idxs] = vmax if v > vmax
17
- end
18
- end
5
+ # def self.in_range! nmat, vrange
6
+ # # Raise if values not in range
7
+ # vmin, vmax = vrange.to_a
8
+ # nmat.each_with_indices do |v, *idxs|
9
+ # raise "Value not in range" unless v&.between? vmin, vmax
10
+ # end
11
+ # end
12
+
13
+ # # Fix if values not in range
14
+ # def self.in_range nmat, vrange
15
+ # vmin, vmax = vrange.to_a
16
+ # nmat.each_with_indices do |v, *idxs|
17
+ # nmat[*idxs] = vmin if v < vmin
18
+ # nmat[*idxs] = vmax if v > vmax
19
+ # end
20
+ # end
19
21
  end
20
22
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: machine_learning_workbench
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.4
4
+ version: 0.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Giuseppe Cuccu