rmatrix 0.1.3 → 0.1.7

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: 2669d0b3df98c92b74abe27833d0a1d11f39bf84
4
- data.tar.gz: e28379824f051e318780f7599bfe1d640dbb3bf1
3
+ metadata.gz: 3ef041f60282c09c58765f159e598b55a3cc7cd4
4
+ data.tar.gz: 624800e2934fe3858adfbbd62e6455574bce512b
5
5
  SHA512:
6
- metadata.gz: adcd594922c431268a3f3734ee9e17055f29b05f3ede14bb1f9df17956bd19ce5f203ab78d5875caf5e1307d8b767fe3d9510545677673331c9d877297ea4471
7
- data.tar.gz: 07642f99e0eca1b196fdee6889684f9271a3f0ea15d4e5c9edec8bb7e1cfd72a361547887b3b41b169be2d5b58da77d6443f718196be5e7e8b5787462dfe491b
6
+ metadata.gz: 1e8bf852ff5b4c8cad5e541b68604784a401ab140c01654f8bd442b4899c5b047de3d55dca050ab0e8452488f2fe56bd22cb0f40f7afa6620eb7277975ffffd2
7
+ data.tar.gz: 35fd9c5279d0b45c7a651c11ea06c9b42f26035697e0093a1b6cc32f95bb6dbb2a3b79ce6fd1f111d1fe0e0c6d3b0c5b316abba785500f298a8b42effdc030a4
data/lib/rmatrix.rb CHANGED
@@ -1,3 +1,4 @@
1
1
  require_relative "rmatrix/version"
2
+ require_relative "rmatrix/indices"
2
3
  require_relative "rmatrix/core"
3
4
  require_relative "rmatrix/shortcuts"
@@ -0,0 +1,107 @@
1
+ module RMatrix
2
+ module Indices
3
+ def [](*args)
4
+ indices = unmap_args(args)
5
+ result_row_map = build_result_map(self.row_map, indices.first, self.rows) if self.row_map
6
+ result_column_map = build_result_map(self.column_map, indices.last, self.columns) if self.column_map
7
+ raw[*indices, column_map: result_column_map, row_map: result_row_map]
8
+ end
9
+
10
+ def raw
11
+ @raw ||= begin
12
+ raw = Struct.new(:narray, :typecode).new(self.narray, self.typecode)
13
+ def raw.[](*args, column_map: nil, row_map: nil)
14
+ args.all?{|x| Fixnum === x } ? narray[*args.reverse] : Matrix.new(narray[*args.reverse], typecode, column_map: column_map, row_map: row_map)
15
+ end
16
+ raw
17
+ end
18
+ end
19
+
20
+ def build_result_map(existing, indices, size)
21
+ return existing if indices == true
22
+ result_map = {}
23
+ indexify(indices, result_map, size)
24
+ result_map.default_proc = ->(h,k) do
25
+ existing_index = existing[k]
26
+ case existing_index
27
+ when TrueClass
28
+ existing_index
29
+ when Range
30
+ if existing_index.exclude_end?
31
+ h[k] = h[existing_index.first]...h[existing_index.end]
32
+ else
33
+ h[k] = h[existing_index.first]..h[existing_index.end]
34
+ end
35
+ when nil
36
+ raise "Couldn't find key #{k} in index mapping"
37
+ else
38
+ h[existing_index]
39
+ end
40
+ end
41
+ result_map
42
+ end
43
+
44
+ def indexify(indices, results, size, total=0)
45
+ Array(indices).each do |index|
46
+ case index
47
+ when TrueClass
48
+ (0...size).each do |i|
49
+ results[i] = i
50
+ end
51
+ when Fixnum
52
+ results[index] ||= total
53
+ total += 1
54
+ when Array
55
+ indexify(index, results, size, total)
56
+ when Range
57
+ inclusive = index.exclude_end? ? index.first..(index.end - 1) : index
58
+ flat_range = inclusive.end < inclusive.first ? [*inclusive.end..inclusive.first].reverse : [*inclusive]
59
+ flat_range.each do |elm|
60
+ indexify(elm, results, size, total)
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ def unmap_args(args)
67
+ if args.length == 1
68
+ if row_map
69
+ return [unmap_index(self.row_map, args[0]), true] rescue nil
70
+ end
71
+ if column_map
72
+ return [true, [unmap_index(self.column_map, args[0])]] rescue nil
73
+ end
74
+ return [args[0]]
75
+ else
76
+ [
77
+ self.row_map ? unmap_index(self.row_map, args[0]) : args[0],
78
+ Array(self.column_map ? unmap_index(self.column_map, args[1]) : args[1])
79
+ ]
80
+ end
81
+ end
82
+
83
+ def unmap_index(map, columns)
84
+ case columns
85
+ when TrueClass, FalseClass
86
+ columns
87
+ when Array
88
+ columns.map{|col| unmap_index(map, col)}.flatten
89
+ when Range
90
+ first = unmap_index(map, columns.first)
91
+ last = unmap_index(map, columns.end)
92
+ first = Range === first ? first.first : first
93
+ if columns.exclude_end?
94
+ last = Range === last ? last.first : last
95
+ first...last
96
+ else
97
+ last = Range === last ? last.end : last
98
+ first..last
99
+ end
100
+ else
101
+ index = map[columns]
102
+ raise "Value not present in index mapping: #{columns}" unless index
103
+ index
104
+ end
105
+ end
106
+ end
107
+ end
@@ -2,21 +2,25 @@ module RMatrix
2
2
  class Matrix
