rmatrix 0.1.9 → 0.1.10

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