rmatrix 0.1.9 → 0.1.10

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: 40e94bbffce59e661eb2dbfc65f0daaa48302d41
4
- data.tar.gz: a03be4886d3db4e977d07598b12ec85faad3bced
3
+ metadata.gz: 66edec4fe60372252f4542b6c2adf5e7ff88349e
4
+ data.tar.gz: 363d633958bce93fb9ae62de0aa8d12ba1c441bc
5
5
  SHA512:
6
- metadata.gz: 2ea1f2a0d881e522a5d700517887d521405a0cfac196814b308374567e6212d8cb8304d50995cd95da168e7ae526a2bc2e70123a28f2237fc000dd0863313de3
7
- data.tar.gz: dad148225008312cb74539bfbdd57567b1529ea88b2bdd47f805bec5714e51d0ee841ae0c25290ecf15dd3a751d6bfc60a905469ee4d2c51ccafc89abe3d7dac
6
+ metadata.gz: cadab96ed60b3bc2912abe69502ef76ed8739a15834fe32938188b1a011e1906b2e10334f7d79853761263f494e842390bf4a28af7e47cafa35392fc330d8c36
7
+ data.tar.gz: 7d6ba92b3df5794e9a315d788b994b454825d54745dd93c62c987a2547d1288976be17ab63ebb3a73b871da0ca4b5a1d22e2e52f04152c802a2728b11bb008c5
data/lib/rmatrix/core.rb CHANGED
@@ -1,2 +1,3 @@
1
1
  require_relative "matrix"
2
2
  require_relative "vector"
3
+ require_relative "print_table"
@@ -1,17 +1,74 @@
1
1
  module RMatrix
2
2
  module Indices
3
+
4
+ def []=(*args, value)
5
+ indices = unmap_args(args)
6
+ raw[*indices] = value
7
+ end
8
+
3
9
  def [](*args)
4
10
  indices = unmap_args(args)
5
11
  result_row_map = build_result_map(self.row_map, indices.first, self.rows) if self.row_map
6
12
  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]
13
+
14
+ row_indices, column_indices = indices
15
+
16
+ result_column_label_map = nil
17
+ result_row_label_map = nil
18
+
19
+ if row_label_map
20
+ case row_indices
21
+ when true then result_row_label_map = row_label_map
22
+ else
23
+ result_row_label_map = walk_indices(row_indices, row_label_map).each_slice(2).to_h
24
+ end
25
+ end
26
+
27
+ if column_label_map
28
+ case column_indices
29
+ when true then result_column_label_map = column_label_map
30
+ else
31
+ result_column_label_map = walk_indices(column_indices, column_label_map).each_slice(2).to_h
32
+ end
33
+ end
34
+
35
+ raw[*indices, column_map: result_column_map, column_label_map: result_column_label_map, row_map: result_row_map, row_label_map: result_row_label_map]
36
+ end
37
+
38
+ def method_missing(name, *args, &block)
39
+ if row_map && row_map.include?(name)
40
+ self[name, true]
41
+ elsif column_map && column_map.include?(name)
42
+ self[true, name]
43
+ else
44
+ super
45
+ end
46
+ end
47
+
48
+ def walk_indices(indices, parent, i={index: 0})
49
+ Array(indices).flat_map do |index|
50
+ res = case index
51
+ when Array, Range then walk_indices(index.to_a, parent, i)
52
+ else [i[:index], parent[index]]
53
+ end
54
+ i[:index] += 1
55
+ res
56
+ end
8
57
  end
9
58
 
10
59
  def raw
11
60
  @raw ||= begin
12
61
  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)
62
+ def raw.[](*args, column_map: nil, row_map: nil, row_label_map: nil, column_label_map: nil)
63
+ begin
64
+ args.all?{|x| Fixnum === x } ? narray[*args.reverse] : Matrix.new(narray[*args.reverse], typecode, column_map: column_map, row_map: row_map, row_label_map: row_label_map, column_label_map: column_label_map)
65
+ rescue StandardError => e
66
+ raise IndexError.new("Error accessing index at #{args}. Shape is #{narray.shape.reverse}")
67
+ end
68
+ end
69
+
70
+ def raw.[]=(*args, value)
71
+ narray[*args.reverse] = value
15
72
  end
16
73
  raw
17
74
  end
@@ -66,16 +123,19 @@ module RMatrix
66
123
  def unmap_args(args)
67
124
  if args.length == 1
68
125
  if row_map
