Class: RMatrix::Matrix

Inherits:
Object
  • Object
show all
Includes:
Enumerable, Indices
Defined in:
lib/rmatrix/matrix.rb,
lib/rmatrix/typecode.rb,
lib/rmatrix/shortcuts.rb

Direct Known Subclasses

Vector

Defined Under Namespace

Modules: Typecode

Constant Summary collapse

OPERATIONS_MAP =
{
  :& => 'and',
  :^ => 'xor',
  :| => 'or'
}

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Indices

#[], #[]=, #build_result_map, #indexify, #method_missing, #raw, #unmap_args, #unmap_index, #walk_indices

Constructor Details

#initialize(source, typecode = Typecode::FLOAT, column_map: nil, row_map: nil, column_label_map: nil, row_label_map: nil) ⇒ Matrix

Returns a new instance of Matrix.



17
18
19
20
21
22
# File 'lib/rmatrix/matrix.rb', line 17

def initialize(source, typecode=Typecode::FLOAT, column_map: nil, row_map: nil, column_label_map: nil, row_label_map: nil)
  self.typecode    = typecode
  self.narray      = two_dimensional(source, typecode)
  self.row_map     = row_map
  self.column_map  = column_map
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class RMatrix::Indices

Class Attribute Details

.named_inspectObject

Returns the value of attribute named_inspect.



5
6
7
# File 'lib/rmatrix/matrix.rb', line 5

def named_inspect
  @named_inspect
end

Instance Attribute Details

#column_label_mapObject

Returns the value of attribute column_label_map.



14
15
16
# File 'lib/rmatrix/matrix.rb', line 14

def column_label_map
  @column_label_map
end

#column_mapObject

Returns the value of attribute column_map.



14
15
16
# File 'lib/rmatrix/matrix.rb', line 14

def column_map
  @column_map
end

#invert_next_operationObject

Returns the value of attribute invert_next_operation.



14
15
16
# File 'lib/rmatrix/matrix.rb', line 14

def invert_next_operation
  @invert_next_operation
end

#matrixObject



43
44
45
# File 'lib/rmatrix/matrix.rb', line 43

def matrix
  @matrix ||= narray.empty? ? narray : NMatrix.refer(narray)
end

#narrayObject

Returns the value of attribute narray.



14
15
16
# File 'lib/rmatrix/matrix.rb', line 14

def narray
  @narray
end

#row_label_mapObject

Returns the value of attribute row_label_map.



14
15
16
# File 'lib/rmatrix/matrix.rb', line 14

def row_label_map
  @row_label_map
end

#row_mapObject

Returns the value of attribute row_map.



14
15
16
# File 'lib/rmatrix/matrix.rb', line 14

def row_map
  @row_map
end

#typecodeObject

Returns the value of attribute typecode.



14
15
16
# File 'lib/rmatrix/matrix.rb', line 14

def typecode
  @typecode
end

Class Method Details

.[](*inputs, typecode: Typecode::FLOAT, row_map: nil, column_map: nil, column_label_map: nil, row_label_map: nil) ⇒ Object



395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/rmatrix/matrix.rb', line 395

def self.[](*inputs, typecode: Typecode::FLOAT, row_map: nil, column_map: nil, column_label_map: nil, row_label_map: nil)
  if inputs.length == 1 && Matrix === inputs[0]
    inputs[0]
  elsif inputs.length == 1 && [String, Symbol].include?(inputs[0].class)
    if ['byte', 'sint', 'int', 'sfloat', 'float', 'scomplex', 'complex', 'object'].include?(inputs[0])
      ->(*source){ Matrix.new(source, inputs[0], row_map: row_map, column_map: column_map, row_label_map: row_label_map, column_label_map: column_label_map)}
    else
      Matrix.new(inputs[0], typecode, row_map: row_map, column_map: column_map, row_label_map: row_label_map, column_label_map: column_label_map)
    end
  else
    Matrix.new(inputs, typecode, row_map: row_map, column_map: column_map, row_label_map: row_label_map, column_label_map: column_label_map)
  end
end

._load(arg) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/rmatrix/matrix.rb', line 76

def self._load arg
  split_index, buffer, index = 0, '', arg.length - 1
  split = Array.new(3)
  while split_index < 3
    case char = arg[index]
    when ':'
      split[split_index] = buffer.reverse.to_i
      split_index += 1
      buffer = ''
    else buffer << char
    end
    index -= 1
  end
  arg[index+1..-1] = ''
  self.new(NArray.to_na(arg, split[0]).reshape(split[2], split[1]), split[0])
