mmcs-ruby/fast_matrix

View on GitHub
lib/matrix/matrix.rb

Summary

Maintainability
A
3 hrs
Test Coverage
require 'matrix/constructors'

module FastMatrix
  # Matrix with fast implementations of + - * determinate in C
  class Matrix

    # Aliases just for compatibility with standard matrix
    #
    # Returns the number of rows.
    #
    alias row_size row_count
    #
    # Returns the number of columns.
    #
    alias column_size column_count
    # 
    # Returns the inverse of the matrix.
    # 
    alias inv inverse
    alias entrywise_product hadamard_product
    #
    # Returns element (+i+,+j+) of the matrix.  That is: row +i+, column +j+.
    #
    alias element []
    alias component []

    alias lup_decomposition lup
    alias t transpose
    alias tr trace

    alias skew_symmetric? antisymmetric?
    # 
    # In real numbers is equivalent to checking for symmetry
    #     
    alias hermitian? symmetric?

    #
    # Explicit conversion to a Matrix. Returns self
    #
    def to_matrix
      self
    end

    def empty?
      row_count * column_count == 0
    end

    #     
    # Returns true if this is a singular matrix.
    #     
    def singular?
      determinant == 0
    end
    
    # 
    # Returns true if this is a regular (i.e. non-singular) matrix.
    #     
    def regular?
      not singular?
    end

    # 
    # Returns true if this is a square matrix.
    #     
    def square?
      column_count == row_count
    end

    # 
    # Returns the imaginary part of the matrix.
    # Always returns a zero matrix
    #     
    def imaginary()
      Matrix.zero(row_count, column_count)
    end

    alias imag imaginary

    # 
    # Laplace expansion is equal to the determinant in the real numbers
    # 
    def laplace_expansion(row: nil, column: nil) 
      determinant
    end
    alias cofactor_expansion laplace_expansion

    # 
    # Returns an array containing matrices corresponding to the real and imaginary parts of the matrix
    #     
    def rect
      [real, imag]
    end

    alias rectangular rect

    # 
    # Conjugate real matrix is equal to this matrix
    #     
    alias conjugate to_matrix
    alias conj conjugate
    # 
    # Real method in real matrix is equal to this matrix
    #     
    alias real to_matrix

    def real?
      true
    end

    def collect
      collected_rows = []
      rows.each do |i|
        collected_rows.push(yield i)
      end
      collected_rows
    end

    #
    # Overrides Object#to_s
    #
    def to_s
      "#{self.class}[#{collect do |row|
        '[' + row.join(', ') + ']'
      end.join(', ')}]"
    end

    alias to_str to_s
    alias inspect to_str

    def to_a
      rows.collect(&:dup)
    end
    
    #
    # Create fast matrix from standard matrix
    #
    def self.convert(matrix)
      fast_matrix = Matrix.new(matrix.row_count, matrix.column_count)
      (0...matrix.row_count).each do |i|
        (0...matrix.column_count).each do |j|
          fast_matrix[i, j] = matrix[i, j]
        end
      end
      fast_matrix
    end

    #
    # Yields all elements of the matrix, starting with those of the first row
    #
    #   Matrix[ [1,2], [3,4] ].each { |e| puts e }
    #     # => prints the numbers 1 to 4
     def each(which = :all) # :yield: e
        return to_enum :each, which unless block_given?
        case which
        when :all
          (0...row_count).each do |i|
            (0...column_count).each do |j|
              yield self[i, j]
            end
          end
        when :diagonal
          (0...[row_count, column_count].min).each do |i|
                yield self[i, i]
          end
        when :off_diagonal
          (0...row_count).each do |i|
            (0...column_count).each do |j|
              if i != j
                yield self[i, j]
              end
            end
          end
        when :lower
          (0...row_count).each do |i|
            (0..[i,column_count-1].min).each do |j|
              yield self[i, j]
            end
          end
        when :strict_lower
          (1...row_count).each do |i|
            (0...[i,column_count].min).each do |j|
                yield self[i, j]
            end
          end
        when :strict_upper
          (0...row_count).each do |i|
            (i+1...column_count).each do |j|
              yield self[i, j]
            end
          end
        when :upper
          (0...row_count).each do |i|
            (i...column_count).each do |j|
              yield self[i, j]
            end
          end
        else
          raise ArgumentError, "expected #{which.inspect} to be one of :all, :diagonal, :off_diagonal, :lower, :strict_lower, :strict_upper or :upper"
        end
        self
      end
    #
    # Same as #each, but the row index and column index in addition to the element
    #
    #   Matrix[ [1,2], [3,4] ].each_with_index do |e, row, col|
    #     puts "#{e} at #{row}, #{col}"
    #   end
    #     # => Prints:
    #     #    1 at 0, 0
    #     #    2 at 0, 1
    #     #    3 at 1, 0
    #     #    4 at 1, 1
    #
    def each_with_index
      raise NotSupportedError unless block_given?

      (0...row_count).each do |i|
        (0...column_count).each do |j|
          yield self[i, j], i, j
        end
      end
      self
    end

    # don't use (Issue#1)
    def each_with_index!
      (0...row_count).each do |i|
        (0...column_count).each do |j|
          self[i, j] = yield self[i, j], i, j
        end
      end
      self
    end

    #
    # Convert to standard ruby matrix.
    #
    def convert
      ::Matrix.build(row_size, column_size) { |i, j| self[i, j] }
    end

    # FIXME: for compare with standard matrix
    def ==(other)
      return eql?(other) if other.class == Matrix
      return false unless %i[row_size column_size \[\]].all? { |x| other.respond_to? x }
      return false unless self.row_size == other.row_size && self.column_size == other.column_size

      result = true
      each_with_index do |elem, i, j|
        result &&= elem == other[i, j].to_f
      end
      result
    end

    private

    def rows
      rows = []
      (0...row_count).each do |i|
        row = []
        (0...column_count).each do |j|
          row.push(element(i, j))
        end
        rows.push(row)
      end
      rows
    end
  end
end