linalg 1.0.2
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.
- 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
|