SciRuby/nmatrix

View on GitHub
lib/nmatrix/lapack_core.rb

Summary

Maintainability
C
7 hrs
Test Coverage
#--
# = NMatrix
#
# A linear algebra library for scientific computation in Ruby.
# NMatrix is part of SciRuby.
#
# NMatrix was originally inspired by and derived from NArray, by
# Masahiro Tanaka: http://narray.rubyforge.org
#
# == Copyright Information
#
# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
#
# Please see LICENSE.txt for additional copyright notices.
#
# == Contributing
#
# By contributing source code to SciRuby, you agree to be bound by
# our Contributor Agreement:
#
# * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
#
# == lapack_core.rb
#
# This file contains friendlier interfaces to LAPACK functions
# implemented in C.
# This file is only for functions available with the core nmatrix gem
# (no external libraries needed).
#
# Note: most of these functions are borrowed from ATLAS, which is available under a BSD-
# style license.
#++

class NMatrix

  module LAPACK

    #Add functions from C extension to main LAPACK module
    class << self
      NMatrix::Internal::LAPACK.singleton_methods.each do |m|
        define_method m, NMatrix::Internal::LAPACK.method(m).to_proc
      end
    end

    class << self
      # Solve the matrix equation AX = B, where A is a symmetric (or Hermitian)
      # positive-definite matrix. If A is a nxn matrix, B must be mxn.
      # Depending on the value of uplo, only the upper or lower half of +a+
      # is read.
      # This uses the Cholesky decomposition so it should be faster than
      # the generic NMatrix#solve method.
      # Doesn't modify inputs.
      # Requires either the nmatrix-atlas or nmatrix-lapacke gem.
      # * *Arguments* :
      #   - +uplo+ -> Either +:upper+ or +:lower+. Specifies which half of +a+ to read.
      #   - +a+ -> The matrix A.
      #   - +b+ -> The right-hand side B.
      # * *Returns* :
      #   - The solution X
      def posv(uplo, a, b)
        raise(NotImplementedError, "Either the nmatrix-atlas or nmatrix-lapacke gem must be installed to use posv")
      end

      #     laswp(matrix, ipiv) -> NMatrix
      #
      # Permute the columns of a matrix (in-place) according to the Array +ipiv+.
      #
      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

      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


      #
      # call-seq:
      #     gesvd(matrix) -> [u, sigma, v_transpose]
      #     gesvd(matrix) -> [u, sigma, v_conjugate_transpose] # complex
      #
      # Compute the singular value decomposition of a matrix using LAPACK's GESVD function.
      #
      # Optionally accepts a +workspace_size+ parameter, which will be honored only if it is larger than what LAPACK
      # requires.
      #
      # Requires either the nmatrix-lapacke or nmatrix-atlas gem.
      #
      def gesvd(matrix, workspace_size=1)
        raise(NotImplementedError,"gesvd requires either the nmatrix-atlas or nmatrix-lapacke gem")
      end

      #
      # call-seq:
      #     gesdd(matrix) -> [u, sigma, v_transpose]
      #     gesdd(matrix) -> [u, sigma, v_conjugate_transpose] # complex
      #
      # Compute the singular value decomposition of a matrix using LAPACK's GESDD function. This uses a divide-and-conquer
      # strategy. See also #gesvd.
      #
      # Optionally accepts a +workspace_size+ parameter, which will be honored only if it is larger than what LAPACK
      # requires.
      #
      # Requires either the nmatrix-lapacke or nmatrix-atlas gem.
      #
      def gesdd(matrix, workspace_size=nil)
        raise(NotImplementedError,"gesvd requires either the nmatrix-atlas or nmatrix-lapacke gem")
      end

      #
      # call-seq:
      #     geev(matrix) -> [eigenvalues, left_eigenvectors, right_eigenvectors]
      #     geev(matrix, :left) -> [eigenvalues, left_eigenvectors]
      #     geev(matrix, :right) -> [eigenvalues, right_eigenvectors]
      #
      # Perform eigenvalue decomposition on a matrix using LAPACK's xGEEV function.
      #
      # +eigenvalues+ is a n-by-1 NMatrix containing the eigenvalues.
      #
      # +right_eigenvalues+ is a n-by-n matrix such that its j'th column
      # contains the (right) eigenvalue of +matrix+ corresponding
      # to the j'th eigenvalue.
      # This means that +matrix+ = RDR^(-1),
      # where R is +right_eigenvalues+ and D is the diagonal matrix formed
      # from +eigenvalues+.
      #
      # +left_eigenvalues+ is n-by-n and its columns are the left
      # eigenvalues of +matrix+, using the {definition of left eigenvalue
      # from LAPACK}[https://software.intel.com/en-us/node/521147].
      #
      # For real dtypes, +eigenvalues+ and the eigenvector matrices
      # will be complex if and only if +matrix+ has complex eigenvalues.
      #
      # Only available if nmatrix-lapack or nmatrix-atlas is installed.
      #
      def geev(matrix, which=:both)
        raise(NotImplementedError, "geev requires either the nmatrix-atlas or nmatrix-lapack gem")
      end

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

      def lapack_gesdd(jobz, m, n, a, lda, s, u, ldu, vt, ldvt, lwork)
        raise(NotImplementedError,"lapack_gesdd requires the nmatrix-atlas gem")
      end

      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

      def clapack_potrf(order, uplo, n, a, lda)
        raise(NotImplementedError,"clapack_potrf requires the nmatrix-atlas gem")
      end

      def clapack_potri(order, uplo, n, a, lda)
        raise(NotImplementedError,"clapack_potri requires the nmatrix-atlas gem")
      end

      def clapack_potrs(order, uplo, n, nrhs, a, lda, b, ldb)
        raise(NotImplementedError,"clapack_potrs requires the nmatrix-atlas gem")
      end

      def clapack_getri(order, n, a, lda, ipiv)
        raise(NotImplementedError,"clapack_getri requires the nmatrix-atlas gem")
      end
    end
  end
end