69
- return [unmap_index(self.row_map, args[0]), true] rescue nil
126
+ return [Array(unmap_index(self.row_map, args[0])), true] rescue nil
70
127
  end
71
128
  if column_map
72
- return [true, [unmap_index(self.column_map, args[0])]] rescue nil
129
+ return [true, Array(unmap_index(self.column_map, args[0]))] rescue nil
73
130
  end
74
131
  return [args[0]]
75
132
  else
133
+ row_index = self.row_map ? unmap_index(self.row_map, args[0]) : args[0]
134
+ column_index = self.column_map ? unmap_index(self.column_map, args[1]) : args[1]
135
+ column_index = [column_index] if column_index.kind_of?(Fixnum)
76
136
  [
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])
137
+ row_index,
138
+ column_index
79
139
  ]
80
140
  end
81
141
  end
@@ -98,7 +158,8 @@ module RMatrix
98
158
  first..last
99
159
  end
100
160
  else
101
- index = map[columns]
161
+ index = (map[columns] rescue nil)
162
+ index = columns if !index && columns.kind_of?(Fixnum)
102
163
  raise "Value not present in index mapping: #{columns}" unless index
103
164
  index
104
165
  end
@@ -1,26 +1,51 @@
1
1
  module RMatrix
2
2
  class Matrix
3
+
4
+ class << self
5
+ attr_accessor :named_inspect
6
+ end
7
+
3
8
  require 'narray'
4
9
  require_relative 'typecode'
5
10
 
6
11
  include Enumerable
7
12
  include Indices
8
13
 
9
- attr_accessor :invert_next_operation, :narray, :typecode, :row_map, :column_map
14
+ attr_accessor :invert_next_operation, :narray, :typecode, :row_map, :column_map, :row_label_map, :column_label_map
10
15
  attr_writer :matrix
11
16
 
12
- def initialize(source, typecode=Typecode::FLOAT, column_map: nil, row_map: nil)
13
- self.typecode = typecode
14
- self.narray = two_dimensional(source, typecode)
15
- self.row_map, self.column_map = row_map, column_map
17
+ def initialize(source, typecode=Typecode::FLOAT, column_map: nil, row_map: nil, column_label_map: nil, row_label_map: nil)
18
+ self.typecode = typecode
19
+ self.narray = two_dimensional(source, typecode)
20
+ self.row_map = row_map
21
+ self.column_map = column_map
22
+ end
23
+
24
+ def row_map=(row_map)
25
+ @row_map = parse_map(row_map)
26
+ @row_label_map = @row_map.invert unless !@row_map || @row_map.default_proc
27
+ end
28
+
29
+ def column_map=(column_map)
30
+ @column_map = parse_map(column_map)
31
+ @column_label_map = @column_map.invert unless !@column_map || @column_map.default_proc
32
+ end
33
+
34
+ def parse_map(map, invert: false)
35
+ case map
36
+ when nil then map
37
+ when Array then invert ? map.each.with_index.map.to_h.invert : map.each.with_index.map.to_h
38
+ when Hash then map
39
+ else raise 'Invalid map type encountered'
40
+ end
16
41
  end
17
42
 
18
43
  def matrix
19
44
  @matrix ||= narray.empty? ? narray : NMatrix.refer(narray)
20
45
  end
21
46
 
22
- def self.blank(rows: 1, columns: 1, typecode: Typecode::FLOAT, initial: 0)
23
- source = self.new(NArray.new(typecode, columns, rows), typecode)
47
+ def self.blank(rows: 1, columns: 1, typecode: Typecode::FLOAT, initial: 0, column_map: nil, row_map: nil)
48
+ source = self.new(NArray.new(typecode, columns, rows), typecode, column_map: column_map, row_map: row_map)
24
49
  source.narray[]= initial unless source.empty?
25
50
  source
26
51
  end
@@ -30,7 +55,7 @@ module RMatrix
30
55
  end
31
56
 
32
57
  def _dump(level)
33
- narray.to_s << ':' << columns.to_s << ':' << rows.to_s << ':' << narray.typecode.to_s
58
+ narray.to_s << ':' << columns.to_s << ':' << rows.to_s << ':' << narray.typecode.to_s
34
59
  end
35
60
 
36
61
  def self._load arg
@@ -280,28 +305,43 @@ module RMatrix
280
305
  end
281
306
 
282
307
  def to_significant_figures(x, p)