3
3
  require 'narray'
4
4
  require_relative 'typecode'
5
+
5
6
  include Enumerable
7
+ include Indices
6
8
 
7
- attr_accessor :invert_next_operation, :matrix, :narray, :typecode
9
+ attr_accessor :invert_next_operation, :narray, :typecode, :row_map, :column_map
10
+ attr_writer :matrix
8
11
 
9
- def initialize(source, typecode=Typecode::FLOAT)
12
+ def initialize(source, typecode=Typecode::SFLOAT, column_map: nil, row_map: nil)
10
13
  self.typecode = typecode
11
14
  self.narray = two_dimensional(source, typecode)
15
+ self.row_map, self.column_map = row_map, column_map
12
16
  end
13
17
 
14
18
  def matrix
15
- @matrix ||= NMatrix.refer(narray)
19
+ @matrix ||= narray.empty? ? narray : NMatrix.refer(narray)
16
20
  end
17
21
 
18
- def self.blank(rows, cols, typecode=Typecode::FLOAT)
19
- self.new(NArray.new(typecode, rows * cols).reshape(cols, rows), typecode)
22
+ def self.blank(rows: 1, columns: 1, typecode: Typecode::SFLOAT, initial: 0)
23
+ self.new(NArray.new(typecode, columns, rows), typecode) + initial
20
24
  end
21
25
 
22
26
  def _dump(level)
@@ -37,6 +41,24 @@ module RMatrix
37
41
  block_given? ? e.each(&block) : e
38
42
  end
39
43
 
44
+ def each_column(&block)
45
+ e = Enumerator.new do |enum|
46
+ (0...self.columns).each do |i|
47
+ enum << self.raw[true, i]
48
+ end
49
+ end
50
+ block_given? ? e.each(&block) : e
51
+ end
52
+
53
+ def each_row(&block)
54
+ e = Enumerator.new do |enum|
55
+ (0...self.rows).each do |i|
56
+ enum << self.raw[i, true]
57
+ end
58
+ end
59
+ block_given? ? e.each(&block) : e
60
+ end
61
+
40
62
  def mmap
