linalg 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +24 -0
- data/ext/g2c_typedefs.h +5 -0
- data/ext/lapack/extconf.rb +309 -0
- data/ext/lapack/include/BLAS.h +154 -0
- data/ext/lapack/include/LAPACK.h +1314 -0
- data/ext/lapack/main.c +49 -0
- data/ext/lapack/rb_lapack.h +36 -0
- data/ext/lapack/rb_lapack_c.c +11614 -0
- data/ext/lapack/rb_lapack_d.c +12663 -0
- data/ext/lapack/rb_lapack_s.c +12649 -0
- data/ext/lapack/rb_lapack_x.c +208 -0
- data/ext/lapack/rb_lapack_z.c +11614 -0
- data/ext/linalg/dcomplex.c +359 -0
- data/ext/linalg/dcomplex.h +40 -0
- data/ext/linalg/ddata.c +194 -0
- data/ext/linalg/extconf.rb +324 -0
- data/ext/linalg/linalg.c +55 -0
- data/ext/linalg/linalg.h +21 -0
- data/ext/linalg/xdata.c +21 -0
- data/ext/linalg/xdata.h +33 -0
- data/ext/linalg/xmatrix.c.tmpl +1630 -0
- data/ext/linalg/xmatrix.h.tmpl +77 -0
- data/ext/linalg/xmatrixc.c.tmpl +138 -0
- data/ext/linalg/xmatrixr.c.tmpl +130 -0
- data/lib/lapack.rb +87 -0
- data/lib/linalg.rb +9 -0
- data/lib/linalg/dcomplex.rb +17 -0
- data/lib/linalg/dmatrix.rb +29 -0
- data/lib/linalg/dmatrix/alias.rb +32 -0
- data/lib/linalg/dmatrix/cholesky.rb +52 -0
- data/lib/linalg/dmatrix/cond.rb +80 -0
- data/lib/linalg/dmatrix/det.rb +36 -0
- data/lib/linalg/dmatrix/eigen.rb +153 -0
- data/lib/linalg/dmatrix/fit.rb +281 -0
- data/lib/linalg/dmatrix/inverse.rb +78 -0
- data/lib/linalg/dmatrix/lu.rb +120 -0
- data/lib/linalg/dmatrix/main.rb +244 -0
- data/lib/linalg/dmatrix/norms.rb +88 -0
- data/lib/linalg/dmatrix/nullspace.rb +114 -0
- data/lib/linalg/dmatrix/qr.rb +129 -0
- data/lib/linalg/dmatrix/schur.rb +88 -0
- data/lib/linalg/dmatrix/solve.rb +78 -0
- data/lib/linalg/dmatrix/svd.rb +125 -0
- data/lib/linalg/exception.rb +32 -0
- data/lib/linalg/iterators.rb +221 -0
- data/lib/linalg/math.rb +23 -0
- data/lib/linalg/scomplex.rb +15 -0
- data/lib/linalg/version.rb +3 -0
- data/lib/linalg/xdata.rb +123 -0
- metadata +94 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2004-2008 by James M. Lawrence
|
3
|
+
#
|
4
|
+
# See LICENSE
|
5
|
+
#
|
6
|
+
|
7
|
+
|
8
|
+
module Linalg
|
9
|
+
class DMatrix
|
10
|
+
|
11
|
+
#
|
12
|
+
# call-seq: inverse
|
13
|
+
#
|
14
|
+
# Matrix inverse. Raises +SingularMatrix+ if the matrix is
|
15
|
+
# singular.
|
16
|
+
#
|
17
|
+
def inverse
|
18
|
+
inverse_private(false)
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# call-seq: inverse!
|
23
|
+
#
|
24
|
+
# In-place matrix inverse. Raises +SingularMatrix+ if the matrix is
|
25
|
+
# singular.
|
26
|
+
#
|
27
|
+
# a.object_id == a.inverse!.object_id
|
28
|
+
#
|
29
|
+
def inverse!
|
30
|
+
inverse_private(true)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def inverse_private(inplace)
|
36
|
+
raise DimensionError unless square?
|
37
|
+
|
38
|
+
m = XInteger.new(vsize)
|
39
|
+
n = m
|
40
|
+
a = inplace ? self : self.clone
|
41
|
+
lda = XInteger.new(n.value)
|
42
|
+
ipiv = IData.new(n.value)
|
43
|
+
info = XInteger.new
|
44
|
+
|
45
|
+
Lapack.dgetrf(m,
|
46
|
+
n,
|
47
|
+
a,
|
48
|
+
lda,
|
49
|
+
ipiv,
|
50
|
+
info)
|
51
|
+
|
52
|
+
raise SingularMatrix unless info.value == 0
|
53
|
+
work = DReal.new # query
|
54
|
+
lwork = XInteger.new(-1) # query
|
55
|
+
|
56
|
+
Lapack.dgetri(n,
|
57
|
+
a,
|
58
|
+
lda,
|
59
|
+
ipiv,
|
60
|
+
work,
|
61
|
+
lwork,
|
62
|
+
info)
|
63
|
+
|
64
|
+
raise SingularMatrix unless info.value == 0
|
65
|
+
lwork = XInteger.new(work.value.to_i)
|
66
|
+
work = DData.new(lwork.value)
|
67
|
+
|
68
|
+
Lapack.dgetri(n,
|
69
|
+
a,
|
70
|
+
lda,
|
71
|
+
ipiv,
|
72
|
+
work,
|
73
|
+
lwork,
|
74
|
+
info)
|
75
|
+
a
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2004-2008 by James M. Lawrence
|
3
|
+
#
|
4
|
+
# See LICENSE
|
5
|
+
#
|
6
|
+
|
7
|
+
|
8
|
+
module Linalg
|
9
|
+
class DMatrix
|
10
|
+
#
|
11
|
+
# call-seq:
|
12
|
+
# lu => [p, l, u]
|
13
|
+
# lu { |p, l, u| ... } => block result
|
14
|
+
#
|
15
|
+
# Generalized LU decomposition for rectangular matrices.
|
16
|
+
# The matrix is decomposed into
|
17
|
+
#
|
18
|
+
# p * l * u
|
19
|
+
#
|
20
|
+
# where +p+ is a permutation matrix, +l+ lower-triangular
|
21
|
+
# (or lower-trapezoidal), and +u+ is upper-triangular (or
|
22
|
+
# upper-trapezoidal).
|
23
|
+
#
|
24
|
+
def lu # :yields: p, l, u
|
25
|
+
res = lu_private(false)
|
26
|
+
if block_given?
|
27
|
+
yield res
|
28
|
+
else
|
29
|
+
res
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# call-seq:
|
35
|
+
# lu! => p
|
36
|
+
#
|
37
|
+
# In-place LU decomposition. The matrix is overwritten with
|
38
|
+
# both L and U factors (the diagonal 1's of L are discarded).
|
39
|
+
# Returns the vector +p+ of pivots.
|
40
|
+
#
|
41
|
+
def lu!
|
42
|
+
lu_private(true)
|
43
|
+
end
|
44
|
+
|
45
|
+
def zero_lower # :nodoc:
|
46
|
+
if vsize > hsize
|
47
|
+
hsize
|
48
|
+
else
|
49
|
+
vsize - 1
|
50
|
+
end.times { |j|
|
51
|
+
replace_minor(j + 1, j, DMatrix.new(vsize - j - 1, 1))
|
52
|
+
}
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
def zero_upper # :nodoc:
|
57
|
+
# triangular portion
|
58
|
+
(1...Math.min(vsize, hsize)).each { |j|
|
59
|
+
replace_minor(0, j, DMatrix.new(j, 1))
|
60
|
+
}
|
61
|
+
|
62
|
+
if hsize > vsize
|
63
|
+
# remaining
|
64
|
+
replace_minor(0, hsize, hsize - vsize, vsize)
|
65
|
+
end
|
66
|
+
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def lu_private(inplace)
|
73
|
+
minsize = Math.min(vsize, hsize)
|
74
|
+
|
75
|
+
m = XInteger.new vsize
|
76
|
+
n = XInteger.new hsize
|
77
|
+
a = inplace ? self : self.clone
|
78
|
+
lda = m
|
79
|
+
ipiv = IData.new minsize
|
80
|
+
info = XInteger.new
|
81
|
+
|
82
|
+
Lapack.dgetrf(m,
|
83
|
+
n,
|
84
|
+
a,
|
85
|
+
lda,
|
86
|
+
ipiv,
|
87
|
+
info)
|
88
|
+
|
89
|
+
raise SingularMatrix unless info.value == 0
|
90
|
+
|
91
|
+
if inplace
|
92
|
+
ipiv
|
93
|
+
else
|
94
|
+
p = DMatrix.identity(vsize)
|
95
|
+
if vsize > hsize
|
96
|
+
uvsize, uhsize = [minsize, minsize]
|
97
|
+
lvsize, lhsize = [vsize, hsize]
|
98
|
+
else
|
99
|
+
uvsize, uhsize = [vsize, hsize]
|
100
|
+
lvsize, lhsize = [minsize, minsize]
|
101
|
+
end
|
102
|
+
|
103
|
+
ipiv.each_with_index { |e, i|
|
104
|
+
# 1-based fortran
|
105
|
+
if i != e - 1
|
106
|
+
p.exchange_columns(i, e - 1)
|
107
|
+
end
|
108
|
+
}
|
109
|
+
|
110
|
+
l = a.minor(0, 0, lvsize, lhsize).zero_upper
|
111
|
+
minsize.times { |i| l[i, i] = 1.0 }
|
112
|
+
u = a.minor(0, 0, uvsize, uhsize).zero_lower
|
113
|
+
|
114
|
+
[p, l, u]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
|
@@ -0,0 +1,244 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2004-2008 by James M. Lawrence
|
3
|
+
#
|
4
|
+
# See LICENSE
|
5
|
+
#
|
6
|
+
|
7
|
+
module Linalg
|
8
|
+
|
9
|
+
class DMatrix
|
10
|
+
include Iterators
|
11
|
+
|
12
|
+
include XData # :nodoc:
|
13
|
+
include Linalg::Exception # :nodoc:
|
14
|
+
|
15
|
+
|
16
|
+
##################################################
|
17
|
+
#
|
18
|
+
# singleton methods
|
19
|
+
#
|
20
|
+
##################################################
|
21
|
+
|
22
|
+
#
|
23
|
+
# Create a matrix by joining together a list of column vectors
|
24
|
+
#
|
25
|
+
# m == DMatrix.join_columns(m.columns.map { |x| x })
|
26
|
+
#
|
27
|
+
def self.join_columns(cols)
|
28
|
+
col_vsize = cols[0].vsize
|
29
|
+
res = DMatrix.reserve(col_vsize, cols.size)
|
30
|
+
cols.each_with_index { |col, j|
|
31
|
+
unless col.vsize == col_vsize
|
32
|
+
raise DimensionError
|
33
|
+
end
|
34
|
+
res.replace_column(j, col)
|
35
|
+
}
|
36
|
+
res
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# Create a matrix by joining together a list of row vectors
|
41
|
+
#
|
42
|
+
# m == DMatrix.join_rows(m.rows.map { |x| x })
|
43
|
+
#
|
44
|
+
def self.join_rows(rows)
|
45
|
+
row_hsize = rows[0].hsize
|
46
|
+
res = DMatrix.reserve(rows.size, row_hsize)
|
47
|
+
rows.each_with_index { |row, i|
|
48
|
+
unless row.hsize == row_hsize
|
49
|
+
raise DimensionError
|
50
|
+
end
|
51
|
+
res.replace_row(i, row)
|
52
|
+
}
|
53
|
+
res
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# calls DMatrix.rows with a splat (*)
|
58
|
+
#
|
59
|
+
# DMatrix[[1,2,3],[4,5,6]]
|
60
|
+
# is equivalent to
|
61
|
+
# DMatrix.rows [[1,2,3],[4,5,6]]
|
62
|
+
#
|
63
|
+
def self.[](*array)
|
64
|
+
self.rows(array)
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
# Create a matrix using the elements of +array+ as columns.
|
69
|
+
#
|
70
|
+
# DMatrix.columns(array)
|
71
|
+
# is equivalent to
|
72
|
+
# DMatrix.new(array[0].size, array.size) { |i,j| array[j][i] }
|
73
|
+
#
|
74
|
+
# +DimensionError+ is raised if +array+ is not rectangular.
|
75
|
+
#
|
76
|
+
def self.columns(array)
|
77
|
+
self.rows(array).transpose!
|
78
|
+
end
|
79
|
+
|
80
|
+
class << self
|
81
|
+
attr_accessor :default_epsilon
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# rdoc can you see me?
|
86
|
+
#
|
87
|
+
self.default_epsilon = 1e-8
|
88
|
+
|
89
|
+
##################################################
|
90
|
+
#
|
91
|
+
# instance methods
|
92
|
+
#
|
93
|
+
##################################################
|
94
|
+
|
95
|
+
#
|
96
|
+
# Returns the singleton class of the object. Convenient for
|
97
|
+
# setting the +default_epsilon+ on a per-object basis.
|
98
|
+
# b = DMatrix.rand(4, 4)
|
99
|
+
# a = b.map { |e| e + 0.0001 }
|
100
|
+
# a.class.default_epsilon # => 1e-8
|
101
|
+
# a =~ b # => false
|
102
|
+
# a.singleton_class.default_epsilon = 0.001
|
103
|
+
# a =~ b # => true
|
104
|
+
#
|
105
|
+
def singleton_class
|
106
|
+
class << self
|
107
|
+
self
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# True if element-wise identical values.
|
113
|
+
#
|
114
|
+
def ==(other)
|
115
|
+
self.within(0.0, other)
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# Sum of diagonal elements.
|
120
|
+
#
|
121
|
+
def trace
|
122
|
+
diags.inject(0.0) { |acc, e| acc + e }
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# Same as to_s but prepended with a newline for nice irb output
|
127
|
+
#
|
128
|
+
def inspect
|
129
|
+
"\n" << self.to_s
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
# Dump to an +eval+-able string
|
134
|
+
#
|
135
|
+
def inspect2
|
136
|
+
res = "#{self.class}["
|
137
|
+
vsize.times { |i|
|
138
|
+
res << "["
|
139
|
+
hsize.times { |j|
|
140
|
+
res << self[i,j].to_s << ","
|
141
|
+
}
|
142
|
+
res.chop!
|
143
|
+
res << "],"
|
144
|
+
}
|
145
|
+
res.chop!
|
146
|
+
res << "]"
|
147
|
+
end
|
148
|
+
|
149
|
+
#
|
150
|
+
# Dump to a readable string
|
151
|
+
#
|
152
|
+
def to_s(format = "% 10.6f")
|
153
|
+
res = ""
|
154
|
+
vsize.times { |i|
|
155
|
+
hsize.times { |j|
|
156
|
+
res << sprintf(format, self[i,j])
|
157
|
+
}
|
158
|
+
res << "\n"
|
159
|
+
}
|
160
|
+
res
|
161
|
+
end
|
162
|
+
|
163
|
+
#
|
164
|
+
# Returns <tt>m[0,0]</tt> when +m+ is a 1x1 matrix, otherwise a
|
165
|
+
# +TypeError+ is raised.
|
166
|
+
#
|
167
|
+
def to_f
|
168
|
+
if vsize == 1 and hsize == 1
|
169
|
+
self[0,0]
|
170
|
+
else
|
171
|
+
raise TypeError, "to_f called on nonscalar matrix"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
#
|
176
|
+
# Implemented as
|
177
|
+
#
|
178
|
+
# m.within(epsilon, other)
|
179
|
+
#
|
180
|
+
# where
|
181
|
+
# epsilon =
|
182
|
+
# self.singleton_class.default_epsilon ||
|
183
|
+
# self.class.default_epsilon
|
184
|
+
#
|
185
|
+
def =~(other)
|
186
|
+
epsilon =
|
187
|
+
self.singleton_class.default_epsilon ||
|
188
|
+
self.class.default_epsilon
|
189
|
+
|
190
|
+
self.within(epsilon, other)
|
191
|
+
end
|
192
|
+
|
193
|
+
#
|
194
|
+
# Returns
|
195
|
+
# self.vsize == self.hsize
|
196
|
+
#
|
197
|
+
def square?
|
198
|
+
self.vsize == self.hsize
|
199
|
+
end
|
200
|
+
|
201
|
+
#
|
202
|
+
# Tests whether the matrix is symmetric within +epsilon+
|
203
|
+
#
|
204
|
+
def symmetric?(epsilon = (self.singleton_class.default_epsilon || self.class.default_epsilon))
|
205
|
+
symmetric_private(epsilon)
|
206
|
+
end
|
207
|
+
|
208
|
+
#
|
209
|
+
# Exchange the <tt>p</tt>-th row with the <tt>q</tt>-th row
|
210
|
+
#
|
211
|
+
def exchange_rows(p, q)
|
212
|
+
tmp = self.row(p)
|
213
|
+
replace_row(p, self.row(q))
|
214
|
+
replace_row(q, tmp)
|
215
|
+
end
|
216
|
+
|
217
|
+
#
|
218
|
+
# Exchange the <tt>p</tt>-th column with the <tt>q</tt>-th column
|
219
|
+
#
|
220
|
+
def exchange_columns(p, q)
|
221
|
+
tmp = self.column(p)
|
222
|
+
replace_column(p, self.column(q))
|
223
|
+
replace_column(q, tmp)
|
224
|
+
end
|
225
|
+
|
226
|
+
private
|
227
|
+
|
228
|
+
#
|
229
|
+
# Create a column vector from the array
|
230
|
+
#
|
231
|
+
def self.column_vector(array)
|
232
|
+
self.columns([array])
|
233
|
+
end
|
234
|
+
|
235
|
+
#
|
236
|
+
# Create a row vector from the array
|
237
|
+
#
|
238
|
+
def self.row_vector(array)
|
239
|
+
self.rows([array])
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2004-2008 by James M. Lawrence
|
3
|
+
#
|
4
|
+
# See LICENSE
|
5
|
+
#
|
6
|
+
|
7
|
+
module Linalg
|
8
|
+
class DMatrix
|
9
|
+
#
|
10
|
+
# The 2-norm. The largest singular value.
|
11
|
+
#
|
12
|
+
def norm_2
|
13
|
+
singular_values[0]
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# call-seq: norm_1
|
18
|
+
#
|
19
|
+
# The 1-norm. Maximum column absolute sum.
|
20
|
+
# For matrix +m+, equivalent to
|
21
|
+
#
|
22
|
+
# m.columns.map { |col|
|
23
|
+
# col.elems.inject(0.0) { |acc, e| acc + e.abs }
|
24
|
+
# }.sort[-1]
|
25
|
+
#
|
26
|
+
def norm_1
|
27
|
+
private_norm(Char.new("1"), NULL)
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# call-seq: norm_inf
|
32
|
+
#
|
33
|
+
# The infinity-norm. Maximum row absolute sum.
|
34
|
+
# For matrix +m+, equivalent to
|
35
|
+
#
|
36
|
+
# m.rows.map { |row|
|
37
|
+
# row.elems.inject(0.0) { |acc, e| acc + e.abs }
|
38
|
+
# }.sort[-1]
|
39
|
+
#
|
40
|
+
def norm_inf
|
41
|
+
private_norm(Char.new("I"), DData.new(vsize))
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# call-seq: norm_f
|
46
|
+
#
|
47
|
+
#
|
48
|
+
# Frobenius norm. Square root of the sum of the squares of the
|
49
|
+
# elements.
|
50
|
+
#
|
51
|
+
# For matrix +m+, equivalent to
|
52
|
+
#
|
53
|
+
# Math.sqrt(m.elems.inject(0.0) { |acc, e| acc + e*e })
|
54
|
+
#
|
55
|
+
#
|
56
|
+
def norm_f
|
57
|
+
private_norm(Char.new("F"), NULL)
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# call-seq: maxabs
|
62
|
+
#
|
63
|
+
# The maximum absolute value of the individual matrix elements.
|
64
|
+
# For matrix +m+, equivalent to
|
65
|
+
#
|
66
|
+
# m.elems.map { |e| e.abs }.to_a.flatten.sort[-1]
|
67
|
+
#
|
68
|
+
# Note +maxabs+ is not a matrix norm.
|
69
|
+
#
|
70
|
+
#
|
71
|
+
def maxabs
|
72
|
+
private_norm(Char.new("M"), NULL)
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def private_norm(kind, work)
|
78
|
+
m = XInteger.new(vsize)
|
79
|
+
n = XInteger.new(hsize)
|
80
|
+
Lapack.dlange(kind,
|
81
|
+
m,
|
82
|
+
n,
|
83
|
+
self,
|
84
|
+
m,
|
85
|
+
work)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|