283
- x.zero? ? 0 : begin
284
- nm = Math.log(x, 10.0).floor + 1.0 - p
285
- (((10.0 ** -nm) * x).round * (10.0 ** nm)).round(nm.abs)
286
- end
308
+ ("%-.#{p}e" % x).gsub(/0+e/,'e').gsub('.e+00','').gsub('e+00','')
287
309
  end
288
310
 
289
- def inspect(sz=10, sig=6)
290
- return 'M[Empty]' if empty?
291
- return Vector::inspect_vector(self) if self.is_vector?
292
- values = condensed(10, sig, '⋮', '…', "⋱")
293
- max_width = 0
294
- values = values.map{|line| line.map{|val| as_str = val.to_s; max_width = [as_str.length, max_width].max; as_str}}
295
- values = values.map{|line| line.map{|val| val.rjust(max_width, ' ') }}
296
- "#{rows} x #{columns} Matrix\nM[#{values.map{|row| "[#{row.join(", ")}]" }.join(",\n ")}"
311
+ def inspect(sz: 10, sig: 6, names: RMatrix::Matrix.named_inspect)
312
+ desc = case
313
+ when self.is_vector? then "Vector(#{self.length})"
314
+ else "Matrix(#{rows} x #{columns})"
315
+ end
316
+ "#{desc}\n#{RMatrix::MatrixTable.new(self).to_s}"
297
317
  end
298
318
 
299
319
  def to_tex(sz = 10, sig=6)
300
320
  values = condensed(sz, sig)
321
+ column_headers = column_label_map ? values[0].map.with_index do |v, i|
322
+ case v
323
+ when '\\cdots' then '\\cdots'
324
+ else (column_label_map && column_label_map[i]) || i
325
+ end
326
+ end : []
327
+
328
+ row_headers = row_label_map ? values.map.with_index do |v, i|
329
+ case v[0]
330
+ when '\\vdots' then '\\vdots'
331
+ else (row_label_map && row_label_map[i]) || i
332
+ end
333
+ end : []
334
+
301
335
  <<-TEX
302
- \\begin{pmatrix}
303
- #{values.map{|line| line.join(" & ")}.join(" \\\\ ")}
304
- \\end{pmatrix}
336
+ $
337
+ \\begin{array}{c} &
338
+ \\begin{array}{c} #{column_headers.join(" & ")} \\end{array}\\\\
339
+ \\begin{array}{c} #{row_headers.join(" \\\\ ")} \\end{array} &
340
+ \\left(\\begin{array}{ccc}
341
+ #{values.map{|line| line.join(" & ")}.join(" \\\\ ")}
342
+ \\end{array}\\right)
343
+ \\end{array}
344
+ $
305
345
  TEX
306
346
  end
307
347
 
@@ -333,21 +373,21 @@ TEX
333
373
  blank.narray.to_a.map{|line| (sig ? Array(line).map{|v| Numeric === v ? to_significant_figures(v,sig) : v } : Array(line))}
334
374
  end
335
375
 
336
- def transpose
337
- Matrix.new(self.matrix.transpose, typecode)
376
+ def transpose()
377
+ 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)
338
378
  end
339
379
 
340
- def self.[](*inputs, typecode: Typecode::FLOAT, row_map: nil, column_map: nil)
380
+ def self.[](*inputs, typecode: Typecode::FLOAT, row_map: nil, column_map: nil, column_label_map: nil, row_label_map: nil)
341
381
  if inputs.length == 1 && Matrix === inputs[0]
342
382
  inputs[0]
343
383
  elsif inputs.length == 1 && [String, Symbol].include?(inputs[0].class)
344
384
  if ['byte', 'sint', 'int', 'sfloat', 'float', 'scomplex', 'complex', 'object'].include?(inputs[0])
345
- ->(*source){ Matrix.new(source, inputs[0], row_map: row_map, column_map: column_map)}
385
+ ->(*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)}
346
386
  else
347
- Matrix.new(inputs[0], typecode, row_map: row_map, column_map: column_map)
387
+ 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)
348
388
  end
349
389
  else
350
- Matrix.new(inputs, typecode, row_map: row_map, column_map: column_map)
390
+ Matrix.new(inputs, typecode, row_map: row_map, column_map: column_map, row_label_map: row_label_map, column_label_map: column_label_map)
351
391
  end
352
392
  end
353
393
 
@@ -475,4 +515,5 @@ TEX
475
515
  end
476
516
 
