module NMatrix::LAPACK

Public Class Methods

alloc_svd_result(matrix) click to toggle source
# File lib/nmatrix/lapack_core.rb, line 77
def alloc_svd_result(matrix)
  [
    NMatrix.new(matrix.shape[0], dtype: matrix.dtype),
    NMatrix.new([[matrix.shape[0],matrix.shape[1]].min,1], dtype: matrix.abs_dtype),
    NMatrix.new(matrix.shape[1], dtype: matrix.dtype)
  ]
end
clapack_getri(order, n, a, lda, ipiv) click to toggle source
# File lib/nmatrix/lapack_core.rb, line 176
def clapack_getri(order, n, a, lda, ipiv)
  raise(NotImplementedError,"clapack_getri requires the nmatrix-atlas gem")
end
clapack_potrf(order, uplo, n, a, lda) click to toggle source
# File lib/nmatrix/lapack_core.rb, line 164
def clapack_potrf(order, uplo, n, a, lda)
  raise(NotImplementedError,"clapack_potrf requires the nmatrix-atlas gem")
end
clapack_potri(order, uplo, n, a, lda) click to toggle source
# File lib/nmatrix/lapack_core.rb, line 168
def clapack_potri(order, uplo, n, a, lda)
  raise(NotImplementedError,"clapack_potri requires the nmatrix-atlas gem")
end
clapack_potrs(order, uplo, n, nrhs, a, lda, b, ldb) click to toggle source
# File lib/nmatrix/lapack_core.rb, line 172
def clapack_potrs(order, uplo, n, nrhs, a, lda, b, ldb)
  raise(NotImplementedError,"clapack_potrs requires the nmatrix-atlas gem")
end
geev(matrix, which=:both) click to toggle source
# File lib/nmatrix/atlas.rb, line 77
def geev(matrix, which=:both)
  raise(StorageTypeError, "LAPACK functions only work on dense matrices") unless matrix.dense?
  raise(ShapeError, "eigenvalues can only be computed for square matrices") unless matrix.dim == 2 && matrix.shape[0] == matrix.shape[1]

  jobvl = (which == :both || which == :left) ? :t : false
  jobvr = (which == :both || which == :right) ? :t : false

  n = matrix.shape[0]

  # Outputs
  eigenvalues = NMatrix.new([n, 1], dtype: matrix.dtype) # For real dtypes this holds only the real part of the eigenvalues.
  imag_eigenvalues = matrix.complex_dtype? ? nil : NMatrix.new([n, 1], dtype: matrix.dtype) # For complex dtypes, this is unused.
  left_output      = jobvl ? matrix.clone_structure : nil
  right_output     = jobvr ? matrix.clone_structure : nil

  # lapack_geev is a pure LAPACK routine so it expects column-major matrices,
  # so we need to transpose the input as well as the output.
  temporary_matrix = matrix.transpose
  NMatrix::LAPACK::lapack_geev(jobvl, # compute left eigenvectors of A?
                               jobvr, # compute right eigenvectors of A? (left eigenvectors of A**T)
                               n, # order of the matrix
                               temporary_matrix,# input matrix (used as work)
                               n, # leading dimension of matrix
                               eigenvalues,# real part of computed eigenvalues
                               imag_eigenvalues,# imag part of computed eigenvalues
                               left_output,     # left eigenvectors, if applicable
                               n, # leading dimension of left_output
                               right_output,    # right eigenvectors, if applicable
                               n, # leading dimension of right_output
                               2*n)
  left_output = left_output.transpose if jobvl
  right_output = right_output.transpose if jobvr


  # For real dtypes, transform left_output and right_output into correct forms.
  # If the j'th and the (j+1)'th eigenvalues form a complex conjugate
  # pair, then the j'th and (j+1)'th columns of the matrix are
  # the real and imag parts of the eigenvector corresponding
  # to the j'th eigenvalue.
  if !matrix.complex_dtype?
    complex_indices = []
    n.times do |i|
      complex_indices << i if imag_eigenvalues[i] != 0.0
    end

    if !complex_indices.empty?
      # For real dtypes, put the real and imaginary parts together
      eigenvalues = eigenvalues + imag_eigenvalues*Complex(0.0,1.0)
      left_output = left_output.cast(dtype: NMatrix.upcast(:complex64, matrix.dtype)) if left_output
      right_output = right_output.cast(dtype: NMatrix.upcast(:complex64, matrix.dtype)) if right_output
    end

    complex_indices.each_slice(2) do |i, _|
      if right_output
        right_output[0...n,i] = right_output[0...n,i] + right_output[0...n,i+1]*Complex(0.0,1.0)
        right_output[0...n,i+1] = right_output[0...n,i].complex_conjugate
      end

      if left_output
        left_output[0...n,i] = left_output[0...n,i] + left_output[0...n,i+1]*Complex(0.0,1.0)
        left_output[0...n,i+1] = left_output[0...n,i].complex_conjugate
      end
    end
  end

  if which == :both
    return [eigenvalues, left_output, right_output]
  elsif which == :left
    return [eigenvalues, left_output]
  else
    return [eigenvalues, right_output]
  end
