Module: MachineLearningWorkbench::Monkey::AdvancelyOperationable

Defined in:
lib/machine_learning_workbench/monkey.rb

Overview

how am I supposed to name these things??

Instance Method Summary collapse

Instance Method Details

#eigen(which = :both) ⇒ Array<NMatrix, NMatrix[, NMatrix]>

Note:

requires LAPACK

Note:

WARNING! a param ‘which` different than :both alters the returns

Note:

WARNING! machine-precision-error imaginary part Complex

Calculate matrix eigenvalues and eigenvectors using LAPACK often returned! For symmetric matrices use #eigen_symm_right below

Parameters:

  • which (:both, :left, :right) (defaults to: :both)

    which eigenvectors do you want?

Returns:

  • (Array<NMatrix, NMatrix[, NMatrix]>)

    eigenvalues (as column vector), left eigenvectors, right eigenvectors. A value different than ‘:both` for param `which` reduces the return size.

Raises:

  • (ArgumentError)


103
104
105
106
# File 'lib/machine_learning_workbench/monkey.rb', line 103

def eigen which=:both
  raise ArgumentError unless [:both, :left, :right].include? which
  NMatrix::LAPACK.geev(self, which)
end

#eigen_symmArray<NMatrix, NMatrix>

TODO:

could it be possible to save some of the transpositions?

Note:

code taken from gem ‘nmatrix-atlas` NMatrix::LAPACK#geev

Note:

FOR SYMMETRIC MATRICES ONLY!!

Note:

WARNING: will return real matrices, imaginary parts are discarded!

Note:

WARNING: only left eigenvectors will be returned!

Eigenvalues and right eigenvectors for symmetric matrices using LAPACK

Returns:

  • (Array<NMatrix, NMatrix>)

    eigenvalues and (left) eigenvectors

Raises:

  • (TypeError)


115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/machine_learning_workbench/monkey.rb', line 115

def eigen_symm
  # TODO: check for symmetry if not too slow
  raise TypeError, "Only real-valued matrices" if complex_dtype?
  raise StorageTypeError, "Only dense matrices (because LAPACK)" unless dense?
  raise ShapeError, "Only square matrices" unless dim == 2 && shape[0] == shape[1]

  n = shape[0]

  # Outputs
  e_values = NMatrix.new([n, 1], dtype: dtype)
  e_values_img = NMatrix.new([n, 1], dtype: dtype) # to satisfy C alloc
  e_vectors = clone_structure

  NMatrix::LAPACK::lapack_geev(
    false,        # compute left eigenvectors of A?
    :t,           # compute right eigenvectors of A? (left eigenvectors of A**T)
    n,            # order of the matrix
    transpose,    # input matrix => needs to be column-wise  # self,
    n,            # leading dimension of matrix
    e_values,     # real part of computed eigenvalues
    e_values_img, # imaginary part of computed eigenvalues (will be discarded)
    nil,          # left eigenvectors, if applicable
    n,            # leading dimension of left_output
    e_vectors,    # right eigenvectors, if applicable
    n,            # leading dimension of right_output
    2*n           # no clue what's this
  )

  raise "Uhm why complex eigenvalues?" if e_values_img.any? {|v| v>1e-10}
  return [e_values, e_vectors.transpose]
end

#exponentialNMatrix

Matrix exponential: ‘e^self` (not to be confused with `self^n`!)

Returns:

  • (NMatrix)


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/machine_learning_workbench/monkey.rb', line 71

def exponential
  # special case: one-dimensional matrix: just exponentiate the values
  if (dim == 1) || (dim == 2 && shape.include?(1))
    return NMatrix.new shape, collect(&Math.method(:exp)), dtype: dtype
  end

  # Eigenvalue decomposition method from scipy/linalg/matfuncs.py#expm2

  # TODO: find out why can't I get away without double transpose!
  e_values, e_vectors = eigen_symm

  e_vals_exp_dmat = NMatrix.diagonal e_values.collect(&Math.method(:exp))
  # ASSUMING WE'RE ONLY USING THIS TO EXPONENTIATE LOG_SIGMA IN XNES
  # Theoretically we need the right eigenvectors, which for a symmetric
  # matrix should be just transposes of the eigenvectors.
  # But we have a positive definite matrix, so the final composition
  # below holds without transposing
  # BUT, strangely, I can't seem to get eigen_symm to green the tests
  # ...with or without transpose
  # e_vectors = e_vectors.transpose
  e_vectors.dot(e_vals_exp_dmat).dot(e_vectors.invert)#.transpose
end

#outer(other) ⇒ NMatrix

Note:

This implementation works only for 2D matrices (same as most other methods here). It’s a quick hack, a proof of concept barely sufficient for my urgent needs.

Note:

Output size is fixed! Since NMatrix does not graciously yield to being composed of other NMatrices (by adapting the shape of the root matrix), the block cannot return matrices in there.

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`.

Parameters:

  • other (NMatrix)

    other matrix

Returns:

  • (NMatrix)

Raises:

  • (ArgumentError)


43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/machine_learning_workbench/monkey.rb', line 43

def outer other
  # NOTE: Map of map in NMatrix 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) } } ]
  raise ArgumentError unless block_given?
  NMatrix.new(self.shape+other.shape).tap do |m|
    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) ⇒ NMatrix

Flat-output generalized outer relationship. Same as ‘#outer`, but the result is a 2-dim matrix of the interactions between all the elements in `self` (as rows) and all the elements in `other` (as columns)

Parameters:

  • other (NMatrix)

    other matrix

Returns:

  • (NMatrix)

Raises:

  • (ArgumentError)


63
64
65
66
67
# File 'lib/machine_learning_workbench/monkey.rb', line 63

def outer_flat other
  raise ArgumentError unless block_given?
  data = collect { |v1| other.collect { |v2| yield(v1, v2) } }
  self.class[*data, dtype: dtype]
end

#to_consistent_aArray<Array> Also known as: to_ca

‘NMatrix#to_a` has inconsistent behavior: single-row matrices are converted to one-dimensional Arrays rather than a 2D Array with only one row. Patching `#to_a` directly is not feasible as the constructor seems to depend on it, and I have little interest in investigating further.

Returns:

  • (Array<Array>)

    a consistent array representation, such that ‘nmat.to_consistent_a.to_nm == nmat` holds for single-row matrices



155
156
157
# File 'lib/machine_learning_workbench/monkey.rb', line 155

def to_consistent_a
  dim == 2 && shape[0] == 1 ? [to_a] : to_a
end