477
517
  class ::NArray; include Enumerable; end
478
- end
518
+ end
519
+ RMatrix::Matrix.named_inspect = true
@@ -0,0 +1,83 @@
1
+ module RMatrix
2
+ class MatrixTable < PrintTable
3
+ def initialize(matrix, max_columns: 10, max_rows: 10)
4
+ super()
5
+ column_offset, row_offset = 0, 0
6
+ column_labels = !!matrix.column_label_map
7
+ row_labels = !!matrix.row_label_map
8
+
9
+ printed_rows = [matrix.rows, max_rows].min
10
+ printed_columns = [matrix.columns, max_columns].min
11
+
12
+ if matrix.column_label_map
13
+ printed_columns.times do |i|
14
+ column_label = matrix.column_label_map[i]
15
+ if column_label
16
+ self[i+1 + (row_labels ? 1 : 0 ), 0] = column_label.inspect
17
+ end
18
+ end
19
+ row_offset += 1
20
+ end
21
+
22
+ if matrix.row_label_map
23
+ printed_rows.times do |i|
24
+ row_label = matrix.row_label_map[i]
25
+ if row_label
26
+ self[0, i + (column_labels ? 1 : 0)] = row_label.inspect
27
+ end
28
+ end
29
+ column_offset += 1
30
+ end
31
+
32
+
33
+ self[column_offset,row_offset] = "#{matrix.is_vector? ? 'V' : 'M'}["
34
+ column_offset += 1
35
+ matrix.each_row.with_index do |row, row_idx|
36
+ break if row_idx > printed_rows
37
+ row.each.with_index do |cell, column_idx|
38
+ break if column_idx > printed_columns
39
+ self[column_idx + column_offset, row_idx + row_offset] = cell
40
+ end
41
+ self[[matrix.columns + column_offset, printed_columns + column_offset + 1].min, row_idx + row_offset] = ',' if (row_idx + row_offset - 1) != printed_rows
42
+ end
43
+
44
+ column_overlap = matrix.columns > max_columns
45
+ row_overlap = matrix.rows > max_rows
46
+ both_overlap = column_overlap && row_overlap
47
+
48
+ if column_overlap
49
+ printed_rows.times do |row|
50
+ self[max_columns + column_offset - 1, row + row_offset] = '…'
51
+ self[max_columns + column_offset, row + row_offset] = matrix[row, -1].first
52
+ end
53
+ end
54
+
55
+ if row_overlap
56
+ printed_columns.times do |column|
57
+ self[column + column_offset, max_rows + row_offset - 1] = '⋮'
58
+ self[column + column_offset, max_rows + row_offset] = matrix[column, -1].first
59
+ end
60
+ end
61
+
62
+ if both_overlap
63
+ self[printed_columns + column_offset - 1, printed_rows + row_offset - 1] = "⋱"
64
+ self[printed_columns + column_offset - 1, printed_rows + row_offset] = '⋮'
65
+ self[printed_columns + column_offset, printed_rows + row_offset - 1] = '…'
66
+ end
67
+
68
+ self[self.column_count - 1, self.row_count - 1] = ']'
69
+
70
+ if row_labels
71
+ self.set_column_separator( 0, ' ')
72
+ self.set_column_separator( 1, '[')
73
+ self.set_column_separator(self.column_count - 1, ']')
74
+ else
75
+ self.set_column_separator( 0, '[')
76
+ self.set_column_separator(self.column_count - 2, ']')
77
+ end
78
+
79
+ self.set_column_separator(self.column_count - 2, ']')
80
+ end
81
+ end
82
+
83
+ end
@@ -0,0 +1,107 @@
1
+ module RMatrix
2
+ class PrintTable
3
+ attr_accessor :row_count, :column_count, :cells, :column_justifications, :separators
4
+ def initialize
5
+ self.row_count = self.column_count = 0
6
+ self.cells = {}
7
+ self.column_justifications = {}
8
+ self.separators = Hash.new(', ')
9
+ end
10
+
11
+ def to_s
12
+ widths = self.column_widths
13
+ self.row_count.times.map do |row|
14
+ self.column_count.times.flat_map do |column|
15
+ cell_text = cell_repr(self.cells[[column, row]])
16
+ justification = column_justification(column)
17
+ width = widths[column]
18
+ contents = case justification
19
+ when :left then cell_text.ljust(width)
20
+ when :right then cell_text.rjust(width)
21
+ end
22
+ [contents,self.separators[[column, row]]]
23
+ end[0...-1].join
24
+ end.join("\n")
25
+ end
26
+
27
+ def to_tex
28
+ tex_map = self.row_count.times.map do |row|
29
+ self.column_count.times.map do |column|
30
+ cell_repr(self.cells[[column, row]])
31
+ end
32
+ end
33
+ <<-TEX
34
+ \\[
35
+ \\text{Mat}_{\\varphi\\text{ to }M} = \\kbordermatrix{
36
+ & c_1 & c_2 & c_3 & c_4 & c_5 \\\\
37
+ r_1 & 1 & 1 & 1 & 1 & 1 \\\\
38
+ r_2 & 0 & 1 & 0 & 0 & 1 \\\\
39
+ r_3 & 0 & 0 & 1 & 0 & 1 \\\\
40
+ r_4 & 0 & 0 & 0 & 1 & 1 \\\\
41
+ r_5 & 0 & 0 & 0 & 0 & 1
42
+ }
43
+ \\]
44
+ TEX
45
+ end
46
+
47
+ def column_justification(i)
48
+ case self.column_justifications[i]
49
+ when nil then :right
50
+ when :right then :right
51
+ when :left then :left
52
+ else raise "Unexpected justification for column #{self.column_justifications[i] }"
53
+ end
54
+ end
55
+
56
+ def set_column_separator(column, separator)
57
+ self.row_count.times do |row|
58
+ self.separators[[column, row]] = separator
59
+ end
60
+ end
61
+
62
+ def column_widths
63
+ column_count.times.map do |i|
64
+ column_width(i)
65
+ end
66
+ end
67
+
68
+ def column_width(column)
69
+ self.row_count.times.reduce(0) do |agg, row|
70
+ [agg, self.cell_repr(self.cells[[column, row]]).length].max
71
+ end
72
+ end
73
+
74
+ def cell_repr(cell)
75
+ case cell
76
+ when nil then ''
77
+ when Numeric then numeric_to_truncated_string(cell)
78
+ else "#{cell}"
79
+ end
80
+ end
81
+
82
+ def numeric_to_truncated_string(numeric)
83
+ ("%-.4e" % numeric).gsub(/(?<!\.)0+e/,'e').gsub('.e+00','').gsub('e+00','')
84
+ end
85
+
86
+ def [](column, row)
87
+ self.cells[[column, row]]
88
+ end
89
+
90
+ def []=(column, row, value)
91
+ build_column!(column)
92
+ build_row!(row)
93
+ self.cells[[column, row]] = value
94
+ end
95
+
96
+ def build_column!(idx)
97
+ self.column_count = [self.column_count || 0, idx.succ].max
98
+ end
99
+
100
+ def build_row!(idx)
101
+ self.row_count = [self.row_count || 0 , idx.succ].max
102
+ end
103
+ end
104
+
105
+
106
+ end
107
+ require_relative 'matrix_table'
@@ -1,3 +1,3 @@
1
1
  module RMatrix