end

.blank(rows: 1, columns: 1, typecode: Typecode::FLOAT, initial: 0, column_map: nil, row_map: nil) ⇒ Object



47
48
49
50
51
# File 'lib/rmatrix/matrix.rb', line 47

def self.blank(rows: 1, columns: 1, typecode: Typecode::FLOAT, initial: 0, column_map: nil, row_map: nil)
  source = self.new(NArray.new(typecode, columns, rows), typecode, column_map: column_map, row_map: row_map)
  source.narray[]= initial unless source.empty?
  source
end

.gen_delegator(name) ⇒ Object



439
440
441
442
443
444
445
446
447
# File 'lib/rmatrix/matrix.rb', line 439

def self.gen_delegator(name)
  define_method(name) do |*args, &blk|
    result = matrix.send(name, *args, &blk)
    case result
    when NArray then Matrix.new(result, typecode)
    else result
    end
  end
end

.gen_matrix_delegator(name) ⇒ Object



420
421
422
423
424
# File 'lib/rmatrix/matrix.rb', line 420

def self.gen_matrix_delegator(name)
  define_method(name) do |*args, &blk|
    Matrix.new(matrix.send(name, *args, &blk), typecode)
  end
end

.gen_mutator(name) ⇒ Object



413
414
415
416
417
418
# File 'lib/rmatrix/matrix.rb', line 413

def self.gen_mutator(name)
  define_method(name) do |*args, &blk|
    matrix.send(name, *args, &blk)
    self
  end
end

.gen_typeconstructor(name) ⇒ Object



449
450
451
452
453
# File 'lib/rmatrix/matrix.rb', line 449

def self.gen_typeconstructor(name)
  define_singleton_method(name) do
    ->(*source){ Matrix.new(source, name.to_s) }
  end
end

.identity(size) ⇒ Object



161
162
163
164
# File 'lib/rmatrix/matrix.rb', line 161

def self.identity(size)
  blank = self.blank(rows: size, columns: size)
  blank.diagonal(1)
end

.ones(rows: 1, columns: 1) ⇒ Object



166
167
168
# File 'lib/rmatrix/matrix.rb', line 166

def self.ones(rows: 1, columns: 1)
  self.blank(rows: rows, columns: columns, initial: 1)
end

.seed(seed) ⇒ Object



488
489
490
# File 'lib/rmatrix/matrix.rb', line 488

def self.seed(seed)
  NArray.srand(seed)
end

.translate_op(op) ⇒ Object



460
461
462
# File 'lib/rmatrix/matrix.rb', line 460

def self.translate_op(op)
  OPERATIONS_MAP.fetch(op, op)
end

Instance Method Details

#*(other) ⇒ Object



289
290
291
292
293
294
295
296
# File 'lib/rmatrix/matrix.rb', line 289

def *(other)
  if other.kind_of?(Matrix)
    raise "Matrix A columns(#{self.columns}) != Matrix B rows(#{other.columns})" if other.rows != self.columns
    Matrix.new(self.matrix * other.matrix, typecode)
  else
    Matrix.new(apply_scalar(:*, other), typecode)
  end
end

#==(other) ⇒ Object



302
303
304
# File 'lib/rmatrix/matrix.rb', line 302

def ==(other)
  self.narray == Matrix[other].narray
end

#_dump(level) ⇒ Object



57
58
59
# File 'lib/rmatrix/matrix.rb', line 57

def _dump(level)
  narray.to_s << ':' << columns.to_s << ':' << rows.to_s << ':' << narray.typecode.to_s
end

#absObject



135
136
137
# File 'lib/rmatrix/matrix.rb', line 135

def abs
  (self ** 2) ** 0.5
end

#adjointObject Also known as: A



285
286
287
# File 'lib/rmatrix/matrix.rb', line 285

def adjoint
  self.cofactor_matrix.transpose
end

#box_lines(lines, add_dots) ⇒ Object



310
311
312
# File 'lib/rmatrix/matrix.rb', line 310

