nmatrix 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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"