Class: NMatrix

Inherits:
Object show all
Includes:
Enumerable
Defined in:
lib/nmatrix/math.rb,
lib/nmatrix/fftw.rb,
lib/nmatrix/atlas.rb,
lib/nmatrix/lapacke.rb,
lib/nmatrix/nmatrix.rb,
lib/nmatrix/version.rb,
lib/nmatrix/enumerate.rb,
lib/nmatrix/shortcuts.rb,
lib/nmatrix/cruby/math.rb,
lib/nmatrix/jruby/math.rb,
lib/nmatrix/homogeneous.rb,
lib/nmatrix/jruby/slice.rb,
lib/nmatrix/lapack_core.rb,
lib/nmatrix/jruby/operators.rb,
lib/nmatrix/io/fortran_format.rb,
lib/nmatrix/io/harwell_boeing.rb,
lib/nmatrix/lapack_ext_common.rb,
lib/nmatrix/jruby/nmatrix_java.rb,
lib/nmatrix/jruby/decomposition.rb,
ext/nmatrix/ruby_nmatrix.c

Overview

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: narray.rubyforge.org

Copyright Information

SciRuby is Copyright © 2010 - 2014, Ruby Science Foundation NMatrix is Copyright © 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:

lapack_ext_common.rb

Contains functions shared by nmatrix-atlas and nmatrix-lapacke gems. ++

Defined Under Namespace

Modules: BLAS, FFTW, FactorizeLUMethods, IO, LAPACK, MagicHelpers, NMMath, VERSION, YaleFunctions

Constant Summary collapse

FLOAT64_EPSILON =

//////////

rb_const_get(rb_cFloat, rb_intern("EPSILON"))
FLOAT32_EPSILON =
DBL2NUM(FLT_EPSILON)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ Object

Forward Declarations



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'ext/nmatrix/ruby_nmatrix.c', line 34

def initialize(*args)
  if args[-1] == :copy
    @shape = [2,2]
    @s = [0,0,0,0]
    @dim = shape.is_a?(Array) ? shape.length : 2
  else
    if (args.length <= 3)
      @shape = args[0]
      if args[1].is_a?(Array)
        elements = args[1]
        if args.length > 2
          hash = args[2]
          # puts hash
          @dtype = hash[:dtype]
          @stype = hash[:stype]
        else
          @dtype = :float64
          @stype = :dense
        end
      else
        # elements = Java::double[shape[0]*shape[1]].new{ Java::Double.NaN }
        if args.length > 1
          if args[1].is_a?(Symbol)
            hash = args[1]
            @dtype = hash[:dtype]
            @stype = hash[:stype]
            elements = Array.new(shape*shape) unless shape.is_a? Array
          else
            elements = Array.new(shape*shape) unless shape.is_a? Array
          end
        end
      end
    else

      offset = 0
      if (!args[0].is_a?(Symbol) && !args[0].is_a?(String))
        @stype = :dense
      else
        offset = 1
        @stype = :dense
        @dtype = args[-1]
      end

      @shape = args[offset]
      elements = args[offset+1]

    end


    @shape = [shape,shape] unless shape.is_a?(Array)
    # @dtype = interpret_dtype(argc-1-offset, argv+offset+1, stype);
    # @dtype = args[:dtype] if args[:dtype]
    @dtype_sym = nil
    @stype_sym = nil
    @default_val_num = nil
    @capacity_num = nil
    @size = (0...@shape.size).inject(1) { |x,i| x * @shape[i] }

    j=0

    if (elements.is_a?(ArrayRealVector))
      @s = elements
    # elsif elements.java_class.to_s == "[D"
    #   @s = ArrayRealVector.new(elements)
    else
      storage = Array.new(size)
      elements = [elements,elements] unless elements.is_a?(Array)
      if size > elements.length
        (0...size).each do |i|
          j=0 unless j!=elements.length
          storage[i] = elements[j]
          j+=1
        end
      else
        storage = elements
      end
      if @dtype == :object
        @s = storage
      else
        @s = ArrayRealVector.new(storage.to_java Java::double)
      end
    end

    @dim = @shape.is_a?(Array) ? @shape.length : 2

  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object

:nodoc:



977
978
979
980
981
982
983
984
985
# File 'lib/nmatrix/nmatrix.rb', line 977

def method_missing name, *args, &block #:nodoc:
  if name.to_s =~ /^__list_elementwise_.*__$/
    raise NotImplementedError, "requested undefined list matrix element-wise operation"
  elsif name.to_s =~ /^__yale_scalar_.*__$/
    raise NotImplementedError, "requested undefined yale scalar element-wise operation"
  else
    super(name, *args, &block)
  end
end

Instance Attribute Details

#dim=(value) ⇒ Object

Sets the attribute dim

Parameters:

  • value

    the value to set the attribute dim to.



19
20
21
# File 'lib/nmatrix/jruby/nmatrix_java.rb', line 19

def dim=(value)
  @dim = value
end

#dtypeObject

Returns the value of attribute dtype



41
42
43
# File 'ext/nmatrix/ruby_nmatrix.c', line 41

def dtype
  @dtype
end

#sObject

Returns the value of attribute s



19
20
21
# File 'lib/nmatrix/jruby/nmatrix_java.rb', line 19

def s
  @s
end

#shapeObject

handles list and dense, which are n-dimensional



48
49
50
# File 'ext/nmatrix/ruby_nmatrix.c', line 48

def shape
  @shape
end

#stypeObject

Returns the value of attribute stype



42
43
44
# File 'ext/nmatrix/ruby_nmatrix.c', line 42

def stype
  @stype
end

Class Method Details

.[](*params) ⇒ Object

call-seq:

NMatrix[Numeric, ..., Numeric, dtype: Symbol] -> NMatrix
NMatrix[Array, dtype: Symbol] -> NMatrix

The default value for dtype is guessed from the first parameter. For example:

NMatrix[1.0, 2.0].dtype # => :float64

But this is just a guess. If the other values can't be converted to this dtype, a TypeError will be raised.

You can use the N constant in this way:

N = NMatrix
N[1, 2, 3]

NMatrix needs to have a succinct way to create a matrix by specifying the components directly. This is very useful for using it as an advanced calculator, it is useful for learning how to use, for testing language features and for developing algorithms.

The NMatrix::[] method provides a way to create a matrix in a way that is compact and natural. The components are specified using Ruby array syntax. Optionally, one can specify a dtype as the last parameter (default is :float64).

Examples:

a = N[ 1,2,3,4 ]          =>  1  2  3  4

a = N[ 1,2,3,4, :int32 ]  =>  1  2  3  4

a = N[ [1,2,3], [3,4,5] ] =>  1.0  2.0  3.0
                              3.0  4.0  5.0

a = N[ 3,6,9 ].transpose => 3
                            6
                            9

SYNTAX COMPARISON:

MATLAB:  a = [ [1 2 3] ; [4 5 6] ]   or  [ 1 2 3 ; 4 5 6 ]
IDL:   a = [ [1,2,3] , [4,5,6] ]
NumPy:  a = array( [1,2,3], [4,5,6] )

SciRuby:      a = NMatrix[ [1,2,3], [4,5,6] ]
Ruby array:   a =  [ [1,2,3], [4,5,6] ]


186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/nmatrix/shortcuts.rb', line 186

def [](*params)
  options = params.last.is_a?(Hash) ? params.pop : {}

  # First find the dimensions of the array.
  i = 0
  shape = []
  row = params
  while row.is_a?(Array)
    shape[i] = row.length
    row = row[0]
    i += 1
  end

  # A row vector should be stored as 1xN, not N
  #shape.unshift(1) if shape.size == 1

  # Then flatten the array.
  NMatrix.new(shape, params.flatten, options)
end

.block_diagonal(*params) ⇒ Object Also known as: block_diag

Generate a block-diagonal NMatrix from the supplied 2D square matrices.

  • Arguments

    • *params -> An array that collects all arguments passed to the method. The method

      can receive any number of arguments. Optionally, the last entry of +params+ is 
      a hash of options from NMatrix#initialize. All other entries of +params+ are 
      the blocks of the desired block-diagonal matrix. Each such matrix block can be 
      supplied as a square 2D NMatrix object, or alternatively as an array of arrays 
      (with dimensions corresponding to a square matrix), or alternatively as a number.
      
  • Returns

    • NMatrix of block-diagonal form filled with specified matrices as the blocks along the diagonal.

  • Example

a = NMatrix.new([2,2], [1,2,3,4])
b = NMatrix.new([1,1], [123], dtype: :float64)
c = Array.new(2) { [[10,10], [10,10]] }
d = Array[[1,2,3], [4,5,6], [7,8,9]]
m = NMatrix.block_diagonal(a, b, *c, d, 10.0, 11, dtype: :int64, stype: :yale)
      => 
      [
        [1, 2,   0,  0,  0,  0,  0, 0, 0, 0,  0,  0]
        [3, 4,   0,  0,  0,  0,  0, 0, 0, 0,  0,  0]
        [0, 0, 123,  0,  0,  0,  0, 0, 0, 0,  0,  0]
        [0, 0,   0, 10, 10,  0,  0, 0, 0, 0,  0,  0]
        [0, 0,   0, 10, 10,  0,  0, 0, 0, 0,  0,  0]
        [0, 0,   0,  0,  0, 10, 10, 0, 0, 0,  0,  0]
        [0, 0,   0,  0,  0, 10, 10, 0, 0, 0,  0,  0]
        [0, 0,   0,  0,  0,  0,  0, 1, 2, 3,  0,  0]
        [0, 0,   0,  0,  0,  0,  0, 4, 5, 6,  0,  0]
        [0, 0,   0,  0,  0,  0,  0, 7, 8, 9,  0,  0]
        [0, 0,   0,  0,  0,  0,  0, 0, 0, 0, 10,  0]
        [0, 0,   0,  0,  0,  0,  0, 0, 0, 0,  0, 11]
      ]


479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
# File 'lib/nmatrix/shortcuts.rb', line 479

def block_diagonal(*params)
  options = params.last.is_a?(Hash) ? params.pop : {}

  params.each_index do |i|
    params[i] = params[i].to_nm if params[i].is_a?(Array) # Convert Array to NMatrix
    params[i] = NMatrix.new([1,1], [params[i]]) if params[i].is_a?(Numeric) # Convert number to NMatrix
  end

  block_sizes = [] #holds the size of each matrix block
  params.each do |b|
    unless b.is_a?(NMatrix)
      raise(ArgumentError, "Only NMatrix or appropriate Array objects or single numbers allowed")
    end
    raise(ArgumentError, "Only 2D matrices or 2D arrays allowed") unless b.shape.size == 2
    raise(ArgumentError, "Only square-shaped blocks allowed") unless b.shape[0] == b.shape[1]
    block_sizes << b.shape[0]
  end

  block_diag_mat = NMatrix.zeros(block_sizes.inject(0,:+), options)
  (0...params.length).each do |n|
    # First determine the size and position of the n'th block in the block-diagonal matrix
    block_size = block_sizes[n]
    block_pos = block_sizes[0...n].inject(0,:+)
    # populate the n'th block in the block-diagonal matrix
    (0...block_size).each do |i|
      (0...block_size).each do |j|
        block_diag_mat[block_pos+i,block_pos+j] = params[n][i,j]
      end
    end
  end

  return block_diag_mat
end

.diagonal(entries, opts = {}) ⇒ Object Also known as: diag, diagonals

call-seq:

diagonals(array) -> NMatrix
diagonals(array, dtype: dtype, stype: stype) -> NMatrix

Creates a matrix filled with specified diagonals.

  • Arguments :

    • entries -> Array containing input values for diagonal matrix

    • options -> (optional) Hash with options for NMatrix#initialize

  • Returns :

    • NMatrix filled with specified diagonal values.

Examples:

NMatrix.diagonal([1.0,2,3,4]) # => 1.0 0.0 0.0 0.0
                                   0.0 2.0 0.0 0.0
                                   0.0 0.0 3.0 0.0
                                   0.0 0.0 0.0 4.0

NMatrix.diagonal([1,2,3,4], dtype: :int32) # => 1 0 0 0
                                                0 2 0 0
                                                0 0 3 0
                                                0 0 0 4


431
432
433
434
435
436
437
438
439
# File 'lib/nmatrix/shortcuts.rb', line 431

def diagonal(entries, opts={})
  m = NMatrix.zeros(entries.size,
                    {:dtype => guess_dtype(entries[0]), :capacity => entries.size + 1}.merge(opts)
                   )
  entries.each_with_index do |n, i|
    m[i,i] = n
  end
  m
end

.eye(shape, opts = {}) ⇒ Object Also known as: identity

call-seq:

eye(shape) -> NMatrix
eye(shape, dtype: dtype) -> NMatrix
eye(shape, stype: stype, dtype: dtype) -> NMatrix

Creates an identity matrix (square matrix rank 2).

  • Arguments :

    • size -> Array (or integer for square matrix) specifying the dimensions.

    • dtype -> (optional) Default is :float64

    • stype -> (optional) Default is :dense.

  • Returns :

    • An identity matrix.

Examples:

NMatrix.eye(3) # =>   1.0   0.0   0.0
                      0.0   1.0   0.0
                      0.0   0.0   1.0

NMatrix.eye(3, dtype: :int32) # =>   1   0   0
                                     0   1   0
                                     0   0   1

NMatrix.eye(2, dtype: :int32, stype: :yale) # =>   1   0
                                                   0   1


315
316
317
318
319
320
321
322
323
# File 'lib/nmatrix/shortcuts.rb', line 315

def eye(shape, opts={})
  # Fill the diagonal with 1's.
  m = NMatrix.zeros(shape, {:dtype => :float64}.merge(opts))
  (0...m.shape[0]).each do |i|
    m[i, i] = 1
  end

  m
end

.guess_dtype(arg) ⇒ Object

FIXME



139
140
141
# File 'lib/nmatrix/jruby/nmatrix_java.rb', line 139

def self.guess_dtype arg
  :float32
end

.hilbert(shape, opts = {}) ⇒ Object

call-seq:

hilbert(shape) -> NMatrix
hilbert(shape, dtype: dtype) -> NMatrix
hilbert(shape, stype: stype, dtype: dtype) -> NMatrix

Creates an hilbert matrix (square matrix).

  • Arguments :

    • size -> integer ( for square matrix) specifying the dimensions.

    • dtype -> (optional) Default is :float64

    • stype -> (optional) Default is :dense.

  • Returns :

    • A hilbert matrix.

Examples:

NMatrix.hilbert(3) # =>  1.0     0.5      0.3333333333333333
        0.5                         0.3333333333333333    0.25
        0.3333333333333333          0.25                  0.2


347
348
349
350
351
352
353
354
355
356
# File 'lib/nmatrix/shortcuts.rb', line 347

def hilbert(shape, opts={})
  m = NMatrix.new([shape,shape], {:dtype => :float64}.merge(opts))
  0.upto(shape - 1) do |i|
    0.upto(i) do |j|
      m[i,j] = 1.0 / (j + i + 1)
      m[j,i] = m[i,j] if i != j
    end
  end
  m
end

.inv_hilbert(shape, opts = {}) ⇒ Object

call-seq:

inv_hilbert(shape) -> NMatrix
inv_hilbert(shape, dtype: dtype) -> NMatrix
inv_hilbert(shape, stype: stype, dtype: dtype) -> NMatrix

Creates an inverse hilbert matrix (square matrix rank 2).

  • Arguments :

    • size -> Array (or integer for square matrix) specifying the dimensions.

    • dtype -> (optional) Default is :float64

    • stype -> (optional) Default is :dense.

  • Returns :

    • A hilbert matrix.

Examples:

NMatrix.inv_hilbert(3) # =>   9.0,  -36.0,   30.0
                      -36.0,  192.0, -180.0
                      30.0, -180.0,  180.0


379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
# File 'lib/nmatrix/shortcuts.rb', line 379

def inv_hilbert(shape, opts={})
  opts = {:dtype => :float64}.merge(opts)
  m = NMatrix.new([shape,shape],opts)
  combination = NMatrix.new([2*shape,2*shape],opts)
  #combinations refers to the combination of n things taken k at a time
  0.upto(2*shape-1) do |i|
    0.upto(i) do |j|
      if j != 0 and j != i
        combination[i,j] = combination[i-1,j] + combination[i-1,j-1]
      else
        combination[i,j] = 1
      end
    end
  end

  0.upto(shape-1) do |i|
    0.upto(i) do |j|
      m[i,j] = combination[shape + j,shape - i - 1] * ((i+j)+1) * \
      combination[shape + i,shape - j - 1] * (-1) ** ((i+j)) * \
      combination[(i+j),i] * combination[(i+j),i]
      m[j,i] = m[i,j] if i != j
    end
  end
  m
end

.linspace(base, limit, shape = [100]) ⇒ Object

call-seq:

linspace(base, limit) -> 1x100 NMatrix
linspace(base, limit, *shape) -> NMatrix

Returns an NMatrix with +[shape x shape x .. x shape]+ values of dtype :float64 equally spaced from base to limit, inclusive.

See: www.mathworks.com/help/matlab/ref/linspace.html

  • Arguments :

    • base -> The first value in the sequence.

    • limit -> The last value in the sequence.

    • shape -> Desired output shape. Default returns a 1x100 row vector.

  • Returns :

    • NMatrix with :float64 values.

Examples :-

NMatrix.linspace(1,Math::PI, 6)
  =>[1.0,
     1.4283185005187988,
     1.8566370010375977,
     2.2849555015563965,
     2.7132740020751953,
     3.1415927410125732
    ]

NMatrix.linspace(1,10, [3,2])
  =>[
      [              1.0, 2.799999952316284]
      [4.599999904632568, 6.400000095367432]
      [8.199999809265137,              10.0]
    ]


