stick 1.3.2 → 1.3.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +3 -4
- data/README +1 -1
- data/lib/stick/constants/cgs.rb +112 -110
- data/lib/stick/constants/mks.rb +6 -4
- data/lib/stick/constants/number.rb +3 -2
- data/lib/stick/constants/typeless_cgs.rb +105 -106
- data/lib/stick/constants/typeless_mks.rb +106 -107
- data/lib/stick/currency.rb +2 -0
- data/lib/stick/mapcar.rb +36 -23
- data/lib/stick/matrix.rb +10 -395
- data/lib/stick/matrix/core.rb +1408 -0
- data/lib/stick/matrix/exception.rb +23 -0
- data/lib/stick/matrix/givens.rb +59 -0
- data/lib/stick/matrix/hessenberg.rb +63 -0
- data/lib/stick/matrix/householder.rb +106 -0
- data/lib/stick/matrix/jacobi.rb +106 -0
- data/lib/stick/matrix/lu.rb +60 -0
- data/lib/stick/quaternion.rb +10 -6
- data/lib/stick/units.rb +2 -0
- data/lib/stick/units/base.rb +75 -72
- data/lib/stick/units/currency.rb +8 -8
- data/lib/stick/units/loaders.rb +3 -2
- data/lib/stick/units/units.rb +2 -0
- data/lib/stick/vector.rb +20 -0
- data/meta/MANIFEST +23 -3
- data/meta/stick.roll +1 -1
- data/task/tests/solo +293 -0
- data/test/spec_matrix.rb +3 -0
- data/test/test_constants.rb +4 -0
- data/test/test_currency.rb +2 -2
- data/test/test_matrix.rb +7 -1
- data/test/test_units.rb +2 -2
- metadata +15 -2
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'e2mmap'
|
2
|
+
|
3
|
+
module Stick
|
4
|
+
|
5
|
+
class Matrix
|
6
|
+
|
7
|
+
module Exceptions # :nodoc:
|
8
|
+
extend Exception2MessageMapper
|
9
|
+
|
10
|
+
def_e2message(TypeError, "wrong argument type %s (expected %s)")
|
11
|
+
def_e2message(ArgumentError, "Wrong # of arguments(%d for %d)")
|
12
|
+
|
13
|
+
def_exception("ErrDimensionMismatch", "\#{self.name} dimension mismatch")
|
14
|
+
def_exception("ErrNotRegular", "Not Regular Matrix")
|
15
|
+
def_exception("ErrOperationNotDefined", "This operation(%s) can\\'t defined")
|
16
|
+
end
|
17
|
+
|
18
|
+
# extend Exception2MessageMapper
|
19
|
+
include Exceptions
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Stick
|
2
|
+
|
3
|
+
class Matrix
|
4
|
+
|
5
|
+
module Givens
|
6
|
+
|
7
|
+
# Returns the values "c and s" of a Given rotation
|
8
|
+
# MC, Golub, pg 216, Alghorithm 5.1.3
|
9
|
+
|
10
|
+
def Givens.givens(a, b)
|
11
|
+
if b == 0
|
12
|
+
c = 0; s = 0
|
13
|
+
else
|
14
|
+
if b.abs > a.abs
|
15
|
+
tau = Float(-a)/b; s = 1/Math.sqrt(1+tau**2); c = s * tau
|
16
|
+
else
|
17
|
+
tau = Float(-b)/a; c = 1/Math.sqrt(1+tau**2); s = c * tau
|
18
|
+
end
|
19
|
+
end
|
20
|
+
return c, s
|
21
|
+
end
|
22
|
+
|
23
|
+
# a QR factorization using Givens rotation
|
24
|
+
# Computes the upper triangular matrix R and the orthogonal matrix Q
|
25
|
+
# where Q^t A = R (MC, Golub, p227 algorithm 5.2.2)
|
26
|
+
|
27
|
+
def Givens.QR(mat)
|
28
|
+
r = mat.clone
|
29
|
+
m = r.row_size
|
30
|
+
n = r.column_size
|
31
|
+
q = Matrix.I(m)
|
32
|
+
n.times{|j|
|
33
|
+
m-1.downto(j+1){|i|
|
34
|
+
c, s = givens(r[i - 1, j], r[i, j])
|
35
|
+
qt = Matrix.I(m); qt[i-1..i, i-1..i] = Matrix[[c, s],[-s, c]]
|
36
|
+
q *= qt
|
37
|
+
r[i-1..i, j..n-1] = Matrix[[c, -s],[s, c]] * r[i-1..i, j..n-1]}}
|
38
|
+
return r, q
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the upper triunghiular matrix R of a Givens QR factorization
|
44
|
+
|
45
|
+
def givensR
|
46
|
+
Givens.QR(self)[0]
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns the orthogonal matrix Q of Givens QR factorization.
|
50
|
+
# Q = G_1 * ... * G_t where G_j is the j'th Givens rotation
|
51
|
+
# and 't' is the total number of rotations
|
52
|
+
|
53
|
+
def givensQ
|
54
|
+
Givens.QR(self)[1]
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'stick/matrix/householder'
|
2
|
+
|
3
|
+
module Stick
|
4
|
+
|
5
|
+
class Matrix
|
6
|
+
|
7
|
+
module Hessenberg
|
8
|
+
|
9
|
+
# The matrix must be an upper R^(n x n) Hessenberg matrix
|
10
|
+
|
11
|
+
def Hessenberg.QR(mat)
|
12
|
+
r = mat.clone
|
13
|
+
n = r.row_size
|
14
|
+
q = Matrix.I(n)
|
15
|
+
for j in (0...n-1)
|
16
|
+
c, s = Givens.givens(r[j,j], r[j+1, j])
|
17
|
+
cs = Matrix[[c, s], [-s, c]]
|
18
|
+
q *= Matrix.diag(Matrix.I(j), cs, Matrix.I(n - j - 2))
|
19
|
+
r[j..j+1, j..n-1] = cs.t * r[j..j+1, j..n-1]
|
20
|
+
end
|
21
|
+
return q, r
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the orthogonal matrix Q of Hessenberg QR factorization
|
26
|
+
# Q = G_1 *...* G_(n-1) where G_j is the Givens rotation G_j = G(j, j+1, omega_j)
|
27
|
+
|
28
|
+
def hessenbergQ
|
29
|
+
Hessenberg.QR(self)[0]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the upper triunghiular matrix R of a Hessenberg QR factorization
|
33
|
+
|
34
|
+
def hessenbergR
|
35
|
+
Hessenberg.QR(self)[1]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Return an upper Hessenberg matrix obtained with Householder reduction to Hessenberg Form algorithm
|
39
|
+
|
40
|
+
def hessenberg_form_H
|
41
|
+
Householder.toHessenberg(self)[0]
|
42
|
+
end
|
43
|
+
|
44
|
+
# The real Schur decomposition.
|
45
|
+
# The eigenvalues are aproximated in diagonal elements of the real Schur decomposition matrix
|
46
|
+
|
47
|
+
def realSchur(eps = 1.0e-10, steps = 100)
|
48
|
+
h = self.hessenberg_form_H
|
49
|
+
h1 = Matrix[]
|
50
|
+
i = 0
|
51
|
+
loop do
|
52
|
+
h1 = h.hessenbergR * h.hessenbergQ
|
53
|
+
break if Matrix.diag_in_delta?(h1, h, eps) or steps <= 0
|
54
|
+
h = h1.clone
|
55
|
+
steps -= 1
|
56
|
+
i += 1
|
57
|
+
end
|
58
|
+
h1
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module Stick
|
2
|
+
|
3
|
+
class Matrix
|
4
|
+
|
5
|
+
module Householder
|
6
|
+
|
7
|
+
# a QR factorization that uses Householder transformation
|
8
|
+
# Q^T * A = R
|
9
|
+
# MC, Golub & van Loan, pg 224, 5.2.1 Householder QR
|
10
|
+
|
11
|
+
def Householder.QR(mat)
|
12
|
+
h = []
|
13
|
+
a = mat.clone
|
14
|
+
m = a.row_size
|
15
|
+
n = a.column_size
|
16
|
+
n.times{|j|
|
17
|
+
v, beta = a[j..m - 1, j].house
|
18
|
+
|
19
|
+
h[j] = Matrix.diag(Matrix.I(j), Matrix.I(m-j)- beta * (v * v.t))
|
20
|
+
|
21
|
+
a[j..m-1, j..n-1] = (Matrix.I(m-j) - beta * (v * v.t)) * a[j..m-1, j..n-1]
|
22
|
+
a[(j+1)..m-1,j] = v[2..(m-j)] if j < m - 1 }
|
23
|
+
h
|
24
|
+
end
|
25
|
+
|
26
|
+
# From the essential part of Householder vector
|
27
|
+
# it returns the coresponding upper(U_j)/lower(V_j) matrix
|
28
|
+
|
29
|
+
def Householder.bidiagUV(essential, dim, beta)
|
30
|
+
v = Vector.concat(Vector[1], essential)
|
31
|
+
dimv = v.size
|
32
|
+
Matrix.diag(Matrix.I(dim - dimv), Matrix.I(dimv) - beta * (v * v.t) )
|
33
|
+
end
|
34
|
+
|
35
|
+
# Householder Bidiagonalization algorithm. MC, Golub, pg 252, Algorithm 5.4.2
|
36
|
+
# Returns the matrices U_B and V_B such that: U_B^T * A * V_B = B,
|
37
|
+
# where B is upper bidiagonal.
|
38
|
+
|
39
|
+
def Householder.bidiag(mat)
|
40
|
+
a = mat.clone
|
41
|
+
m = a.row_size
|
42
|
+
n = a.column_size
|
43
|
+
ub = Matrix.I(m)
|
44
|
+
vb = Matrix.I(n)
|
45
|
+
n.times{|j|
|
46
|
+
v, beta = a[j..m-1,j].house
|
47
|
+
a[j..m-1, j..n-1] = (Matrix.I(m-j) - beta * (v * v.t)) * a[j..m-1, j..n-1]
|
48
|
+
a[j+1..m-1, j] = v[1..(m-j-1)]
|
49
|
+
ub *= bidiagUV(a[j+1..m-1,j], m, beta) #Ub = U_1 * U_2 * ... * U_n
|
50
|
+
if j < n - 2
|
51
|
+
v, beta = (a[j, j+1..n-1]).house
|
52
|
+
a[j..m-1, j+1..n-1] = a[j..m-1, j+1..n-1] * (Matrix.I(n-j-1) - beta * (v * v.t))
|
53
|
+
a[j, j+2..n-1] = v[1..n-j-2]
|
54
|
+
vb *= bidiagUV(a[j, j+2..n-1], n, beta) #Vb = V_1 * U_2 * ... * V_n-2
|
55
|
+
end }
|
56
|
+
return ub, vb
|
57
|
+
end
|
58
|
+
|
59
|
+
# Householder Reduction to Hessenberg Form
|
60
|
+
|
61
|
+
def Householder.toHessenberg(mat)
|
62
|
+
h = mat.clone
|
63
|
+
n = h.row_size
|
64
|
+
u0 = Matrix.I(n)
|
65
|
+
for k in (0...n - 2)
|
66
|
+
v, beta = h[k+1..n-1, k].house #the householder matrice part
|
67
|
+
houseV = Matrix.I(n-k-1) - beta * (v * v.t)
|
68
|
+
u0 *= Matrix.diag(Matrix.I(k+1), houseV)
|
69
|
+
h[k+1..n-1, k..n-1] = houseV * h[k+1..n-1, k..n-1]
|
70
|
+
h[0..n-1, k+1..n-1] = h[0..n-1, k+1..n-1] * houseV
|
71
|
+
end
|
72
|
+
return h, u0
|
73
|
+
end
|
74
|
+
|
75
|
+
end #end of Householder module
|
76
|
+
|
77
|
+
# Returns the upper bidiagonal matrix obtained with Householder Bidiagonalization algorithm
|
78
|
+
|
79
|
+
def bidiagonal
|
80
|
+
ub, vb = Householder.bidiag(self)
|
81
|
+
ub.t * self * vb
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns the orthogonal matrix Q of Householder QR factorization
|
85
|
+
# where Q = H_1 * H_2 * H_3 * ... * H_n,
|
86
|
+
|
87
|
+
def houseQ
|
88
|
+
h = Householder.QR(self)
|
89
|
+
q = h[0]
|
90
|
+
(1...h.size).each{|i| q *= h[i]}
|
91
|
+
q
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns the matrix R of Householder QR factorization
|
95
|
+
# R = H_n * H_n-1 * ... * H_1 * A is an upper triangular matrix
|
96
|
+
|
97
|
+
def houseR
|
98
|
+
h = Householder.QR(self)
|
99
|
+
r = self.clone
|
100
|
+
h.size.times{|i| r = h[i] * r}
|
101
|
+
r
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module Stick
|
2
|
+
|
3
|
+
class Matrix
|
4
|
+
|
5
|
+
module Jacobi
|
6
|
+
|
7
|
+
# Returns the nurm of the off-diagonal element
|
8
|
+
|
9
|
+
def Jacobi.off(a)
|
10
|
+
n = a.row_size
|
11
|
+
sum = 0
|
12
|
+
n.times{|i| n.times{|j| sum += a[i, j]**2 if j != i}}
|
13
|
+
Math.sqrt(sum)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the index pair (p, q) with 1<= p < q <= n and A[p, q] is the maximum in absolute value
|
17
|
+
|
18
|
+
def Jacobi.max(a)
|
19
|
+
n = a.row_size
|
20
|
+
max = 0
|
21
|
+
p = 0
|
22
|
+
q = 0
|
23
|
+
n.times{|i|
|
24
|
+
((i+1)...n).each{|j|
|
25
|
+
val = a[i, j].abs
|
26
|
+
if val > max
|
27
|
+
max = val
|
28
|
+
p = i
|
29
|
+
q = j
|
30
|
+
end }}
|
31
|
+
return p, q
|
32
|
+
end
|
33
|
+
|
34
|
+
# Compute the cosine-sine pair (c, s) for the element A[p, q]
|
35
|
+
|
36
|
+
def Jacobi.sym_schur2(a, p, q)
|
37
|
+
if a[p, q] != 0
|
38
|
+
tau = Float(a[q, q] - a[p, p])/(2 * a[p, q])
|
39
|
+
if tau >= 0
|
40
|
+
t = 1./(tau + Math.sqrt(1 + tau ** 2))
|
41
|
+
else
|
42
|
+
t = -1./(-tau + Math.sqrt(1 + tau ** 2))
|
43
|
+
end
|
44
|
+
c = 1./Math.sqrt(1 + t ** 2)
|
45
|
+
s = t * c
|
46
|
+
else
|
47
|
+
c = 1
|
48
|
+
s = 0
|
49
|
+
end
|
50
|
+
return c, s
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns the Jacobi rotation matrix
|
54
|
+
|
55
|
+
def Jacobi.J(p, q, c, s, n)
|
56
|
+
j = Matrix.I(n)
|
57
|
+
j[p,p] = c; j[p, q] = s
|
58
|
+
j[q,p] = -s; j[q, q] = c
|
59
|
+
j
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
# Classical Jacobi 8.4.3 Golub & van Loan
|
65
|
+
|
66
|
+
def cJacobi(tol = 1.0e-10)
|
67
|
+
a = self.clone
|
68
|
+
n = row_size
|
69
|
+
v = Matrix.I(n)
|
70
|
+
eps = tol * a.normF
|
71
|
+
while Jacobi.off(a) > eps
|
72
|
+
p, q = Jacobi.max(a)
|
73
|
+
c, s = Jacobi.sym_schur2(a, p, q)
|
74
|
+
#print "\np:#{p} q:#{q} c:#{c} s:#{s}\n"
|
75
|
+
j = Jacobi.J(p, q, c, s, n)
|
76
|
+
a = j.t * a * j
|
77
|
+
v = v * j
|
78
|
+
end
|
79
|
+
return a, v
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns the aproximation matrix computed with Classical Jacobi algorithm.
|
83
|
+
# The aproximate eigenvalues values are in the diagonal of the matrix A.
|
84
|
+
|
85
|
+
def cJacobiA(tol = 1.0e-10)
|
86
|
+
cJacobi(tol)[0]
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns a Vector with the eigenvalues aproximated values.
|
90
|
+
# The eigenvalues are computed with the Classic Jacobi Algorithm.
|
91
|
+
|
92
|
+
def eigenvaluesJacobi
|
93
|
+
a = cJacobiA
|
94
|
+
Vector[*(0...row_size).collect{|i| a[i, i]}]
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns the orthogonal matrix obtained with the Jacobi eigenvalue algorithm.
|
98
|
+
# The columns of V are the eigenvector.
|
99
|
+
|
100
|
+
def cJacobiV(tol = 1.0e-10)
|
101
|
+
cJacobi(tol)[1]
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Stick
|
2
|
+
|
3
|
+
class Matrix
|
4
|
+
|
5
|
+
module LU
|
6
|
+
|
7
|
+
# Return the Gauss vector, MC, Golub, 3.2.1 Gauss Transformation, p94
|
8
|
+
|
9
|
+
def LU.gauss_vector(mat, k)
|
10
|
+
t = mat.column2matrix(k)
|
11
|
+
tk = t[k, 0]
|
12
|
+
(0..k).each{|i| t[i, 0] = 0}
|
13
|
+
return t if tk == 0
|
14
|
+
(k+1...mat.row_size).each{|i| t[i, 0] = t[i, 0].to_f / tk}
|
15
|
+
t
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return the Gauss transformation matrix: M_k = I - tau * e_k^T
|
19
|
+
|
20
|
+
def LU.gauss(mat, k)
|
21
|
+
i = Matrix.I(mat.column_size)
|
22
|
+
tau = gauss_vector(mat, k)
|
23
|
+
e = i.row2matrix(k)
|
24
|
+
i - tau * e
|
25
|
+
end
|
26
|
+
|
27
|
+
# LU factorization: A = LU
|
28
|
+
# where L is lower triangular and U is upper triangular
|
29
|
+
|
30
|
+
def LU.factorization(mat)
|
31
|
+
u = mat.clone
|
32
|
+
n = u.column_size
|
33
|
+
i = Matrix.I(n)
|
34
|
+
l = i.clone
|
35
|
+
(n-1).times {|k|
|
36
|
+
mk = gauss(u, k)
|
37
|
+
u = mk * u # M_{n-1} * ... * M_1 * A = U
|
38
|
+
l += i - mk # L = M_1^{-1} * ... * M_{n-1}^{-1} = I + sum_{k=1}^{n-1} tau * e
|
39
|
+
}
|
40
|
+
return l, u
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return the upper triangular matrix of LU factorization
|
45
|
+
# M_{n-1} * ... * M_1 * A = U
|
46
|
+
|
47
|
+
def U
|
48
|
+
LU.factorization(self)[1]
|
49
|
+
end
|
50
|
+
|
51
|
+
# Return the lower triangular matrix of LU factorization
|
52
|
+
# L = M_1^{-1} * ... * M_{n-1}^{-1} = I + sum_{k=1}^{n-1} tau * e
|
53
|
+
|
54
|
+
def L
|
55
|
+
LU.factorization(self)[0]
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
data/lib/stick/quaternion.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# Quaternion
|
4
4
|
#
|
5
5
|
# Copyright:
|
6
|
-
#
|
6
|
+
#
|
7
7
|
# Copyright (c) 2002 K. Kodama
|
8
8
|
#
|
9
9
|
# Authors:
|
@@ -23,9 +23,9 @@ require "complex"
|
|
23
23
|
# NOTE This Quaternion class is still very experimental.
|
24
24
|
#
|
25
25
|
# Quaternions are attributed to Sir William Rowan Hamilton
|
26
|
-
# who find it in 1843, and published a major analysis in 1844 called
|
26
|
+
# who find it in 1843, and published a major analysis in 1844 called
|
27
27
|
# "On a Species of Imaginary Quantities Connected with a Theory of Quaternions"
|
28
|
-
# in the Proceedings of the Royal Irish Academ. (2, pp 424-434).
|
28
|
+
# in the Proceedings of the Royal Irish Academ. (2, pp 424-434).
|
29
29
|
#
|
30
30
|
# Typical quaternion number q is of the form q = r + a i + b j + c k.
|
31
31
|
# Bases i j k behaves as follows:
|
@@ -42,7 +42,7 @@ require "complex"
|
|
42
42
|
#
|
43
43
|
# A Quaternion q = r + a i + b j + k c have 1st level polar form such that
|
44
44
|
#
|
45
|
-
# q = |q|(cos t1 + sin t1 u1) , where u1 is unit vector of u1 = a1 i + b1 j + c1 k.
|
45
|
+
# q = |q|(cos t1 + sin t1 u1) , where u1 is unit vector of u1 = a1 i + b1 j + c1 k.
|
46
46
|
#
|
47
47
|
# u1 have 2nd level
|
48
48
|
#
|
@@ -126,7 +126,7 @@ Quaternian::vector(v)
|
|
126
126
|
Quaternion::rotation(v,t)
|
127
127
|
# t-rotatin along the 3-D vector v
|
128
128
|
q.rotate(r)
|
129
|
-
rotate by r = q r^(-1)
|
129
|
+
rotate by r = q r^(-1)
|
130
130
|
q.rotate_angle
|
131
131
|
# = q.amplitude/2
|
132
132
|
|
@@ -189,7 +189,7 @@ q.sinh
|
|
189
189
|
q.cosh
|
190
190
|
q.tanh
|
191
191
|
|
192
|
-
* Trigonometric functions
|
192
|
+
* Trigonometric functions
|
193
193
|
q.sin
|
194
194
|
q.cos
|
195
195
|
q.tan
|
@@ -211,6 +211,7 @@ q.hash
|
|
211
211
|
q.inspect
|
212
212
|
=end
|
213
213
|
|
214
|
+
module Stick
|
214
215
|
|
215
216
|
def Quaternion(a=0, b=0,c=0, d=0)
|
216
217
|
if a.kind_of?(Quaternion);
|
@@ -224,6 +225,8 @@ def Quaternion(a=0, b=0,c=0, d=0)
|
|
224
225
|
end
|
225
226
|
end
|
226
227
|
|
228
|
+
module_function :Quaternion
|
229
|
+
|
227
230
|
class Quaternion < Numeric
|
228
231
|
attr :re
|
229
232
|
attr :im
|
@@ -549,3 +552,4 @@ class Quaternion < Numeric
|
|
549
552
|
end
|
550
553
|
|
551
554
|
end # Quaternion
|
555
|
+
end # Stick
|