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,114 @@
|
|
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
|
+
# Returns a matrix whose columns form an orthogonal basis for the
|
12
|
+
# nullspace. (This is simply taken from <tt>vt.t</tt> in DMatrix#svd.)
|
13
|
+
# Returns +nil+ if the matrix is of full rank.
|
14
|
+
#
|
15
|
+
# For matrix +m+, a singular value less than or equal
|
16
|
+
# to <tt>m.norm*epsilon</tt> is considered a rank
|
17
|
+
# deficiency.
|
18
|
+
#
|
19
|
+
def nullspace(epsilon = (self.singleton_class.default_epsilon ||self.class.default_epsilon))
|
20
|
+
u, s, vt = self.svd
|
21
|
+
v = vt.transpose!
|
22
|
+
null_indexes = sv_null_indexes(s.diags, s[0,0]*epsilon)
|
23
|
+
|
24
|
+
if null_indexes.empty?
|
25
|
+
nil
|
26
|
+
else
|
27
|
+
ns = DMatrix.reserve(v.vsize, null_indexes.size)
|
28
|
+
null_indexes.each_with_index { |e, j|
|
29
|
+
ns.replace_column(j, v.column(e))
|
30
|
+
}
|
31
|
+
ns
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Returns a matrix whose columns form an orthonormal basis
|
37
|
+
# for the span of the matrix.
|
38
|
+
#
|
39
|
+
# This is just the complement of the nullspace.
|
40
|
+
# See DMatrix#nullspace.
|
41
|
+
#
|
42
|
+
def rankspace(epsilon = (self.singleton_class.default_epsilon ||self.class.default_epsilon))
|
43
|
+
u, s, vt = self.svd
|
44
|
+
v = vt.transpose!
|
45
|
+
null_indexes = sv_null_indexes(s.diags, s[0,0]*epsilon)
|
46
|
+
|
47
|
+
if null_indexes.empty?
|
48
|
+
v
|
49
|
+
else
|
50
|
+
orth_indexes = (0...v.hsize).to_a - null_indexes
|
51
|
+
orth = DMatrix.reserve(v.vsize, orth_indexes.size)
|
52
|
+
orth_indexes.each_with_index { |e, j|
|
53
|
+
orth.replace_column(j, v.column(e))
|
54
|
+
}
|
55
|
+
orth
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Returns the column rank of the matrix.
|
61
|
+
# hsize == rank + nullity
|
62
|
+
#
|
63
|
+
# see DMatrix#nullspace
|
64
|
+
#
|
65
|
+
def rank(epsilon = (self.singleton_class.default_epsilon ||self.class.default_epsilon))
|
66
|
+
sv = self.sv
|
67
|
+
indexes = sv_null_indexes(sv, sv[0]*epsilon)
|
68
|
+
indexes ? (hsize - indexes.size) : 0
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
# Returns the nullity of the matrix (the dimension of the nullspace).
|
73
|
+
# hsize == rank + nullity
|
74
|
+
#
|
75
|
+
# see DMatrix#nullspace
|
76
|
+
#
|
77
|
+
def nullity(epsilon = self.class.default_epsilon)
|
78
|
+
sv = self.sv
|
79
|
+
indexes = sv_null_indexes(sv, sv[0]*epsilon)
|
80
|
+
indexes ? indexes.size : 0
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# True if the rank less than number of columns.
|
85
|
+
#
|
86
|
+
# see DMatrix#nullspace
|
87
|
+
#
|
88
|
+
def singular?(epsilon = (self.singleton_class.default_epsilon ||self.class.default_epsilon))
|
89
|
+
not regular?(epsilon)
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# True if the rank is equal to the number of columns.
|
94
|
+
#
|
95
|
+
# see DMatrix#nullspace
|
96
|
+
#
|
97
|
+
def regular?(epsilon = (self.singleton_class.default_epsilon ||self.class.default_epsilon))
|
98
|
+
square? and rank(epsilon) == hsize
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def sv_null_indexes(sv, epsilon)
|
104
|
+
nulls = []
|
105
|
+
sv.each_with_index { |e, i|
|
106
|
+
nulls << i if e.abs <= epsilon
|
107
|
+
}
|
108
|
+
nulls += (self.vsize...self.hsize).to_a
|
109
|
+
nulls
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
|
@@ -0,0 +1,129 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2004-2008 by James M. Lawrence
|
3
|
+
#
|
4
|
+
# See LICENSE
|
5
|
+
#
|
6
|
+
|
7
|
+
module Linalg
|
8
|
+
class DMatrix
|
9
|
+
#
|
10
|
+
# call-seq:
|
11
|
+
# qr => [q, r]
|
12
|
+
# qr { |q, r| ... } => block result
|
13
|
+
#
|
14
|
+
# QR factorization. Decompose the matrix into
|
15
|
+
# q * r
|
16
|
+
# where +q+ is orthogonal and +r+ is upper-triangular.
|
17
|
+
#
|
18
|
+
def qr # :yields: q, r
|
19
|
+
res = qr_private(false)
|
20
|
+
if block_given?
|
21
|
+
yield res
|
22
|
+
else
|
23
|
+
res
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# In-place QR factorization. The contents of the matrix are
|
29
|
+
# left in the state returned by dgeqrf().
|
30
|
+
#
|
31
|
+
def qr!
|
32
|
+
qr_private(true)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def qr_private(inplace)
|
38
|
+
mindim = Math.min(vsize, hsize)
|
39
|
+
|
40
|
+
m = XInteger.new(vsize)
|
41
|
+
n = XInteger.new(hsize)
|
42
|
+
a = inplace ? self : self.clone
|
43
|
+
lda = m
|
44
|
+
tau = DData.new(mindim)
|
45
|
+
work = DReal.new # query
|
46
|
+
lwork = XInteger.new(-1) # query
|
47
|
+
info = XInteger.new
|
48
|
+
|
49
|
+
# query
|
50
|
+
Lapack.dgeqrf(m,
|
51
|
+
n,
|
52
|
+
a,
|
53
|
+
lda,
|
54
|
+
tau,
|
55
|
+
work,
|
56
|
+
lwork,
|
57
|
+
info)
|
58
|
+
|
59
|
+
raise Diverged unless info.value == 0
|
60
|
+
lwork[0] = work[0].to_i
|
61
|
+
work = DData.new(lwork[0])
|
62
|
+
|
63
|
+
Lapack.dgeqrf(m,
|
64
|
+
n,
|
65
|
+
a,
|
66
|
+
lda,
|
67
|
+
tau,
|
68
|
+
work,
|
69
|
+
lwork,
|
70
|
+
info)
|
71
|
+
|
72
|
+
raise Diverged unless info.value == 0
|
73
|
+
|
74
|
+
return self if inplace
|
75
|
+
|
76
|
+
r = a.clone.zero_lower
|
77
|
+
|
78
|
+
iden = DMatrix.identity(vsize)
|
79
|
+
q = iden.clone
|
80
|
+
|
81
|
+
v = DMatrix.reserve(vsize, 1)
|
82
|
+
|
83
|
+
transa = Char.new("N")
|
84
|
+
transb = Char.new("T")
|
85
|
+
alpha = DReal.new
|
86
|
+
neg_one = DReal.new(-1.0)
|
87
|
+
one = XInteger.new(1)
|
88
|
+
d_one = DReal.new(1.0)
|
89
|
+
|
90
|
+
mindim.times { |p|
|
91
|
+
v.fill(0.0)
|
92
|
+
v[p] = 1.0
|
93
|
+
if p < vsize - 1
|
94
|
+
v.replace_minor(p + 1, 0, a.minor(p + 1,
|
95
|
+
p,
|
96
|
+
vsize - p - 1,
|
97
|
+
1))
|
98
|
+
end
|
99
|
+
|
100
|
+
# using dgemm instead
|
101
|
+
#h = iden - tau[p]*v*v.t
|
102
|
+
|
103
|
+
# this is not worth it ...
|
104
|
+
alpha[0] = -tau[p]
|
105
|
+
h = iden.clone
|
106
|
+
Lapack.dgemm(transa,
|
107
|
+
transb,
|
108
|
+
m, # m
|
109
|
+
m, # n
|
110
|
+
one, # k
|
111
|
+
alpha,
|
112
|
+
v, # a
|
113
|
+
m, # lda
|
114
|
+
v, # b
|
115
|
+
m, # ldb
|
116
|
+
d_one, # beta
|
117
|
+
h, # c
|
118
|
+
m) # ldc
|
119
|
+
q.postmul!(h)
|
120
|
+
}
|
121
|
+
|
122
|
+
[q, r]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
|
129
|
+
|
@@ -0,0 +1,88 @@
|
|
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
|
+
# schur => [z, t, real, imag]
|
13
|
+
# schur { |z, t, real, imag| ... } => block result
|
14
|
+
#
|
15
|
+
# Decompose into
|
16
|
+
# z * t * z.t
|
17
|
+
# where +z+ is orthogonal and +t+ is quasi upper-diagonal.
|
18
|
+
# +real+ and +imag+ contain the respective eigenvalues in the format
|
19
|
+
# described in DMatrix#eigensystem.
|
20
|
+
#
|
21
|
+
def schur # :yields: z, t, real, imag
|
22
|
+
raise DimensionError unless square?
|
23
|
+
jobvs = Char.new("V")
|
24
|
+
sort = Char.new("N")
|
25
|
+
select = XData::NULL
|
26
|
+
n = XInteger.new(vsize)
|
27
|
+
a = self.clone
|
28
|
+
lda = n
|
29
|
+
sdim = XInteger.new
|
30
|
+
wr = DMatrix.reserve(n.value, 1)
|
31
|
+
wi = DMatrix.reserve(n.value, 1)
|
32
|
+
vs = DMatrix.reserve(n.value, n.value)
|
33
|
+
ldvs = n
|
34
|
+
work = DReal.new # query
|
35
|
+
lwork = XInteger.new(-1) # query
|
36
|
+
bwork = XData::NULL
|
37
|
+
info = XInteger.new
|
38
|
+
|
39
|
+
# query
|
40
|
+
Lapack.dgees(jobvs,
|
41
|
+
sort,
|
42
|
+
select,
|
43
|
+
n,
|
44
|
+
a,
|
45
|
+
lda,
|
46
|
+
sdim,
|
47
|
+
wr,
|
48
|
+
wi,
|
49
|
+
vs,
|
50
|
+
ldvs,
|
51
|
+
work,
|
52
|
+
lwork,
|
53
|
+
bwork,
|
54
|
+
info)
|
55
|
+
|
56
|
+
raise Diverged unless info.value == 0
|
57
|
+
lwork = XInteger.new(work.value.to_i)
|
58
|
+
work = DData.new(lwork.value)
|
59
|
+
|
60
|
+
Lapack.dgees(jobvs,
|
61
|
+
sort,
|
62
|
+
select,
|
63
|
+
n,
|
64
|
+
a,
|
65
|
+
lda,
|
66
|
+
sdim,
|
67
|
+
wr,
|
68
|
+
wi,
|
69
|
+
vs,
|
70
|
+
ldvs,
|
71
|
+
work,
|
72
|
+
lwork,
|
73
|
+
bwork,
|
74
|
+
info)
|
75
|
+
|
76
|
+
raise Diverged unless info.value == 0
|
77
|
+
|
78
|
+
res = [vs, a, wr, wi]
|
79
|
+
if block_given?
|
80
|
+
yield res
|
81
|
+
else
|
82
|
+
res
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
|
@@ -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: solve(a, b)
|
13
|
+
#
|
14
|
+
# Solve the linear equation
|
15
|
+
#
|
16
|
+
# a * x == b
|
17
|
+
#
|
18
|
+
# Returns the solution +x+, or raises +SingularMatrix+ if +a+ was
|
19
|
+
# singular.
|
20
|
+
#
|
21
|
+
#
|
22
|
+
def self.solve(a, b)
|
23
|
+
solve_private(a, b, false)
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# call-seq: solve!(a, b)
|
28
|
+
#
|
29
|
+
# Solve the linear equation
|
30
|
+
#
|
31
|
+
# a * x == b
|
32
|
+
#
|
33
|
+
# +a+ and +b+ are *both* *overwritten*.
|
34
|
+
#
|
35
|
+
# If a unique solution is found, +a+ contains both L and U factors
|
36
|
+
# while +b+ holds the solution +x+ above.
|
37
|
+
#
|
38
|
+
# Returns the solution +x+, or raises +SingularMatrix+ if +a+ was
|
39
|
+
# singular.
|
40
|
+
#
|
41
|
+
# x = DMatrix.solve!(a, b)
|
42
|
+
# x.object_id == b.object_id
|
43
|
+
#
|
44
|
+
#
|
45
|
+
def self.solve!(a, b)
|
46
|
+
solve_private(a, b, true)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def self.solve_private(a, b, inplace)
|
52
|
+
unless a.square? and a.vsize == b.vsize
|
53
|
+
raise DimensionError
|
54
|
+
end
|
55
|
+
|
56
|
+
n = XInteger.new(a.vsize)
|
57
|
+
nrhs = XInteger.new(b.hsize)
|
58
|
+
a = inplace ? a : a.clone
|
59
|
+
lda = XInteger.new(a.vsize)
|
60
|
+
ipiv = IData.new(n.value)
|
61
|
+
b = inplace ? b : b.clone
|
62
|
+
ldb = XInteger.new(b.vsize)
|
63
|
+
info = XInteger.new
|
64
|
+
|
65
|
+
Lapack.dgesv(n,
|
66
|
+
nrhs,
|
67
|
+
a,
|
68
|
+
lda,
|
69
|
+
ipiv,
|
70
|
+
b,
|
71
|
+
ldb,
|
72
|
+
info)
|
73
|
+
|
74
|
+
raise SingularMatrix unless info.value == 0
|
75
|
+
b
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,125 @@
|
|
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
|
+
# singular_value_decomposition => [u, s, vt]
|
13
|
+
# singular_value_decomposition { |u, s, vt| ... } => block result
|
14
|
+
#
|
15
|
+
# Decompose the +m+ x +n+ matrix into
|
16
|
+
#
|
17
|
+
# u * s * vt
|
18
|
+
#
|
19
|
+
# where +u+ and +vt+ are orthogonal matrices and +s+ is an +m+ x +n+
|
20
|
+
# matrix whose non-diagonal elements are all zero. The diagonal
|
21
|
+
# elements of +s+ are called the <em>singular values</em> of the
|
22
|
+
# matrix. If called with
|
23
|
+
# m.singular_value_decomposition(:diagonalize => false)
|
24
|
+
# then +s+ is a vector of singular values rather than a diagonal
|
25
|
+
# matrix.
|
26
|
+
#
|
27
|
+
# May raise +Diverged+.
|
28
|
+
#
|
29
|
+
def singular_value_decomposition(opts = nil, &b)
|
30
|
+
if opts
|
31
|
+
svd_private(true, opts[:diagonalize], &b)
|
32
|
+
else
|
33
|
+
svd_private(true, true, &b)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# call-seq:
|
39
|
+
# singular_values => s
|
40
|
+
# singular_values { |s| .. } => block result
|
41
|
+
#
|
42
|
+
# Returns a vector containing the singular values of the matrix. See
|
43
|
+
# DMatrix#singular_value_decomposition.
|
44
|
+
#
|
45
|
+
def singular_values(&b)
|
46
|
+
svd_private(false, false, &b)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def svd_private(decomp, diagonalize)
|
52
|
+
jobu = Char.new(decomp ? "A" : "N")
|
53
|
+
jobvt = Char.new(decomp ? "A" : "N")
|
54
|
+
m = XInteger.new(vsize)
|
55
|
+
n = XInteger.new(hsize)
|
56
|
+
a = self.clone
|
57
|
+
lda = XInteger.new(a.vsize)
|
58
|
+
s = DMatrix.new(Math.min(vsize, hsize), 1)
|
59
|
+
u = decomp ? DMatrix.reserve(vsize, vsize) : DMatrix.reserve(1, 1)
|
60
|
+
ldu = XInteger.new(u.vsize)
|
61
|
+
vt = decomp ? DMatrix.reserve(hsize, hsize) : DMatrix.reserve(1, 1)
|
62
|
+
ldvt = XInteger.new(vt.vsize)
|
63
|
+
work = DReal.new
|
64
|
+
lwork = XInteger.new(-1) # query
|
65
|
+
info = XInteger.new
|
66
|
+
|
67
|
+
# query
|
68
|
+
Lapack.dgesvd(jobu,
|
69
|
+
jobvt,
|
70
|
+
m,
|
71
|
+
n,
|
72
|
+
a,
|
73
|
+
lda,
|
74
|
+
s,
|
75
|
+
u,
|
76
|
+
ldu,
|
77
|
+
vt,
|
78
|
+
ldvt,
|
79
|
+
work,
|
80
|
+
lwork,
|
81
|
+
info)
|
82
|
+
|
83
|
+
raise Diverged unless info.value == 0
|
84
|
+
lwork = XInteger.new(work.value.to_i)
|
85
|
+
work = DData.new(lwork.value)
|
86
|
+
|
87
|
+
Lapack.dgesvd(jobu,
|
88
|
+
jobvt,
|
89
|
+
m,
|
90
|
+
n,
|
91
|
+
a,
|
92
|
+
lda,
|
93
|
+
s,
|
94
|
+
u,
|
95
|
+
ldu,
|
96
|
+
vt,
|
97
|
+
ldvt,
|
98
|
+
work,
|
99
|
+
lwork,
|
100
|
+
info)
|
101
|
+
|
102
|
+
if decomp
|
103
|
+
if diagonalize
|
104
|
+
sm = DMatrix.new(vsize, hsize)
|
105
|
+
Math.min(vsize, hsize).times { |i|
|
106
|
+
sm[i, i] = s[i]
|
107
|
+
}
|
108
|
+
res = [u, sm, vt]
|
109
|
+
else
|
110
|
+
res = [u, s, vt]
|
111
|
+
end
|
112
|
+
else
|
113
|
+
res = s
|
114
|
+
end
|
115
|
+
|
116
|
+
if block_given?
|
117
|
+
yield res
|
118
|
+
else
|
119
|
+
res
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|