41
63
  as_na = NArray.to_na(
42
64
  matrix.each.map do |elm|
@@ -46,13 +68,23 @@ module RMatrix
46
68
  Matrix.new(as_na.reshape(*shape), typecode)
47
69
  end
48
70
 
71
+ def mask
72
+ mmap do |elm|
73
+ (yield elm) ? 0 : elm
74
+ end
75
+ end
76
+
77
+ def abs
78
+ (self ** 2) ** 0.5
79
+ end
80
+
49
81
  def coerce(other)
50
82
  self.invert_next_operation = true
51
83
  [self, other]
52
84
  end
53
85
 
54
86
  def size
55
- self.shape.inject(:*)
87
+ self.shape.inject(:*).to_i
56
88
  end
57
89
 
58
90
  def rows
@@ -63,12 +95,68 @@ module RMatrix
63
95
  self.shape.first
64
96
  end
65
97
 
98
+ def diag(dim=0)
99
+ raise "Must be square matrix" unless self.shape[0] == self.shape[1]
100
+ Matrix.new((self.class.identity(self.shape[0]).mult self).sum(dim))
101
+ end
102
+
103
+ def self.identity(size)
104
+ blank = self.blank(rows: size, columns: size)
105
+ blank.diagonal(1)
106
+ end
107
+
66
108
  def sum_rows
67
- Matrix.new(sum(0), typecode)
109
+ sum(1)
68
110
  end
69
111
 
70
112
  def sum_columns
71
- Matrix.new(sum(1), typecode)
113
+ sum(0)
114
+ end
115
+
116
+ def concat(*others, rows: true)
117
+ others.map!{|o| Matrix === o ? o.narray : NArray.to_na(o)}
118
+
119
+ joined = case rows
120
+ when true
121
+ # raise "Rows must match #{self.rows}, #{others.map(&:rows)}" unless [self.rows, *others.map(&:shape).map(&:last)].uniq.count.one?
122
+ height = self.rows + others.map(&:shape).map(&:last).inject(:+)
123
+ width = others[0].shape.first
124
+ joined = ::NArray.new(typecode, width, height)
125
+ joined[true, 0...self.rows] = self.narray
126
+ current_row = self.rows
127
+ others.each do |slice|
128
+ slice_height = slice.shape[1]
129
+ joined[true, current_row...current_row+slice_height] = slice
130
+ current_row += slice_height
131
+ end
132
+ joined
133
+ else
134
+ width = self.columns + others.map(&:shape).map(&:first).inject(:+)
135
+ height = others[0].shape.last
136
+ joined = ::NArray.new(typecode, width, height)
137
+ joined[0...self.columns, true] = self.narray
138
+ current_col = self.columns
139
+ others.each do |slice|
140
+ slice_width = slice.shape[0]
141
+ joined[current_col...current_col+slice_width, true] = slice
142
+ current_col += slice_width
143
+ end
144
+ joined
145
+ # raise "Rows must match #{self.columns}, #{others.map(&:columns)}" unless [self.columns, *others.map(&:columns)].uniq.count.one?
146
+ end
147
+
148
+ Matrix.new(joined, typecode)
149
+ end
150
+
151
+ def join(other)
152
+ case true
153
+ when self.rows == 1 && other.rows == 1
154
+ Vector.new(NArray.to_na([self.narray,other.narray]).to_type(self.typecode).reshape(self.columns + other.columns, 1))
155
+ when self.columns == 1 && other.columns == 1
156
+ Vector.new(NArray.to_na([self.narray,other.narray]).to_type(self.typecode).reshape(1, self.rows + other.rows))
157
+ else
158
+ raise "Couldn't join mismatched dimensions"
159
+ end
72
160
  end
73
161
 
74
162
  def two_dimensional(source, type)
@@ -82,6 +170,12 @@ module RMatrix
82
170
  source = NArray.to_na([source])
83
171
  else
84
172
  source = NArray.to_na(source)
173
+ if type != RMatrix::Matrix::Typecode::OBJECT &&
174
+ source.typecode == RMatrix::Matrix::Typecode::OBJECT &&
175
+ RMatrix::Matrix === source[0]
176
+ source = NArray.to_na(source.map(&:to_a).to_a).to_type(typecode)
177
+ end
178
+ source
85
179
  end
86
180
 
87
181
  source = source.to_type(type) unless type == source.typecode
@@ -89,7 +183,7 @@ module RMatrix
89
183
  case source.dim
90
184
  when 1
91
185
  source.reshape(source.length, 1)
92
- when 2
186
+ when 2, 0
93
187
  source
94
188
  else
95
189
  raise "Source for matrix must be either one or two dimensional" unless source.shape[2..-1].all?{|x| x == 1}
@@ -116,11 +210,11 @@ module RMatrix
116
210
 
117
211
  def determinant
118
212
  raise "Cannot calculate determinant of non-square matrix" unless columns == rows
119
- return self[0, 0] * self[1, 1]- self[0, 1] * self[1, 0] if(self.columns == 2)
213
+ return self.raw[0, 0] * self.raw[1, 1]- self.raw[0, 1] * self.raw[1, 0] if(self.columns == 2)
120
214
  sign = 1
121
215
  det = 0
122
216
  self.columns.times do |i|
123
- det += sign * self[0,i] * self.minor(0, i).determinant
217
+ det += sign * self.raw[0,i] * self.minor(0, i).determinant
124
218
  sign *= -1
125
219
  end
126
220
  return det
@@ -159,9 +253,13 @@ module RMatrix
159
253
  mmap{|x| x.round(dp) }
160
254
  end
161
255
 
162
- def inspect
163
- return Vector::inspect_vector(self) if [rows, columns].include?(1)
256
+ def is_vector?
257
+ [rows, columns].include?(1)
258
+ end
164
259
 
260
+ def inspect
261
+ return 'M[Empty]' if empty?
262
+ return Vector::inspect_vector(self) if self.is_vector?
165
263
  inspect_rows = [10, self.rows].min
166
264
  inspect_columns = [10, self.columns].min
167
265
  more_rows = inspect_rows < self.rows
@@ -176,29 +274,28 @@ module RMatrix
176
274
  "#{rows} x #{columns} Matrix\nM#{box_lines(as_strings.map{|row| row.join(", ") }, more_columns)}"
177
275
  end
178
276
 
179
-
180
- def [](*args)
181
- args.all?{|x| Fixnum === x } ? narray[*args.reverse] : Matrix.new(narray[*args.reverse], typecode)
182
- end
183
-
184
277
  def transpose
185
278
  Matrix.new(self.matrix.transpose, typecode)
186
279
  end
187
280
 
188
- def self.[](*inputs)
281
+ def self.[](*inputs, typecode: Typecode::SFLOAT, row_map: nil, column_map: nil)
189
282
  if inputs.length == 1 && Matrix === inputs[0]
190
283
  inputs[0]
191
284
  elsif inputs.length == 1 && [String, Symbol].include?(inputs[0].class)
192
285
  if ['byte', 'sint', 'int', 'sfloat', 'float', 'scomplex', 'complex', 'object'].include?(inputs[0])
193
- ->(*source){ Matrix.new(source, inputs[0]) }
286
+ ->(*source){ Matrix.new(source, inputs[0], row_map: row_map, column_map: column_map)}
194
287
  else
195
- Matrix.new(inputs[0])
288
+ Matrix.new(inputs[0], typecode, row_map: row_map, column_map: column_map)
196
289
  end
197
290
  else
198
- Matrix.new(inputs)
291
+ Matrix.new(inputs, typecode, row_map: row_map, column_map: column_map)
199
292
  end
200
293
  end
201
294
 
295
+ def zip(*others)
296
+ Matrix.new(super(*others), self.typecode)
297
+ end
298
+
202
299
  def self.gen_mutator(name)
203
300
  define_method(name) do |*args, &blk|
204
301
  matrix.send(name, *args, &blk)
@@ -212,9 +309,22 @@ module RMatrix
212
309
  end
213
310
  end
214
311
 
312
+ def sum(dim=nil)
313
+ case dim
314
+ when nil then
315
+ res = self.narray.sum
316
+ NArray === res ? Matrix.new(0, typecode)[0] : res
317
+ else Matrix.new(self.matrix.sum(dim), typecode)
318
+ end
319
+ end
320
+
215
321
  def self.gen_delegator(name)
216
322
  define_method(name) do |*args, &blk|
217
- matrix.send(name, *args, &blk)
323
+ result = matrix.send(name, *args, &blk)
324
+ case result
325
+ when NArray then Matrix.new(result, typecode)
326
+ else result
327
+ end
218
328
  end
219
329
  end
220
330
 
@@ -234,13 +344,12 @@ module RMatrix
234
344
  end
235
345
 
236
346
  [:fill!, :random!, :indgen!].each(&method(:gen_mutator))
237
- [:reshape, :sort, :sort_index, :inverse, :lu, :delete_at, :where, :where2, :not, :-@].each(&method(:gen_matrix_delegator))
238
- [:sum, :prod, :mean, :stddev, :rms, :rmsdev, :min, :max, :shape, :to_a].each(&method(:gen_delegator))
347
+ [:reshape, :sort, :sort_index, :inverse, :lu, :delete_at, :where, :where2, :not, :-@, :reverse, :diagonal].each(&method(:gen_matrix_delegator))
348
+ [:prod, :min, :max, :stddev, :mean, :rms, :rmsdev, :shape, :empty?].each(&method(:gen_delegator))
239
349
  [:byte, :sint, :int, :sfloat, :float, :scomplex, :complex, :object].each(&method(:gen_typeconstructor))
240
-
241
- [:+, :/, :-, :**, :&, :^, :|].each do |op|
242
- op = translate_op(op)
243
- define_method(op) do |other|
350
+ [:+, :/, :-, :**, :&, :^, :|].each do |_op|
351
+ op = translate_op(_op)
352
+ define_method(_op) do |other|
244
353
  result = case other
245
354
  when Matrix
246
355
  apply_elementwise(op, other)
@@ -253,10 +362,23 @@ module RMatrix
253
362
  end
254
363
  end
255
364
 
365
+ def to_a
366
+ return narray.reshape(narray.length).to_a if is_vector?
367
+ return narray.to_a
368
+ end
369
+
256
370
  def self.seed(seed)
257
371
  NArray.srand(seed)
258
372
  end
259
373
 
374
+ def to_m
375
+ self
376
+ end
377
+
378
+ alias_method :cols, :columns
379
+ alias_method :avg, :mean
380
+ alias_method :length, :size
381
+
260
382
  private
261
383
  def test_inverse
262
384
  if self.invert_next_operation
@@ -287,6 +409,7 @@ module RMatrix
287
409
  end
288
410
  end
289
411
 
412
+
290
413
  end
291
414
 
292
415
  class ::NArray; include Enumerable; end
@@ -14,7 +14,7 @@ module RMatrix
14
14
  def self.[](*inputs)
15
15
  if inputs.length == 1 && [String, Symbol].include?(inputs[0].class)
16
16
  if ['byte', 'sint', 'int', 'sfloat', 'float', 'scomplex', 'complex', 'object'].include?(inputs[0].to_s)
17
- ->(*source){ Matrix.new(source, typecode: inputs[0].to_s) }
17
+ ->(*source){ Matrix.new(source, inputs[0].to_s) }
18
18
  else
19
19
  Vector.new(inputs[0])
20
20
  end
@@ -1,3 +1,3 @@
1
1
  module Rmatrix
2
- VERSION = "0.1.3"
2
+ VERSION = "0.1.7"
3
3
  end
data/rmatrix.gemspec CHANGED
@@ -35,4 +35,5 @@ Gem::Specification.new do |spec|
35
35
  spec.add_development_dependency "pry"
36
36
  spec.add_development_dependency "pry-byebug"
37
37
  spec.add_runtime_dependency "narray"
38
+ spec.add_runtime_dependency "opencl_ruby_ffi"
38
39
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rmatrix
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wouter Coppieters
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-04-11 00:00:00.000000000 Z
11
+ date: 2016-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: opencl_ruby_ffi
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
97
111
  description: |-
98
112
  RMatrix is a lightning fast library for Ruby. It provides numerous enhancements over the Matrix class in the standard library.
99
113
  Features include the ability to calculate Matrix inverse, transpose, determinant, minor, adjoint, cofactor_matrix, hadamard product and other elementwise operations, slicing, masking and more.
@@ -114,6 +128,7 @@ files:
114
128
  - bin/setup
115
129
  - lib/rmatrix.rb
116
130
  - lib/rmatrix/core.rb
131
+ - lib/rmatrix/indices.rb
117
132
  - lib/rmatrix/matrix.rb
118
133
  - lib/rmatrix/shortcuts.rb
119
134
  - lib/rmatrix/typecode.rb
@@ -141,9 +156,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
156
  version: '0'
142
157
  requirements: []
143
158
  rubyforge_project:
144
- rubygems_version: 2.4.5
159
+ rubygems_version: 2.5.1
145
160
  signing_key:
146
161
  specification_version: 4
147
162
  summary: Fast matrix/linear algebra library for Ruby
148
163
  test_files: []
149
- has_rdoc: