rmatrix 0.1.3 → 0.1.7
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 +4 -4
- data/lib/rmatrix.rb +1 -0
- data/lib/rmatrix/indices.rb +107 -0
- data/lib/rmatrix/matrix.rb +152 -29
- data/lib/rmatrix/vector.rb +1 -1
- data/lib/rmatrix/version.rb +1 -1
- data/rmatrix.gemspec +1 -0
- metadata +18 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ef041f60282c09c58765f159e598b55a3cc7cd4
|
4
|
+
data.tar.gz: 624800e2934fe3858adfbbd62e6455574bce512b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e8bf852ff5b4c8cad5e541b68604784a401ab140c01654f8bd442b4899c5b047de3d55dca050ab0e8452488f2fe56bd22cb0f40f7afa6620eb7277975ffffd2
|
7
|
+
data.tar.gz: 35fd9c5279d0b45c7a651c11ea06c9b42f26035697e0093a1b6cc32f95bb6dbb2a3b79ce6fd1f111d1fe0e0c6d3b0c5b316abba785500f298a8b42effdc030a4
|
data/lib/rmatrix.rb
CHANGED
@@ -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
|
data/lib/rmatrix/matrix.rb
CHANGED
@@ -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, :
|
9
|
+
attr_accessor :invert_next_operation, :narray, :typecode, :row_map, :column_map
|
10
|
+
attr_writer :matrix
|
8
11
|
|
9
|
-
def initialize(source, typecode=Typecode::
|
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,
|
19
|
-
self.new(NArray.new(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
|
-
|
109
|
+
sum(1)
|
68
110
|
end
|
69
111
|
|
70
112
|
def sum_columns
|
71
|
-
|
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
|
163
|
-
|
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,
|
238
|
-
[:
|
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
|
-
|
242
|
-
|
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
|
data/lib/rmatrix/vector.rb
CHANGED
@@ -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,
|
17
|
+
->(*source){ Matrix.new(source, inputs[0].to_s) }
|
18
18
|
else
|
19
19
|
Vector.new(inputs[0])
|
20
20
|
end
|
data/lib/rmatrix/version.rb
CHANGED
data/rmatrix.gemspec
CHANGED
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.
|
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-
|
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.
|
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:
|