def box_lines(lines, add_dots)
  "[#{lines.map{|l| "[#{l}#{add_dots ? ' ...' : '' }]"}.join(",\n  ")}]"
end

#coerce(other) ⇒ Object



139
140
141
142
# File 'lib/rmatrix/matrix.rb', line 139

def coerce(other)
  self.invert_next_operation = true
  [self, other]
end

#cofactor_matrix(*args) ⇒ Object Also known as: C



260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/rmatrix/matrix.rb', line 260

def cofactor_matrix(*args)
  return cofactor(*args) if args.length == 2

  result = []
  rows.times do |i|
    result << []
    columns.times do |j|
      result[i] << cofactor(i, j)
    end
  end
  return Matrix.new(result, typecode)
end

#columnsObject Also known as: cols



152
153
154
# File 'lib/rmatrix/matrix.rb', line 152

def columns
  self.shape.first
end

#concat(*others, rows: true) ⇒ Object



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/rmatrix/matrix.rb', line 178

def concat(*others, rows: true)
  others.map!{|o| Matrix === o ? o.narray : NArray.to_na(o)}

  joined = case rows
  when true
    # raise "Rows must match #{self.rows}, #{others.map(&:rows)}" unless [self.rows, *others.map(&:shape).map(&:last)].uniq.count.one?
    height = self.rows + others.map(&:shape).map(&:last).inject(:+)
    width  = others[0].shape.first
    joined = ::NArray.new(typecode, width, height)
    joined[true, 0...self.rows] = self.narray
    current_row = self.rows
    others.each do |slice|
      slice_height = slice.shape[1]
      joined[true, current_row...current_row+slice_height] = slice
      current_row += slice_height
    end
    joined
  else
    width  = self.columns + others.map(&:shape).map(&:first).inject(:+)
    height = others[0].shape.last
    joined = ::NArray.new(typecode, width, height)
    joined[0...self.columns, true] = self.narray
    current_col = self.columns
    others.each do |slice|
      slice_width = slice.shape[0]
      joined[current_col...current_col+slice_width, true] = slice
      current_col += slice_width
    end
    joined
    # raise "Rows must match #{self.columns}, #{others.map(&:columns)}" unless [self.columns, *others.map(&:columns)].uniq.count.one?
  end

  Matrix.new(joined, typecode)
end

#condensed(sz = 10, sig = 6, vdots = '\\vdots', cdots = '\\cdots', ddots = '\\ddots') ⇒ Object



363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
# File 'lib/rmatrix/matrix.rb', line 363

def condensed(sz=10, sig=6, vdots='\\vdots', cdots='\\cdots', ddots='\\ddots')
  width  = [sz, self.cols].min
  height = [sz, self.rows].min
  insert_cdots = self.cols > sz
  insert_vdots = self.rows > sz

  width  += 1 if insert_cdots
  height += 1 if insert_vdots

  blank = M.blank(rows: height, columns: width, typecode: Typecode::OBJECT)
  blank.narray[0...width, 0...height] = self.narray[0...width, 0...height]

  blank.narray[0...width, -1] = self.narray[0...width, -1]
  blank.narray[-1,0...height] = self.narray[-1, 0...height]

  blank.narray[0...width, -2] = vdots if insert_vdots
  blank.narray[-2, 0...height] = cdots if insert_cdots

  if insert_cdots && insert_vdots
    blank.narray[-2, -2] = ddots
    blank.narray[-1, -2] = vdots
    blank.narray[-2, -1] = cdots
    blank.narray[-1, -1] = self.narray[-1, -1]
  end

  blank.narray.to_a.map{|line| (sig ? Array(line).map{|v| Numeric === v ? to_significant_figures(v,sig) : v } : Array(line))}
end

#determinantObject Also known as: D



273
274
275
276
277
278
279
280
281
282
283
# File 'lib/rmatrix/matrix.rb', line 273

def determinant
  raise "Cannot calculate determinant of non-square matrix" unless columns == rows
  return self.raw[0, 0] * self.raw[1, 1]- self.raw[0, 1] * self.raw[1, 0] if(self.columns == 2)
  sign = 1
  det = 0
  self.columns.times do |i|
    det += sign * self.raw[0,i] * self.minor(0, i).determinant
    sign *= -1
  end
  return det
end

#diag(dim = 0) ⇒ Object



156
157
158
159
# File 'lib/rmatrix/matrix.rb', line 156

def diag(dim=0)
  raise "Must be square matrix" unless self.shape[0] == self.shape[1]
  Matrix.new((self.class.identity(self.shape[0]).mult self).sum(dim))
end

#each(&block) ⇒ Object



93
94
95
96
97
98
99
100
# File 'lib/rmatrix/matrix.rb', line 93

def each(&block)
  e = Enumerator.new do |enum|
    matrix.each do |elm|
      enum << elm
    end
  end
  block_given? ? e.each(&block) : e
end

#each_column(&block) ⇒ Object



102
103
104
105
106
107
108
109
# File 'lib/rmatrix/matrix.rb', line 102

def each_column(&block)
  e = Enumerator.new do |enum|
    (0...self.columns).each  do |i|
      enum << self.raw[true, i]
    end
  end
  block_given? ? e.each(&block) : e
end

#each_row(&block) ⇒ Object



111
112
113
114
115
116
117
118
# File 'lib/rmatrix/matrix.rb', line 111

def each_row(&block)
 e = Enumerator.new do |enum|
    (0...self.rows).each  do |i|
      enum << self.raw[i, true]
    end
  end
  block_given? ? e.each(&block) : e
end

#inspect(sz: 10, sig: 6, names: RMatrix::Matrix.named_inspect) ⇒ Object



326
327
328
329
330
331
332
# File 'lib/rmatrix/matrix.rb', line 326

def inspect(sz: 10, sig: 6, names: RMatrix::Matrix.named_inspect)
  desc = case
  when self.is_vector? then "Vector(#{self.length})"
  else "Matrix(#{rows} x #{columns})"
  end
  "#{desc}\n#{RMatrix::MatrixTable.new(self).to_s}"
end

#is_vector?Boolean

Returns:

  • (Boolean)


318
319
320
# File 'lib/rmatrix/matrix.rb', line 318

def is_vector?
  [rows, columns].include?(1)
end

#join(other) ⇒ Object



213
214
215
216
217
218
219
220
221
222
# File 'lib/rmatrix/matrix.rb', line 213

def join(other)
  case true
  when self.rows == 1 && other.rows == 1
    Vector.new(NArray.to_na([self.narray,other.narray]).to_type(self.typecode).reshape(self.columns + other.columns, 1))
  when self.columns == 1 && other.columns == 1
    Vector.new(NArray.to_na([self.narray,other.narray]).to_type(self.typecode).reshape(1, self.rows + other.rows))
  else
    raise "Couldn't join mismatched dimensions"
  end
end

#maskObject



129
130
131
132
133
# File 'lib/rmatrix/matrix.rb', line 129

def mask
  mmap do |elm|
    (yield elm) ? 0 : elm
  end
end

#minor(x, y) ⇒ Object Also known as: M



256
257
258
# File 'lib/rmatrix/matrix.rb', line 256

def minor(x,y)
  return self.delete_at(y,x)
end

#mmapObject



120
121
122
123
124
125
126
127
# File 'lib/rmatrix/matrix.rb', line 120

def mmap
  as_na = NArray.to_na(
    matrix.each.map do |elm|
      yield elm
    end
  ).to_type(typecode)
  Matrix.new(as_na.reshape(*shape), typecode)
end

#mult(other) ⇒ Object



298
299
300
# File 'lib/rmatrix/matrix.rb', line 298

def mult(other)
  Matrix.new(self.narray * other.narray, typecode)
end

#parse_map(map, invert: false) ⇒ Object



34
35
36
37
38
39
40
41
# File 'lib/rmatrix/matrix.rb', line 34

def parse_map(map, invert: false)
  case map
  when nil then map
  when Array then invert ? map.each.with_index.map.to_h.invert : map.each.with_index.map.to_h
  when Hash then map
  else raise 'Invalid map type encountered'
  end
end

#round(dp) ⇒ Object



314
315
316
# File 'lib/rmatrix/matrix.rb', line 314

def round(dp)
  mmap{|x| x.round(dp) }
end

#rowsObject



148
149
150
# File 'lib/rmatrix/matrix.rb', line 148

def rows
  self.shape.last
end

#set_all(value) ⇒ Object



53
54
55
# File 'lib/rmatrix/matrix.rb', line 53

def set_all(value)
  narray[]=(value)
end

#sizeObject Also known as: length



144
145
146
# File 'lib/rmatrix/matrix.rb', line 144

def size
  self.shape.inject(:*).to_i
end

#sum(dim = nil) ⇒ Object



426
427
428
429
430
431
432
433
# File 'lib/rmatrix/matrix.rb', line 426

def sum(dim=nil)
  case dim
  when nil then
    res = self.narray.sum
    NArray === res ? Matrix.new(0, typecode)[0] : res
  else Matrix.new(self.matrix.sum(dim), typecode)
  end
end

#sum_columnsObject



174
175
176
# File 'lib/rmatrix/matrix.rb', line 174

def sum_columns
  sum(0)
end

#sum_rowsObject



170
171
172
# File 'lib/rmatrix/matrix.rb', line 170

def sum_rows
  sum(1)
end

#to_aObject



483
484
485
486
# File 'lib/rmatrix/matrix.rb', line 483

def to_a
  return narray.reshape(narray.length).to_a if is_vector?
  return narray.to_a
end

#to_fObject



61
62
63
64
65
66
67
# File 'lib/rmatrix/matrix.rb', line 61

def to_f
  if length === 1
    self[0].to_f
  else
    raise "Can only call to_f on vectors of length 1"
  end
end

#to_iObject



69
70
71
72
73
74
75
# File 'lib/rmatrix/matrix.rb', line 69

def to_i
  if length === 1
    self[0].to_i
  else
    raise "Can only call to_i on vectors of length 1"
  end
end

#to_mObject



492
493
494
# File 'lib/rmatrix/matrix.rb', line 492

def to_m
  self
end

#to_sObject



306
307
308
# File 'lib/rmatrix/matrix.rb', line 306

def to_s
  inspect
end

#to_significant_figures(x, p) ⇒ Object



322
323
324
# File 'lib/rmatrix/matrix.rb', line 322

def to_significant_figures(x, p)
  ("%-.#{p}e" % x).gsub(/0+e/,'e').gsub('.e+00','').gsub('e+00','')
end

#to_tex(sz = 10, sig = 6) ⇒ Object



334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/rmatrix/matrix.rb', line 334

def to_tex(sz = 10, sig=6)
  values = condensed(sz, sig)
  column_headers = column_label_map ? values[0].map.with_index do |v, i|
    case v
    when '\\cdots' then '\\cdots'
    else (column_label_map && column_label_map[i]) || i
    end
  end : []

  row_headers = row_label_map ? values.map.with_index do |v, i|
    case v[0]
    when '\\vdots' then '\\vdots'
    else (row_label_map && row_label_map[i]) || i
    end
  end : []

  <<-TEX
$
\\begin{array}{c} &
\\begin{array}{c} #{column_headers.join(" & ")} \\end{array}\\\\
\\begin{array}{c} #{row_headers.join(" \\\\ ")} \\end{array} &
  \\left(\\begin{array}{ccc}
#{values.map{|line| line.join(" & ")}.join(" \\\\ ")}
  \\end{array}\\right)
\\end{array}
$
TEX
end

#to_type(type) ⇒ Object



435
436
437
# File 'lib/rmatrix/matrix.rb', line 435

def to_type(type)
  Matrix.new(narray.to_type(type), type)
end

#transposeObject Also known as: T



391
392
393
# File 'lib/rmatrix/matrix.rb', line 391

def transpose()
  Matrix.new(self.matrix.transpose, typecode, column_map: self.row_map, row_map: self.column_map, column_label_map: self.row_label_map, row_label_map: self.column_label_map)
end

#two_dimensional(source, type) ⇒ Object



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/rmatrix/matrix.rb', line 224

def two_dimensional(source, type)
  case source
  when NArray
    if NMatrix === source
      @matrix = source
      source = NArray.refer(source)
    end
  when Numeric
    source = NArray.to_na([source])
  else
    source = NArray.to_na(source)
    if type != RMatrix::Matrix::Typecode::OBJECT &&
      source.typecode == RMatrix::Matrix::Typecode::OBJECT &&
      RMatrix::Matrix === source[0]
      source = NArray.to_na(source.map(&:to_a).to_a).to_type(typecode)
    end
    source
  end

  source = source.to_type(type) unless type == source.typecode

  case source.dim
  when 1
    source.reshape(source.length, 1)
  when 2, 0
    source
  else
    raise "Source for matrix must be either one or two dimensional" unless source.shape[2..-1].all?{|x| x == 1}
    source.reshape(source.shape[0], source.shape[1])
  end
end

#zip(*others) ⇒ Object



409
410
411
# File 'lib/rmatrix/matrix.rb', line 409

def zip(*others)
  Matrix.new(super(*others), self.typecode)
end