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,29 @@
1
+ #
2
+ # Copyright (c) 2004-2008 by James M. Lawrence
3
+ #
4
+ # See LICENSE
5
+ #
6
+
7
+ require 'linalg'
8
+
9
+ require 'linalg/exception'
10
+ require 'linalg/math'
11
+ require 'linalg/iterators'
12
+ require 'linalg/xdata'
13
+
14
+ require 'linalg/dmatrix/main'
15
+ require 'linalg/dmatrix/qr'
16
+ require 'linalg/dmatrix/cholesky'
17
+ require 'linalg/dmatrix/fit'
18
+ require 'linalg/dmatrix/schur'
19
+ require 'linalg/dmatrix/svd'
20
+ require 'linalg/dmatrix/lu'
21
+ require 'linalg/dmatrix/cond'
22
+ require 'linalg/dmatrix/nullspace'
23
+ require 'linalg/dmatrix/solve'
24
+ require 'linalg/dmatrix/eigen'
25
+ require 'linalg/dmatrix/inverse'
26
+ require 'linalg/dmatrix/norms'
27
+ require 'linalg/dmatrix/det'
28
+ require 'linalg/dmatrix/alias'
29
+
@@ -0,0 +1,32 @@
1
+ #
2
+ # Copyright (c) 2004-2008 by James M. Lawrence
3
+ #
4
+ # See LICENSE
5
+ #
6
+
7
+ module Linalg
8
+ class DMatrix
9
+ alias_method :mul!, :postmul!
10
+ alias_method :t, :transpose
11
+ alias_method :inv, :inverse
12
+ alias_method :det, :determinant
13
+ alias_method :col, :column
14
+ alias_method :svd, :singular_value_decomposition
15
+ alias_method :sv, :singular_values
16
+ alias_method :chol, :cholesky
17
+ alias_method :nrow, :vsize
18
+ alias_method :num_rows, :vsize
19
+ alias_method :ncol, :hsize
20
+ alias_method :num_columns, :hsize
21
+ alias_method :pinv, :pseudo_inverse
22
+ alias_method :orth, :rankspace
23
+ alias_method :norm, :norm_2
24
+ alias_method :column_norm, :norm_1
25
+ alias_method :row_norm, :norm_inf
26
+ alias_method :frobenius_norm, :norm_f
27
+
28
+ private :symmetric_private
29
+ end
30
+ end
31
+
32
+
@@ -0,0 +1,52 @@
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
+ # cholesky => u
13
+ # cholesky { |u| ... } => block result
14
+ #
15
+ # Given a symmetric positive definite matrix +m+, gives
16
+ # an upper-triangular matrix +u+ such that
17
+ # u.t * u
18
+ # is equal to +m+.
19
+ #
20
+ # Only the upper-triangular portion of +m+ is considered
21
+ # for the algorithm.
22
+ #
23
+ # Raises +Diverged+ if +m+ was not positive definite.
24
+ #
25
+ def cholesky # :yields: u
26
+ raise DimensionError unless square?
27
+ uplo = Char.new "U"
28
+ n = XInteger.new vsize
29
+ a = self.clone
30
+ lda = n
31
+ info = XInteger.new
32
+
33
+ Lapack.dpotrf(uplo,
34
+ n,
35
+ a,
36
+ lda,
37
+ info)
38
+
39
+ raise Diverged unless info.value == 0
40
+
41
+ a.zero_lower
42
+
43
+ if block_given?
44
+ yield a
45
+ else
46
+ a
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+
@@ -0,0 +1,80 @@
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
+ # The condition number. Ratio of the largest singular value to
12
+ # the smallest.
13
+ #
14
+ def cond
15
+ s = singular_values
16
+ begin
17
+ c = s[0]/s[s.vsize-1]
18
+ c.nan? ? 1.0/0.0 : c
19
+ rescue SingularMatrix
20
+ 1.0/0.0
21
+ end
22
+ end
23
+
24
+ #
25
+ # Estimate the condition number using the
26
+ # 1-norm. Seems to be within an order of magnitude.
27
+ #
28
+ def cond_est_1
29
+ begin
30
+ 1.0/rcond_private(:col)
31
+ rescue SingularMatrix
32
+ 1.0/0.0
33
+ end
34
+ end
35
+
36
+ #
37
+ # Estimate the condition number using the
38
+ # infinity-norm. Seems to be within an order of magnitude.
39
+ #
40
+ def cond_est_inf
41
+ begin
42
+ 1.0/rcond_private(:row)
43
+ rescue SingularMatrix
44
+ 1.0/0.0
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def rcond_private(which)
51
+ raise DimensionError unless square?
52
+
53
+ norm = (which == :col) ? Char.new("1") : Char.new("I")
54
+ n = XInteger.new vsize
55
+ a = self.clone
56
+ a.lu!
57
+ lda = n
58
+ anorm = DData.new(1) {
59
+ (which == :col) ? a.column_norm : a.row_norm
60
+ }
61
+ rcond = DReal.new
62
+ work = DData.new(4*n.value)
63
+ iwork = IData.new(n.value)
64
+ info = XInteger.new
65
+
66
+ Lapack.dgecon(norm,
67
+ n,
68
+ a,
69
+ lda,
70
+ anorm,
71
+ rcond,
72
+ work,
73
+ iwork,
74
+ info)
75
+ rcond[0]
76
+ end
77
+ end
78
+ end
79
+
80
+
@@ -0,0 +1,36 @@
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
+ # Returns the determinant of the matrix. Raises
13
+ # +DimensionError+ if the matrix is not square.
14
+ #
15
+ def determinant
16
+ raise DimensionError unless square?
17
+ begin
18
+ a = self.clone
19
+ ipiv = a.lu!
20
+ res = 1.0
21
+ sign = 1
22
+ vsize.times { |i|
23
+ res *= a[i,i]
24
+
25
+ # 1-based fortran
26
+ if ipiv[i] != i + 1
27
+ sign *= -1
28
+ end
29
+ }
30
+ res*sign
31
+ rescue SingularMatrix
32
+ 0.0
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,153 @@
1
+ #
2
+ # Copyright (c) 2004-2008 by James M. Lawrence
3
+ #
4
+ # See LICENSE
5
+ #
6
+
7
+
8
+
9
+ module Linalg
10
+ class DMatrix
11
+
12
+ #
13
+ # call-seq:
14
+ # eigensystem => [eigvs, real, imag]
15
+ # eigensystem { |eigvs, real, imag| ... } => block result
16
+ #
17
+ # Find the eigenvectors and eigenvalues of the matrix.
18
+ #
19
+ # The columns of +eigvs+ hold the eigenvectors; the column vectors
20
+ # +real+ and +imag+ hold the eigenvalues.
21
+ #
22
+ # Stepping through the eigenvalues in succession starting from the
23
+ # beginning, one will encounter either a real eigenvalue or a complex
24
+ # conjugate pair. That is, either the imaginary part will be exactly
25
+ # equal to zero
26
+ #
27
+ # imag[n] == 0.0
28
+ #
29
+ # or there will be a conjugate pair with the following exactly equal,
30
+ #
31
+ # real[n] == real[n+1]
32
+ # imag[n] == -imag[n+1]
33
+ #
34
+ # In the former case, the real eigenvector
35
+ #
36
+ # eigvs.column(n)
37
+ #
38
+ # corresponds to the real eigenvalue
39
+ #
40
+ # real[n]
41
+ #
42
+ # In the latter case, the complex eigenvector
43
+ #
44
+ # eigvs.column(n) + i*eigvs.column(n+1)
45
+ #
46
+ # corresponds to the complex eigenvalue
47
+ #
48
+ # real[n] + i*imag[n]
49
+ #
50
+ # and the conjugate eigenvector
51
+ #
52
+ # eigvs.column(n) - i*eigvs.column(n+1)
53
+ #
54
+ # corresponds to the conjugate eigenvalue
55
+ #
56
+ # real[n] - i*imag[n]
57
+ #
58
+ # for <tt>i = sqrt(-1)</tt>.
59
+ #
60
+ # The eigenvectors are each given unit length with largest component
61
+ # real.
62
+ #
63
+ # Raises +DimensionError+ if the given matrix is not square. May
64
+ # raise +Diverged+.
65
+ #
66
+ def eigensystem(&b)
67
+ eigen_private(true, &b)
68
+ end
69
+
70
+ #
71
+ # call-seq:
72
+ # eigenvalues => [real, imag]
73
+ # eigenvalues { |real, imag| ... } => block result
74
+ #
75
+ # Returns the eigenvalues. See DMatrix#eigensystem for the format in
76
+ # which eigenvalues are returned.
77
+ #
78
+ def eigenvalues(&b)
79
+ eigen_private(false, &b)
80
+ end
81
+
82
+ private
83
+
84
+ def eigen_private(vecs)
85
+ raise DimensionError unless square?
86
+
87
+ jobvl = Char.new("N")
88
+ jobvr = Char.new(vecs ? "V" : "N")
89
+ n = XInteger.new(vsize)
90
+ a = self.clone
91
+ lda = XInteger.new(n.value)
92
+ wr = DMatrix.reserve(n.value, 1)
93
+ wi = DMatrix.reserve(n.value, 1)
94
+ vl = XData::NULL
95
+ ldvl = XInteger.new(1)
96
+ vr = DMatrix.reserve(n.value, n.value)
97
+ ldvr = XInteger.new(n.value)
98
+ work = DReal.new # query
99
+ lwork = XInteger.new(-1) # query
100
+ info = XInteger.new
101
+
102
+ # query
103
+ Lapack.dgeev(jobvl,
104
+ jobvr,
105
+ n,
106
+ a,
107
+ lda,
108
+ wr,
109
+ wi,
110
+ vl,
111
+ ldvl,
112
+ vr,
113
+ ldvr,
114
+ work,
115
+ lwork,
116
+ info)
117
+
118
+ raise Diverged unless info.value == 0
119
+ lwork = XInteger.new(work.value.to_i)
120
+ work = DData.new(lwork.value)
121
+
122
+ Lapack.dgeev(jobvl,
123
+ jobvr,
124
+ n,
125
+ a,
126
+ lda,
127
+ wr,
128
+ wi,
129
+ vl,
130
+ ldvl,
131
+ vr,
132
+ ldvr,
133
+ work,
134
+ lwork,
135
+ info)
136
+
137
+ raise Diverged unless info.value == 0
138
+
139
+ if vecs
140
+ res = [vr, wr, wi]
141
+ else
142
+ res = [wr, wi]
143
+ end
144
+
145
+ if block_given?
146
+ yield res
147
+ else
148
+ res
149
+ end
150
+ end
151
+ end
152
+ end
153
+
@@ -0,0 +1,281 @@
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
+ # Pseudo-inverse.
12
+ #
13
+ # For matrix +m+, find +x+ which minimizes
14
+ # (m*x - DMatrix.identity(m.vsize)).norm
15
+ #
16
+ def pseudo_inverse(epsilon = nil)
17
+ DMatrix.fit(self, DMatrix.identity(vsize), epsilon)[0]
18
+ end
19
+
20
+ # call-seq:
21
+ # fit(a, b, epsilon = nil) => x
22
+ # fit(a, b, epsilon = nil) { |x| ... } => block value
23
+ #
24
+ # Least squares minimization using QR factorization.
25
+ #
26
+ # Find +x+ for which
27
+ # (a*x - b).norm_2
28
+ # is a minimum.
29
+ #
30
+ # Assumes a matrix of full rank.
31
+ #
32
+ def self.fit(a, b)
33
+ raise DimensionError unless a.vsize == b.vsize
34
+ b_in = b
35
+
36
+ trans = Char.new("N")
37
+ m = XInteger.new(a.vsize)
38
+ n = XInteger.new(a.hsize)
39
+ nrhs = XInteger.new(b.hsize)
40
+ a = a.clone
41
+ lda = XInteger.new(a.vsize)
42
+ b = DMatrix.new(Math.max(m.value, n.value),
43
+ nrhs.value).replace_minor(0, 0, b_in)
44
+ ldb = XInteger.new(b.vsize)
45
+ work = DReal.new # query
46
+ lwork = XInteger.new(-1) # query
47
+ info = XInteger.new
48
+
49
+ # query
50
+ Lapack.dgels(trans,
51
+ m,
52
+ n,
53
+ nrhs,
54
+ a,
55
+ lda,
56
+ b,
57
+ ldb,
58
+ work,
59
+ lwork,
60
+ info)
61
+
62
+ raise Diverged unless info.value == 0
63
+ lwork = XInteger.new(work.value.to_i)
64
+ work = DData.new(lwork.value)
65
+
66
+ Lapack.dgels(trans,
67
+ m,
68
+ n,
69
+ nrhs,
70
+ a,
71
+ lda,
72
+ b,
73
+ ldb,
74
+ work,
75
+ lwork,
76
+ info)
77
+
78
+ raise Diverged unless info.value == 0
79
+
80
+ x = b.minor(0, 0, n.value, nrhs.value)
81
+ if block_given?
82
+ yield x
83
+ else
84
+ x
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ #
91
+ # call-seq:
92
+ # fit(a, b) => [x, sv, rank]
93
+ # fit(a, b) { |x, sv, rank| ... } => block value
94
+ #
95
+ # Least squares minimization using singular value decomposition
96
+ # in conjunction with householder transformations.
97
+ # Rank deficiency is allowed.
98
+ #
99
+ # Find +x+ for which
100
+ # (a*x - b).norm
101
+ # is a minimum. (+norm+ is the 2-norm.)
102
+ #
103
+ # Also returns the singular values +sv+ and the +rank+.
104
+ #
105
+ # For matrix +m+, a singular value less than or equal to
106
+ # <tt>m.norm*epsilon</tt> is considered a rank deficiency. An
107
+ # +epsilon+ of +nil+ (default) specifies machine precision.
108
+ #
109
+ # Disabled. Crash from a tester.
110
+ #
111
+ def self.fit_svdhh(a, b, epsilon = nil) # :yields: x, sv, rank
112
+ raise DimensionError unless a.vsize == b.vsize
113
+ b_in = b
114
+
115
+ m = XInteger.new(a.vsize)
116
+ n = XInteger.new(a.hsize)
117
+ nrhs = XInteger.new(b.hsize)
118
+ a = a.clone
119
+ lda = XInteger.new(a.vsize)
120
+ b = nil
121
+ ldb = XInteger.new(Math.max(m.value, n.value))
122
+ b = DMatrix.new(ldb.value, nrhs.value)
123
+ b.replace_minor(0, 0, b_in)
124
+ s = DMatrix.new(Math.min(m.value, n.value), 1)
125
+ rcond = DReal.new(epsilon ? epsilon : -1.0)
126
+ rank = XInteger.new
127
+ work = DReal.new # query
128
+ lwork = XInteger.new(-1) # query
129
+ # iwork defined below
130
+ info = XInteger.new
131
+
132
+ ###################################
133
+ # ilaenv
134
+ ###################################
135
+ ary_name = "dgelsd".split("")
136
+
137
+ ispec = XInteger.new(9)
138
+ name = CharData.new(ary_name.size) { |i| ary_name[i] }
139
+ opts = Char.new("N")
140
+ nX = XInteger.new(0)
141
+ smlsiz = Lapack.ilaenv(ispec,
142
+ name,
143
+ opts,
144
+ nX,
145
+ nX,
146
+ nX,
147
+ nX)
148
+ raise "ilaenv: bad parameter #{-smlsiz}" if smlsiz < 0
149
+
150
+ ###################################
151
+ # iwork
152
+ ###################################
153
+ minmn = Math.min(m.value, n.value)
154
+ nlvl = Math.max(0, (Math.log2(minmn.to_f/(smlsiz + 1))).to_i + 1)
155
+ iwork = IData.new(3*minmn*nlvl + 11*minmn)
156
+
157
+ ###################################
158
+ # dgesld
159
+ ###################################
160
+
161
+ # query
162
+ Lapack.dgelsd(m,
163
+ n,
164
+ nrhs,
165
+ a,
166
+ lda,
167
+ b,
168
+ ldb,
169
+ s,
170
+ rcond,
171
+ rank,
172
+ work,
173
+ lwork,
174
+ iwork,
175
+ info)
176
+
177
+ raise Diverged unless info.value == 0
178
+ lwork = XInteger.new(work.value.to_i)
179
+ work = DData.new(lwork.value)
180
+
181
+ Lapack.dgelsd(m,
182
+ n,
183
+ nrhs,
184
+ a,
185
+ lda,
186
+ b,
187
+ ldb,
188
+ s,
189
+ rcond,
190
+ rank,
191
+ work,
192
+ lwork,
193
+ iwork,
194
+ info)
195
+
196
+ raise Diverged unless info.value == 0
197
+
198
+ res = [b.minor(0, 0, n.value, nrhs.value), s, rank.value]
199
+ if block_given?
200
+ yield res
201
+ else
202
+ res
203
+ end
204
+ end
205
+
206
+ #
207
+ # call-seq:
208
+ # fit_svd(a, b) => [x, sv, rank]
209
+ # fit_svd(a, b) { |x, sv, rank| ... } => block value
210
+ #
211
+ # Least squares minimization with posible rank deficiency.
212
+ # Uses singular value decomposition.
213
+ #
214
+ # Disabled. Crash for unknown reason.
215
+ #
216
+ def self.fit_svd2(a, b)
217
+ raise DimensionError unless a.vsize == b.vsize
218
+ b_in = b
219
+
220
+ m = XInteger.new a.vsize
221
+ n = XInteger.new a.hsize
222
+ nrhs = XInteger.new b.hsize
223
+ a = a.clone
224
+ lda = XInteger.new a.vsize
225
+ # b
226
+ ldb = XInteger.new Math.max(m.value, n.value)
227
+ b = DMatrix.new(ldb.value, nrhs.value)
228
+ b.replace_minor(0, 0, b_in)
229
+ s = DMatrix.new(Math.min(m.value, n.value), 1)
230
+ rcond = DReal.new(-1.0) # use machine precision
231
+ rank = XInteger.new
232
+ work = DReal.new # query
233
+ lwork = XInteger.new(-1) # query
234
+ info = XInteger.new
235
+
236
+ # query
237
+ Lapack.dgelss(m,
238
+ n,
239
+ nrhs,
240
+ a,
241
+ lda,
242
+ b,
243
+ ldb,
244
+ s,
245
+ rcond,
246
+ rank,
247
+ work,
248
+ lwork,
249
+ info)
250
+
251
+ raise Diverged unless info.value == 0
252
+ lwork = XInteger.new(work.value.to_i)
253
+ work = DData.new(lwork.value)
254
+
255
+ Lapack.dgelss(m,
256
+ n,
257
+ nrhs,
258
+ a,
259
+ lda,
260
+ b,
261
+ ldb,
262
+ s,
263
+ rcond,
264
+ rank,
265
+ work,
266
+ lwork,
267
+ info)
268
+
269
+ raise Diverged unless info.value == 0
270
+
271
+ res = [b.minor(0, 0, a.hsize, b.hsize), s, rank.value]
272
+ if block_given?
273
+ yield res
274
+ else
275
+ res
276
+ end
277
+ end
278
+ end
279
+ end
280
+
281
+