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.
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
+