end
gesdd(matrix, workspace_size=nil) click to toggle source
# File lib/nmatrix/atlas.rb, line 166
def gesdd(matrix, workspace_size=nil)
  min_workspace_size = matrix.shape.min * (6 + 4 * matrix.shape.min) + matrix.shape.max
  workspace_size = min_workspace_size if workspace_size.nil? || workspace_size < min_workspace_size

  result = alloc_svd_result(matrix)

  m = matrix.shape[0]
  n = matrix.shape[1]

  # This is a pure LAPACK function so it expects column-major functions.
  # So we need to transpose the input as well as the output.
  matrix = matrix.transpose
  NMatrix::LAPACK::lapack_gesdd(:a, m, n, matrix, m, result[1], result[0], m, result[2], n, workspace_size)
  result[0] = result[0].transpose
  result[2] = result[2].transpose
  result
end
gesvd(matrix, workspace_size=1) click to toggle source
# File lib/nmatrix/atlas.rb, line 151
def gesvd(matrix, workspace_size=1)
  result = alloc_svd_result(matrix)

  m = matrix.shape[0]
  n = matrix.shape[1]

  # This is a pure LAPACK function so it expects column-major functions.
  # So we need to transpose the input as well as the output.
  matrix = matrix.transpose
  NMatrix::LAPACK::lapack_gesvd(:a, :a, m, n, matrix, m, result[1], result[0], m, result[2], n, workspace_size)
  result[0] = result[0].transpose
  result[2] = result[2].transpose
  result
end
lapack_geev(jobvl, jobvr, n, a, lda, w, wi, vl, ldvl, vr, ldvr, lwork) click to toggle source
# File lib/nmatrix/lapack_core.rb, line 160
def lapack_geev(jobvl, jobvr, n, a, lda, w, wi, vl, ldvl, vr, ldvr, lwork)
  raise(NotImplementedError,"lapack_geev requires the nmatrix-atlas gem")
end
lapack_gesdd(jobz, m, n, a, lda, s, u, ldu, vt, ldvt, lwork) click to toggle source
# File lib/nmatrix/lapack_core.rb, line 156
def lapack_gesdd(jobz, m, n, a, lda, s, u, ldu, vt, ldvt, lwork)
  raise(NotImplementedError,"lapack_gesdd requires the nmatrix-atlas gem")
end
lapack_gesvd(jobu, jobvt, m, n, a, lda, s, u, ldu, vt, ldvt, lwork) click to toggle source

The following are functions that used to be implemented in C, but now require nmatrix-atlas to run properly, so we can just implemented their stubs in Ruby.

# File lib/nmatrix/lapack_core.rb, line 152
def lapack_gesvd(jobu, jobvt, m, n, a, lda, s, u, ldu, vt, ldvt, lwork)
  raise(NotImplementedError,"lapack_gesvd requires the nmatrix-atlas gem")
end
laswp(matrix, ipiv) click to toggle source
laswp(matrix, ipiv) -> NMatrix

Permute the columns of a matrix (in-place) according to the Array ipiv.

# File lib/nmatrix/lapack_core.rb, line 69
def laswp(matrix, ipiv)
  raise(ArgumentError, "expected NMatrix for argument 0") unless matrix.is_a?(NMatrix)
  raise(StorageTypeError, "LAPACK functions only work on :dense NMatrix instances") unless matrix.stype == :dense
  raise(ArgumentError, "expected Array ipiv to have no more entries than NMatrix a has columns") if ipiv.size > matrix.shape[1]

  clapack_laswp(matrix.shape[0], matrix, matrix.shape[1], 0, ipiv.size-1, ipiv, 1)
end
posv(uplo, a, b) click to toggle source
# File lib/nmatrix/atlas.rb, line 59
def posv(uplo, a, b)
  raise(ShapeError, "a must be square") unless a.dim == 2 && a.shape[0] == a.shape[1]
  raise(ShapeError, "number of rows of b must equal number of cols of a") unless a.shape[1] == b.shape[0]
  raise(StorageTypeError, "only works with dense matrices") unless a.stype == :dense && b.stype == :dense
  raise(DataTypeError, "only works for non-integer, non-object dtypes") if 
    a.integer_dtype? || a.object_dtype? || b.integer_dtype? || b.object_dtype?

  x     = b.clone
  clone = a.clone
  n = a.shape[0]
  nrhs = b.shape[1]
  clapack_potrf(:row, uplo, n, clone, n)
  # Must transpose b before and after: http://math-atlas.sourceforge.net/faq.html#RowSolve
  x = x.transpose
  clapack_potrs(:row, uplo, n, nrhs, clone, n, x, n)
  x.transpose
end