644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
# File 'lib/nmatrix/shortcuts.rb', line 644

def linspace(base, limit, shape = [100])
  
  # Convert shape to array format 
  shape = [shape] if shape.is_a? Integer 
  
  #Calculate number of elements 
  count = shape.inject(:*)
        
  # Linear spacing between elements calculated in step
  #   step = limit - base / (count - 1)
  #   [Result Sequence] = [0->N sequence] * step + [Base]
  step = (limit - base) * (1.0 / (count - 1))
  result = NMatrix.seq(shape, {:dtype => :float64}) * step
  result += NMatrix.new(shape, base)
  result
end

.load_matlab_file(file_path) ⇒ Object

call-seq:

load_matlab_file(path) -> Mat5Reader
  • Arguments :

    • file_path -> The path to a version 5 .mat file.

  • Returns :

    • A Mat5Reader object.



99
100
101
# File 'lib/nmatrix/nmatrix.rb', line 99

def load_matlab_file(file_path)
  NMatrix::IO::Matlab::Mat5Reader.new(File.open(file_path, 'rb')).to_ruby
end

.load_pcd_file(file_path) ⇒ Object

call-seq:

load_pcd_file(path) -> PointCloudReader::MetaReader
  • Arguments :

    • file_path -> The path to a PCL PCD file.

  • Returns :

    • A PointCloudReader::MetaReader object with the matrix stored in its matrix property



110
111
112
# File 'lib/nmatrix/nmatrix.rb', line 110

def load_pcd_file(file_path)
  NMatrix::IO::PointCloudReader::MetaReader.new(file_path)
end

.logspace(base, limit, shape = [50], exponent_base: 10) ⇒ Object

call-seq:

logspace(base, limit) -> 1x50 NMatrix with exponent_base = 10 
logspace(base, limit, shape , exponent_base:) -> NMatrix
logspace(base, :pi, n) -> 1xn NMatrix with interval [10 ^ base, Math::PI]

Returns an NMatrix with +[shape x shape x .. x shape]+ values of dtype :float64 logarithmically spaced from exponent_base ^ base to +exponent_base ^ limit+, inclusive.

See: www.mathworks.com/help/matlab/ref/logspace.html

  • Arguments :

    • base -> exponent_base ** base is the first value in the sequence

    • limit -> exponent_base ** limit is the last value in the sequence.

    • shape -> Desired output shape. Default returns a 1x50 row vector.

  • Returns :

    • NMatrix with :float64 values.

Examples :-

NMatrix.logspace(1,:pi,7)
  =>[
      10.0000, 
      8.2450, 
      6.7980, 
      5.6050, 
      4.6213, 
      3.8103, 
      3.1416
    ]

NMatrix.logspace(1,2,[3,2])
  =>[
      [10.0, 15.8489]
      [25.1189, 39.8107]
      [63.0957, 100.0]
    ]


698
699
700
701
702
703
704
705
706
707
# File 'lib/nmatrix/shortcuts.rb', line 698

def logspace(base, limit, shape = [50], exponent_base: 10)

  #Calculate limit for [10 ^ base ... Math::PI] if limit = :pi
  limit = Math.log(Math::PI, exponent_base = 10) if limit == :pi 
  shape = [shape] if shape.is_a? Integer

  #[base...limit]  -> [exponent_base ** base ... exponent_base ** limit]
  result = NMatrix.linspace(base, limit, shape)
  result.map {|element| exponent_base ** element}
end

.magic(shape, opts = {}) ⇒ Object

call-seq:

  magic(shape) -> NMatrix
  magic(shape, dtype: dtype) -> NMatrix

The parameter is the dimension of the matrix.

Creates a +:dense+ NMatrix with the following properties:
  - An arrangement of the numbers from 1 to n^2 (n-squared) in the matrix, with each number occurring exactly once.
  - The sum of the entries of any row, any column, or any main diagonal is the same.
  - This sum must be n(n^2+1)/2.

See: http://www.mathworks.com/help/matlab/ref/magic.html

* *Arguments* :
 - +shape+ -> Array (or integer for square matrix) specifying the dimensions.
 - +dtype+ -> (optional) Default is +:float64+
* *Returns* :
 - NMatrix with the above given properties.

Examples:

  NMatrix.magic(3) # => [  [4.0, 9.0, 2.0]   [3.0, 5.0, 7.0]   [8.0, 1.0, 6.0] ]

  NMatrix.magic(4, dtype :int32) # => [  [ 1, 15, 14,  4]
                                         [12,  6,  7,  9]
                                         [ 8, 10, 11,  5]
                                         [13,  3,  2, 16] ]

  NMatrix.magic(6,dtype: :int64) # => [  [31,  9,  2, 22, 27, 20]
                                         [ 3, 32,  7, 21, 23, 25]
                                         [35,  1,  6, 26, 19, 24]
                                         [ 4, 36, 29, 13, 18, 11]
                                         [30,  5, 34, 12, 14, 16]
                                         [ 8, 28, 33, 17, 10, 15] ]

Raises:

  • (ArgumentError)


596
597
598
599
600
601
602
603
604
605
606
607
# File 'lib/nmatrix/shortcuts.rb', line 596

def magic(shape, opts={})
  raise(ArgumentError, "shape of two is not allowed") if shape == 2
  nm = NMatrix.new([shape,shape], 0, {:dtype => :float64}.merge(opts))
  if shape % 2 != 0
    MagicHelpers.odd_magic nm, shape
  elsif shape % 4 == 0
    MagicHelpers.doubly_even_magic nm, shape
  else   
    MagicHelpers.singly_even_magic nm, shape
  end
  nm
end

.meshgrid(vectors, options = {}) ⇒ Object

Make N-D coordinate arrays for vectorized evaluations of N-D scalar/vector fields over N-D grids, given N coordinate arrays arrs. N > 1.

call-seq:

meshgrid(arrs) -> Array of NMatrix
meshgrid(arrs, options) -> Array of NMatrix
  • Arguments :

    • vectors -> Array of N coordinate arrays (Array or NMatrix), if any have more than one dimension they will be flatten

    • options -> Hash with options (:sparse Boolean, false by default; :indexing Symbol, may be :ij or :xy, :xy by default)

  • Returns :

    • Array of N N-D NMatrixes

  • Examples :

    x, y = NMatrix::meshgrid([[1, [2, 3]], [4, 5]])
    x.to_a #<= [[1, 2, 3], [1, 2, 3]]
    y.to_a #<= [[4, 4, 4], [5, 5, 5]]
    
  • Using options :

    x, y = NMatrix::meshgrid([[[1, 2], 3], [4, 5]], sparse: true)
    x.to_a #<= [[1, 2, 3]]
    y.to_a #<= [[4], [5]]
    
    x, y = NMatrix::meshgrid([[1, 2, 3], [[4], 5]], indexing: :ij)
    x.to_a #<= [[1, 1], [2, 2], [3, 3]]
    y.to_a #<= [[4, 5], [4, 5], [4, 5]]
    

Raises:

  • (ArgumentError)


147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/nmatrix/nmatrix.rb', line 147

def meshgrid(vectors, options = {})
  raise(ArgumentError, 'Expected at least 2 arrays.') if vectors.size < 2
  options[:indexing] ||= :xy
  raise(ArgumentError, 'Indexing must be :xy of :ij') unless [:ij, :xy].include? options[:indexing]
  mats = vectors.map { |arr| arr.respond_to?(:flatten) ? arr.flatten : arr.to_flat_array }
  mats[0], mats[1] = mats[1], mats[0] if options[:indexing] == :xy
  new_dim = mats.size
  lengths = mats.map(&:size)
  result = mats.map.with_index do |matrix, axis|
    if options[:sparse]
      new_shape = Array.new(new_dim, 1)
      new_shape[axis] = lengths[axis]
      new_elements = matrix
    else
      before_axis = lengths[0...axis].reduce(:*)
      after_axis = lengths[(axis+1)..-1].reduce(:*)
      new_shape = lengths
      new_elements = after_axis ? matrix.map{ |el| [el] * after_axis }.flatten : matrix
      new_elements *= before_axis if before_axis
    end
    NMatrix.new(new_shape, new_elements)
  end
  result[0], result[1] = result[1], result[0] if options[:indexing] == :xy
  result
end

.min_dtype(alpha) ⇒ Object

Needs to be properly implemented



110
111
112
# File 'lib/nmatrix/jruby/nmatrix_java.rb', line 110

def self.min_dtype(alpha)
  :int8
end

.ones(shape, opts = {}) ⇒ Object

call-seq:

ones(shape) -> NMatrix
ones(shape, dtype: dtype, stype: stype) -> NMatrix

Creates a matrix filled with ones.

  • Arguments :

    • shape -> Array (or integer for square matrix) specifying the shape.

    • opts -> (optional) Hash of options from NMatrix#initialize

  • Returns :

    • NMatrix filled with ones.

Examples:

NMatrix.ones([1, 3]) # =>  1.0   1.0   1.0

NMatrix.ones([2, 3], dtype: :int32) # =>  1  1  1
                                          1  1  1


257
258
259
# File 'lib/nmatrix/shortcuts.rb', line 257

def ones(shape, opts={})
  NMatrix.new(shape, 1, {:dtype => :float64, :default => 1}.merge(opts))
end

.ones_like(nm) ⇒ NMatrix

call-seq:

ones_like(nm) -> NMatrix

Creates a new matrix of ones with the same dtype and shape as the provided matrix.

Parameters:

  • nm (NMatrix)

    the nmatrix whose dtype and shape will be used

Returns:

  • (NMatrix)

    a new nmatrix filled with ones.



270
271
272
# File 'lib/nmatrix/shortcuts.rb', line 270

def ones_like(nm)
  NMatrix.ones(nm.shape, dtype: nm.dtype, stype: nm.stype, capacity: nm.capacity, default: 1)
end

.random(shape, opts = {}) ⇒ Object Also known as: rand

call-seq:

random(shape) -> NMatrix

Creates a :dense NMatrix with random numbers between 0 and 1 generated by Random::rand. The parameter is the dimension of the matrix.

If you use an integer dtype, make sure to specify :scale as a parameter, or you'll only get a matrix of 0s.

  • Arguments :

    • shape -> Array (or integer for square matrix) specifying the dimensions.

  • Returns :

    • NMatrix filled with random values.

Examples:

NMatrix.random([2, 2]) # => 0.4859439730644226   0.1783195585012436
                            0.23193766176700592  0.4503345191478729

NMatrix.random([2, 2], :dtype => :byte, :scale => 255) # => [ [252, 108] [44, 12] ]


536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
# File 'lib/nmatrix/shortcuts.rb', line 536

def random(shape, opts={})
  scale = opts.delete(:scale) || 1.0

  if opts[:seed].nil?
    rng = Random.new
  else
    rng = Random.new(opts[:seed])
  end
  

  random_values = []


  # Construct the values of the final matrix based on the dimension.
  if opts[:dtype] == :complex64 || opts[:dtype] == :complex128
    NMatrix.size(shape).times { |i| random_values << Complex(rng.rand(scale), rng.rand(scale)) }
  else
    NMatrix.size(shape).times { |i| random_values << rng.rand(scale) }
  end

  NMatrix.new(shape, random_values, {:dtype => :float64, :stype => :dense}.merge(opts))
end

.readObject



37
# File 'ext/nmatrix/ruby_nmatrix.c', line 37

static VALUE nm_read(int argc, VALUE* argv, VALUE self);

.register_lapack_extension(name) ⇒ Object



30
31
32
33
34
35
36
# File 'lib/nmatrix/lapack_ext_common.rb', line 30

def NMatrix.register_lapack_extension(name)
  if (defined? @@lapack_extension)
    raise "Attempting to load #{name} when #{@@lapack_extension} is already loaded. You can only load one LAPACK extension."
  end

  @@lapack_extension = name
end

.seq(shape, options = {}) ⇒ Object

call-seq:

seq(shape) -> NMatrix
seq(shape, options) -> NMatrix
bindgen(shape) -> NMatrix of :byte
indgen(shape) -> NMatrix of :int64
findgen(shape) -> NMatrix of :float32
dindgen(shape) -> NMatrix of :float64
cindgen(shape) -> NMatrix of :complex64
zindgen(shape) -> NMatrix of :complex128
rbindgen(shape) -> NMatrix of :object

Creates a matrix filled with a sequence of integers starting at zero.

  • Arguments :

    • shape -> Array (or integer for square matrix) specifying the dimensions.

    • options -> (optional) Options permissible for NMatrix#initialize

  • Returns :

    • NMatrix filled with values 0 through size.

Examples:

NMatrix.seq(2) # =>   0   1
              2   3

NMatrix.seq([3, 3], dtype: :float32) # =>  0.0  1.0  2.0
                                    3.0  4.0  5.0
                                    6.0  7.0  8.0


838
839
840
841
842
843
844
845
# File 'lib/nmatrix/shortcuts.rb', line 838

def seq(shape, options={})

  # Construct the values of the final matrix based on the dimension.
  values = (0 ... NMatrix.size(shape)).to_a

  # It'll produce :int32, except if a dtype is provided.
  NMatrix.new(shape, values, {:stype => :dense}.merge(options))
end

.size(shape) ⇒ Object

Calculate the size of an NMatrix of a given shape.



115
116
117
118
# File 'lib/nmatrix/nmatrix.rb', line 115

def size(shape)
  shape = [shape,shape] unless shape.is_a?(Array)
  (0...shape.size).inject(1) { |x,i| x * shape[i] }
end

.translation(*args) ⇒ Object

call-seq:

translation(x, y, z) -> NMatrix
translation([x,y,z]) -> NMatrix
translation(translation_matrix) -> NMatrix
translation(translation_matrix) -> NMatrix
translation(translation, dtype: dtype) -> NMatrix
translation(x, y, z, dtype: dtype) -> NMatrix

Generate a 4x4 homogeneous transformation matrix representing a translation.

  • Returns :

    • A homogeneous transformation matrix consisting of a translation.

Examples:

NMatrix.translation(4.0,5.0,6.0) # =>
                                      1.0   0.0   0.0   4.0
                                      0.0   1.0   0.0   5.0
                                      0.0   0.0   1.0   6.0
                                      0.0   0.0   0.0   1.0

NMatrix.translation(4.0,5.0,6.0, dtype: :int64) # =>
                                                     1  0  0  4
                                                     0  1  0  5
                                                     0  0  1  6
                                                     0  0  0  1
NMatrix.translation(4,5,6) # =>
                                 1  0  0  4
                                 0  1  0  5
                                 0  0  1  6
                                 0  0  0  1


127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/nmatrix/homogeneous.rb', line 127

def translation *args
  xyz = args.shift if args.first.is_a?(NMatrix) || args.first.is_a?(Array)
  default_dtype = xyz.respond_to?(:dtype) ? xyz.dtype : NMatrix.guess_dtype(xyz)
  opts = {dtype: default_dtype}
  opts = opts.merge(args.pop) if args.size > 0 && args.last.is_a?(Hash)
  xyz ||= args

  n = if args.size > 0
    NMatrix.eye(4, opts)
  else
    NMatrix.eye(4, opts)
  end
  n[0..2,3] = xyz
  n
end

.upcast(alpha, beta) ⇒ Object

Singleton methods



170
171
172
# File 'ext/nmatrix/ruby_nmatrix.c', line 170

def self.upcast(alpha, beta)
  false
end

.x_rotation(angle_in_radians, opts = {}) ⇒ Object

call-seq:

x_rotation(angle_in_radians) -> NMatrix
x_rotation(angle_in_radians, dtype: dtype) -> NMatrix
y_rotation(angle_in_radians) -> NMatrix
y_rotation(angle_in_radians, dtype: dtype) -> NMatrix
z_rotation(angle_in_radians) -> NMatrix
z_rotation(angle_in_radians, dtype: dtype) -> NMatrix

Generate a 4x4 homogeneous transformation matrix representing a rotation about the x, y, or z axis respectively.

  • Arguments :

    • angle_in_radians -> The angle of rotation in radians.

    • dtype -> (optional) Default is :float64

  • Returns :

    • A homogeneous transformation matrix consisting of a single rotation.

Examples:

NMatrix.x_rotation(Math::PI.quo(6)) # =>
                                          1.0      0.0       0.0       0.0
                                          0.0      0.866025 -0.499999  0.0
                                          0.0      0.499999  0.866025  0.0
                                          0.0      0.0       0.0       1.0

NMatrix.x_rotation(Math::PI.quo(6), dtype: :float32) # =>
                                          1.0      0.0       0.0       0.0
                                          0.0      0.866025 -0.5       0.0
                                          0.0      0.5       0.866025  0.0
                                          0.0      0.0       0.0       1.0


66
67
68
69
70
71
72
73
# File 'lib/nmatrix/homogeneous.rb', line 66

def x_rotation angle_in_radians, opts={}
  c = Math.cos(angle_in_radians)
  s = Math.sin(angle_in_radians)
  NMatrix.new(4, [1.0, 0.0, 0.0, 0.0,
                  0.0, c,   -s,  0.0,
                  0.0, s,    c,  0.0,
                  0.0, 0.0, 0.0, 1.0], {dtype: :float64}.merge(opts))
end

.y_rotation(angle_in_radians, opts = {}) ⇒ Object



75
76
77
78
79
80
81
82
# File 'lib/nmatrix/homogeneous.rb', line 75

def y_rotation angle_in_radians, opts={}
  c = Math.cos(angle_in_radians)
  s = Math.sin(angle_in_radians)
  NMatrix.new(4, [ c,  0.0,  s,  0.0,
                  0.0, 1.0, 0.0, 0.0,
                  -s,  0.0,  c,  0.0,
                  0.0, 0.0, 0.0, 1.0], {dtype: :float64}.merge(opts))
end

.z_rotation(angle_in_radians, opts = {}) ⇒ Object



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

def z_rotation angle_in_radians, opts={}
  c = Math.cos(angle_in_radians)
  s = Math.sin(angle_in_radians)
  NMatrix.new(4, [ c,  -s,  0.0, 0.0,
                   s,   c,  0.0, 0.0,
                  0.0, 0.0, 1.0, 0.0,
                  0.0, 0.0, 0.0, 1.0], {dtype: :float64}.merge(opts))
end

.zeros(shape, opts = {}) ⇒ Object Also known as: zeroes

call-seq:

zeros(shape) -> NMatrix
zeros(shape, dtype: dtype) -> NMatrix
zeros(shape, dtype: dtype, stype: stype) -> NMatrix

Creates a new matrix of zeros with the dimensions supplied as parameters.

  • Arguments :

    • shape -> Array (or integer for square matrix) specifying the dimensions.

    • dtype -> (optional) Default is :float64

    • stype -> (optional) Default is :dense.

  • Returns :

    • NMatrix filled with zeros.

Examples:

NMatrix.zeros(2) # =>  0.0   0.0
                       0.0   0.0

NMatrix.zeros([2, 3], dtype: :int32) # =>  0  0  0
                                           0  0  0

NMatrix.zeros([1, 5], dtype: :int32) # =>  0  0  0  0  0


232
233
234
# File 'lib/nmatrix/shortcuts.rb', line 232

def zeros(shape, opts = {})
  NMatrix.new(shape, 0, {:dtype => :float64}.merge(opts))
end

.zeros_like(nm) ⇒ NMatrix

call-seq:

zeros_like(nm) -> NMatrix

Creates a new matrix of zeros with the same stype, dtype, and shape as the provided matrix.

Parameters:

  • nm (NMatrix)

    the nmatrix whose stype, dtype, and shape will be used

Returns:

  • (NMatrix)

    a new nmatrix filled with zeros.



283
284
285
# File 'lib/nmatrix/shortcuts.rb', line 283

def zeros_like(nm)
  NMatrix.zeros(nm.shape, dtype: nm.dtype, stype: nm.stype, capacity: nm.capacity, default: 0)
end

Instance Method Details

#!~(other) ⇒ Object



545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
# File 'lib/nmatrix/jruby/nmatrix_java.rb', line 545

def !~ (other)
  lha = @s.toArray.to_a
  rha = other.s.toArray.to_a
  resultArray = Array.new(lha.length)
  if (other.is_a?(NMatrix))
    #check dimension
    if (@dim != other.dim)
      raise(ShapeError, "cannot compare matrices with different dimension")
      return nil
    end
    #check shape
    (0...dim).each do |i|
      if (@shape[i] != other.shape[i])
        raise(ShapeError, "cannot compare matrices with different shapes");
        return nil
      end
    end
    #check the entries
    (0...lha.length).each do |i|
      resultArray[i] = lha[i] != rha[i] ? true : false
    end
    result = NMatrix.new(:copy)
    result.shape = @shape
    result.dtype = :object
    result.s = resultArray
  end
  result
end

#%(other) ⇒ Object

Raises:

  • (Exception)


77
78
79
# File 'lib/nmatrix/jruby/operators.rb', line 77

def %(other)
  raise Exception.new("modulus not supported in NMatrix-jruby")
end

#*(other) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/nmatrix/jruby/operators.rb', line 37

def *(other)
  result = create_dummy_nmatrix
  if (other.is_a?(NMatrix))
    #check dimension
    raise(ShapeError, "Cannot multiply matrices with different dimension") if (@dim != other.dim)
    #check shape
    (0...dim).each do |i|
      raise(ShapeError, "Cannot multiply matrices with different shapes") if (@shape[i] != other.shape[i])
    end
    result.s = @s.copy.ebeMultiply(other.s)
  else
    result.s = @s.copy.mapMultiplyToSelf(other)
  end
  result
end

#**(val) ⇒ Object



69
70
71
72
73
74
75
# File 'lib/nmatrix/jruby/operators.rb', line 69

def ** val
  result = NMatrix.new(:copy)
  result.shape = @shape
  result.dim = @dim
  result.s = @s.copy.mapToSelf(Power.new(val))
  result
end

#+(other) ⇒ Object

A dummy matrix is a matrix without the elements atrribute. NMatrix#create_dummy_matrix prevents creating copies as @s is set explicitly.



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/nmatrix/jruby/operators.rb', line 5

def +(other)
  result = create_dummy_nmatrix
  if (other.is_a?(NMatrix))
    #check dimension
    raise(ShapeError, "Cannot add matrices with different dimension") if (@dim != other.dim)
    #check shape
    (0...dim).each do |i|
      raise(ShapeError, "Cannot add matrices with different shapes") if (@shape[i] != other.shape[i])
    end
    result.s = @s.copy.add(other.s)
  else
    result.s = @s.copy.mapAddToSelf(other)
  end
  result
end

#-(other) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/nmatrix/jruby/operators.rb', line 21

def -(other)
  result = create_dummy_nmatrix
  if (other.is_a?(NMatrix))
    #check dimension
    raise(ShapeError, "Cannot subtract matrices with different dimension") if (@dim != other.dim)
    #check shape
    (0...dim).each do |i|
      raise(ShapeError, "Cannot subtract matrices with different shapes") if (@shape[i] != other.shape[i])
    end
    result.s = @s.copy.subtract(other.s)
  else
    result.s = @s.copy.mapSubtractToSelf(other)
  end
  result
end

#-@Object



253
254
255
256
257
# File 'lib/nmatrix/jruby/operators.rb', line 253

def -@
  result = create_dummy_nmatrix
  result.s = @s.copy.mapMultiplyToSelf(-1)
  result
end

#/(other) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/nmatrix/jruby/operators.rb', line 53

def /(other)
  result = create_dummy_nmatrix
  if (other.is_a?(NMatrix))
    #check dimension
    raise(ShapeError, "Cannot divide matrices with different dimension") if (@dim != other.dim)
    #check shape
    (0...dim).each do |i|
      raise(ShapeError, "Cannot divide matrices with different shapes") if (@shape[i] != other.shape[i])
    end
    result.s = @s.copy.ebeDivide(other.s)
  else
    result.s = @s.copy.mapDivideToSelf(other)
  end
  result
end

#<(other) ⇒ Object



632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
# File 'lib/nmatrix/jruby/nmatrix_java.rb', line 632

def < (other)
  lha = @s.toArray.to_a
  rha = other.s.toArray.to_a
  resultArray = Array.new(lha.length)
  if (other.is_a?(NMatrix))
    #check dimension
    if (@dim != other.dim)
      raise(ShapeError, "cannot compare matrices with different dimension")
      return nil
    end
    #check shape
    (0...dim).each do |i|
      if (@shape[i] != other.shape[i])
        raise(ShapeError, "cannot compare matrices with different shapes");
        return nil
      end
    end
    #check the entries
    (0...lha.length).each do |i|
      resultArray[i] = lha[i] < rha[i] ? true : false
    end
    result = NMatrix.new(:copy)
    result.shape = @shape
    result.dtype = :object
    result.s = resultArray
  end
  result
end

#<=(other) ⇒ Object



574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
# File 'lib/nmatrix/jruby/nmatrix_java.rb', line 574

def <= (other)
  lha = @s.toArray.to_a
  rha = other.s.toArray.to_a
  resultArray = Array.new(lha.length)
  if (other.is_a?(NMatrix))
    #check dimension
    if (@dim != other.dim)
      raise(ShapeError, "cannot compare matrices with different dimension")
      return nil
    end
    #check shape
    (0...dim).each do |i|
      if (@shape[i] != other.shape[i])
        raise(ShapeError, "cannot compare matrices with different shapes");
        return nil
      end
    end
    #check the entries
    (0...lha.length).each do |i|
      resultArray[i] = lha[i] <= rha[i] ? true : false
    end
    result = NMatrix.new(:copy)
    result.shape = @shape
    result.dtype = :object
    result.s = resultArray
  end
  result
end

#==(otherNmatrix) ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'ext/nmatrix/ruby_nmatrix.c', line 152

def ==(otherNmatrix)
  result = false
  if (otherNmatrix.is_a?(NMatrix))
    #check dimension
    if (@dim != otherNmatrix.dim)
      raise(ShapeError, "cannot compare matrices with different dimension")
    end
    #check shape
    (0...dim).each do |i|
      if (@shape[i] != otherNmatrix.shape[i])
        raise(ShapeError, "cannot compare matrices with different shapes");
      end
    end

    #check the entries
    if dtype == :object
      result = @s == otherNmatrix.s
    else
      result = ArrayComparator.equals(@s.toArray, otherNmatrix.s.toArray)
    end
  end
  result
end

#=~(other) ⇒ Object



516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
# File 'lib/nmatrix/jruby/nmatrix_java.rb', line 516

def =~ (other)
  lha = @s.toArray.to_a
  rha = other.s.toArray.to_a
  resultArray = Array.new(lha.length)
  if (other.is_a?(NMatrix))
    #check dimension
    if (@dim != other.dim)
      raise(ShapeError, "cannot compare matrices with different dimension")
      return nil
    end
    #check shape
    (0...dim).each do |i|
      if (@shape[i] != other.shape[i])
        raise(ShapeError, "cannot compare matrices with different shapes");
        return nil
      end
    end
    #check the entries
    (0...lha.length).each do |i|
      resultArray[i] = lha[i] == rha[i] ? true : false
    end
    result = NMatrix.new(:copy)
    result.shape = @shape
    result.dtype = :object
    result.s = resultArray
  end
  result
end

#>(other) ⇒ Object



661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
# File 'lib/nmatrix/jruby/nmatrix_java.rb', line 661

def > (other)
  lha = @s.toArray.to_a
  rha = other.s.toArray.to_a
  resultArray = Array.new(lha.length)
  if (other.is_a?(NMatrix))
    #check dimension
    if (@dim != other.dim)
      raise(ShapeError, "cannot compare matrices with different dimension")
      return nil
    end
    #check shape
    (0...dim).each do |i|
      if (@shape[i] != other.shape[i])
        raise(ShapeError, "cannot compare matrices with different shapes");
        return nil
      end
    end
    #check the entries
    (0...lha.length).each do |i|
      resultArray[i] = lha[i] > rha[i] ? true : false
    end
    result = NMatrix.new(:copy)
    result.shape = @shape
    result.dtype = :object
    result.s = resultArray
  end
  result
end

#>=(other) ⇒ Object



603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
# File 'lib/nmatrix/jruby/nmatrix_java.rb', line 603

def >= (other)
  lha = @s.toArray.to_a
  rha = other.s.toArray.to_a
  resultArray = Array.new(lha.length)
  if (other.is_a?(NMatrix))
    #check dimension
    if (@dim != other.dim)
      raise(ShapeError, "cannot compare matrices with different dimension")
      return nil
    end
    #check shape
    (0...dim).each do |i|
      if (@shape[i] != other.shape[i])
        raise(ShapeError, "cannot compare matrices with different shapes");
        return nil
      end
    end
    #check the entries
    (0...lha.length).each do |i|
      resultArray[i] = lha[i] >= rha[i] ? true : false
    end
    result = NMatrix.new(:copy)
    result.shape = @shape
    result.dtype = :object
    result.s = resultArray
  end
  result
end

#[](*args) ⇒ Object



60
61
62
# File 'ext/nmatrix/ruby_nmatrix.c', line 60

def [] *args
  return xslice(args)
end

#[]=(*args) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
# File 'ext/nmatrix/ruby_nmatrix.c', line 58

def []=(*args)
  to_return = nil
  if args.length > @dim+1
    raise(ArgumentError, "wrong number of arguments (#{args.length} for #{effective_dim(dim+1)})" )
  else
    slice = get_slice(@dim, args, @shape)
    dense_storage_set(slice, args[-1])
    to_return = args[-1]
  end
  return to_return
end

#__list_default_value__Object



160
161
162
# File 'lib/nmatrix/jruby/nmatrix_java.rb', line 160

def __list_default_value__
  #not implemented currently
end

#__yale_ary__to_s(sym) ⇒ Object

:nodoc:



346
347
348
349
350
# File 'lib/nmatrix/nmatrix.rb', line 346

def __yale_ary__to_s(sym) #:nodoc:
  ary = self.send("__yale_#{sym.to_s}__".to_sym)

  '[' + ary.collect { |a| a ? a : 'nil'}.join(',') + ']'
end

#__yale_default_value__Object



164
165
166
# File 'lib/nmatrix/jruby/nmatrix_java.rb', line 164

def __yale_default_value__
  #not implemented currently
end

#absObject

call-seq:

abs -> NMatrix

Maps all values in a matrix to their absolute values.



617
618
619
620
621
622
623
624
625
626
# File 'lib/nmatrix/math.rb', line 617

def abs
  if stype == :dense
    self.__dense_map__ { |v| v.abs }
  elsif stype == :list
    # FIXME: Need __list_map_stored__, but this will do for now.
    self.__list_map_merged_stored__(nil, nil) { |v,dummy| v.abs }
  else
    self.__yale_map_stored__ { |v| v.abs }
  end.cast(self.stype, abs_dtype)
end

#abs_dtypeObject

call-seq:

abs_dtype -> Symbol

Returns the dtype of the result of a call to #abs. In most cases, this is the same as dtype; it should only differ for :complex64 (where it's :float32) and :complex128 (:float64).



601
602
603
604
605
606
607
608
609
# File 'lib/nmatrix/math.rb', line 601

def abs_dtype
  if self.dtype == :complex64
    :float32
  elsif self.dtype == :complex128
    :float64
  else
    self.dtype
  end
end

#acosObject



147
148
149
150
151
# File 'lib/nmatrix/jruby/operators.rb', line 147

def acos
  result = create_dummy_nmatrix
  result.s = @s.copy.mapToSelf(Acos.new())
  result
end

#acoshObject



185
186
187
188
189
# File 'lib/nmatrix/jruby/operators.rb', line 185

def acosh
  result = create_dummy_nmatrix
  result.s = @s.copy.mapToSelf(Acosh.new())
  result
end

#adjugateObject Also known as: adjoint

call-seq:

adjugate -> NMatrix

Make a copy of the matrix and calculate the adjugate of the matrix. Only works on dense matrices.

  • Returns :

    • A dense NMatrix. Will be the same type as the input NMatrix,

    except if the input is an integral dtype, in which case it will be a :float64 NMatrix.

  • Raises :

    • StorageTypeError -> only implemented on dense matrices.

    • ShapeError -> matrix must be square.

Raises:



271
272
273
274
275
276
277
278
# File 'lib/nmatrix/math.rb', line 271

def adjugate
  raise(StorageTypeError, "adjugate only works on dense matrices currently") unless self.dense?
  raise(ShapeError, "Cannot calculate adjugate of a non-square matrix") unless self.dim == 2 && self.shape[0] == self.shape[1]
  d = self.det
  mat = self.invert
  mat.map! { |e| e * d }
  mat
end

#adjugate!Object Also known as: adjoint!

call-seq:

adjugate! -> NMatrix

Calculate the adjugate of the matrix (in-place). Only works on dense matrices.

  • Raises :

    • StorageTypeError -> only implemented on dense matrices.

    • ShapeError -> matrix must be square.

    • DataTypeError -> cannot calculate adjugate of an integer matrix in-place.

Raises:



244
245
246
247
248
249
250
251
252
# File 'lib/nmatrix/math.rb', line 244

def adjugate!
  raise(StorageTypeError, "adjugate only works on dense matrices currently") unless self.dense?
  raise(ShapeError, "Cannot calculate adjugate of a non-square matrix") unless self.dim == 2 && self.shape[0] == self.shape[1]
  raise(DataTypeError, "Cannot calculate adjugate of an integer matrix in-place") if self.integer_dtype?
  d = self.det
  self.invert!
  self.map! { |e| e * d }
  self
end

#angle_vectorObject

call-seq:

angle_vector -> [angle, about_vector]

Find the angle vector for a quaternion. Assumes the quaternion has unit length.

Source: www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/

  • Returns :

    • An angle (in radians) describing the rotation about the about_vector.

    • A length-3 NMatrix representing the corresponding quaternion.

Examples:

q.angle_vector # => [1, 0, 0, 0]

Raises:



228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/nmatrix/homogeneous.rb', line 228

def angle_vector
  raise(ShapeError, "Expected length-4 vector or matrix (quaternion)") if self.shape[0] != 4
  raise("Expected unit quaternion") if self[0] > 1

  xyz = NMatrix.new([3], dtype: self.dtype)

  angle = 2 * Math.acos(self[0])
  s = Math.sqrt(1.0 - self[0]*self[0])

  xyz[0..2] = self[1..3]
  xyz /= s if s >= 0.001 # avoid divide by zero
  return [angle, xyz]
end

#asinObject



141
142
143
144
145
# File 'lib/nmatrix/jruby/operators.rb', line 141

def asin
  result = create_dummy_nmatrix
  result.s = @s.copy.mapToSelf(Asin.new())
  result
end

#asinhObject



179
180
181
182
183
# File 'lib/nmatrix/jruby/operators.rb', line 179

def asinh
  result = create_dummy_nmatrix
  result.s = @s.copy.mapToSelf(Asinh.new())
  result
end

#asum(incx = 1, n = nil) ⇒ Object Also known as: absolute_sum

call-seq:

absolute_sum -> Numeric

Arguments

- +incx+ -> the skip size (defaults to 1, no skip)
- +n+ -> the number of elements to include

Return the sum of the contents of the vector. This is the BLAS asum routine.



680
681
682
683
684
685
686
687
# File 'lib/nmatrix/cruby/math.rb', line 680

def asum incx=1, n=nil
  if self.shape == [1]
    return self[0].abs unless self.complex_dtype?
    return self[0].real.abs + self[0].imag.abs
  end
  return method_missing(:asum, incx, n) unless vector?
  NMatrix::BLAS::asum(self, incx, self.size / incx)
end

#atanObject



153
154
155
156
157
# File 'lib/nmatrix/jruby/operators.rb', line 153

def atan
  result = create_dummy_nmatrix
  result.s = @s.copy.mapToSelf(Atan.new())
  result
end

#atan2(other, scalar = false) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/nmatrix/jruby/operators.rb', line 81

def atan2(other, scalar=false)
  result = create_dummy_nmatrix
  if scalar
    result.s = ArrayRealVector.new MathHelper.atan2Scalar(other, @s.toArray)
  else
    if other.is_a? NMatrix
      result.s = ArrayRealVector.new MathHelper.atan2(other.s.toArray, @s.toArray)
    else
      result.s = ArrayRealVector.new MathHelper.atan2Scalar2(other, @s.toArray)
    end
  end
  result
end

#atanhObject



191
192
193
194
195
# File 'lib/nmatrix/jruby/operators.rb', line 191

def atanh
  result = create_dummy_nmatrix
  result.s = @s.copy.mapToSelf(Atanh.new())
  result
end

#binned_sorted_indicesObject

call-seq:

binned_sorted_indices -> Array

Returns an array of arrays of indices ordered by value sorted. Functions basically like sorted_indices, but groups indices together for those values that are the same.



960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
# File 'lib/nmatrix/nmatrix.rb', line 960

def binned_sorted_indices
  return method_missing(:sorted_indices) unless vector?
  ary = self.to_flat_array
  ary2 = []
  last_bin = ary.each_index.sort_by { |i| [ary[i]] }.inject([]) do |result, element|
    if result.empty? || ary[result[-1]] == ary[element]
      result << element
    else
      ary2 << result
      [element]
    end
  end
  ary2 << last_bin unless last_bin.empty?
  ary2
end

#capacityObject



50
51
52
# File 'ext/nmatrix/ruby_nmatrix.c', line 50

def capacity

end

#cast(*params) ⇒ Object

call-seq:

cast(stype, dtype, default) -> NMatrix
cast(stype, dtype) -> NMatrix
cast(stype) -> NMatrix
cast(options) -> NMatrix

This is a user-friendly helper for calling #cast_full. The easiest way to call this function is using an options hash, e.g.,

n.cast(:stype => :yale, :dtype => :int64, :default => false)

For list and yale, :default sets the “default value” or “init” of the matrix. List allows a bit more freedom since non-zeros are permitted. For yale, unpredictable behavior may result if the value is not false, nil, or some version of 0. Dense discards :default.

dtype and stype are inferred from the matrix upon which #cast is called – so you only really need to provide one. You can actually call this function with no arguments, in which case it functions like #clone.

If your dtype is :object and you are converting from :dense to a sparse type, it is recommended that you provide a :default, as 0 may behave differently from its Float or Complex equivalent. If no option is given, Integer 0 will be used.



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/nmatrix/nmatrix.rb', line 239

def cast(*params)
  if (params.size > 0 && params[0].is_a?(Hash))
    opts = {
        :stype => self.stype,
        :dtype => self.dtype,
        :default => self.stype == :dense ? 0 : self.default_value
    }.merge(params[0])

    self.cast_full(opts[:stype], opts[:dtype], opts[:default])
  else
    params << self.stype if params.size == 0
    params << self.dtype if params.size == 1
    #HACK: the default value can cause an exception if dtype is not complex
    #and default_value is. (The ruby C code apparently won't convert these.)
    #Perhaps this should be fixed in the C code (in rubyval_to_cval).
    default_value = maybe_get_noncomplex_default_value(params[1])
    params << (self.stype == :dense ? 0 : default_value) if params.size == 2
    self.cast_full(*params)
  end

end

#cast_full(*args) ⇒ Object



147
148
149
150
151
152
153
154
# File 'lib/nmatrix/jruby/nmatrix_java.rb', line 147

def cast_full *args
  if args.is_a? Hash
    self.dtype = args[:dtype]
  else
    self.dtype = args[1]
  end
  return self
end

#cbrtObject



241
242
243
244
245
# File 'lib/nmatrix/jruby/operators.rb', line 241

def cbrt
  result = create_dummy_nmatrix
  result.s = @s.copy.mapToSelf(Cbrt.new())
  result
end

#ceilObject



267
268
269
270
271
272
273
# File 'lib/nmatrix/jruby/operators.rb', line 267

def ceil
  result = create_dummy_nmatrix
  # Need to be changed later
  result.dtype = :int64
  result.s = @s.copy.mapToSelf(Ceil.new())
  result
end

#cloneObject



118
119
120
121
122
123
124
# File 'lib/nmatrix/jruby/nmatrix_java.rb', line 118

def clone
  result = create_dummy_nmatrix
  # ArrayRealVector#clone is disable, hence use copy
  # that returns a deep copy of the object.
  result.s = @s.copy
  return result
end

#clone_structure(capacity = nil) ⇒ Object

call-seq:

clone_structure -> NMatrix

This function is like clone, but it only copies the structure and the default value. None of the other values are copied. It takes an optional capacity argument. This is mostly only useful for dense, where you may not want to initialize; for other types, you should probably use zeros_like.



1036
1037
1038
1039
1040
# File 'lib/nmatrix/nmatrix.rb', line 1036

def clone_structure(capacity = nil)
  opts = {stype: self.stype, default: self.default_value, dtype: self.dtype}
  opts = {capacity: capacity}.merge(opts) if self.yale?
  NMatrix.new(self.shape, opts)
end

#colsObject

call-seq:

cols -> Integer

This shortcut use #shape to return the number of columns (the second dimension) of the matrix.



280
281
282
# File 'lib/nmatrix/nmatrix.rb', line 280

def cols
  shape[1]
end

#column(column_number, get_by = :copy) ⇒ Object Also known as: col

call-seq:

column(column_number) -> NMatrix
column(column_number, get_by) -> NMatrix

Returns the column specified. Uses slicing by copy as default.

  • Arguments :

    • column_number -> Integer.

    • get_by -> Type of slicing to use, :copy or :reference.

  • Returns :

    • A NMatrix representing the requested column as a column vector.

Examples:

m = NMatrix.new(2, [1, 4, 9, 14], :int32) # =>  1   4
                                                9  14

m.column(1) # =>   4
                  14


529
530
531
# File 'lib/nmatrix/nmatrix.rb', line 529

def column(column_number, get_by = :copy)
  rank(1, column_number, get_by)
end

#complex_conjugate(new_stype = self.stype) ⇒ Object

call-seq:

complex_conjugate -> NMatrix
complex_conjugate(new_stype) -> NMatrix

Get the complex conjugate of this matrix. See also complex_conjugate! for an in-place operation (provided the dtype is already :complex64 or :complex128).

Doesn't work on list matrices, but you can optionally pass in the stype you want to cast to if you're dealing with a list matrix.

  • Arguments :

    • new_stype -> stype for the new matrix.

  • Returns :

    • If the original NMatrix isn't complex, the result is a :complex128 NMatrix. Otherwise, it's the original dtype.



653
654
655
# File 'lib/nmatrix/cruby/math.rb', line 653

def complex_conjugate(new_stype = self.stype)
  self.cast(new_stype, NMatrix::upcast(dtype, :complex64)).complex_conjugate!
end

#complex_conjugate!Object



161
162
163
# File 'ext/nmatrix/ruby_nmatrix.c', line 161

def complex_conjugate!

end

#complex_dtype?Boolean

call-seq:

complex_dtype?() -> Boolean

Checks if dtype is a complex type

Returns:

  • (Boolean)


377
378
379
# File 'lib/nmatrix/nmatrix.rb', line 377

def complex_dtype?
  [:complex64, :complex128].include?(self.dtype)
end

#concat(*matrices) ⇒ Object

call-seq:

matrix1.concat(*m2) -> NMatrix
matrix1.concat(*m2, rank) -> NMatrix
matrix1.hconcat(*m2) -> NMatrix
matrix1.vconcat(*m2) -> NMatrix
matrix1.dconcat(*m3) -> NMatrix

Joins two matrices together into a new larger matrix. Attempts to determine which direction to concatenate on by looking for the first common element of the matrix shape in reverse. In other words, concatenating two columns together without supplying rank will glue them into an n x 2 matrix.

You can also use hconcat, vconcat, and dconcat for the first three ranks. concat performs an hconcat when no rank argument is provided.

The two matrices must have the same dim.

  • Arguments :

    • matrices -> one or more matrices

    • rank -> Integer (for rank); alternatively, may use :row, :column, or

    :layer for 0, 1, 2, respectively



695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
# File 'lib/nmatrix/nmatrix.rb', line 695

def concat(*matrices)
  rank = nil
  rank = matrices.pop unless matrices.last.is_a?(NMatrix)

  # Find the first matching dimension and concatenate along that (unless rank is specified)
  if rank.nil?
    rank = self.dim-1
    self.shape.reverse_each.with_index do |s,i|
      matrices.each do |m|
        if m.shape[i] != s
          rank -= 1
          break
        end
      end
    end
  elsif rank.is_a?(Symbol) # Convert to numeric
    rank = {:row => 0, :column => 1, :col => 1, :lay => 2, :layer => 2}[rank]
  end

  # Need to figure out the new shape.
  new_shape = self.shape.dup
  new_shape[rank] = matrices.inject(self.shape[rank]) { |total,m| total + m.shape[rank] }

  # Now figure out the options for constructing the concatenated matrix.
  opts = {stype: self.stype, default: self.default_value, dtype: self.dtype}
  if self.yale?
    # We can generally predict the new capacity for Yale. Subtract out the number of rows
    # for each matrix being concatenated, and then add in the number of rows for the new
    # shape. That takes care of the diagonal. The rest of the capacity is represented by
    # the non-diagonal non-default values.
    new_cap = matrices.inject(self.capacity - self.shape[0]) do |total,m|
      total + m.capacity - m.shape[0]
    end - self.shape[0] + new_shape[0]
    opts = {capacity: new_cap}.merge(opts)
  end

  # Do the actual construction.
  n = NMatrix.new(new_shape, opts)

  # Figure out where to start concatenation. We don't know where it will end,
  # because each matrix may have own size along concat dimension.
  pos = Array.new(self.dim) { 0 }

  matrices.unshift(self)
  matrices.each do |m|
    # Figure out where to start and stop the concatenation. We'll use
    # NMatrices instead of Arrays because then we can do elementwise addition.
    ranges = m.shape.map.with_index { |s,i| pos[i]...(pos[i] + s) }

    n[*ranges] = m

    # Move over by the requisite amount
    pos[rank] = pos[rank] + m.shape[rank]
  end

  n
end

#conjugate_transposeObject

call-seq:

conjugate_transpose -> NMatrix

Calculate the conjugate transpose of a matrix. If your dtype is already complex, this should only require one copy (for the transpose).

  • Returns :

    • The conjugate transpose of the matrix as a copy.



667
668
669
# File 'lib/nmatrix/cruby/math.rb', line 667

def conjugate_transpose
  self.transpose.complex_conjugate!
end

#corrObject

Calculate the correlation matrix.

Raises:

  • (NotImplementedError)


368
369
370
371
372
# File 'lib/nmatrix/math.rb', line 368

def corr
  raise NotImplementedError, "Does not work for complex dtypes" if complex_dtype?
  standard_deviation = std
  cov / (standard_deviation.transpose.dot(standard_deviation))
end

#cosObject



129
130
131
132
133
# File 'lib/nmatrix/jruby/operators.rb', line 129

def cos
  result = create_dummy_nmatrix
  result.s = @s.copy.mapToSelf(Cos.new())
  result
end

#coshObject



165
166
167
168
169
# File 'lib/nmatrix/jruby/operators.rb', line 165

def cosh
  result = create_dummy_nmatrix
  result.s = @s.copy.mapToSelf(Cosh.new())
  result
end

#cov(opts = {}) ⇒ Object

Calculate the variance co-variance matrix

Options

  • :for_sample_data - Default true. If set to false will consider the denominator for population data (i.e. N, as opposed to N-1 for sample data).

References

Raises:

  • (TypeError)


355
356
357
358
359
360
361
362
363
364
365
# File 'lib/nmatrix/math.rb', line 355

def cov(opts={})
  raise TypeError, "Only works for non-integer dtypes" if integer_dtype?
   opts = {
    for_sample_data: true
  }.merge(opts)

  denominator      = opts[:for_sample_data] ? rows - 1 : rows
  ones             = NMatrix.ones [rows,1]
  deviation_scores = self - ones.dot(ones.transpose).dot(self) / rows
  deviation_scores.transpose.dot(deviation_scores) / denominator
end

#data_pointerObject

///////////////



68
# File 'ext/nmatrix/ruby_nmatrix.c', line 68

static VALUE nm_data_pointer(VALUE self);

#dconcat(*matrices) ⇒ Object

Depth concatenation with matrices.



764
765
766
# File 'lib/nmatrix/nmatrix.rb', line 764

def dconcat(*matrices)
  concat(*matrices, :layer)
end

#default_valueObject



43
44
45
# File 'ext/nmatrix/ruby_nmatrix.c', line 43

def default_value
  return nil
end

#dense?Boolean

call-seq:

m.dense? -> true or false

Determine if m is a dense matrix.

Returns:

  • (Boolean)


127
# File 'lib/nmatrix/shortcuts.rb', line 127

def dense?; return stype == :dense; end

#dense_storage_coords(s, slice_pos, coords_out, stride, offset) ⇒ Object

array, int, array



181
182
183
184
185
186
187
188
189
190
# File 'lib/nmatrix/jruby/slice.rb', line 181

def dense_storage_coords(s, slice_pos, coords_out, stride, offset)  #array, int, array
  temp_pos = slice_pos;

  (0...dim).each do |i|
    coords_out[i] = (temp_pos - temp_pos % stride[i])/stride[i] - offset[i];
    temp_pos = temp_pos % stride[i]
  end

  return temp_pos
end

#dense_storage_get(slice, stride) ⇒ Object



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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/nmatrix/jruby/slice.rb', line 118

def dense_storage_get(slice,stride)
  if slice[:single]
    return dense_storage_pos(slice[:coords],stride)
  else
    shape = @shape.dup
    (0...@dim).each do |i|
      shape[i] = slice[:lengths][i]
    end
    psrc = dense_storage_pos(slice[:coords], stride)
    src = {}
    result = NMatrix.new(:copy)
    result.dim = dim
    result.dtype = @dtype
    resultShape= Array.new(dim)
    (0...dim).each do |i|
      resultShape[i]  = slice[:lengths][i]
    end
    result.shape = resultShape
    dest = {}
    src[:stride] = get_stride(self)
    if (@dtype == :object)
      src[:elements] = @s
    else
      src[:elements] = @s.toArray().to_a
    end
    dest[:stride] = get_stride(result)
    dest[:shape] = resultShape
    dest[:elements] = []
    temp = []
    s = (slice_copy(src, dest, slice[:lengths], 0, psrc,0))
    # if
    # arr = Java::double[s.length].new
    if (@dtype == :object)
      arr = Java::boolean[s.length].new
    else
      arr = Java::double[s.length].new
    end
    (0...s.length).each do |i|
      arr[i] = s[i]
    end
    if (@dtype == :object)
      result.s = arr
    else
      result.s = ArrayRealVector.new(arr)
    end

    return result
  end
end

#dense_storage_pos(coords, stride) ⇒ Object



192
193
194
195
196
197
198
199
# File 'lib/nmatrix/jruby/slice.rb', line 192

def dense_storage_pos(coords,stride)
  pos = 0;
  offset = 0
  (0...@dim).each do |i|
    pos += coords[i]  * stride[i] ;
  end
  return pos + offset;
end

#dense_storage_set(slice, right) ⇒ Object



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/nmatrix/jruby/slice.rb', line 221

def dense_storage_set(slice, right)
  stride = get_stride(self)
  v_size = 1

  if right.is_a?(NMatrix)
    right = right.s.toArray.to_a
  end

  if(right.is_a?(Array))
    v_size = right.length
    v = right
    if (dtype == :RUBYOBJ)
      # nm_register_values(reinterpret_cast<VALUE*>(v), v_size)
    end

    (0...v_size).each do |m|
      v[m] = right[m]
    end
  else
    v = [right]
    if (@dtype == :RUBYOBJ)
      # nm_register_values(reinterpret_cast<VALUE*>(v), v_size)
    end
  end
  if(slice[:single])
    # reinterpret_cast<D*>(s->elements)[nm_dense_storage_pos(s, slice->coords)] = v;
    pos = dense_storage_pos(slice[:coords],stride)
    if @dtype == :object
      @s[pos] = v[0]
    else
      @s.setEntry(pos, v[0])
    end
  else
    v_offset = 0
    dest = {}
    dest[:stride] = get_stride(self)
    dest[:shape] = shape
    # dest[:elements] = @s.toArray().to_a
    dense_pos = dense_storage_pos(slice[:coords],stride)
    slice_set(dest, slice[:lengths], dense_pos, 0, v, v_size, v_offset)
  end
end

#detObject

call-seq:

det -> determinant

Calculate the determinant by way of LU decomposition. This is accomplished using clapack_getrf, and then by taking the product of the diagonal elements. There is a risk of underflow/overflow.

There are probably also more efficient ways to calculate the determinant. This method requires making a copy of the matrix, since clapack_getrf modifies its input.

For smaller matrices, you may be able to use #det_exact.

This function is guaranteed to return the same type of data in the matrix upon which it is called.

Integer matrices are converted to floating point matrices for the purposes of performing the calculation, as xGETRF can't work on integer matrices.

  • Returns :

    • The determinant of the matrix. It's the same type as the matrix's dtype.

  • Raises :

    • ShapeError -> Must be used on square matrices.

Raises:



611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
# File 'lib/nmatrix/cruby/math.rb', line 611

def det
  raise(ShapeError, "determinant can be calculated only for square matrices") unless self.dim == 2 && self.shape[0] == self.shape[1]

  # Cast to a dtype for which getrf is implemented
  new_dtype = self.integer_dtype? ? :float64 : self.dtype
  copy = self.cast(:dense, new_dtype)

  # Need to know the number of permutations. We'll add up the diagonals of
  # the factorized matrix.
  pivot = copy.getrf!

  num_perm = 0 #number of permutations
  pivot.each_with_index do |swap, i|
    #pivot indexes rows starting from 1, instead of 0, so need to subtract 1 here
    num_perm += 1 if swap-1 != i
  end
  prod = num_perm % 2 == 1 ? -1 : 1 # odd permutations => negative
  [shape[0],shape[1]].min.times do |i|
    prod *= copy[i,i]
  end

  # Convert back to an integer if necessary
  new_dtype != self.dtype ? prod.round : prod #prevent rounding errors
end

#det_exactObject

Raises:



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'ext/nmatrix/ruby_nmatrix.c', line 157

def det_exact
  # if (:stype != :dense)
  #   raise Exception.new("can only calculate exact determinant for dense matrices")
  #   return nil
  # end
  raise(DataTypeError, "cannot call det_exact on unsigned type") if(self.dtype == :byte)
  if (@dim != 2 || @shape[0] != @shape[1])
    raise(ShapeError, "matrices must be square to have a determinant defined")
    return nil
  end
  to_return = nil
  if (dtype == :object)
    # to_return = *reinterpret_cast<VALUE*>(result);
  else
    to_return = LUDecomposition.new(self.twoDMat).getDeterminant()
  end

  return to_return.round(3)
end

#det_exact2Object



273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/nmatrix/jruby/nmatrix_java.rb', line 273

def det_exact2
  if (@dim != 2 || @shape[0] != @shape[1])
    raise(ShapeError, "matrices must be square to have a determinant defined")
    return nil
  end
  to_return = nil
  if (dtype == :object)
    # to_return = *reinterpret_cast<VALUE*>(result);
  else
    to_return = LUDecomposition.new(self.twoDMat).getDeterminant()
  end

  return to_return.round(3)
end

#diagonal(main_diagonal = true) ⇒ Object

Return the main diagonal or antidiagonal a matrix. Only works with 2D matrices.

Arguments

  • main_diagonal - Defaults to true. If passed 'false', then will return the antidiagonal of the matrix.

References



294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/nmatrix/nmatrix.rb', line 294

def diagonal main_diagonal=true
  diag_size = [cols, rows].min
  diag = NMatrix.new [diag_size], dtype: dtype

  if main_diagonal
    0.upto(diag_size-1) do |i|
      diag[i] = self[i,i]
    end
  else
    row = 0
    (diag_size-1).downto(0) do |col|
      diag[row] = self[row,col]
      row += 1
    end
  end

  diag
end

#dot(other) ⇒ Object

///////////////////////



694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
# File 'lib/nmatrix/jruby/nmatrix_java.rb', line 694

def dot(right_v)
  if (right_v.is_a?(NMatrix) && self.stype == :dense && right_v.stype == :dense &&
      self.dim == 2 && right_v.dim == 2 && self.shape[1] == right_v.shape[0])

    result_dtype = NMatrix.upcast(self.dtype,right_v.dtype)
    left = self.dtype == result_dtype ? self : self.cast(dtype: result_dtype)
    right = right_v.dtype == result_dtype ? right_v : right_v.cast(dtype: result_dtype)

    left = left.clone if left.is_ref?
    right = right.clone if right.is_ref?

    result_m = left.shape[0]
    result_n = right.shape[1]
    left_n = left.shape[1]
    vector = result_n == 1
    result = NMatrix.new([result_m,result_n], dtype: result_dtype)

    if vector
      NMatrix::BLAS.cblas_gemv(false, result_m, left_n, 1, left, left_n, right, 1, 0, result, 1)
    else
      NMatrix::BLAS.cblas_gemm(:row, false, false, result_m, result_n, left_n, 1, left, left_n, right, result_n, 0, result, result_n)
    end
    return result
  else
    #internal_dot will handle non-dense matrices (and also dot-products for NMatrix's with dim=1),
    #and also all error-handling if the input is not valid
    self.internal_dot(right_v)
  end
end

#each(&bl) ⇒ Object

call-seq:

each -> Enumerator

Enumerate through the matrix. @see Enumerable#each

For dense, this actually calls a specialized each iterator (in C). For yale and list, it relies upon #each_with_indices (which is about as fast as reasonably possible for C code).



40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/nmatrix/enumerate.rb', line 40

def each &bl
  if self.stype == :dense
    self.__dense_each__(&bl)
  elsif block_given?
    self.each_with_indices(&bl)
  else # Handle case where no block is given
    Enumerator.new do |yielder|
      self.each_with_indices do |params|
        yielder.yield params
      end
    end
  end
end

#each_column(get_by = :reference) ⇒ Object

call-seq:

each_column { |column| block } -> NMatrix

Iterate through each column, referencing it as an NMatrix slice.



145
146
147
148
149
150
151
# File 'lib/nmatrix/enumerate.rb', line 145

def each_column(get_by=:reference)
  return enum_for(:each_column, get_by) unless block_given?
  (0...self.shape[1]).each do |j|
    yield self.column(j, get_by)
  end
  self
end

#each_layer(get_by = :reference) ⇒ Object

call-seq:

each_layer -> { |column| block } -> ...

Iterate through each layer, referencing it as an NMatrix slice.

Note: If you have a 3-dimensional matrix, the first dimension contains rows, the second contains columns, and the third contains layers.



161
162
163
164
165
166
167
# File 'lib/nmatrix/enumerate.rb', line 161

def each_layer(get_by=:reference)
  return enum_for(:each_layer, get_by) unless block_given?
  (0...self.shape[2]).each do |k|
    yield self.layer(k, get_by)
  end
  self
end

#each_ordered_stored_with_indicesObject



53
54
55
# File 'ext/nmatrix/ruby_nmatrix.c', line 53

def each_ordered_stored_with_indices

end

#each_rank(dimen = 0, get_by = :reference) ⇒ Object Also known as: each_along_dim

call-seq:

each_rank() -> NMatrix
each_rank() { |rank| block } -> NMatrix
each_rank(dimen) -> Enumerator
each_rank(dimen) { |rank| block } -> NMatrix

Generic for @each_row, @each_col

Iterate through each rank by reference.

Parameters:

  • dimen (Fixnum) (defaults to: 0)

    the rank being iterated over.



118
119
120
121
122
123
124
# File 'lib/nmatrix/enumerate.rb', line 118

def each_rank(dimen=0, get_by=:reference)
  return enum_for(:each_rank, dimen, get_by) unless block_given?
  (0...self.shape[dimen]).each do |idx|
    yield self.rank(dimen, idx, get_by)
  end
  self
end

#each_row(get_by = :reference) ⇒ Object

call-seq:

each_row { |row| block } -> NMatrix

Iterate through each row, referencing it as an NMatrix slice.



132
133
134
135
136
137
138
# File 'lib/nmatrix/enumerate.rb', line 132

def each_row(get_by=:reference)
  return enum_for(:each_row, get_by) unless block_given?
  (0...self.shape[0]).each do |i|
    yield self.row(i, get_by)
  end
  self
end

#each_stored_with_index(&block) ⇒ Object

call-seq:

each_stored_with_index -> Enumerator

Allow iteration across a vector NMatrix's stored values. See also @each_stored_with_indices

Raises:

  • (NotImplementedError)


176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/nmatrix/enumerate.rb', line 176

def each_stored_with_index(&block)
  raise(NotImplementedError, "only works for dim 2 vectors") unless self.dim <= 2
  return enum_for(:each_stored_with_index) unless block_given?

  self.each_stored_with_indices do |v, i, j|
    if shape[0] == 1
      yield(v,j)
    elsif shape[1] == 1
      yield(v,i)
    else
      method_missing(:each_stored_with_index, &block)
    end
  end
  self
end

#each_stored_with_indicesObject



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'ext/nmatrix/ruby_nmatrix.c', line 52

def each_stored_with_indices
  nmatrix = create_dummy_nmatrix
  stride = get_stride(self)
  offset = 0
  #Create indices and initialize them to zero
  coords = Array.new(dim){ 0 }

  shape_copy =  Array.new(dim)

  (0...size).each do |k|
    dense_storage_coords(nmatrix, k, coords, stride, offset)
    slice_index = dense_storage_pos(coords,stride)
    ary = Array.new
    if (@dtype == :object)
      ary << self.s[slice_index]
    else
      ary << self.s.toArray.to_a[slice_index]
    end
    (0...dim).each do |p|
      ary << coords[p]
    end
    # yield the array which now consists of the value and the indices
    yield(ary)
  end if block_given?

  return nmatrix
end

#each_with_indicesObject

Iterators public methods



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'ext/nmatrix/ruby_nmatrix.c', line 51

def each_with_indices
  nmatrix = create_dummy_nmatrix
  stride = get_stride(self)
  offset = 0
  #Create indices and initialize them to zero
  coords = Array.new(dim){ 0 }

  shape_copy =  Array.new(dim)
  (0...size).each do |k|
    dense_storage_coords(nmatrix, k, coords, stride, offset)
    slice_index = dense_storage_pos(coords,stride)
    ary = Array.new
    if (@dtype == :object)
      ary << self.s[slice_index]
    else
      ary << self.s.toArray.to_a[slice_index]
    end
    (0...dim).each do |p|
      ary << coords[p]
    end

    # yield the array which now consists of the value and the indices
    yield(ary)
  end if block_given?

  return nmatrix
end

#entriesObject



126
127
128
# File 'lib/nmatrix/jruby/nmatrix_java.rb', line 126

def entries
  return @s.toArray.to_a
end

#erfObject



229
230
231
232
233
# File 'lib/nmatrix/jruby/operators.rb', line 229

def erf
  result = create_dummy_nmatrix
  result.s = ArrayRealVector.new MathHelper.erf(@s.toArray)
  result
end

#erfcObject



235
236
237
238
239
# File 'lib/nmatrix/jruby/operators.rb', line 235

def erfc
  result = create_dummy_nmatrix
  result.s = ArrayRealVector.new MathHelper.erfc(@s.toArray)
  result
end

#exact_inverseObject Also known as: invert_exactly

call-seq:

exact_inverse -> NMatrix

Make a copy of the matrix, then invert using exact_inverse

  • Returns :

    • A dense NMatrix. Will be the same type as the input NMatrix,

    except if the input is an integral dtype, in which case it will be a :float64 NMatrix.

  • Raises :

    • StorageTypeError -> only implemented on dense matrices.

    • ShapeError -> matrix must be square.

    • NotImplementedError -> cannot find exact inverse of matrix with size greater than 3



153
154
155
156
157
158
159
160
161
162
163
# File 'lib/nmatrix/math.rb', line 153

def exact_inverse
  #write this in terms of exact_inverse! so plugins will only have to overwrite
  #exact_inverse! and not exact_inverse
  if self.integer_dtype?
    cloned = self.cast(dtype: :float64)
    cloned.exact_inverse!
  else
    cloned = self.clone
    cloned.exact_inverse!
  end
end

#exact_inverse!Object

call-seq:

exact_inverse! -> NMatrix

Calulates inverse_exact of a matrix of size 2 or 3. Only works on dense matrices.

  • Raises :

    • DataTypeError -> cannot invert an integer matrix in-place.

    • NotImplementedError -> cannot find exact inverse of matrix with size greater than 3 #

Raises:



124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/nmatrix/math.rb', line 124

def exact_inverse!
  raise(ShapeError, "Cannot invert non-square matrix") unless self.dim == 2 && self.shape[0] == self.shape[1]
  raise(DataTypeError, "Cannot invert an integer matrix in-place") if self.integer_dtype?
  #No internal implementation of getri, so use this other function
  n = self.shape[0]
  if n>3
    raise(NotImplementedError, "Cannot find exact inverse of matrix of size greater than 3")
  else
    clond=self.clone
    __inverse_exact__(clond, n, n)
  end
end

#expObject



197
198
199
200
201
# File 'lib/nmatrix/jruby/operators.rb', line 197

def exp
  result = create_dummy_nmatrix
  result.s = @s.copy.mapToSelf(Exp.new())
  result
end

#factorize_choleskyObject

call-seq:

factorize_cholesky -> [upper NMatrix, lower NMatrix]

Calculates the Cholesky factorization of a matrix and returns the upper and lower matrices such that A=LU and L=U*, where * is either the transpose or conjugate transpose.

Unlike potrf!, this makes method requires that the original is matrix is symmetric or Hermitian. However, it is still your responsibility to make sure it is positive-definite.



220
221
222
223
224
225
# File 'lib/nmatrix/cruby/math.rb', line 220

def factorize_cholesky
  raise "Matrix must be symmetric/Hermitian for Cholesky factorization" unless self.hermitian?
  l = self.clone.potrf_lower!.tril!
  u = l.conjugate_transpose
  [u,l]
end

#factorize_lu(with_permutation_matrix = nil) ⇒ Object

call-seq:

factorize_lu -> ...

LU factorization of a matrix. Optionally return the permutation matrix.

Note that computing the permutation matrix will introduce a slight memory
and time overhead.

Arguments

with_permutation_matrix - If set to true will return the permutation

matrix alongwith the LU factorization as a second return value.

Raises:

  • (NotImplementedError)


240
241
242
243
244
245
246
247
248
249
# File 'lib/nmatrix/cruby/math.rb', line 240

def factorize_lu with_permutation_matrix=nil
  raise(NotImplementedError, "only implemented for dense storage") unless self.stype == :dense
  raise(NotImplementedError, "matrix is not 2-dimensional") unless self.dimensions == 2

  t     = self.clone
  pivot = t.getrf!
  return t unless with_permutation_matrix

  [t, FactorizeLUMethods.permutation_matrix_from(pivot)]
end

#factorize_qrObject

call-seq:

factorize_qr -> [Q,R]

QR factorization of a matrix without column pivoting. Q is orthogonal and R is upper triangular if input is square or upper trapezoidal if input is rectangular.

Only works for dense matrices.

  • Returns :

    • Array containing Q and R matrices

  • Raises :

    • StorageTypeError -> only implemented for desnse storage.

    • ShapeError -> Input must be a 2-dimensional matrix to have a QR decomposition.

Raises:

  • (NotImplementedError)


268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/nmatrix/cruby/math.rb', line 268

def factorize_qr
  raise(NotImplementedError, "only implemented for dense storage") unless self.stype == :dense
  raise(ShapeError, "Input must be a 2-dimensional matrix to have a QR decomposition") unless self.dim == 2

  rows, columns = self.shape
  r = self.clone
  tau =  r.geqrf!

  #Obtain Q
  q = self.complex_dtype? ? r.unmqr(tau) : r.ormqr(tau)

  #Obtain R
  if rows <= columns
    r.upper_triangle!
  #Need to account for upper trapezoidal structure if R is a tall rectangle (rows > columns)
  else
    r[0...columns, 0...columns].upper_triangle!
    r[columns...rows, 0...columns] = 0
  end

  [q,r]
end

#fftNMatrix

Compute 1D FFT of the matrix using FFTW default parameters.

Examples:

Compute 1D FFT of an NMatrix.

nm = NMatrix.new([10],
  [
    Complex(9.32,0), Complex(44,0), Complex(125,0), Complex(34,0),
    Complex(31,0),   Complex(44,0), Complex(12,0),  Complex(1,0),
    Complex(53.23,0),Complex(-23.23,0)
  ], dtype: :complex128)
nm.fft

Returns:

  • (NMatrix)

    NMatrix of dtype :complex128 containing computed values.



45
46
47
48
49
50
51
# File 'lib/nmatrix/fftw.rb', line 45

def fft
  input = self.dtype == :complex128 ? self : self.cast(dtype: :complex128)
  plan  = NMatrix::FFTW::Plan.new([self.size])
  plan.set_input input
  plan.execute
  plan.output
end

#fft2NMatrix

Compute 2D FFT of a 2D matrix using FFTW default parameters.

Returns:

  • (NMatrix)

    NMatrix of dtype :complex128 containing computed values.

Raises:



55
56
57
58
59
60
61
62
# File 'lib/nmatrix/fftw.rb', line 55

def fft2
  raise ShapeError, "Shape must be 2 (is #{self.shape})" if self.shape.size != 2
  input = self.dtype == :complex128 ? self : self.cast(dtype: :complex128)
  plan  = NMatrix::FFTW::Plan.new(self.shape, dim: 2)
  plan.set_input input
  plan.execute
  plan.output
end

#flat_mapObject

call-seq:

flat_map -> Enumerator
flat_map { |elem| block } -> Array

Maps using Enumerator (returns an Array or an Enumerator)



60
# File 'lib/nmatrix/enumerate.rb', line 60

alias_method :flat_map, :map

#float_dtype?Boolean

call-seq:

float_dtype?() -> Boolean

Checks if dtype is a floating point type

Returns:

  • (Boolean)


367
368
369
# File 'lib/nmatrix/nmatrix.rb', line 367

def float_dtype?
  [:float32, :float64].include?(dtype)
end

#floorObject



259
260
261
262
263
264
265
# File 'lib/nmatrix/jruby/operators.rb', line 259

def floor
  result = create_dummy_nmatrix
  # Need to be changed later
  result.dtype = :int64
  result.s = @s.copy.mapToSelf(Floor.new())
  result
end

#fro_matrix_normObject

Norm calculation methods Frobenius norm: the Euclidean norm of the matrix, treated as if it were a vector



630
631
632
633
634
635
636
637
# File 'lib/nmatrix/math.rb', line 630

def fro_matrix_norm
  #float64 has to be used in any case, since nrm2 will not yield correct result for float32
  self_cast = self.cast(:dtype => :float64)

  column_vector = self_cast.reshape([self.size, 1])

  return column_vector.nrm2
end

#gammaObject



247
248
249
250
251
# File 'lib/nmatrix/jruby/operators.rb', line 247

def gamma
  result = create_dummy_nmatrix
  result.s = ArrayRealVector.new MathHelper.gamma(@s.toArray)
  result
end

#geqrf!Object

call-seq:

geqrf! -> shape.min x 1 NMatrix

QR factorization of a general M-by-N matrix A.

The QR factorization is A = QR, where Q is orthogonal and R is Upper Triangular A is overwritten with the elements of R and Q with Q being represented by the elements below A's diagonal and an array of scalar factors in the output NMatrix.

The matrix Q is represented as a product of elementary reflectors

Q = H(1) H(2) . . . H(k), where k = min(m,n).

Each H(i) has the form

H(i) = I - tau * v * v'

www.netlib.org/lapack/explore-html/d3/d69/dgeqrf_8f.html

Only works for dense matrices.

  • Returns :

    • Vector TAU. Q and R are stored in A. Q is represented by TAU and A

  • Raises :

    • StorageTypeError -> LAPACK functions only work on dense matrices.

Raises:

  • (NotImplementedError)


262
263
264
265
266
267
268
269
# File 'lib/nmatrix/lapacke.rb', line 262

def geqrf!
  raise(StorageTypeError, "LAPACK functions only work on dense matrices") unless self.dense?
  
  tau = NMatrix.new([self.shape.min,1], dtype: self.dtype)
  NMatrix::LAPACK::lapacke_geqrf(:row, self.shape[0], self.shape[1], self, self.shape[1], tau)
  
  tau
end

#gesdd(workspace_size = nil) ⇒ Object

call-seq:

gesdd -> [u, sigma, v_transpose]
gesdd -> [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.



507
508
509
# File 'lib/nmatrix/cruby/math.rb', line 507

def gesdd(workspace_size=nil)
  self.clone.gesdd!(workspace_size)
end

#gesdd!(workspace_size = nil) ⇒ Object

call-seq:

gesdd! -> [u, sigma, v_transpose]
gesdd! -> [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. This is destructive, modifying the source NMatrix. See also #gesvd.

Optionally accepts a workspace_size parameter, which will be honored only if it is larger than what LAPACK requires.



492
493
494
# File 'lib/nmatrix/cruby/math.rb', line 492

def gesdd!(workspace_size=nil)
  NMatrix::LAPACK::gesdd(self, workspace_size)
end

#gesvd(workspace_size = 1) ⇒ Object

call-seq:

gesvd -> [u, sigma, v_transpose]
gesvd -> [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.



475
476
477
# File 'lib/nmatrix/cruby/math.rb', line 475

def gesvd(workspace_size=1)
  self.clone.gesvd!(workspace_size)
end

#gesvd!(workspace_size = 1) ⇒ Object

call-seq:

gesvd! -> [u, sigma, v_transpose]
gesvd! -> [u, sigma, v_conjugate_transpose] # complex

Compute the singular value decomposition of a matrix using LAPACK's GESVD function. This is destructive, modifying the source NMatrix. See also #gesdd.

Optionally accepts a workspace_size parameter, which will be honored only if it is larger than what LAPACK requires.



461
462
463
# File 'lib/nmatrix/cruby/math.rb', line 461

def gesvd!(workspace_size=1)
  NMatrix::LAPACK::gesvd(self, workspace_size)
end

#get_slice(dim, args, shape_array) ⇒ Object



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/nmatrix/jruby/slice.rb', line 3

def get_slice(dim, args, shape_array)
  slice = {}
  slice[:coords]=[]
  slice[:lengths]=[]
  slice[:single] = true

  argc = args.length

  t = 0
  (0...dim).each do |r|
    v = t == argc ? nil : args[t]

    if(argc - t + r < dim && shape_array[r] ==1)
      slice[:coords][r]  = 0
      slice[:lengths][r] = 1
    elsif v.is_a?(Fixnum)
      v_ = v.to_i.to_int
      if (v_ < 0) # checking for negative indexes
        slice[:coords][r]  = shape_array[r]+v_
      else
        slice[:coords][r]  = v_
      end
      slice[:lengths][r] = 1
      t+=1
    elsif (v.is_a?(Symbol) && v == :*)
      slice[:coords][r] = 0
      slice[:lengths][r] = shape_array[r]
      slice[:single] = false
      t+=1
    elsif v.is_a?(Range)
      begin_ = v.begin
      end_ = v.end
      excl = v.exclude_end?
      slice[:coords][r] = (begin_ < 0) ? shape[r] + begin_ : begin_

      # Exclude last element for a...b range
      if (end_ < 0)
        slice[:lengths][r] = shape_array[r] + end_ - slice[:coords][r] + (excl ? 0 : 1)
      else
        slice[:lengths][r] = end_ - slice[:coords][r] + (excl ? 0 : 1)
      end

      slice[:single] = false
      t+=1
    else
      raise(ArgumentError, "expected Fixnum or Range for slice component instead of #{v.class}")
    end

    if (slice[:coords][r] > shape_array[r] || slice[:coords][r] + slice[:lengths][r] > shape_array[r])
      raise(RangeError, "slice is larger than matrix in dimension #{r} (slice component #{t})")
    end
  end

  return slice
end

#get_stride(nmatrix) ⇒ Object



59
60
61
62
63
64
65
66
67
68
# File 'lib/nmatrix/jruby/slice.rb', line 59

def get_stride(nmatrix)
  stride = Array.new()
  (0...nmatrix.dim).each do |i|
    stride[i] = 1;
    (i+1...dim).each do |j|
      stride[i] *= nmatrix.shape[j]
    end
  end
  stride
end

#getrf!Object

call-seq:

getrf! -> Array

LU factorization of a general M-by-N matrix A using partial pivoting with row interchanges. The LU factorization is A = PLU, where P is a row permutation matrix, L is a lower triangular matrix with unit diagonals, and U is an upper triangular matrix (note that this convention is different from the clapack_getrf behavior, but matches the standard LAPACK getrf). A is overwritten with the elements of L and U (the unit diagonal elements of L are not saved). P is not returned directly and must be constructed from the pivot array ipiv. The row indices in ipiv are indexed starting from 1. Only works for dense matrices.

  • Returns :

    • The IPIV vector. The L and U matrices are stored in A.

  • Raises :

    • StorageTypeError -> ATLAS functions only work on dense matrices.



53
54
55
56
57
58
59
# File 'lib/nmatrix/cruby/math.rb', line 53

def getrf!
  raise(StorageTypeError, "LAPACK functions only work on dense matrices") unless self.dense?

  ipiv = NMatrix::LAPACK::lapacke_getrf(:row, self.shape[0], self.shape[1], self, self.shape[1])

  return ipiv
end

#hconcat(*matrices) ⇒ Object

Horizontal concatenation with matrices.



754
755
756
# File 'lib/nmatrix/nmatrix.rb', line 754

def hconcat(*matrices)
  concat(*matrices, :column)
end

#hermitian?Boolean

Returns:

  • (Boolean)
  • (Boolean)


150
151
152
# File 'ext/nmatrix/ruby_nmatrix.c', line 150

def hermitian?
  return is_symmetric(true)
end

#hessenbergObject

Reduce self to upper hessenberg form using householder transforms.

References



287
288
289
# File 'lib/nmatrix/math.rb', line 287

def hessenberg
  clone.hessenberg!
end

#hessenberg!Object

Destructive version of #hessenberg

Raises:



292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/nmatrix/math.rb', line 292

def hessenberg!
  raise ShapeError, "Trying to reduce non 2D matrix to hessenberg form" if
    shape.size != 2
  raise ShapeError, "Trying to reduce non-square matrix to hessenberg form" if
    shape[0] != shape[1]
  raise StorageTypeError, "Matrix must be dense" if stype != :dense
  raise TypeError, "Works with float matrices only" unless
    [:float64,:float32].include?(dtype)

  __hessenberg__(self)
  self
end

#hypot(other, scalar = false) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/nmatrix/jruby/operators.rb', line 109

def hypot(other, scalar=false)
  result = create_dummy_nmatrix
  if scalar
    result.s = ArrayRealVector.new MathHelper.hypotScalar(other, @s.toArray)
  else
    if other.is_a? NMatrix
      result.s = ArrayRealVector.new MathHelper.hypot(other.s.toArray, @s.toArray)
    else
      result.s = ArrayRealVector.new MathHelper.hypotScalar(other, @s.toArray)
    end
  end
  result
end

#index(value) ⇒ Object

Returns the index of the first occurence of the specified value. Returns an array containing the position of the value, nil in case the value is not found.



1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
# File 'lib/nmatrix/nmatrix.rb', line 1013

def index(value)
  index = nil

  self.each_with_indices do |yields|
    if yields.first == value
      yields.shift
      index = yields
      break
    end
  end

  index
end

#inf_matrix_norm(minus = false) ⇒ Object

Infinity norm: the maximum/minimum absolute row sum of the matrix



665
666
667
668
669
670
671
672
673
674
675
# File 'lib/nmatrix/math.rb', line 665

def inf_matrix_norm minus = false
  number_of_rows = self.rows
  row_sums = []

  number_of_rows.times do |i|
    row_sums << self.row(i).inject(0) { |sum, number| sum += number.abs}
  end

  return row_sums.max unless minus
  return row_sums.min
end

#initialize_copyObject



35
# File 'ext/nmatrix/ruby_nmatrix.c', line 35

static VALUE nm_init_copy(VALUE copy, VALUE original);

#inject(sym) ⇒ Object

call-seq:

inject -> symbol

This overrides the inject function to use map_stored for yale matrices



1005
1006
1007
1008
# File 'lib/nmatrix/nmatrix.rb', line 1005

def inject(sym)
  return super(sym) unless self.yale?
  return self.map_stored.inject(sym)
end

#inject_rank(dimen = 0, initial = nil, dtype = nil) ⇒ NMatrix Also known as: reduce_along_dim, inject_along_dim

call-seq:

inject_rank() -> Enumerator
inject_rank(dimen) -> Enumerator
inject_rank(dimen, initial) -> Enumerator
inject_rank(dimen, initial, dtype) -> Enumerator
inject_rank() { |elem| block } -> NMatrix
inject_rank(dimen) { |elem| block } -> NMatrix
inject_rank(dimen, initial) { |elem| block } -> NMatrix
inject_rank(dimen, initial, dtype) { |elem| block } -> NMatrix

Reduces an NMatrix using a supplied block over a specified dimension. The block should behave the same way as for Enumerable#reduce.

Parameters:

  • dimen (Integer) (defaults to: 0)

    the dimension being reduced

  • initial (Numeric) (defaults to: nil)

    the initial value for the reduction (i.e. the usual parameter to Enumerable#reduce). Supply nil or do not supply this argument to have it follow the usual Enumerable#reduce behavior of using the first element as the initial value.

  • dtype (Symbol) (defaults to: nil)

    if non-nil/false, forces the accumulated result to have this dtype

Returns:

  • (NMatrix)

    an NMatrix with the same number of dimensions as the input, but with the input dimension now having size 1. Each element is the result of the reduction at that position along the specified dimension.

Raises:

  • (RangeError)


218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/nmatrix/enumerate.rb', line 218

def inject_rank(dimen=0, initial=nil, dtype=nil)

  raise(RangeError, "requested dimension (#{dimen}) does not exist (shape: #{shape})") if dimen > self.dim

  return enum_for(:inject_rank, dimen, initial, dtype) unless block_given?

  new_shape = shape.dup
  new_shape[dimen] = 1

  first_as_acc = false

  if initial then
    acc = NMatrix.new(new_shape, initial, :dtype => dtype || self.dtype, stype: self.stype)
  else
    each_rank(dimen) do |sub_mat|
      acc = (sub_mat.is_a?(NMatrix) and !dtype.nil? and dtype != self.dtype) ? sub_mat.cast(self.stype, dtype) : sub_mat
      break
    end
    first_as_acc = true
  end

  each_rank(dimen) do |sub_mat|
    if first_as_acc
      first_as_acc = false
      next
    end
    acc = yield(acc, sub_mat)
  end

  acc
end

#inspectObject

:nodoc:



340
341
342
343
344
# File 'lib/nmatrix/nmatrix.rb', line 340

def inspect #:nodoc:
  original_inspect = super()
  original_inspect = original_inspect[0...original_inspect.size-1]
  original_inspect + " " + inspect_helper.join(" ") + ">"
end

#integer_dtype?Boolean

call-seq:

integer_dtype?() -> Boolean

Checks if dtype is an integer type

Returns:

  • (Boolean)


358
359
360
# File 'lib/nmatrix/nmatrix.rb', line 358

def integer_dtype?
  [:byte, :int8, :int16, :int32, :int64].include?(self.dtype)
end

#internal_dotObject



38
# File 'lib/nmatrix/lapack_ext_common.rb', line 38

alias_method :internal_dot, :dot

#invertObject Also known as: inverse

call-seq:

invert -> NMatrix

Make a copy of the matrix, then invert using Gauss-Jordan elimination. Works without LAPACK.

  • Returns :

    • A dense NMatrix. Will be the same type as the input NMatrix,

    except if the input is an integral dtype, in which case it will be a :float64 NMatrix.

  • Raises :

    • StorageTypeError -> only implemented on dense matrices.

    • ShapeError -> matrix must be square.



102
103
104
105
106
107
108
109
110
111
112
# File 'lib/nmatrix/math.rb', line 102

def invert
  #write this in terms of invert! so plugins will only have to overwrite
  #invert! and not invert
  if self.integer_dtype?
    cloned = self.cast(dtype: :float64)
    cloned.invert!
  else
    cloned = self.clone
    cloned.invert!
  end
end

#invert!Object

call-seq:

invert! -> NMatrix

Use LAPACK to calculate the inverse of the matrix (in-place) if available. Only works on dense matrices. Alternatively uses in-place Gauss-Jordan elimination.

  • Raises :

    • StorageTypeError -> only implemented on dense matrices.

    • ShapeError -> matrix must be square.

    • DataTypeError -> cannot invert an integer matrix in-place.

Raises:



77
78
79
80
81
82
83
84
# File 'lib/nmatrix/math.rb', line 77

def invert!
  raise(StorageTypeError, "invert only works on dense matrices currently") unless self.dense?
  raise(ShapeError, "Cannot invert non-square matrix") unless self.dim == 2 && self.shape[0] == self.shape[1]
  raise(DataTypeError, "Cannot invert an integer matrix in-place") if self.integer_dtype?

  #No internal implementation of getri, so use this other function
  __inverse__(self, true)
end

#is_ref?Boolean

Returns:

  • (Boolean)
  • (Boolean)


61
62
63
# File 'ext/nmatrix/ruby_nmatrix.c', line 61

def is_ref?

end

#is_symmetric(hermitian) ⇒ Object



726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
# File 'lib/nmatrix/jruby/nmatrix_java.rb', line 726

def is_symmetric(hermitian)
  is_symmetric = true

  if (@shape[0] == @shape[1] and @dim == 2)
    if @stype == :dense
      if (hermitian)
        #Currently, we are not dealing with complex matrices.
        eps = 0
        is_symmetric = MatrixUtils.isSymmetric(self.twoDMat, eps)
      else
        eps = 0
        is_symmetric = MatrixUtils.isSymmetric(self.twoDMat, eps)
      end

    else
      #TODO: Implement, at the very least, yale_is_symmetric. Model it after yale/transp.template.c.
      # raise Exception.new("symmetric? and hermitian? only implemented for dense currently")
    end
  end
  return is_symmetric ? true : false
end

#kron_prod(mat) ⇒ Object

Compute the Kronecker product of self and other NMatrix

Arguments

* +mat+ - A 2D NMatrix object

Usage

a = NMatrix.new([2,2],[1,2,
                       3,4])
b = NMatrix.new([2,3],[1,1,1,
                       1,1,1], dtype: :float64)
a.kron_prod(b) # => [ [1.0, 1.0, 1.0, 2.0, 2.0, 2.0]
                      [1.0, 1.0, 1.0, 2.0, 2.0, 2.0]
                      [3.0, 3.0, 3.0, 4.0, 4.0, 4.0]
                      [3.0, 3.0, 3.0, 4.0, 4.0, 4.0] ]


432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
# File 'lib/nmatrix/math.rb', line 432

def kron_prod(mat)
  unless self.dimensions==2 and mat.dimensions==2
    raise ShapeError, "Implemented for 2D NMatrix objects only."
  end

  # compute the shape [n,m] of the product matrix
  n, m = self.shape[0]*mat.shape[0], self.shape[1]*mat.shape[1]
  # compute the entries of the product matrix
  kron_prod_array = []
  if self.yale?
    # +:yale+ requires to get the row by copy in order to apply +#transpose+ to it
    self.each_row(getby=:copy) do |selfr|
      mat.each_row do |matr|
        kron_prod_array += (selfr.transpose.dot matr).to_flat_a
      end
    end
  else
    self.each_row do |selfr|
      mat.each_row do |matr|
        kron_prod_array += (selfr.transpose.dot matr).to_flat_a
      end
    end
  end

  NMatrix.new([n,m], kron_prod_array)
end

#lastObject

call-seq:

last -> Element of self.dtype

Returns the last element stored in an NMatrix



556
557
558
# File 'lib/nmatrix/nmatrix.rb', line 556

def last
  self[*Array.new(self.dim, -1)]
end

#laswp(ary, opts = {}) ⇒ Object Also known as: permute_columns

call-seq:

laswp(ary) -> NMatrix

Permute the columns of a dense matrix using LASWP according to the order given in an array ary.

If :convention is :lapack, then ary represents a sequence of pair-wise permutations which are performed successively. That is, the i'th entry of ary is the index of the column to swap the i'th column with, having already applied all earlier swaps. This is the default.

If :convention is :intuitive, then ary represents the order of columns after the permutation. That is, the i'th entry of ary is the index of the column that will be in position i after the reordering (Matlab-like behaviour).

Not yet implemented for yale or list.

Arguments

  • ary - An Array specifying the order of the columns. See above for details.

Options

  • :covention - Possible values are :lapack and :intuitive. Default is :lapack. See above for details.



582
583
584
# File 'lib/nmatrix/cruby/math.rb', line 582

def laswp(ary, opts={})
  self.clone.laswp!(ary, opts)
end

#laswp!(ary, opts = {}) ⇒ Object Also known as: permute_columns!

call-seq:

laswp!(ary) -> NMatrix

In-place permute the columns of a dense matrix using LASWP according to the order given as an array ary.

If :convention is :lapack, then ary represents a sequence of pair-wise permutations which are performed successively. That is, the i'th entry of ary is the index of the column to swap the i'th column with, having already applied all earlier swaps.

If :convention is :intuitive, then ary represents the order of columns after the permutation. That is, the i'th entry of ary is the index of the column that will be in position i after the reordering (Matlab-like behaviour). This is the default.

Not yet implemented for yale or list.

Arguments

  • ary - An Array specifying the order of the columns. See above for details.

Options

  • :covention - Possible values are :lapack and :intuitive. Default is :intuitive. See above for details.

Raises:



535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
# File 'lib/nmatrix/cruby/math.rb', line 535

def laswp!(ary, opts={})
  raise(StorageTypeError, "ATLAS functions only work on dense matrices") unless self.dense?
  opts = { convention: :intuitive }.merge(opts)

  if opts[:convention] == :intuitive
    if ary.length != ary.uniq.length
      raise(ArgumentError, "No duplicated entries in the order array are allowed under convention :intuitive")
    end
    n = self.shape[1]
    p = []
    order = (0...n).to_a
    0.upto(n-2) do |i|
      p[i] = order.index(ary[i])
      order[i], order[p[i]] = order[p[i]], order[i]
    end
    p[n-1] = n-1
  else
    p = ary
  end

  NMatrix::LAPACK::laswp(self, p)
end

#layer(layer_number, get_by = :copy) ⇒ Object

call-seq:

layer(layer_number) -> NMatrix
row(layer_number, get_by) -> NMatrix
  • Arguments :

    • layer_number -> Integer.

    • get_by -> Type of slicing to use, :copy or :reference.

  • Returns :

    • A NMatrix representing the requested layer as a layer vector.



890
891
892
893
894
895
896
897
898
899
900
901
902
# File 'lib/nmatrix/nmatrix.rb', line 890

def layer(layer_number, get_by = :copy)
  layer = rank(2, layer_number, get_by)

  if jruby?
    nmatrix = NMatrix.new :copy
    nmatrix.shape = layer.shape
    nmatrix.s = layer.s
    return nmatrix
  else
    layer
  end

end

#ldexp(other, scalar = false) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/nmatrix/jruby/operators.rb', line 95

def ldexp(other, scalar=false)
  result = create_dummy_nmatrix
  if scalar
    result.s = ArrayRealVector.new MathHelper.ldexpScalar(other, @s.toArray)
  else
    if other.is_a? NMatrix
      result.s = ArrayRealVector.new MathHelper.ldexp(other.s.toArray, @s.toArray)
    else
      result.s = ArrayRealVector.new MathHelper.ldexpScalar2(other, @s.toArray)
    end
  end
  result
end

#least_squares(b, tolerance: 10e-6) ⇒ Object

call-seq:

least_squares(b) -> NMatrix
least_squares(b, tolerance: 10e-10) -> NMatrix

Provides the linear least squares approximation of an under-determined system using QR factorization provided that the matrix is not rank-deficient.

Only works for dense matrices.

  • Arguments :

    • b -> The solution column vector NMatrix of A * X = b.

    • tolerance: -> Absolute tolerance to check if a diagonal element in A = QR is near 0

  • Returns :

    • NMatrix that is a column vector with the LLS solution

  • Raises :

    • ArgumentError -> least squares approximation only works for non-complex types

    • ShapeError -> system must be under-determined ( rows > columns )

Examples :-

a = NMatrix.new([3,2], [2.0, 0, -1, 1, 0, 2])

b = NMatrix.new([3,1], [1.0, 0, -1])

a.least_squares(b)
  =>[
      [ 0.33333333333333326 ]
      [ -0.3333333333333334 ]
    ]

Raises:

  • (ArgumentError)


412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
# File 'lib/nmatrix/cruby/math.rb', line 412

def least_squares(b, tolerance: 10e-6)
  raise(ArgumentError, "least squares approximation only works for non-complex types") if
    self.complex_dtype?

  rows, columns = self.shape

  raise(ShapeError, "system must be under-determined ( rows > columns )") unless
    rows > columns

  #Perform economical QR factorization
  r = self.clone
  tau = r.geqrf!
  q_transpose_b = r.ormqr(tau, :left, :transpose, b)

  #Obtain R from geqrf! intermediate
  r[0...columns, 0...columns].upper_triangle!
  r[columns...rows, 0...columns] = 0

  diagonal = r.diagonal

  raise(ArgumentError, "rank deficient matrix") if diagonal.any? { |x| x == 0 }

  if diagonal.any? { |x| x.abs < tolerance }
    warn "warning: A diagonal element of R in A = QR is close to zero ;" <<
         " indicates a possible loss of precision"
  end

  # Transform the system A * X = B to R1 * X = B2 where B2 = Q1_t * B
  r1 = r[0...columns, 0...columns]
  b2 = q_transpose_b[0...columns]

  nrhs = b2.shape[1]

  #Solve the upper triangular system
  NMatrix::BLAS::cblas_trsm(:row, :left, :upper, false, :nounit, r1.shape[0], nrhs, 1.0, r1, r1.shape[0], b2, nrhs)
  b2
end

#list?Boolean

call-seq:

m.list? -> true or false

Determine if m is a list-of-lists matrix.

Returns:

  • (Boolean)


139
# File 'lib/nmatrix/shortcuts.rb', line 139

def list?;  return stype == :list; end

#log(val = :natural) ⇒ Object



203
204
205
206
207
208
209
210
211
# File 'lib/nmatrix/jruby/operators.rb', line 203

def log(val = :natural)
  result = create_dummy_nmatrix
  if val == :natural
    result.s = @s.copy.mapToSelf(Log.new())
  else
    result.s = ArrayRealVector.new MathHelper.log(val, @s.toArray)
  end
  result
end

#log10Object



217
218
219
220
221
# File 'lib/nmatrix/jruby/operators.rb', line 217

def log10
  result = create_dummy_nmatrix
  result.s = @s.copy.mapToSelf(Log10.new())
  result
end

#log2Object



213
214
215
# File 'lib/nmatrix/jruby/operators.rb', line 213

def log2
  self.log(2)
end

#lower_triangle(k = 0) ⇒ Object Also known as: tril

call-seq:

lower_triangle -> NMatrix
lower_triangle(k) -> NMatrix
tril -> NMatrix
tril(k) -> NMatrix

Returns the lower triangular portion of a matrix. This is analogous to the tril method in MATLAB.

  • Arguments :

    • k -> Integer. How many extra diagonals to include in the lower triangular portion.

Raises:

  • (NotImplementedError)


837
838
839
840
841
842
843
844
845
846
847
848
849
850
# File 'lib/nmatrix/nmatrix.rb', line 837

def lower_triangle(k = 0)
  raise(NotImplementedError, "only implemented for 2D matrices") if self.shape.size > 2

  t = self.clone_structure
  (0...self.shape[0]).each do |i|
    if i + k >= shape[0]
      t[i, :*] = self[i, :*]
    else
      t[i, (i+k+1)...self.shape[1]] = 0
      t[i, 0..(i+k)] = self[i, 0..(i+k)]
    end
  end
  t
end

#lower_triangle!(k = 0) ⇒ Object Also known as: tril!

call-seq:

lower_triangle! -> NMatrix
lower_triangle!(k) -> NMatrix
tril! -> NMatrix
tril!(k) -> NMatrix

Deletes the upper triangular portion of the matrix (in-place) so only the lower portion remains.

  • Arguments :

    • k -> Integer. How many extra diagonals to include in the deletion.

Raises:

  • (NotImplementedError)


866
867
868
869
870
871
872
873
874
875
# File 'lib/nmatrix/nmatrix.rb', line 866

def lower_triangle!(k = 0)
  raise(NotImplementedError, "only implemented for 2D matrices") if self.shape.size > 2

  (0...self.shape[0]).each do |i|
    if i + k < shape[0]
      self[i, (i+k+1)...self.shape[1]] = 0
    end
  end
  self
end

#map(&bl) ⇒ Object

call-seq:

map -> Enumerator
map { |elem| block } -> NMatrix

Returns an NMatrix if a block is given. For an Array, use #flat_map

Note that #map will always return an :object matrix, because it has no way of knowing how to handle operations on the different dtypes.



72
73
74
75
76
77
78
# File 'lib/nmatrix/enumerate.rb', line 72

def map(&bl)
  return enum_for(:map) unless block_given?
  # NMatrix-jruby currently supports only doubles
  cp  = jruby? ? self : self.cast(dtype: :object)
  cp.map!(&bl)
  cp
end

#map!Object

call-seq:

map! -> Enumerator
map! { |elem| block } -> NMatrix

Maps in place.

See Also:



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/nmatrix/enumerate.rb', line 88

def map!
  return enum_for(:map!) unless block_given?
  iterated = false
  self.each_stored_with_indices do |e, *i|
    iterated = true
    self[*i] = (yield e)
  end
  #HACK: if there's a single element in a non-dense matrix, it won't iterate and
  #won't change the default value; this ensures that it does get changed.
  unless iterated then
    self.each_with_indices do |e, *i|
      self[*i] = (yield e)
    end
  end
end

#map_storedObject



54
55
56
# File 'ext/nmatrix/ruby_nmatrix.c', line 54

def map_stored

end

#matrix_norm(type = 2) ⇒ Object

call-seq:

 matrix_norm -> Numeric

Calculates the selected norm (defaults to 2-norm) of a 2D matrix.

This should be used for small or medium sized matrices.
For greater matrices, there should be a separate implementation where
the norm is estimated rather than computed, for the sake of computation speed.

Currently implemented norms are 1-norm, 2-norm, Frobenius, Infinity.
A minus on the 1, 2 and inf norms returns the minimum instead of the maximum value.

Tested mainly with dense matrices. Further checks and modifications might
be necessary for sparse matrices.
  • Returns :

  • The selected norm of the matrix.

  • Raises :

  • NotImplementedError -> norm can be calculated only for 2D matrices

  • ArgumentError -> unrecognized norm

Raises:

  • (NotImplementedError)


327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/nmatrix/math.rb', line 327

def matrix_norm type = 2
  raise(NotImplementedError, "norm can be calculated only for 2D matrices") unless self.dim == 2
  raise(NotImplementedError, "norm only implemented for dense storage") unless self.stype == :dense
  raise(ArgumentError, "norm not defined for byte dtype")if self.dtype == :byte
  case type
  when nil, 2, -2
    return self.two_matrix_norm (type == -2)
  when 1, -1
    return self.one_matrix_norm (type == -1)
  when :frobenius, :fro
    return self.fro_matrix_norm
  when :infinity, :inf, :'-inf', :'-infinity'
    return self.inf_matrix_norm  (type == :'-inf' || type == :'-infinity')
  else
    raise ArgumentError.new("argument must be a valid integer or symbol")
  end
end

#matrix_solve(rhs) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/nmatrix/jruby/decomposition.rb', line 5

def matrix_solve rhs
  if rhs.shape[1] > 1
    nmatrix = NMatrix.new :copy
    nmatrix.shape = rhs.shape
    res = []
    #Solve a matrix and store the vectors in a matrix
    (0...rhs.shape[1]).each do |i|
      res << self.solve(rhs.col(i)).s.toArray.to_a
    end
    #res is in col major format
    result = ArrayGenerator.getArrayColMajorDouble res.to_java :double, rhs.shape[0], rhs.shape[1]
    nmatrix.s = ArrayRealVector.new result

    return nmatrix
  else
    return self.solve rhs
  end
end

#max(dimen = 0) ⇒ Object

call-seq:

max() -> NMatrix
max(dimen) -> NMatrix

Calculates the maximum along the specified dimension.

See Also:



545
546
547
548
549
550
551
552
553
# File 'lib/nmatrix/math.rb', line 545

def max(dimen=0)
  inject_rank(dimen) do |max, sub_mat|
    if max.is_a? NMatrix then
      max * (max >= sub_mat).cast(self.stype, self.dtype) + ((max)*0.0 + (max < sub_mat).cast(self.stype, self.dtype)) * sub_mat
    else
      max >= sub_mat ? max : sub_mat
    end
  end
end

#mean(dimen = 0) ⇒ Object

call-seq:

mean() -> NMatrix
mean(dimen) -> NMatrix

Calculates the mean along the specified dimension.

This will force integer types to float64 dtype.

See Also:



490
491
492
493
494
495
496
497
498
# File 'lib/nmatrix/math.rb', line 490

def mean(dimen=0)
  reduce_dtype = nil
  if integer_dtype? then
    reduce_dtype = :float64
  end
  inject_rank(dimen, 0.0, reduce_dtype) do |mean, sub_mat|
    mean + sub_mat
  end / shape[dimen]
end

#min(dimen = 0) ⇒ Object

call-seq:

min() -> NMatrix
min(dimen) -> NMatrix

Calculates the minimum along the specified dimension.

See Also:



526
527
528
529
530
531
532
533
534
# File 'lib/nmatrix/math.rb', line 526

def min(dimen=0)
  inject_rank(dimen) do |min, sub_mat|
    if min.is_a? NMatrix then
      min * (min <= sub_mat).cast(self.stype, self.dtype) + ((min)*0.0 + (min > sub_mat).cast(self.stype, self.dtype)) * sub_mat
    else
      min <= sub_mat ? min : sub_mat
    end
  end
end

#nrm2(incx = 1, n = nil) ⇒ Object Also known as: norm2

call-seq:

norm2 -> Numeric

Arguments

- +incx+ -> the skip size (defaults to 1, no skip)
- +n+ -> the number of elements to include

Return the 2-norm of the vector. This is the BLAS nrm2 routine.



699
700
701
702
# File 'lib/nmatrix/cruby/math.rb', line 699

def nrm2 incx=1, n=nil
  return method_missing(:nrm2, incx, n) unless vector?
  NMatrix::BLAS::nrm2(self, incx, self.size / incx)
end

#nvector?Boolean

call-seq:

nvector? -> true or false

Shortcut function for determining whether the effective dimension is less than the dimension. Useful when we take slices of n-dimensional matrices where n > 2.

Returns:

  • (Boolean)


442
443
444
# File 'lib/nmatrix/nmatrix.rb', line 442

def nvector?
  self.effective_dim < self.dim
end

#object_dtype?Boolean

call-seq:

object_dtype?() -> Boolean

Checks if dtype is a ruby object

Returns:

  • (Boolean)


387
388
389
# File 'lib/nmatrix/nmatrix.rb', line 387

def object_dtype?
  dtype == :object
end

#offsetObject



47
48
49
50
51
# File 'ext/nmatrix/ruby_nmatrix.c', line 47

def offset
  # ArrayRealVector takes care of the offset value when indexing a Vector.
  # Hence, return 0.
  0
end

#one_matrix_norm(minus = false) ⇒ Object

1-norm: the maximum/minimum absolute column sum of the matrix



651
652
653
654
655
656
657
658
659
660
661
662
# File 'lib/nmatrix/math.rb', line 651

def one_matrix_norm minus = false
  #TODO: change traversing method for sparse matrices
  number_of_columns = self.cols
  col_sums = []

  number_of_columns.times do |i|
    col_sums << self.col(i).inject(0) { |sum, number| sum += number.abs}
  end

  return col_sums.max unless minus
  return col_sums.min
end

#ormqr(tau, side = :left, transpose = false, c = nil) ⇒ Object

call-seq:

ormqr(tau) -> NMatrix
ormqr(tau, side, transpose, c) -> NMatrix

Returns the product Q * c or c * Q after a call to geqrf! used in QR factorization. c is overwritten with the elements of the result NMatrix if supplied. Q is the orthogonal matrix represented by tau and the calling NMatrix

Only works on float types, use unmqr for complex types.

Arguments

  • tau - vector containing scalar factors of elementary reflectors

  • side - direction of multiplication [:left, :right]

  • transpose - apply Q with or without transpose [false, :transpose]

  • c - NMatrix multplication argument that is overwritten, no argument assumes c = identity

  • Returns :

    • Q * c or c * Q Where Q may be transposed before multiplication.

  • Raises :

    • StorageTypeError -> LAPACK functions only work on dense matrices.

    • TypeError -> Works only on floating point matrices, use unmqr for complex types

    • TypeError -> c must have the same dtype as the calling NMatrix

Raises:

  • (NotImplementedError)


299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/nmatrix/lapacke.rb', line 299

def ormqr(tau, side=:left, transpose=false, c=nil)
  raise(StorageTypeError, "LAPACK functions only work on dense matrices") unless self.dense?
  raise(TypeError, "Works only on floating point matrices, use unmqr for complex types") if self.complex_dtype?
  raise(TypeError, "c must have the same dtype as the calling NMatrix") if c and c.dtype != self.dtype


  #Default behaviour produces Q * I  = Q if c is not supplied.
  result = c ? c.clone : NMatrix.identity(self.shape[0], dtype: self.dtype)
  NMatrix::LAPACK::lapacke_ormqr(:row, side, transpose, result.shape[0], result.shape[1], tau.shape[0], self, self.shape[1], tau, result, result.shape[1])
  
  result
end

#pinv(tolerance = 1e-15) ⇒ Object Also known as: pseudo_inverse, pseudoinverse

call-seq:

pinv -> NMatrix

Compute the Moore-Penrose pseudo-inverse of a matrix using its singular value decomposition (SVD).

This function requires the nmatrix-atlas gem installed.

  • Arguments :

  • tolerance(optional) -> Cutoff for small singular values.

  • Returns :

    • Pseudo-inverse matrix.

  • Raises :

    • NotImplementedError -> If called without nmatrix-atlas or nmatrix-lapacke gem.

    • TypeError -> If called without float or complex data type.

  • Examples :

a = NMatrix.new([2,2],[1,2,
                       3,4], dtype: :float64)
a.pinv # => [ [-2.0000000000000018, 1.0000000000000007]
              [1.5000000000000016, -0.5000000000000008] ]

b = NMatrix.new([4,1],[1,2,3,4], dtype: :float64)
b.pinv # => [ [ 0.03333333, 0.06666667, 0.99999999, 0.13333333] ]

References

Raises:



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/nmatrix/math.rb', line 202

def pinv(tolerance = 1e-15)
  raise DataTypeError, "pinv works only with matrices of float or complex data type" unless
    [:float32, :float64, :complex64, :complex128].include?(dtype)
  if self.complex_dtype?
    u, s, vt = self.complex_conjugate.gesvd # singular value decomposition
  else
    u, s, vt = self.gesvd
  end
  rows = self.shape[0]
  cols = self.shape[1]
  if rows < cols
    u_reduced = u
    vt_reduced = vt[0..rows - 1, 0..cols - 1].transpose
  else
    u_reduced = u[0..rows - 1, 0..cols - 1]
    vt_reduced = vt.transpose
  end
  largest_singular_value = s.max.to_f
  cutoff = tolerance * largest_singular_value
  (0...[rows, cols].min).each do |i|
    s[i] = 1 / s[i] if s[i] > cutoff
    s[i] = 0        if s[i] <= cutoff
  end
  multiplier = u_reduced.dot(NMatrix.diagonal(s.to_a)).transpose
  vt_reduced.dot(multiplier)
end

#positive_definite?Boolean

call-seq:

positive_definite? -> boolean

A matrix is positive definite if it’s symmetric and all its eigenvalues are positive

  • Returns :

    • A boolean value telling if the NMatrix is positive definite or not.

  • Raises :

    • ShapeError -> Must be used on square matrices.

Returns:

  • (Boolean)

Raises:



688
689
690
691
692
693
694
695
696
697
698
699
# File 'lib/nmatrix/math.rb', line 688

def positive_definite?
  raise(ShapeError, "positive definite calculated only for square matrices") unless
    self.dim == 2 && self.shape[0] == self.shape[1]
  cond = 0
  while cond != self.cols
    if self[0..cond, 0..cond].det <= 0
      return false
    end
    cond += 1
  end
  true
end

#potrf!(which) ⇒ Object

call-seq:

potrf!(upper_or_lower) -> NMatrix

Cholesky factorization of a symmetric positive-definite matrix – or, if complex, a Hermitian positive-definite matrix A. The result will be written in either the upper or lower triangular portion of the matrix, depending on whether the argument is :upper or :lower. Also the function only reads in the upper or lower part of the matrix, so it doesn't actually have to be symmetric/Hermitian. However, if the matrix (i.e. the symmetric matrix implied by the lower/upper half) is not positive-definite, the function will return nonsense.

This functions requires either the nmatrix-atlas or nmatrix-lapacke gem installed.

  • Returns : the triangular portion specified by the parameter

  • Raises :

    • StorageTypeError -> ATLAS functions only work on dense matrices.

    • ShapeError -> Must be square.

    • NotImplementedError -> If called without nmatrix-atlas or nmatrix-lapacke gem



195
196
197
198
199
200
201
202
# File 'lib/nmatrix/cruby/math.rb', line 195

def potrf!(which)
  raise(StorageTypeError, "ATLAS functions only work on dense matrices") \
   unless self.dense?
  raise(ShapeError, "Cholesky decomposition only valid for square matrices") \
   unless self.dim == 2 && self.shape[0] == self.shape[1]

  NMatrix::LAPACK::clapack_potrf(:row, which, self.shape[0], self, self.shape[1])
end

#potrf_lower!Object



204
205
206
# File 'lib/nmatrix/cruby/math.rb', line 204

def potrf_lower!
  potrf! :lower
end

#potrf_upper!Object



200
201
202
# File 'lib/nmatrix/cruby/math.rb', line 200

def potrf_upper!
  potrf! :upper
end

#pow(n) ⇒ Object

Raise a square matrix to a power. Be careful of numeric overflows! In case n is 0, an identity matrix of the same dimension is returned. In case of negative n, the matrix is inverted and the absolute value of n taken for computing the power.

Arguments

  • n - Integer to which self is to be raised.

References

  • R.G Dromey - How to Solve it by Computer. Link -

    http://www.amazon.com/Solve-Computer-Prentice-Hall-International-Science/dp/0134340019/ref=sr_1_1?ie=UTF8&qid=1422605572&sr=8-1&keywords=how+to+solve+it+by+computer
    

Raises:



387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
# File 'lib/nmatrix/math.rb', line 387

def pow n
  raise ShapeError, "Only works with 2D square matrices." if
    shape[0] != shape[1] or shape.size != 2
  raise TypeError, "Only works with integer powers" unless n.is_a?(Integer)

  sequence = (integer_dtype? ? self.cast(dtype: :int64) : self).clone
  product  = NMatrix.eye shape[0], dtype: sequence.dtype, stype: sequence.stype

  if n == 0
    return NMatrix.eye(shape, dtype: dtype, stype: stype)
  elsif n == 1
    return sequence
  elsif n < 0
    n = n.abs
    sequence.invert!
    product = NMatrix.eye shape[0], dtype: sequence.dtype, stype: sequence.stype
  end

  # Decompose n to reduce the number of multiplications.
  while n > 0
    product = product.dot(sequence) if n % 2 == 1
    n = n / 2
    sequence = sequence.dot(sequence)
  end

  product
end

#pretty_print(q) ⇒ Object

TODO: Make this actually pretty.



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/nmatrix/nmatrix.rb', line 175

def pretty_print(q) #:nodoc:
  if self.shape.size > 1 and self.shape[1] > 100
    self.inspect.pretty_print(q)
  elsif self.dim > 3 || self.dim == 1
    self.to_a.pretty_print(q)
  else
    # iterate through the whole matrix and find the longest number
    longest = Array.new(self.shape[1], 0)
    self.each_column.with_index do |col, j|
      col.each do |elem|
        elem_len   = elem.inspect.size
        longest[j] = elem_len if longest[j] < elem_len
      end
    end

    if self.dim == 3
      q.group(0, "\n{ layers:", "}") do
        self.each_layer.with_index do |layer,k|
          q.group(0, "\n  [\n", "  ]\n") do
            layer.each_row.with_index do |row,i|
              q.group(0, "    [", "]\n") do
                q.seplist(self[i,0...self.shape[1],k].to_flat_array, lambda { q.text ", "}, :each_with_index) { |v,j| q.text v.inspect.rjust(longest[j]) }
              end
            end
          end
        end
      end
    else # dim 2
      q.group(0, "\n[\n ", "]") do
        self.each_row.with_index do |row, i|
          q.group(1, " [", "]\n") do
            q.seplist(row.to_a, -> { q.text ", " }, :each_with_index) do |v,j|
              q.text v.inspect.rjust(longest[j])
            end
          end
          q.breakable unless i + 1 == self.shape[0]
        end
      end
    end
  end
end

#quaternionObject

call-seq:

quaternion -> NMatrix

Find the quaternion for a 3D rotation matrix.

Code borrowed from: courses.cms.caltech.edu/cs171/quatut.pdf

  • Returns :

    • A length-4 NMatrix representing the corresponding quaternion.

Examples:

n.quaternion # => [1, 0, 0, 0]

Raises:



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/nmatrix/homogeneous.rb', line 159

def quaternion
  raise(ShapeError, "Expected square matrix") if self.shape[0] != self.shape[1]
  raise(ShapeError, "Expected 3x3 rotation (or 4x4 homogeneous) matrix") if self.shape[0] > 4 || self.shape[0] < 3

  q = NMatrix.new([4], dtype: self.dtype == :float32 ? :float32: :float64)
  rotation_trace = self[0,0] + self[1,1] + self[2,2]
  if rotation_trace >= 0
    self_w = self.shape[0] == 4 ? self[3,3] : 1.0
    root_of_homogeneous_trace = Math.sqrt(rotation_trace + self_w)
    q[0] = root_of_homogeneous_trace * 0.5
    s = 0.5 / root_of_homogeneous_trace
    q[1] = (self[2,1] - self[1,2]) * s
    q[2] = (self[0,2] - self[2,0]) * s
    q[3] = (self[1,0] - self[0,1]) * s
  else
    h = 0
    h = 1 if self[1,1] > self[0,0]
    h = 2 if self[2,2] > self[h,h]

    case_macro = Proc.new do |i,j,k,ii,jj,kk|
      qq = NMatrix.new([4], dtype: :float64)
      self_w = self.shape[0] == 4 ? self[3,3] : 1.0
      s = Math.sqrt( (self[ii,ii] - (self[jj,jj] + self[kk,kk])) + self_w)
      qq[i] = s*0.5
      s = 0.5 / s
      qq[j] = (self[ii,jj] + self[jj,ii]) * s
      qq[k] = (self[kk,ii] + self[ii,kk]) * s
      qq[0] = (self[kk,jj] - self[jj,kk]) * s
      qq
    end

    case h
    when 0
      q = case_macro.call(1,2,3, 0,1,2)
    when 1
      q = case_macro.call(2,3,1, 1,2,0)
    when 2
      q = case_macro.call(3,1,2, 2,0,1)
    end

    self_w = self.shape[0] == 4 ? self[3,3] : 1.0
    if self_w != 1
      s = 1.0 / Math.sqrt(self_w)
      q[0] *= s
      q[1] *= s
      q[2] *= s
      q[3] *= s
    end
  end

  q
end

#rank(shape_idx, rank_idx, meth = :copy) ⇒ Object

call-seq:

rank(dimension, row_or_column_number) -> NMatrix
rank(dimension, row_or_column_number, :reference) -> NMatrix reference slice

Returns the rank (e.g., row, column, or layer) specified, using slicing by copy as default.

See @row (dimension = 0), @column (dimension = 1)



494
495
496
497
498
499
500
501
502
503
504
505
506
# File 'lib/nmatrix/nmatrix.rb', line 494

def rank(shape_idx, rank_idx, meth = :copy)

  if shape_idx > (self.dim-1)
    raise(RangeError, "#rank call was out of bounds")
  end

  params = Array.new(self.dim)
  params.each.with_index do |v,d|
    params[d] = d == shape_idx ? rank_idx : 0...self.shape[d]
  end

  meth == :reference ? self[*params] : self.slice(*params)
end

#repeat(count, axis) ⇒ Object

call-seq:

repeat(count, axis) -> NMatrix
  • Arguments :

    • count -> how many times NMatrix should be repeated

    • axis -> index of axis along which NMatrix should be repeated

  • Returns :

    • NMatrix created by repeating the existing one along an axis

  • Examples :

    m = NMatrix.new([2, 2], [1, 2, 3, 4])
    m.repeat(2, 0).to_a #<= [[1, 2], [3, 4], [1, 2], [3, 4]]
    m.repeat(2, 1).to_a #<= [[1, 2, 1, 2], [3, 4, 3, 4]]
    

Raises:

  • (ArgumentError)


1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
# File 'lib/nmatrix/nmatrix.rb', line 1055

def repeat(count, axis)
  raise(ArgumentError, 'Matrix should be repeated at least 2 times.') if count < 2
  new_shape = shape
  new_shape[axis] *= count
  new_matrix = NMatrix.new(new_shape, dtype: dtype)
  slice = new_shape.map { |axis_size| 0...axis_size }
  start = 0
  count.times do
    slice[axis] = start...(start += shape[axis])
    new_matrix[*slice] = self
  end
  new_matrix
end

#reshape(new_shape, *shapes) ⇒ Object

call-seq:

reshape(new_shape) -> NMatrix

Clone a matrix, changing the shape in the process. Note that this function does not do a resize; the product of the new and old shapes' components must be equal.

  • Arguments :

    • new_shape -> Array of positive Integers.

  • Returns :

    • A copy with a different shape.



573
574
575
576
577
578
579
580
581
582
583
584
# File 'lib/nmatrix/nmatrix.rb', line 573

def reshape new_shape,*shapes
  if new_shape.is_a?Integer
    newer_shape =  [new_shape]+shapes
  else  # new_shape is an Array
    newer_shape = new_shape
  end
  t = reshape_clone_structure(newer_shape)
  left_params  = [:*]*newer_shape.size
  right_params = [:*]*self.shape.size
  t[*left_params] = self[*right_params]
  t
end

#reshape!(new_shape, *shapes) ⇒ Object

call-seq:

reshape!(new_shape) -> NMatrix
reshape! new_shape  -> NMatrix

Reshapes the matrix (in-place) to the desired shape. Note that this function does not do a resize; the product of the new and old shapes' components must be equal.

  • Arguments :

    • new_shape -> Array of positive Integer.



598
599
600
601
602
603
604
605
606
607
608
609
# File 'lib/nmatrix/nmatrix.rb', line 598

def reshape! new_shape,*shapes
  if self.is_ref?
    raise(ArgumentError, "This operation cannot be performed on reference slices")
  else
    if new_shape.is_a?Integer
      shape =  [new_shape]+shapes
    else  # new_shape is an Array
      shape = new_shape
    end
    self.reshape_bang(shape)
  end
end

#respond_to?(method, include_all = false) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


988
989
990
991
992
993
994
995
996
# File 'lib/nmatrix/nmatrix.rb', line 988

def respond_to?(method, include_all = false) #:nodoc:
  if [:shuffle, :shuffle!, :each_with_index, :sorted_indices, :binned_sorted_indices, :nrm2, :asum].include?(method.intern) # vector-only methods
    return vector?
  elsif [:each_layer, :layer].include?(method.intern) # 3-or-more dimensions only
    return dim > 2
  else
    super
  end
end

#roundObject



143
144
145
146
147
148
149
# File 'ext/nmatrix/ruby_nmatrix.c', line 143

def round
  result = create_dummy_nmatrix
  # Need to be changed later
  result.dtype = :int64
  result.s = ArrayRealVector.new MathHelper.round(@s.toArray)
  result
end

#row(row_number, get_by = :copy) ⇒ Object

call-seq:

row(row_number) -> NMatrix
row(row_number, get_by) -> NMatrix
  • Arguments :

    • row_number -> Integer.

    • get_by -> Type of slicing to use, :copy or :reference.

  • Returns :

    • An NMatrix representing the requested row as a row vector.



546
547
548
# File 'lib/nmatrix/nmatrix.rb', line 546

def row(row_number, get_by = :copy)
  rank(0, row_number, get_by)
end

#rowsObject

call-seq:

rows -> Integer

This shortcut use #shape to return the number of rows (the first dimension) of the matrix.



269
270
271
# File 'lib/nmatrix/nmatrix.rb', line 269

def rows
  shape[0]
end

#scale(alpha, incx = 1, n = nil) ⇒ Object

call-seq:

scale -> NMatrix

Arguments

- +alpha+ -> Scalar value used in the operation.
- +inc+ -> Increment used in the scaling function. Should generally be 1.
- +n+ -> Number of elements of +vector+.

Return the scaling result of the matrix. BLAS scal will be invoked if provided.

Raises:



737
738
739
# File 'lib/nmatrix/cruby/math.rb', line 737

def scale(alpha, incx=1, n=nil)
  return self.clone.scale!(alpha, incx, n)
end

#scale!(alpha, incx = 1, n = nil) ⇒ Object

call-seq:

scale! -> NMatrix

Arguments

- +alpha+ -> Scalar value used in the operation.
- +inc+ -> Increment used in the scaling function. Should generally be 1.
- +n+ -> Number of elements of +vector+.

This is a destructive method, modifying the source NMatrix. See also #scale. Return the scaling result of the matrix. BLAS scal will be invoked if provided.

Raises:



717
718
719
720
721
722
723
724
# File 'lib/nmatrix/cruby/math.rb', line 717

def scale!(alpha, incx=1, n=nil)
  raise(DataTypeError, "Incompatible data type for the scaling factor") unless
      NMatrix::upcast(self.dtype, NMatrix::min_dtype(alpha)) == self.dtype
  return NMatrix::BLAS::scal(alpha, self, incx, self.size / incx) if NMatrix::BLAS.method_defined? :scal
  self.each_stored_with_indices do |e, *i|
    self[*i] = e*alpha
  end
end