2
- VERSION = "0.1.9"
2
+ VERSION = "0.1.10"
3
3
  end
data/rmatrix.todo CHANGED
@@ -1,6 +1,10 @@
1
+ ☐ Refactor + tidy
2
+ Tests:
3
+ ☐ Indexing
4
+ ☐ Assignment
5
+ ✔ Add matrix assignment @done (17-02-28 08:32)
1
6
  ✘ Use Numo::NArray (Not ready yet) @cancelled (16-09-29 08:23)
2
- Add matrix assignment
3
- ☐ Add random int matrix
7
+ Add random int matrix @cancelled (17-02-27 09:19)
4
8
  ✔ Implement to_tex for notebooks @done (16-09-29 08:23)
5
9
  ✔ Update print @done (16-04-06 07:40)
6
10
  ✔ Basic matrix functions and tests @done (16-04-06 07:40)
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.9
4
+ version: 0.1.10
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-10-10 00:00:00.000000000 Z
11
+ date: 2017-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -158,6 +158,8 @@ files:
158
158
  - lib/rmatrix/core.rb
159
159
  - lib/rmatrix/indices.rb
160
160
  - lib/rmatrix/matrix.rb
161
+ - lib/rmatrix/matrix_table.rb
162
+ - lib/rmatrix/print_table.rb
161
163
  - lib/rmatrix/shortcuts.rb
162
164
  - lib/rmatrix/typecode.rb
163
165
  - lib/rmatrix/vector.rb