Class: NMatrix

Inherits:
Object
  • Object
show all
Defined in:
lib/neuroevo/monkey.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.build(*args) ⇒ Object

Dearly-missing initialize with block Note: now NMatrix::random and such are the realy monkey patch This should be the default behavior for initialize!


22
23
24
25
26
27
28
29
# File 'lib/neuroevo/monkey.rb', line 22

def self.build *args
  raise "Hell!" unless block_given?
  new(*args).tap do |m|
    m.each_stored_with_indices do |_,r,c|
      m[r,c] = yield(r,c)
    end
  end
end

Instance Method Details

#approximates?(other, epsilon = 1e-5) ⇒ Boolean

Returns:

  • (Boolean)

116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/neuroevo/monkey.rb', line 116

def approximates? other, epsilon=1e-5
  # Only used for testing, should I move to spec_helper?
  raise "Hell!" unless self.shape == other.shape or self.shape.size != 2
  # two ways to go here:
  # - epsilon is global: total cumulative accepted error
  # (self - other).reduce(:+) < epsilon
  # - epsilon is local: per element accepted error
  # I choose local to avoid possibility of errors with
  # opposite signs balancing up (law of large numbers)
  self.each_stored_with_indices.all? do |v,r,c|
    v.approximates? other[r,c], epsilon
  end
end

#each_diag(get_by = :reference) ⇒ Object


31
32
33
34
35
36
37
38
# File 'lib/neuroevo/monkey.rb', line 31

def each_diag(get_by=:reference)
  # Simple iteration on matrix diagonal, based on #row and #column
  return enum_for(:each_diag, get_by) unless block_given?
  (0...self.shape.min).each do |i|
    yield self.row(i, get_by).column(i, get_by)
  end
  self
end

#each_stored_diag(get_by = :reference) ⇒ Object


40
41
42
43
44
45
46
47
48
# File 'lib/neuroevo/monkey.rb', line 40

def each_stored_diag(get_by=:reference)
  # Same as above, yielding the value rather than a NMatrix
  return enum_for(:each_stored_diag, get_by) unless block_given?
  raise "Hell!" unless get_by == :reference
  (0...self.shape.min).each do |i|
    yield self[i,i]
  end
  self
end

#eigenObject


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

def eigen
  # IMPORTANT: requires lapack
  # IMPORTANT: currently fetching only right eigenvectors!
  #            actually left are also computed, then just discarded
  # RETURNS: eigenvalues, left_eigenvectors, right_eigenvectors
  NMatrix::LAPACK.geev(
    self.float_dtype? ? self : self.cast(dtype: :float64))
end

#exponentialObject


102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/neuroevo/monkey.rb', line 102

def exponential
  # Matrix exponential: e^self (not to be confused with self^n !)
  # special cases: one-dimensional matrix: just exponentiate the values
  if self.dim == 1 or self.dim == 2 && self.shape.include?(1)
    NMatrix.new self.shape, self.collect(&Math.method(:exp))
  else
    # Eigenvalue decomposition method from scipy/linalg/matfuncs.py#expm2
    values, _, vectors = eigen
    e_vecs_inv = vectors.invert
    diag_e_vals_exp = NMatrix.diagonal values.collect &Math.method(:exp)
    vectors.dot(diag_e_vals_exp).dot(e_vecs_inv)
  end
end

#hjoin(other) ⇒ Object


130
131
132
133
# File 'lib/neuroevo/monkey.rb', line 130

def hjoin other
  raise "Hell!" unless self.dim == 2 and other.dim == 2
  NMatrix[*self.true_to_a.zip(other.true_to_a).collect { |a,b| a+b }]
end

#old_reshapeObject


157
# File 'lib/neuroevo/monkey.rb', line 157

alias :old_reshape :reshape

#old_shapeObject

Introducing memoization for size I can't believe it's not hardcoded in C++, it's tied to the underlying implementation anyway!


149
# File 'lib/neuroevo/monkey.rb', line 149

alias :old_shape :shape

#old_sizeObject


153
# File 'lib/neuroevo/monkey.rb', line 153

alias :old_size :size

#outer(other) ⇒ Object


64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/neuroevo/monkey.rb', line 64

def outer other
  # Outer matrix relationship generalization
  # Make a matrix the same shape as `self`; each element is a matrix,
  # with the same shape as `other`, resulting from the interaction of
  # the corresponding element in `self` and all the elements in `other`.
  # NOTE: Map of map does not work as expected!
  # self.map { |v1| other.map { |v2| yield(v1,v2) } }
  # NOTE: this doesn't cut it either... can't capture the structure
  # NMatrix[ *self.collect { |v1| other.collect { |v2| yield(v1,v2) } } ]
  # NOTE: this works only for 2D matrices, same as most other methods here :(
  # NOTE: output size is fixed! Block cannot return matrices in there :(((
  NMatrix.new(self.shape+other.shape).tap do |m|
    self.each_stored_with_indices do |v1,r1,c1|
      other.each_stored_with_indices do |v2,r2,c2|
        m[r1,c1,r2,c2] = yield(v1,v2)
      end
    end
  end
end

#outer_flat(other) ⇒ Object


84
85
86
87
88
89
90
91
# File 'lib/neuroevo/monkey.rb', line 84

def outer_flat other
  # TODO: use NMatrix#build
  NMatrix[*self.collect do |v1|
    other.collect do |v2|
      yield(v1, v2)
    end
  end, dtype: :float64]
end

#reshape(*args) ⇒ Object


158
159
160
161
162
# File 'lib/neuroevo/monkey.rb', line 158

def reshape *args
  @size = nil
  @shape = nil
  old_reshape *args
end

#set_diag(&block) ⇒ Object


50
51
52
# File 'lib/neuroevo/monkey.rb', line 50

def set_diag &block
  self.clone.set_diag! &block
end

#set_diag!Object


54
55
56
57
58
59
60
61
62
# File 'lib/neuroevo/monkey.rb', line 54

def set_diag!
  # Set values on diagonal
  # Should be achieved rather with iterators above - couldn't :(
  raise "Hell!" unless block_given?
  (0...self.shape.min).each do |i|
    self[i,i] = yield i
  end
  self
end

#shapeObject


150
151
152
# File 'lib/neuroevo/monkey.rb', line 150

def shape
  @shape ||= old_shape
end

#sizeObject


154
155
156
# File 'lib/neuroevo/monkey.rb', line 154

def size
  @size ||= old_size
end

#true_to_aObject Also known as: tto_a


140
141
142
143
# File 'lib/neuroevo/monkey.rb', line 140

def true_to_a
  # Fix inconsistent to_a with single row matrices
  dim == 2 && shape[0] == 1 ? [to_a] : to_a
end

#vjoin(other) ⇒ Object


135
136
137
138
# File 'lib/neuroevo/monkey.rb', line 135

def vjoin other
  raise "Hell!" unless self.dim == 2 and other.dim == 2
  self.transpose.hjoin(other.transpose).transpose
end