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.
- data/Gemfile +1 -1
- data/History.txt +31 -3
- data/Manifest.txt +5 -0
- data/README.rdoc +29 -27
- data/ext/nmatrix/binary_format.txt +53 -0
- data/ext/nmatrix/data/data.cpp +18 -18
- data/ext/nmatrix/data/data.h +38 -7
- data/ext/nmatrix/data/rational.h +13 -0
- data/ext/nmatrix/data/ruby_object.h +10 -0
- data/ext/nmatrix/extconf.rb +2 -0
- data/ext/nmatrix/nmatrix.cpp +655 -103
- data/ext/nmatrix/nmatrix.h +26 -14
- data/ext/nmatrix/ruby_constants.cpp +4 -0
- data/ext/nmatrix/ruby_constants.h +2 -0
- data/ext/nmatrix/storage/dense.cpp +99 -41
- data/ext/nmatrix/storage/dense.h +3 -3
- data/ext/nmatrix/storage/list.cpp +36 -14
- data/ext/nmatrix/storage/list.h +4 -4
- data/ext/nmatrix/storage/storage.cpp +19 -19
- data/ext/nmatrix/storage/storage.h +11 -11
- data/ext/nmatrix/storage/yale.cpp +17 -20
- data/ext/nmatrix/storage/yale.h +13 -11
- data/ext/nmatrix/util/io.cpp +25 -23
- data/ext/nmatrix/util/io.h +5 -5
- data/ext/nmatrix/util/math.cpp +634 -17
- data/ext/nmatrix/util/math.h +958 -9
- data/ext/nmatrix/util/sl_list.cpp +7 -7
- data/ext/nmatrix/util/sl_list.h +2 -2
- data/lib/nmatrix.rb +9 -0
- data/lib/nmatrix/blas.rb +4 -4
- data/lib/nmatrix/io/market.rb +227 -0
- data/lib/nmatrix/io/mat_reader.rb +7 -7
- data/lib/nmatrix/lapack.rb +80 -0
- data/lib/nmatrix/nmatrix.rb +78 -52
- data/lib/nmatrix/shortcuts.rb +486 -0
- data/lib/nmatrix/version.rb +1 -1
- data/spec/2x2_dense_double.mat +0 -0
- data/spec/blas_spec.rb +59 -9
- data/spec/elementwise_spec.rb +25 -12
- data/spec/io_spec.rb +69 -1
- data/spec/lapack_spec.rb +53 -4
- data/spec/math_spec.rb +9 -0
- data/spec/nmatrix_list_spec.rb +95 -0
- data/spec/nmatrix_spec.rb +10 -53
- data/spec/nmatrix_yale_spec.rb +17 -15
- data/spec/shortcuts_spec.rb +154 -0
- 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
|
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);
|
data/ext/nmatrix/util/sl_list.h
CHANGED
@@ -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
|
data/lib/nmatrix.rb
CHANGED
data/lib/nmatrix/blas.rb
CHANGED
@@ -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
|
41
|
-
beta = Complex
|
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
|
61
|
-
beta = Complex
|
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/
|
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
|
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
|
data/lib/nmatrix/nmatrix.rb
CHANGED
@@ -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
|
-
|
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"
|