machine_learning_workbench 0.4.4 → 0.4.5

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
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