linalg 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +24 -0
  3. data/ext/g2c_typedefs.h +5 -0
  4. data/ext/lapack/extconf.rb +309 -0
  5. data/ext/lapack/include/BLAS.h +154 -0
  6. data/ext/lapack/include/LAPACK.h +1314 -0
  7. data/ext/lapack/main.c +49 -0
  8. data/ext/lapack/rb_lapack.h +36 -0
  9. data/ext/lapack/rb_lapack_c.c +11614 -0
  10. data/ext/lapack/rb_lapack_d.c +12663 -0
  11. data/ext/lapack/rb_lapack_s.c +12649 -0
  12. data/ext/lapack/rb_lapack_x.c +208 -0
  13. data/ext/lapack/rb_lapack_z.c +11614 -0
  14. data/ext/linalg/dcomplex.c +359 -0
  15. data/ext/linalg/dcomplex.h +40 -0
  16. data/ext/linalg/ddata.c +194 -0
  17. data/ext/linalg/extconf.rb +324 -0
  18. data/ext/linalg/linalg.c +55 -0
  19. data/ext/linalg/linalg.h +21 -0
  20. data/ext/linalg/xdata.c +21 -0
  21. data/ext/linalg/xdata.h +33 -0
  22. data/ext/linalg/xmatrix.c.tmpl +1630 -0
  23. data/ext/linalg/xmatrix.h.tmpl +77 -0
  24. data/ext/linalg/xmatrixc.c.tmpl +138 -0
  25. data/ext/linalg/xmatrixr.c.tmpl +130 -0
  26. data/lib/lapack.rb +87 -0
  27. data/lib/linalg.rb +9 -0
  28. data/lib/linalg/dcomplex.rb +17 -0
  29. data/lib/linalg/dmatrix.rb +29 -0
  30. data/lib/linalg/dmatrix/alias.rb +32 -0
  31. data/lib/linalg/dmatrix/cholesky.rb +52 -0
  32. data/lib/linalg/dmatrix/cond.rb +80 -0
  33. data/lib/linalg/dmatrix/det.rb +36 -0
  34. data/lib/linalg/dmatrix/eigen.rb +153 -0
  35. data/lib/linalg/dmatrix/fit.rb +281 -0
  36. data/lib/linalg/dmatrix/inverse.rb +78 -0
  37. data/lib/linalg/dmatrix/lu.rb +120 -0
  38. data/lib/linalg/dmatrix/main.rb +244 -0
  39. data/lib/linalg/dmatrix/norms.rb +88 -0
  40. data/lib/linalg/dmatrix/nullspace.rb +114 -0
  41. data/lib/linalg/dmatrix/qr.rb +129 -0
  42. data/lib/linalg/dmatrix/schur.rb +88 -0
  43. data/lib/linalg/dmatrix/solve.rb +78 -0
  44. data/lib/linalg/dmatrix/svd.rb +125 -0
  45. data/lib/linalg/exception.rb +32 -0
  46. data/lib/linalg/iterators.rb +221 -0
  47. data/lib/linalg/math.rb +23 -0
  48. data/lib/linalg/scomplex.rb +15 -0
  49. data/lib/linalg/version.rb +3 -0
  50. data/lib/linalg/xdata.rb +123 -0
  51. 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
+