nmatrix 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. data/Gemfile +1 -1
  2. data/History.txt +31 -3
  3. data/Manifest.txt +5 -0
  4. data/README.rdoc +29 -27
  5. data/ext/nmatrix/binary_format.txt +53 -0
  6. data/ext/nmatrix/data/data.cpp +18 -18
  7. data/ext/nmatrix/data/data.h +38 -7
  8. data/ext/nmatrix/data/rational.h +13 -0
  9. data/ext/nmatrix/data/ruby_object.h +10 -0
  10. data/ext/nmatrix/extconf.rb +2 -0
  11. data/ext/nmatrix/nmatrix.cpp +655 -103
  12. data/ext/nmatrix/nmatrix.h +26 -14
  13. data/ext/nmatrix/ruby_constants.cpp +4 -0
  14. data/ext/nmatrix/ruby_constants.h +2 -0
  15. data/ext/nmatrix/storage/dense.cpp +99 -41
  16. data/ext/nmatrix/storage/dense.h +3 -3
  17. data/ext/nmatrix/storage/list.cpp +36 -14
  18. data/ext/nmatrix/storage/list.h +4 -4
  19. data/ext/nmatrix/storage/storage.cpp +19 -19
  20. data/ext/nmatrix/storage/storage.h +11 -11
  21. data/ext/nmatrix/storage/yale.cpp +17 -20
  22. data/ext/nmatrix/storage/yale.h +13 -11
  23. data/ext/nmatrix/util/io.cpp +25 -23
  24. data/ext/nmatrix/util/io.h +5 -5
  25. data/ext/nmatrix/util/math.cpp +634 -17
  26. data/ext/nmatrix/util/math.h +958 -9
  27. data/ext/nmatrix/util/sl_list.cpp +7 -7
  28. data/ext/nmatrix/util/sl_list.h +2 -2
  29. data/lib/nmatrix.rb +9 -0
  30. data/lib/nmatrix/blas.rb +4 -4
  31. data/lib/nmatrix/io/market.rb +227 -0
  32. data/lib/nmatrix/io/mat_reader.rb +7 -7
  33. data/lib/nmatrix/lapack.rb +80 -0
  34. data/lib/nmatrix/nmatrix.rb +78 -52
  35. data/lib/nmatrix/shortcuts.rb +486 -0
  36. data/lib/nmatrix/version.rb +1 -1
  37. data/spec/2x2_dense_double.mat +0 -0
  38. data/spec/blas_spec.rb +59 -9
  39. data/spec/elementwise_spec.rb +25 -12
  40. data/spec/io_spec.rb +69 -1
  41. data/spec/lapack_spec.rb +53 -4
  42. data/spec/math_spec.rb +9 -0
  43. data/spec/nmatrix_list_spec.rb +95 -0
  44. data/spec/nmatrix_spec.rb +10 -53
  45. data/spec/nmatrix_yale_spec.rb +17 -15
  46. data/spec/shortcuts_spec.rb +154 -0
  47. metadata +22 -15
@@ -344,14 +344,14 @@ NODE* find_nearest_from(NODE* prev, size_t key) {
344
344
 
345
345
  f = find_preceding_from(prev, key);
346
346
 
347
- if (!f->next) {
347
+ if (!f->next) { // key exceeds final node; return final node.
348
348
  return f;
349
349
 
350
- } else if (key == f->next->key) {
350
+ } else if (key == f->next->key) { // node already present; return location
351
351
  return f->next;
352
-
352
+
353
353
  } else {
354
- return prev;
354
+ return f;
355
355
  }
356
356
  }
357
357
 
@@ -417,7 +417,7 @@ extern "C" {
417
417
  /*
418
418
  * C access for copying the contents of a list.
419
419
  */
420
- void nm_list_cast_copy_contents(LIST* lhs, const LIST* rhs, dtype_t lhs_dtype, dtype_t rhs_dtype, size_t recursions) {
420
+ void nm_list_cast_copy_contents(LIST* lhs, const LIST* rhs, nm::dtype_t lhs_dtype, nm::dtype_t rhs_dtype, size_t recursions) {
421
421
  LR_DTYPE_TEMPLATE_TABLE(nm::list::cast_copy_contents, void, LIST*, const LIST*, size_t);
422
422
 
423
423
  ttable[lhs_dtype][rhs_dtype](lhs, rhs, recursions);
@@ -429,7 +429,7 @@ extern "C" {
429
429
  * the default value is going to be a hash with default value of hash with default value of default_value, and so on.
430
430
  * In other words, it's recursive.
431
431
  */
432
- static VALUE empty_list_to_hash(const dtype_t dtype, size_t recursions, VALUE default_value) {
432
+ static VALUE empty_list_to_hash(const nm::dtype_t dtype, size_t recursions, VALUE default_value) {
433
433
  VALUE h = rb_hash_new();
434
434
  if (recursions) {
435
435
  RHASH_IFNONE(h) = empty_list_to_hash(dtype, recursions-1, default_value);
@@ -443,7 +443,7 @@ extern "C" {
443
443
  /*
444
444
  * Copy a list to a Ruby Hash
445
445
  */
446
- VALUE nm_list_copy_to_hash(const LIST* l, const dtype_t dtype, size_t recursions, VALUE default_value) {
446
+ VALUE nm_list_copy_to_hash(const LIST* l, const nm::dtype_t dtype, size_t recursions, VALUE default_value) {
447
447
 
448
448
  // Create a hash with default values appropriately specified for a sparse matrix.
449
449
  VALUE h = empty_list_to_hash(dtype, recursions, default_value);
@@ -248,8 +248,8 @@ void cast_copy_contents(LIST* lhs, const LIST* rhs, size_t recursions);
248
248
  }} // end of namespace nm::list
249
249
 
250
250
  extern "C" {
251
- void nm_list_cast_copy_contents(LIST* lhs, const LIST* rhs, dtype_t lhs_dtype, dtype_t rhs_dtype, size_t recursions);
252
- VALUE nm_list_copy_to_hash(const LIST* l, const dtype_t dtype, size_t recursions, VALUE default_value);
251
+ void nm_list_cast_copy_contents(LIST* lhs, const LIST* rhs, nm::dtype_t lhs_dtype, nm::dtype_t rhs_dtype, size_t recursions);
252
+ VALUE nm_list_copy_to_hash(const LIST* l, const nm::dtype_t dtype, size_t recursions, VALUE default_value);
253
253
  } // end of extern "C" block
254
254
 
255
255
  #endif // SL_LIST_H
@@ -53,3 +53,12 @@ end
53
53
 
54
54
  # Monkey patches.
55
55
  require 'nmatrix/monkeys'
56
+
57
+ #############
58
+ # Autoloads #
59
+ #############
60
+
61
+ autoload :NMatrix, 'nmatrix/nmatrix'
62
+ autoload :NVector, 'nmatrix/nvector'
63
+
64
+ require "nmatrix/shortcuts.rb"
@@ -37,8 +37,8 @@ module NMatrix::BLAS
37
37
 
38
38
  # NM_COMPLEX64 and NM_COMPLEX128 both require complex alpha and beta.
39
39
  if a.dtype == :complex64 or a.dtype == :complex128
40
- alpha = Complex.new(1.0, 0.0) if alpha == 1.0
41
- beta = Complex.new(0.0, 0.0) if beta == 0.0
40
+ alpha = Complex(1.0, 0.0) if alpha == 1.0
41
+ beta = Complex(0.0, 0.0) if beta == 0.0
42
42
  end
43
43
 
44
44
  # For argument descriptions, see: http://www.netlib.org/blas/dgemm.f
@@ -57,8 +57,8 @@ module NMatrix::BLAS
57
57
 
58
58
  # NM_COMPLEX64 and NM_COMPLEX128 both require complex alpha and beta.
59
59
  if a.dtype == :complex64 or a.dtype == :complex128
60
- alpha = Complex.new(1.0, 0.0) if alpha == 1.0
61
- beta = Complex.new(0.0, 0.0) if beta == 0.0
60
+ alpha = Complex(1.0, 0.0) if alpha == 1.0
61
+ beta = Complex(0.0, 0.0) if beta == 0.0
62
62
  end
63
63
 
64
64
  ::NMatrix::BLAS.cblas_gemv(transpose_a, m, n, alpha, a, lda, x, incx, beta, y, incy)
@@ -0,0 +1,227 @@
1
+ # = NMatrix
2
+ #
3
+ # A linear algebra library for scientific computation in Ruby.
4
+ # NMatrix is part of SciRuby.
5
+ #
6
+ # NMatrix was originally inspired by and derived from NArray, by
7
+ # Masahiro Tanaka: http://narray.rubyforge.org
8
+ #
9
+ # == Copyright Information
10
+ #
11
+ # SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
12
+ # NMatrix is Copyright (c) 2012, Ruby Science Foundation
13
+ #
14
+ # Please see LICENSE.txt for additional copyright notices.
15
+ #
16
+ # == Contributing
17
+ #
18
+ # By contributing source code to SciRuby, you agree to be bound by
19
+ # our Contributor Agreement:
20
+ #
21
+ # * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
22
+ #
23
+ # == io/market.rb
24
+ #
25
+ # MatrixMarket reader and writer.
26
+ #
27
+ module NMatrix::IO::Market
28
+ CONVERTER_AND_DTYPE = {
29
+ :real => [:to_f, :float64],
30
+ :complex => [:to_c, :complex128],
31
+ :integer => [:to_i, :int64],
32
+ :pattern => [:to_i, :byte]
33
+ }
34
+
35
+ ENTRY_TYPE = {
36
+ :byte => :integer, :int8 => :integer, :int16 => :integer, :int32 => :integer, :int64 => :integer,
37
+ :float32 => :real, :float64 => :real, :complex64 => :complex, :complex128 => :complex
38
+ }
39
+
40
+ class << self
41
+ # Load a MatrixMarket file. Requires a filename as an argument.
42
+ def load filename
43
+
44
+ f = File.new(filename, "r")
45
+
46
+ header = f.gets
47
+ header.chomp!
48
+ raise(IOError, "expected type code line beginning with '%%MatrixMarket matrix'") if header !~ /^\%\%MatrixMarket\ matrix/
49
+
50
+ header = header.split
51
+
52
+ entry_type = header[3].downcase.to_sym
53
+ symmetry = header[4].downcase.to_sym
54
+ converter, default_dtype = CONVERTER_AND_DTYPE[entry_type]
55
+
56
+ if header[2] == 'coordinate'
57
+ load_coordinate f, converter, default_dtype, entry_type, symmetry
58
+ else
59
+ load_array f, converter, default_dtype, entry_type, symmetry
60
+ end
61
+ end
62
+
63
+
64
+ # Can optionally set :symmetry to :general, :symmetric, :hermitian; and can set :pattern => true if you're writing
65
+ # a sparse matrix and don't want values stored.
66
+ def save matrix, filename, options = {}
67
+ options = {:pattern => false,
68
+ :symmetry => :general}.merge(options)
69
+
70
+ mode = matrix.stype == :dense ? :array : :coordinate
71
+ if [:rational32,:rational64,:rational128,:object].include?(matrix.dtype)
72
+ raise(DataTypeError, "MatrixMarket does not support rational or Ruby objects")
73
+ end
74
+ entry_type = options[:pattern] ? :pattern : ENTRY_TYPE[matrix.dtype]
75
+
76
+ raise(ArgumentError, "expected two-dimensional NMatrix") if matrix.dim != 2
77
+
78
+ f = File.new(filename, 'w')
79
+
80
+ f.puts "%%MatrixMarket matrix #{mode} #{entry_type} #{options[:symmetry]}"
81
+
82
+ if matrix.stype == :dense
83
+ save_array matrix, f, options[:symmetry]
84
+ elsif [:list,:yale].include?(matrix.stype)
85
+ save_coordinate matrix, f, options[:symmetry], options[:pattern]
86
+ end
87
+
88
+ f.close
89
+
90
+ true
91
+ end
92
+
93
+
94
+ protected
95
+
96
+ def save_coordinate matrix, file, symmetry, pattern
97
+ # Convert to a hash in order to store
98
+ rows = matrix.to_h
99
+
100
+ # Count non-zeros
101
+ count = 0
102
+ rows.each_pair do |i, columns|
103
+ columns.each_pair do |j, val|
104
+ next if symmetry != :general && j > i
105
+ count += 1
106
+ end
107
+ end
108
+
109
+ # Print dimensions and non-zeros
110
+ file.puts "#{matrix.shape[0]}\t#{matrix.shape[1]}\t#{count}"
111
+
112
+ # Print coordinates
113
+ rows.each_pair do |i, columns|
114
+ columns.each_pair do |j, val|
115
+ next if symmetry != :general && j > i
116
+ file.puts(pattern ? "\t#{i+1}\t#{j+1}" : "\t#{i+1}\t#{j+1}\t#{val}")
117
+ end
118
+ end
119
+
120
+ file
121
+ end
122
+
123
+
124
+ def save_array matrix, file, symmetry
125
+ file.puts [matrix.shape[0], matrix.shape[1]].join("\t")
126
+
127
+ if symmetry == :general
128
+ (0...matrix.shape[1]).each do |j|
129
+ (0...matrix.shape[0]).each do |i|
130
+ file.puts matrix[i,j]
131
+ end
132
+ end
133
+ else # :symmetric, :'skew-symmetric', :hermitian
134
+ (0...matrix.shape[1]).each do |j|
135
+ (j...matrix.shape[0]).each do |i|
136
+ file.puts matrix[i,j]
137
+ end
138
+ end
139
+ end
140
+
141
+ file
142
+ end
143
+
144
+
145
+ def load_array file, converter, dtype, entry_type, symmetry
146
+ mat = nil
147
+
148
+ line = file.gets
149
+ line.chomp!
150
+ line.lstrip!
151
+
152
+ fields = line.split
153
+
154
+ mat = NMatrix.new :dense, [fields[0].to_i, fields[1].to_i], dtype
155
+
156
+ (0...mat.shape[1]).each do |j|
157
+ (0...mat.shape[0]).each do |i|
158
+ datum = file.gets.chomp.send(converter)
159
+ mat[i,j] = datum
160
+
161
+ unless i == j || symmetry == :general
162
+ if symmetry == :symmetric
163
+ mat[j,i] = datum
164
+ elsif symmetry == :hermitian
165
+ mat[j,i] = Complex.new(datum.real, -datum.imag)
166
+ elsif symmetry == :'skew-symmetric'
167
+ mat[j,i] = -datum
168
+ end
169
+ end
170
+ end
171
+ end
172
+
173
+ file.close
174
+
175
+ mat
176
+ end
177
+
178
+
179
+ # Creates a :list NMatrix from a coordinate-list MatrixMarket file.
180
+ def load_coordinate file, converter, dtype, entry_type, symmetry
181
+
182
+ mat = nil
183
+
184
+ # Read until we get the dimensions and nonzeros
185
+ while line = file.gets
186
+ line.chomp!
187
+ line.lstrip!
188
+ line, comment = line.split('%', 2) # ignore comments
189
+ if line.size > 4
190
+ shape0, shape1, nz = line.split
191
+ mat = NMatrix.new(:list, [shape0.to_i, shape1.to_i], 0, dtype)
192
+ break
193
+ end
194
+ end
195
+
196
+ # Now read the coordinates
197
+ while line = file.gets
198
+ line.chomp!
199
+ line.lstrip!
200
+ line, comment = line.split('%', 2) # ignore comments
201
+
202
+ next unless line.size >= 5 # ignore empty lines
203
+
204
+ fields = line.split
205
+
206
+ i = fields[0].to_i - 1
207
+ j = fields[1].to_i - 1
208
+ datum = entry_type == :pattern ? 1 : fields[2].send(converter)
209
+
210
+ mat[i, j] = datum # add to the matrix
211
+ unless i == j || symmetry == :general
212
+ if symmetry == :symmetric
213
+ mat[j, i] = datum
214
+ elsif symmetry == :'skew-symmetric'
215
+ mat[j, i] = -datum
216
+ elsif symmetry == :hermitian
217
+ mat[j, i] = Complex.new(datum.real, -datum.imag)
218
+ end
219
+ end
220
+ end
221
+
222
+ file.close
223
+
224
+ mat
225
+ end
226
+ end
227
+ end
@@ -21,7 +21,7 @@ require "packable"
21
21
  #
22
22
  # * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
23
23
  #
24
- # == io/matlab/mat_reader.rb
24
+ # == io/mat_reader.rb
25
25
  #
26
26
  # Base class for .mat file reading (Matlab files).
27
27
  #
@@ -42,8 +42,8 @@ module NMatrix::IO::Matlab
42
42
  :miUINT16 => [Integer, {:signed => false, :bytes => 2}],
43
43
  :miINT32 => [Integer, {:signed => true, :bytes => 4}],
44
44
  :miUINT32 => [Integer, {:signed => false, :bytes => 4}],
45
- :miSINGLE => [Float, {:precision => :single, :bytes => 4}],
46
- :miDOUBLE => [Float, {:precision => :double, :bytes => 8}],
45
+ :miSINGLE => [Float, {:precision => :single, :bytes => 4, :endian => :native}],
46
+ :miDOUBLE => [Float, {:precision => :double, :bytes => 4, :endian => :native}],
47
47
  :miINT64 => [Integer, {:signed => true, :bytes => 8}],
48
48
  :miUINT64 => [Integer, {:signed => false, :bytes => 8}]
49
49
  }
@@ -54,10 +54,10 @@ module NMatrix::IO::Matlab
54
54
  :int16 => [Integer, {:signed => true, :bytes => 2}],
55
55
  :int32 => [Integer, {:signed => true, :bytes => 4}],
56
56
  :int64 => [Integer, {:signed => true, :bytes => 8}],
57
- :float32 => [Float, {:precision => :single, :bytes => 4}],
58
- :float64 => [Float, {:precision => :double, :bytes => 8}],
59
- :complex64 => [Float, {:precision => :single, :bytes => 4}], #2x
60
- :complex128 => [Float, {:precision => :double, :bytes => 8}]
57
+ :float32 => [Float, {:precision => :single, :bytes => 4, :endian => :native}],
58
+ :float64 => [Float, {:precision => :double, :bytes => 8, :endian => :native}],
59
+ :complex64 => [Float, {:precision => :single, :bytes => 4, :endian => :native}], #2x
60
+ :complex128 => [Float, {:precision => :double, :bytes => 8, :endian => :native}]
61
61
  }
62
62
 
63
63
  ITYPE_PACK_ARGS = {
@@ -0,0 +1,80 @@
1
+ # = NMatrix
2
+ #
3
+ # A linear algebra library for scientific computation in Ruby.
4
+ # NMatrix is part of SciRuby.
5
+ #
6
+ # NMatrix was originally inspired by and derived from NArray, by
7
+ # Masahiro Tanaka: http://narray.rubyforge.org
8
+ #
9
+ # == Copyright Information
10
+ #
11
+ # SciRuby is Copyright (c) 2010 - 2012, Ruby Science Foundation
12
+ # NMatrix is Copyright (c) 2012, Ruby Science Foundation
13
+ #
14
+ # Please see LICENSE.txt for additional copyright notices.
15
+ #
16
+ # == Contributing
17
+ #
18
+ # By contributing source code to SciRuby, you agree to be bound by
19
+ # our Contributor Agreement:
20
+ #
21
+ # * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
22
+ #
23
+ # == lapack.rb
24
+ #
25
+ # This file contains LAPACK functions accessible in their C versions,
26
+ # e.g., NMatrix::LAPACK::clapack_func. There are some exceptions,
27
+ # such as clapack_gesv, which is implemented in Ruby but calls
28
+ # clapack_getrf and clapack_getrs.
29
+ #
30
+ # Note: most of these functions are borrowed from ATLAS, which is available under a BSD-
31
+ # style license.
32
+
33
+ class NMatrix
34
+ module LAPACK
35
+ class << self
36
+
37
+ # clapack_gesv computes the solution to a system of linear equations
38
+ # A * X = B,
39
+ # where A is an N-by-N matrix and X and B are N-by-NRHS matrices.
40
+ # The LU factorization used to factor A is dependent on the Order parameter,
41
+ # as detailed in the leading comments of clapack_getrf.
42
+ # The factored form of A is then used solve the system of equations A * X = B.
43
+ # A is overwritten with the appropriate LU factorization, and B, which
44
+ # contains B on input, is overwritten with the solution X on output.
45
+ #
46
+ # From ATLAS 3.8.0.
47
+ #
48
+ # Note: Because this function is implemented in Ruby, the ATLAS lib version is
49
+ # never called! For float32, float64, complex64, and complex128, the ATLAS lib
50
+ # versions of getrf and getrs *will* be called.
51
+ def clapack_gesv(order, n, nrhs, a, lda, ipiv, b, ldb)
52
+ clapack_getrf(order, n, n, a, lda, ipiv)
53
+ clapack_getrs(order, :no_transpose, n, nrhs, a, lda, ipiv, b, ldb)
54
+ end
55
+
56
+ # clapack_posv computes the solution to a real system of linear equations
57
+ # A * X = B,
58
+ # where A is an N-by-N symmetric positive definite matrix and X and B
59
+ # are N-by-NRHS matrices.
60
+ #
61
+ # The Cholesky decomposition is used to factor A as
62
+ # A = U**T* U, if UPLO = 'U', or
63
+ # A = L * L**T, if UPLO = 'L',
64
+ # where U is an upper triangular matrix and L is a lower triangular
65
+ # matrix. The factored form of A is then used to solve the system of
66
+ # equations A * X = B.
67
+ #
68
+ # From ATLAS 3.8.0.
69
+ #
70
+ # Note: Because this function is implemented in Ruby, the ATLAS lib version is
71
+ # never called! For float32, float64, complex64, and complex128, the ATLAS lib
72
+ # versions of potrf and potrs *will* be called.
73
+ def clapack_posv(order, uplo, n, nrhs, a, lda, b, ldb)
74
+ clapack_potrf(order, uplo, n, a, lda)
75
+ clapack_potrs(order, uplo, n, nrhs, a, lda, b, ldb)
76
+ end
77
+
78
+ end
79
+ end
80
+ end
@@ -29,6 +29,8 @@
29
29
  # Requires #
30
30
  ############
31
31
 
32
+ require_relative './shortcuts.rb'
33
+
32
34
  #######################
33
35
  # Classes and Modules #
34
36
  #######################
@@ -47,7 +49,11 @@ class NMatrix
47
49
  autoload :MatReader, 'nmatrix/io/mat_reader'
48
50
  autoload :Mat5Reader, 'nmatrix/io/mat5_reader'
49
51
  end
50
- end
52
+
53
+ # FIXME: Remove autoloads
54
+ autoload :Market, 'nmatrix/io/market'
55
+ end
56
+
51
57
 
52
58
  # TODO: Make this actually pretty.
53
59
  def pretty_print(q = nil)
@@ -80,6 +86,74 @@ class NMatrix
80
86
  end
81
87
  alias :pp :pretty_print
82
88
 
89
+ # These shortcuts use #shape to return the number of rows and columns.
90
+ def rows
91
+ shape[0]
92
+ end
93
+
94
+ def cols
95
+ shape[1]
96
+ end
97
+
98
+ # Use LAPACK to calculate the inverse of the matrix (in-place). Only works on dense matrices.
99
+ #
100
+ # Note: If you don't have LAPACK, e.g., on a Mac, this may not work yet.
101
+ def invert!
102
+ # Get the pivot array; factor the matrix
103
+ pivot = self.getrf!
104
+
105
+ # Now calculate the inverse using the pivot array
106
+ NMatrix::LAPACK::clapack_getri(:row, self.shape[0], self, self.shape[0], pivot)
107
+
108
+ self
109
+ end
110
+
111
+ # Make a copy of the matrix, then invert it (requires LAPACK). Returns a dense matrix.
112
+ def invert
113
+ self.cast(:dense, self.dtype).invert!
114
+ end
115
+
116
+ alias :inverse :invert
117
+
118
+ # Calls clapack_getrf and returns the pivot array (dense only).
119
+ def getrf!
120
+ raise(StorageTypeError, "ATLAS functions only work on dense matrices") unless self.stype == :dense
121
+ NMatrix::LAPACK::clapack_getrf(:row, self.shape[0], self.shape[1], self, self.shape[0])
122
+ end
123
+
124
+ # Calculate the determinant by way of LU decomposition. This is accomplished using
125
+ # clapack_getrf, and then by summing the diagonal elements. There is a risk of
126
+ # underflow/overflow.
127
+ #
128
+ # There are probably also more efficient ways to calculate the determinant. This method
129
+ # requires making a copy of the matrix, since clapack_getrf modifies its input.
130
+ #
131
+ # For smaller matrices, you may be able to use det_exact.
132
+ #
133
+ # This function is guaranteed to return the same type of data in the matrix upon which it is called.
134
+ # In other words, if you call it on a rational matrix, you'll get a rational number back.
135
+ #
136
+ # Integer matrices are converted to rational matrices for the purposes of performing the calculation,
137
+ # as xGETRF can't work on integer matrices.
138
+ def det
139
+ raise(NotImplementedError, "determinant can be calculated only for 2D matrices") unless self.dim == 2
140
+
141
+ # Cast to a dtype for which getrf is implemented
142
+ new_dtype = [:byte,:int8,:int16,:int32,:int64].include?(self.dtype) ? :rational128 : self.dtype
143
+ copy = self.cast(:dense, new_dtype)
144
+
145
+ # Need to know the number of permutations. We'll add up the diagonals of the factorized matrix.
146
+ pivot = copy.getrf!
147
+
148
+ prod = pivot.size % 2 == 1 ? -1 : 1 # odd permutations => negative
149
+ [shape[0],shape[1]].min.times do |i|
150
+ prod *= copy[i,i]
151
+ end
152
+
153
+ # Convert back to an integer if necessary
154
+ new_dtype != self.dtype ? prod.to_i : prod
155
+ end
156
+
83
157
 
84
158
  # Get the complex conjugate of this matrix. See also complex_conjugate! for
85
159
  # an in-place operation (provided the dtype is already :complex64 or
@@ -124,57 +198,7 @@ class NMatrix
124
198
  def load_file(file_path)
125
199
  NMatrix::IO::Mat5Reader.new(File.open(file_path, 'rb')).to_ruby
126
200
  end
127
-
128
- # Helper function for loading a file in the first sparse format given here:
129
- # http://math.nist.gov/MatrixMarket/formats.html
130
- #
131
- # Override type specifier (e.g., 'real') using :read_with => :to_f (or any other string-to-numeric conversion
132
- # function), and with :dtype => :float32 or :dtype => :int8 to force storage in a lesser type.
133
- def load_matrix_matrix_coordinate_file(filename, options = {})
134
- f = File.new(filename, "r")
135
-
136
- func = options[:read_with]
137
- dtype = options[:dtype]
138
-
139
- line = f.gets
140
- raise IOError, 'Incorrect file type specifier.' unless line =~ /^%%MatrixMarket\ matrix\ coordinate/
141
- spec = line.split
142
-
143
- case spec[3]
144
- when 'real'
145
- func ||= :to_f
146
- dtype ||= :float64
147
- when 'integer'
148
- func ||= :to_i
149
- dtype ||= :int64
150
- when 'complex'
151
- func ||= :to_complex
152
- dtype ||= :complex128
153
- when 'rational'
154
- func ||= :to_rational
155
- dtype ||= :rational128
156
- else
157
- raise ArgumentError, 'Unrecognized dtype.'
158
- end unless func and dtype
159
-
160
- begin
161
- line = f.gets
162
- end while line =~ /^%/
163
-
164
- # Close the file.
165
- f.close
166
-
167
- rows, cols, entries = line.split.collect { |x| x.to_i }
168
-
169
- matrix = NMatrix.new(:yale, [rows, cols], entries, dtype)
170
-
171
- entries.times do
172
- i, j, v = line.split
173
- matrix[i.to_i - 1, j.to_i - 1] = v.send(func)
174
- end
175
-
176
- matrix
177
- end
201
+
178
202
  end
179
203
 
180
204
  protected
@@ -197,3 +221,5 @@ protected
197
221
  ary
198
222
  end
199
223
  end
224
+
225
+ require_relative "./lapack.rb"