stick 1.2.0
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.
- data/CHANGES +7 -0
- data/COPYING +344 -0
- data/README +110 -0
- data/lib/stick/constants.rb +3 -0
- data/lib/stick/constants/cgs.rb +151 -0
- data/lib/stick/constants/mks.rb +158 -0
- data/lib/stick/constants/number.rb +33 -0
- data/lib/stick/constants/typeless_cgs.rb +141 -0
- data/lib/stick/constants/typeless_mks.rb +142 -0
- data/lib/stick/currency.rb +8 -0
- data/lib/stick/mapcar.rb +61 -0
- data/lib/stick/matrix.rb +1022 -0
- data/lib/stick/quaternion.rb +562 -0
- data/lib/stick/times.rb +441 -0
- data/lib/stick/units.rb +112 -0
- data/lib/stick/units/base.rb +980 -0
- data/lib/stick/units/currency.rb +159 -0
- data/lib/stick/units/data/binary/base.rb +4 -0
- data/lib/stick/units/data/cex.rb +5 -0
- data/lib/stick/units/data/currency-default.rb +5 -0
- data/lib/stick/units/data/currency-standard.rb +2 -0
- data/lib/stick/units/data/currency/base.rb +89 -0
- data/lib/stick/units/data/iec.rb +5 -0
- data/lib/stick/units/data/iec_binary/base.rb +6 -0
- data/lib/stick/units/data/si.rb +7 -0
- data/lib/stick/units/data/si/base.rb +9 -0
- data/lib/stick/units/data/si/derived.rb +26 -0
- data/lib/stick/units/data/si/extra.rb +22 -0
- data/lib/stick/units/data/uk.rb +10 -0
- data/lib/stick/units/data/uk/base.rb +22 -0
- data/lib/stick/units/data/units-default.rb +11 -0
- data/lib/stick/units/data/units-standard.rb +5 -0
- data/lib/stick/units/data/us.rb +10 -0
- data/lib/stick/units/data/us/base.rb +23 -0
- data/lib/stick/units/data/xmethods.rb +5 -0
- data/lib/stick/units/data/xmethods/cached.rb +84 -0
- data/lib/stick/units/data/xmethods/mapping.rb +87 -0
- data/lib/stick/units/loaders.rb +98 -0
- data/lib/stick/units/units.rb +109 -0
- data/meta/MANIFEST +76 -0
- data/meta/ROLLRC +2 -0
- data/meta/icli.yaml +16 -0
- data/meta/project.yaml +18 -0
- data/task/clobber/package +10 -0
- data/task/publish +57 -0
- data/task/release +10 -0
- data/task/setup +1616 -0
- data/task/test +25 -0
- data/test/spec_matrix.rb +342 -0
- data/test/test_currency.rb +26 -0
- data/test/test_matrix.rb +359 -0
- data/test/test_units.rb +205 -0
- data/work/TODO +20 -0
- data/work/bytes.rb +231 -0
- data/work/multipliers.rb +195 -0
- metadata +138 -0
@@ -0,0 +1,142 @@
|
|
1
|
+
# Title:
|
2
|
+
#
|
3
|
+
# Unitless MKS Constants
|
4
|
+
#
|
5
|
+
# Copyright:
|
6
|
+
#
|
7
|
+
# Copyright (C) 2003 Daniel Carrera, Brian Gough
|
8
|
+
#
|
9
|
+
# License:
|
10
|
+
#
|
11
|
+
# GNU General Public License
|
12
|
+
#
|
13
|
+
# This program is free software; you can redistribute it and/or modify
|
14
|
+
# it under the terms of the GNU General Public License as published by
|
15
|
+
# the Free Software Foundation; either version 2 of the License, or (at
|
16
|
+
# your option) any later version.
|
17
|
+
#
|
18
|
+
# This program is distributed in the hope that it will be useful, but
|
19
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
20
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
21
|
+
# General Public License for more details.
|
22
|
+
#
|
23
|
+
# You should have received a copy of the GNU General Public License
|
24
|
+
# along with this program; if not, write to the Free Software
|
25
|
+
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
26
|
+
#
|
27
|
+
# Authors:
|
28
|
+
#
|
29
|
+
# - Daniel Carrera
|
30
|
+
# - Brian Gough
|
31
|
+
# - Thomas Sawyer
|
32
|
+
|
33
|
+
#
|
34
|
+
module Stick
|
35
|
+
module Constants
|
36
|
+
module Typeless
|
37
|
+
# Unitless constants in the MKS system (meters, kg, sec)
|
38
|
+
|
39
|
+
module MKS
|
40
|
+
SPEED_OF_LIGHT = 2.99792458e8 # m / s
|
41
|
+
GRAVITATIONAL_CONSTANT = 6.673e-11 # m^3 / kg s^2
|
42
|
+
PLANCKS_CONSTANT_H = 6.62606876e-34 # kg m^2 / s
|
43
|
+
PLANCKS_CONSTANT_HBAR = 1.05457159642e-34 # kg m^2 / s
|
44
|
+
VACUUM_PERMEABILITY = 1.25663706144e-6 # kg m / A^2 s^2
|
45
|
+
ASTRONOMICAL_UNIT = 1.49597870691e11 # m
|
46
|
+
LIGHT_YEAR = 9.46053620707e15 # m
|
47
|
+
PARSEC = 3.08567758135e16 # m
|
48
|
+
GRAV_ACCEL = 9.80665e0 # m / s^2
|
49
|
+
ELECTRON_VOLT = 1.602176462e-19 # kg m^2 / s^2
|
50
|
+
MASS_ELECTRON = 9.10938188e-31 # kg
|
51
|
+
MASS_MUON = 1.88353109e-28 # kg
|
52
|
+
MASS_PROTON = 1.67262158e-27 # kg
|
53
|
+
MASS_NEUTRON = 1.67492716e-27 # kg
|
54
|
+
RYDBERG = 2.17987190389e-18 # kg m^2 / s^2
|
55
|
+
BOLTZMANN = 1.3806503e-23 # kg m^2 / K s^2
|
56
|
+
BOHR_MAGNETON = 9.27400899e-24 # A m^2
|
57
|
+
NUCLEAR_MAGNETON = 5.05078317e-27 # A m^2
|
58
|
+
ELECTRON_MAGNETIC_MOMENT = 9.28476362e-24 # A m^2
|
59
|
+
PROTON_MAGNETIC_MOMENT = 1.410606633e-26 # A m^2
|
60
|
+
MOLAR_GAS = 8.314472e0 # kg m^2 / K mol s^2
|
61
|
+
STANDARD_GAS_VOLUME = 2.2710981e-2 # m^3 / mol
|
62
|
+
MINUTE = 6e1 # s
|
63
|
+
HOUR = 3.6e3 # s
|
64
|
+
DAY = 8.64e4 # s
|
65
|
+
WEEK = 6.048e5 # s
|
66
|
+
INCH = 2.54e-2 # m
|
67
|
+
FOOT = 3.048e-1 # m
|
68
|
+
YARD = 9.144e-1 # m
|
69
|
+
MILE = 1.609344e3 # m
|
70
|
+
NAUTICAL_MILE = 1.852e3 # m
|
71
|
+
FATHOM = 1.8288e0 # m
|
72
|
+
MIL = 2.54e-5 # m
|
73
|
+
POINT = 3.52777777778e-4 # m
|
74
|
+
TEXPOINT = 3.51459803515e-4 # m
|
75
|
+
MICRON = 1e-6 # m
|
76
|
+
ANGSTROM = 1e-10 # m
|
77
|
+
HECTARE = 1e4 # m^2
|
78
|
+
ACRE = 4.04685642241e3 # m^2
|
79
|
+
BARN = 1e-28 # m^2
|
80
|
+
LITER = 1e-3 # m^3
|
81
|
+
US_GALLON = 3.78541178402e-3 # m^3
|
82
|
+
QUART = 9.46352946004e-4 # m^3
|
83
|
+
PINT = 4.73176473002e-4 # m^3
|
84
|
+
CUP = 2.36588236501e-4 # m^3
|
85
|
+
FLUID_OUNCE = 2.95735295626e-5 # m^3
|
86
|
+
TABLESPOON = 1.47867647813e-5 # m^3
|
87
|
+
TEASPOON = 4.92892159375e-6 # m^3
|
88
|
+
CANADIAN_GALLON = 4.54609e-3 # m^3
|
89
|
+
UK_GALLON = 4.546092e-3 # m^3
|
90
|
+
MILES_PER_HOUR = 4.4704e-1 # m / s
|
91
|
+
KILOMETERS_PER_HOUR = 2.77777777778e-1 # m / s
|
92
|
+
KNOT = 5.14444444444e-1 # m / s
|
93
|
+
POUND_MASS = 4.5359237e-1 # kg
|
94
|
+
OUNCE_MASS = 2.8349523125e-2 # kg
|
95
|
+
TON = 9.0718474e2 # kg
|
96
|
+
METRIC_TON = 1e3 # kg
|
97
|
+
UK_TON = 1.0160469088e3 # kg
|
98
|
+
TROY_OUNCE = 3.1103475e-2 # kg
|
99
|
+
CARAT = 2e-4 # kg
|
100
|
+
UNIFIED_ATOMIC_MASS = 1.66053873e-27 # kg
|
101
|
+
ATOMIC_MASS = 1.66053873e-27 # kg
|
102
|
+
GRAM_FORCE = 9.80665e-3 # kg m / s^2
|
103
|
+
POUND_FORCE = 4.44822161526e0 # kg m / s^2
|
104
|
+
KILOPOUND_FORCE = 4.44822161526e3 # kg m / s^2
|
105
|
+
POUNDAL = 1.38255e-1 # kg m / s^2
|
106
|
+
CALORIE = 4.1868e0 # kg m^2 / s^2
|
107
|
+
BTU = 1.05505585262e3 # kg m^2 / s^2
|
108
|
+
THERM = 1.05506e8 # kg m^2 / s^2
|
109
|
+
HORSEPOWER = 7.457e2 # kg m^2 / s^3
|
110
|
+
BAR = 1e5 # kg / m s^2
|
111
|
+
STD_ATMOSPHERE = 1.01325e5 # kg / m s^2
|
112
|
+
TORR = 1.33322368421e2 # kg / m s^2
|
113
|
+
METER_OF_MERCURY = 1.33322368421e5 # kg / m s^2
|
114
|
+
INCH_OF_MERCURY = 3.38638815789e3 # kg / m s^2
|
115
|
+
INCH_OF_WATER = 2.490889e2 # kg / m s^2
|
116
|
+
PSI = 6.89475729317e3 # kg / m s^2
|
117
|
+
POISE = 1e-1 # kg m^-1 s^-1
|
118
|
+
STOKES = 1e-4 # m^2 / s
|
119
|
+
FARADAY = 9.6485341472e4 # A s / mol
|
120
|
+
ELECTRON_CHARGE = 1.602176462e-19 # A s
|
121
|
+
GAUSS = 1e-4 # kg / A s^2
|
122
|
+
STILB = 1e4 # cd / m^2
|
123
|
+
LUMEN = 1e0 # cd sr
|
124
|
+
LUX = 1e0 # cd sr / m^2
|
125
|
+
PHOT = 1e4 # cd sr / m^2
|
126
|
+
FOOTCANDLE = 1.076e1 # cd sr / m^2
|
127
|
+
LAMBERT = 1e4 # cd sr / m^2
|
128
|
+
FOOTLAMBERT = 1.07639104e1 # cd sr / m^2
|
129
|
+
CURIE = 3.7e10 # 1 / s
|
130
|
+
ROENTGEN = 2.58e-4 # A s / kg
|
131
|
+
RAD = 1e-2 # m^2 / s^2
|
132
|
+
SOLAR_MASS = 1.98892e30 # kg
|
133
|
+
BOHR_RADIUS = 5.291772083e-11 # m
|
134
|
+
VACUUM_PERMITTIVITY = 8.854187817e-12 # A^2 s^4 / kg m^3
|
135
|
+
NEWTON = 1e0 # kg m / s^2
|
136
|
+
DYNE = 1e-5 # kg m / s^2
|
137
|
+
JOULE = 1e0 # kg m^2 / s^2
|
138
|
+
ERG = 1e-7 # kg m^2 / s^2
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
data/lib/stick/mapcar.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# = TITLE:
|
2
|
+
#
|
3
|
+
# Mapcar
|
4
|
+
#
|
5
|
+
# AUTHOR:
|
6
|
+
#
|
7
|
+
# Cosmin Bonchis
|
8
|
+
#
|
9
|
+
# NOTES:
|
10
|
+
#
|
11
|
+
# Google Summer of Code 2007 project for Ruby Central Inc.
|
12
|
+
|
13
|
+
require 'generator'
|
14
|
+
#
|
15
|
+
# Non-recursive mapcar (works on all Enumerables)
|
16
|
+
#
|
17
|
+
def mapcar(*enums)
|
18
|
+
generators = enums.collect { |e| Generator.new(e) }
|
19
|
+
result = []
|
20
|
+
while true
|
21
|
+
begin
|
22
|
+
params = generators.collect { |g| g.current; g.next }
|
23
|
+
rescue EOFError
|
24
|
+
return result
|
25
|
+
end
|
26
|
+
result << yield(*params)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def map(*enums)
|
31
|
+
generators = []; enums.each { |e| generators << Generator.new(e) }
|
32
|
+
while true
|
33
|
+
begin
|
34
|
+
params = []; generators.each { |g| g.current; params << g.next }
|
35
|
+
rescue EOFError
|
36
|
+
return
|
37
|
+
end
|
38
|
+
yield(*params)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Array
|
43
|
+
def Array.map(n, *arrays)
|
44
|
+
len = arrays.length
|
45
|
+
if n == 0
|
46
|
+
n = arrays[0].length
|
47
|
+
1.upto(arrays.length - 1) do |i|
|
48
|
+
al = arrays[i].length
|
49
|
+
n = al if al < n
|
50
|
+
end
|
51
|
+
end
|
52
|
+
0.upto(n - 1) do |i|
|
53
|
+
params = []
|
54
|
+
0.upto(len - 1) do |arr|
|
55
|
+
params << arrays[arr][i]
|
56
|
+
end
|
57
|
+
yield(*params)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
data/lib/stick/matrix.rb
ADDED
@@ -0,0 +1,1022 @@
|
|
1
|
+
# = TITLE:
|
2
|
+
#
|
3
|
+
# Matrix Extensions
|
4
|
+
#
|
5
|
+
# AUTHOR:
|
6
|
+
#
|
7
|
+
# Cosmin Bonchis
|
8
|
+
#
|
9
|
+
# NOTES:
|
10
|
+
#
|
11
|
+
# Google Summer of Code 2007 project for Ruby Central Inc.
|
12
|
+
|
13
|
+
require 'rational'
|
14
|
+
require 'matrix'
|
15
|
+
|
16
|
+
require 'stick/mapcar'
|
17
|
+
|
18
|
+
|
19
|
+
class Vector
|
20
|
+
include Enumerable
|
21
|
+
|
22
|
+
module Norm
|
23
|
+
def Norm.sqnorm(obj, p)
|
24
|
+
sum = 0
|
25
|
+
obj.each{|x| sum += x ** p}
|
26
|
+
sum
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
alias :length :size
|
31
|
+
alias :index :[]
|
32
|
+
#
|
33
|
+
# Returns the value of an index vector or
|
34
|
+
# a Vector with the values of a range
|
35
|
+
# v = Vector[1, 2, 3, 4]
|
36
|
+
# v[0] => 1
|
37
|
+
# v[0..2] => Vector[1, 2, 3]
|
38
|
+
#
|
39
|
+
def [](i)
|
40
|
+
case i
|
41
|
+
when Range
|
42
|
+
Vector[*to_a.slice(i)]
|
43
|
+
else
|
44
|
+
index(i)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Sets a vector value/(range of values) with a new value/(values from a vector)
|
50
|
+
# v = Vector[1, 2, 3]
|
51
|
+
# v[2] = 9 => Vector[1, 2, 9]
|
52
|
+
# v[1..2] = Vector[9, 9, 9, 9, 9] => v: Vector[1, 9, 9]
|
53
|
+
#
|
54
|
+
def []=(i, v)
|
55
|
+
case i
|
56
|
+
when Range
|
57
|
+
(self.size..i.begin - 1).each{|e| self[e] = 0} # self.size must be in the first place because the size of self can be modified
|
58
|
+
[v.size, i.entries.size].min.times {|e| self[e + i.begin] = v[e]}
|
59
|
+
(v.size + i.begin .. i.end).each {|e| self[e] = 0}
|
60
|
+
else
|
61
|
+
@elements[i]=v
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class << self
|
66
|
+
#
|
67
|
+
# Returns a concatenated Vector
|
68
|
+
#
|
69
|
+
def concat(*args)
|
70
|
+
v = []
|
71
|
+
args.each{|x| v += x.to_a}
|
72
|
+
Vector[*v]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Changes the elements of vector and returns a Vector
|
78
|
+
#
|
79
|
+
def collect!
|
80
|
+
els = @elements.collect! {|v| yield(v)}
|
81
|
+
Vector.elements(els, false)
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# Iterates the elements of a vector
|
86
|
+
#
|
87
|
+
def each
|
88
|
+
(0...size).each {|i| yield(self[i])}
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# Returns the maximum element of a vector
|
94
|
+
#
|
95
|
+
def max
|
96
|
+
to_a.max
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Returns the minimum element of a vector
|
101
|
+
#
|
102
|
+
def min
|
103
|
+
to_a.min
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# Returns the p-norm of a vector
|
108
|
+
#
|
109
|
+
def norm(p = 2)
|
110
|
+
Norm.sqnorm(self, p) ** (Float(1)/p)
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# Returns the infinite-norm
|
115
|
+
#
|
116
|
+
def norm_inf
|
117
|
+
[min.abs, max.abs].max
|
118
|
+
end
|
119
|
+
|
120
|
+
#
|
121
|
+
# Returns a slice of vector
|
122
|
+
#
|
123
|
+
def slice(*args)
|
124
|
+
Vector[*to_a.slice(*args)]
|
125
|
+
end
|
126
|
+
|
127
|
+
def slice_set(v, b, e)
|
128
|
+
for i in b..e
|
129
|
+
self[i] = v[i-b]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
#
|
134
|
+
# Sets a slice of vector
|
135
|
+
#
|
136
|
+
def slice=(args)
|
137
|
+
case args[1]
|
138
|
+
when Range
|
139
|
+
slice_set(args[0], args[1].begin, args[1].last)
|
140
|
+
else
|
141
|
+
slice_set(args[0], args[1], args[2])
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
#
|
146
|
+
# Return the vector divided by a scalar
|
147
|
+
#
|
148
|
+
def /(c)
|
149
|
+
map {|e| e.quo(c)}
|
150
|
+
end
|
151
|
+
|
152
|
+
#
|
153
|
+
# Return the matrix column coresponding to the vector transpose
|
154
|
+
#
|
155
|
+
def transpose
|
156
|
+
Matrix[self.to_a]
|
157
|
+
end
|
158
|
+
|
159
|
+
alias :t :transpose
|
160
|
+
|
161
|
+
#
|
162
|
+
# Computes the Householder vector (MC, Golub, p. 210, algorithm 5.1.1)
|
163
|
+
#
|
164
|
+
def house
|
165
|
+
s = self[1..length-1]
|
166
|
+
sigma = s.inner_product(s)
|
167
|
+
v = clone; v[0] = 1
|
168
|
+
if sigma == 0
|
169
|
+
beta = 0
|
170
|
+
else
|
171
|
+
mu = Math.sqrt(self[0] ** 2 + sigma)
|
172
|
+
if self[0] <= 0
|
173
|
+
v[0] = self[0] - mu
|
174
|
+
else
|
175
|
+
v[0] = - sigma.quo(self[0] + mu)
|
176
|
+
end
|
177
|
+
v2 = v[0] ** 2
|
178
|
+
beta = 2 * v2.quo(sigma + v2)
|
179
|
+
v /= v[0]
|
180
|
+
end
|
181
|
+
return v, beta
|
182
|
+
end
|
183
|
+
|
184
|
+
#
|
185
|
+
#Projection operator
|
186
|
+
#(http://en.wikipedia.org/wiki/Gram-Schmidt_process#The_Gram.E2.80.93Schmidt_process)
|
187
|
+
#
|
188
|
+
def proj(v)
|
189
|
+
vp = v.inner_product(self)
|
190
|
+
vp = Float vp if vp.is_a?(Integer)
|
191
|
+
self * (vp / inner_product(self))
|
192
|
+
end
|
193
|
+
|
194
|
+
#
|
195
|
+
# Return the vector normalized
|
196
|
+
#
|
197
|
+
def normalize
|
198
|
+
self / self.norm
|
199
|
+
end
|
200
|
+
|
201
|
+
#
|
202
|
+
# Stabilized Gram-Schmidt process
|
203
|
+
# (http://en.wikipedia.org/wiki/Gram-Schmidt_process#Algorithm)
|
204
|
+
#
|
205
|
+
def Vector.gram_schmidt(*vectors)
|
206
|
+
v = vectors.clone
|
207
|
+
for j in 0...v.size
|
208
|
+
for i in 0..j-1
|
209
|
+
v[j] -= v[i] * v[j].inner_product(v[i])
|
210
|
+
end
|
211
|
+
v[j] /= v[j].norm
|
212
|
+
end
|
213
|
+
v
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
class Matrix
|
218
|
+
include Enumerable
|
219
|
+
public_class_method :new
|
220
|
+
|
221
|
+
attr_reader :rows, :wrap
|
222
|
+
@wrap = nil
|
223
|
+
|
224
|
+
def initialize(*argv)
|
225
|
+
return initialize_old(*argv) if argv[0].is_a?(Symbol)
|
226
|
+
n, m, val = argv; val = 0 if not val
|
227
|
+
f = (block_given?)? lambda {|i,j| yield(i, j)} : lambda {|i,j| val}
|
228
|
+
init_rows((0...n).collect {|i| (0...m).collect {|j| f.call(i,j)}}, true)
|
229
|
+
end
|
230
|
+
|
231
|
+
#
|
232
|
+
# For invoking a method
|
233
|
+
# in Ruby1.8 is working 'send' and
|
234
|
+
# in Ruby1.9 is working 'funcall'
|
235
|
+
#
|
236
|
+
def initialize_old(init_method, *argv)
|
237
|
+
if RUBY_VERSION < "1.9.0"
|
238
|
+
self.send(init_method, *argv) # in Ruby1.8
|
239
|
+
else
|
240
|
+
self.funcall(init_method, *argv) # in Ruby1.9
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
alias :ids :[]
|
245
|
+
#
|
246
|
+
# Return a value or a vector/matrix of values depending
|
247
|
+
# if the indexes are ranges or not
|
248
|
+
# m = Matrix.new(4, 3){|i, j| i * 3 + j}
|
249
|
+
# m: 0 1 2
|
250
|
+
# 3 4 5
|
251
|
+
# 6 7 8
|
252
|
+
# 9 10 11
|
253
|
+
# m[1, 2] => 5
|
254
|
+
# m[3,1..2] => Vector[10, 11]
|
255
|
+
# m[0..1, 0..2] => Matrix[[0, 1, 2], [3, 4, 5]]
|
256
|
+
#
|
257
|
+
def [](i, j)
|
258
|
+
case i
|
259
|
+
when Range
|
260
|
+
case j
|
261
|
+
when Range
|
262
|
+
Matrix[*i.collect{|l| self.row(l)[j].to_a}]
|
263
|
+
else
|
264
|
+
column(j)[i]
|
265
|
+
end
|
266
|
+
else
|
267
|
+
case j
|
268
|
+
when Range
|
269
|
+
row(i)[j]
|
270
|
+
else
|
271
|
+
ids(i, j)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
#
|
277
|
+
# Set the values of a matrix
|
278
|
+
# m = Matrix.new(3, 3){|i, j| i * 3 + j}
|
279
|
+
# m: 0 1 2
|
280
|
+
# 3 4 5
|
281
|
+
# 6 7 8
|
282
|
+
# m[1, 2] = 9 => Matrix[[0, 1, 2], [3, 4, 9], [6, 7, 8]]
|
283
|
+
# m[2,1..2] = Vector[8, 8] => Matrix[[0, 1, 2], [3, 8, 8], [6, 7, 8]]
|
284
|
+
# m[0..1, 0..1] = Matrix[[0, 0, 0],[0, 0, 0]]
|
285
|
+
# => Matrix[[0, 0, 2], [0, 0, 8], [6, 7, 8]]
|
286
|
+
#
|
287
|
+
def []=(i, j, v)
|
288
|
+
case i
|
289
|
+
when Range
|
290
|
+
if i.entries.size == 1
|
291
|
+
self[i.begin, j] = (v.is_a?(Matrix) ? v.row(0) : v)
|
292
|
+
else
|
293
|
+
case j
|
294
|
+
when Range
|
295
|
+
if j.entries.size == 1
|
296
|
+
self[i, j.begin] = (v.is_a?(Matrix) ? v.column(0) : v)
|
297
|
+
else
|
298
|
+
i.each{|l| self.row= l, v.row(l - i.begin), j}
|
299
|
+
end
|
300
|
+
else
|
301
|
+
self.column= j, v, i
|
302
|
+
end
|
303
|
+
end
|
304
|
+
else
|
305
|
+
case j
|
306
|
+
when Range
|
307
|
+
if j.entries.size == 1
|
308
|
+
self[i, j.begin] = (v.is_a?(Vector) ? v[0] : v)
|
309
|
+
else
|
310
|
+
self.row= i, v, j
|
311
|
+
end
|
312
|
+
else
|
313
|
+
@rows[i][j] = (v.is_a?(Vector) ? v[0] : v)
|
314
|
+
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
#
|
320
|
+
# Return a clone matrix
|
321
|
+
#
|
322
|
+
def clone
|
323
|
+
super
|
324
|
+
end
|
325
|
+
|
326
|
+
def initialize_copy(orig)
|
327
|
+
init_rows(orig.rows, true)
|
328
|
+
self.wrap=(orig.wrap)
|
329
|
+
end
|
330
|
+
|
331
|
+
|
332
|
+
class << self
|
333
|
+
#
|
334
|
+
# Creates a matrix with the given matrices as diagonal blocks
|
335
|
+
#
|
336
|
+
def diag(*args)
|
337
|
+
dsize = 0
|
338
|
+
sizes = args.collect{|e| x = (e.is_a?(Matrix)) ? e.row_size : 1; dsize += x; x}
|
339
|
+
m = Matrix.zero(dsize)
|
340
|
+
count = 0
|
341
|
+
|
342
|
+
sizes.size.times{|i|
|
343
|
+
range = count..(count+sizes[i]-1)
|
344
|
+
m[range, range] = args[i]
|
345
|
+
count += sizes[i]
|
346
|
+
}
|
347
|
+
m
|
348
|
+
end
|
349
|
+
|
350
|
+
#
|
351
|
+
# Tests if all the elements of two matrix are equal in delta
|
352
|
+
#
|
353
|
+
def equal_in_delta?(m0, m1, delta = 1.0e-10)
|
354
|
+
delta = delta.abs
|
355
|
+
mapcar(m0, m1){|x, y| return false if (x < y - delta or x > y + delta) }
|
356
|
+
true
|
357
|
+
end
|
358
|
+
|
359
|
+
#
|
360
|
+
# Tests if all the diagonal elements of two matrix are equal in delta
|
361
|
+
#
|
362
|
+
def diag_in_delta?(m1, m0, eps = 1.0e-10)
|
363
|
+
n = m1.row_size
|
364
|
+
return false if n != m0.row_size or m1.column_size != m0.column_size
|
365
|
+
n.times{|i|
|
366
|
+
return false if (m1[i,i]-m0[i,i]).abs > eps
|
367
|
+
}
|
368
|
+
true
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
#
|
373
|
+
# Returns the matrix divided by a scalar
|
374
|
+
#
|
375
|
+
def quo(v)
|
376
|
+
map {|e| e.quo(v)}
|
377
|
+
end
|
378
|
+
|
379
|
+
#
|
380
|
+
# quo seems always desirable
|
381
|
+
#
|
382
|
+
alias :/ :quo
|
383
|
+
|
384
|
+
#
|
385
|
+
# Set de values of a matrix and the value of wrap property
|
386
|
+
#
|
387
|
+
def set(m)
|
388
|
+
0.upto(m.row_size - 1) do |i|
|
389
|
+
0.upto(m.column_size - 1) do |j|
|
390
|
+
self[i, j] = m[i, j]
|
391
|
+
end
|
392
|
+
end
|
393
|
+
self.wrap = m.wrap
|
394
|
+
end
|
395
|
+
|
396
|
+
def wraplate(ijwrap = "")
|
397
|
+
"class << self
|
398
|
+
def [](i, j)
|
399
|
+
#{ijwrap}; @rows[i][j]
|
400
|
+
end
|
401
|
+
|
402
|
+
def []=(i, j, v)
|
403
|
+
#{ijwrap}; @rows[i][j] = v
|
404
|
+
end
|
405
|
+
end"
|
406
|
+
end
|
407
|
+
|
408
|
+
#
|
409
|
+
# Set wrap feature of a matrix
|
410
|
+
#
|
411
|
+
def wrap=(mode = :torus)
|
412
|
+
case mode
|
413
|
+
when :torus then eval(wraplate("i %= row_size; j %= column_size"))
|
414
|
+
when :h_cylinder then eval(wraplate("i %= row_size"))
|
415
|
+
when :v_cylinder then eval(wraplate("j %= column_size"))
|
416
|
+
when :nil then eval(wraplate)
|
417
|
+
end
|
418
|
+
@wrap = mode
|
419
|
+
end
|
420
|
+
|
421
|
+
#
|
422
|
+
# Returns the maximum length of column elements
|
423
|
+
#
|
424
|
+
def max_len_column(j)
|
425
|
+
column_collect(j) {|x| x.to_s.length}.max
|
426
|
+
end
|
427
|
+
|
428
|
+
#
|
429
|
+
# Returns a list with the maximum lengths
|
430
|
+
#
|
431
|
+
def cols_len
|
432
|
+
(0...column_size).collect {|j| max_len_column(j)}
|
433
|
+
end
|
434
|
+
|
435
|
+
#
|
436
|
+
# Returns a string for nice printing matrix
|
437
|
+
#
|
438
|
+
def to_s(mode = :pretty, len_col = 3)
|
439
|
+
return super if empty?
|
440
|
+
if mode == :pretty
|
441
|
+
clen = cols_len
|
442
|
+
to_a.collect {|r| mapcar(r, clen) {|x, l| format("%#{l}s ",x.to_s)} << "\n"}.join("")
|
443
|
+
else
|
444
|
+
i = 0; s = ""; cs = column_size
|
445
|
+
each do |e|
|
446
|
+
i = (i + 1) % cs
|
447
|
+
s += format("%#{len_col}s ", e.to_s)
|
448
|
+
s += "\n" if i == 0
|
449
|
+
end
|
450
|
+
s
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
#
|
455
|
+
# Iterate the elements of a matrix
|
456
|
+
#
|
457
|
+
def each
|
458
|
+
@rows.each {|x| x.each {|e| yield(e)}}
|
459
|
+
nil
|
460
|
+
end
|
461
|
+
|
462
|
+
#
|
463
|
+
# a hided module of Matrix
|
464
|
+
module MMatrix
|
465
|
+
def MMatrix.default_block(block)
|
466
|
+
block ? lambda { |i| block.call(i) } : lambda {|i| i }
|
467
|
+
end
|
468
|
+
|
469
|
+
#
|
470
|
+
# Returns:
|
471
|
+
# 1) the index of row/column and
|
472
|
+
# 2) the values Vector for changing the row/column and
|
473
|
+
# 3) the range of changes
|
474
|
+
#
|
475
|
+
def MMatrix.id_vect_range(args, l)
|
476
|
+
i = args[0] # the column(/the row) to be change
|
477
|
+
vect = args[1] # the values vector
|
478
|
+
|
479
|
+
case args.size
|
480
|
+
when 3 then range = args[2] # the range of the elements to be change
|
481
|
+
when 4 then range = args[2]..args[3] #the range by borders
|
482
|
+
else range = 0...l
|
483
|
+
end
|
484
|
+
return i, vect, range
|
485
|
+
end
|
486
|
+
|
487
|
+
end
|
488
|
+
|
489
|
+
#
|
490
|
+
# Returns an array with the elements collected from the row "i".
|
491
|
+
# When a block is given, the elements of that vector are iterated.
|
492
|
+
#
|
493
|
+
def row_collect(i, &block)
|
494
|
+
f = MMatrix.default_block(block)
|
495
|
+
@rows[i].collect {|e| f.call(e)}
|
496
|
+
end
|
497
|
+
|
498
|
+
#
|
499
|
+
# Returns row vector number "i" like Matrix.row as a Vector.
|
500
|
+
# When the block is given, the elements of row "i" are modified
|
501
|
+
#
|
502
|
+
def row!(i)
|
503
|
+
if block_given?
|
504
|
+
@rows[i].collect! {|e| yield e }
|
505
|
+
else
|
506
|
+
Vector.elements(@rows[i], false)
|
507
|
+
end
|
508
|
+
end
|
509
|
+
alias :row_collect! :row!
|
510
|
+
|
511
|
+
#
|
512
|
+
# Returns an array with the elements collected from the column "j".
|
513
|
+
# When a block is given, the elements of that vector are iterated.
|
514
|
+
#
|
515
|
+
def column_collect(j, &block)
|
516
|
+
f = MMatrix.default_block(block)
|
517
|
+
(0...row_size).collect {|r| f.call(self[r, j])}
|
518
|
+
end
|
519
|
+
|
520
|
+
#
|
521
|
+
# Returns column vector number "j" as a Vector.
|
522
|
+
# When the block is given, the elements of column "j" are mmodified
|
523
|
+
#
|
524
|
+
def column!(j)
|
525
|
+
if block_given?
|
526
|
+
(0...row_size).collect { |i| @rows[i][j] = yield @rows[i][j] }
|
527
|
+
else
|
528
|
+
column(j)
|
529
|
+
end
|
530
|
+
end
|
531
|
+
alias :column_collect! :column!
|
532
|
+
|
533
|
+
#
|
534
|
+
# Set a certain column with the values of a Vector
|
535
|
+
# m = Matrix.new(3, 3){|i, j| i * 3 + j + 1}
|
536
|
+
# m.column= 1, Vector[1, 1, 1], 1..2
|
537
|
+
# m => 1 2 3
|
538
|
+
# 4 1 6
|
539
|
+
# 7 1 9
|
540
|
+
#
|
541
|
+
def column=(args)
|
542
|
+
m = row_size
|
543
|
+
c, v, r = MMatrix.id_vect_range(args, m)
|
544
|
+
(m..r.begin - 1).each{|i| self[i, c] = 0}
|
545
|
+
[v.size, r.entries.size].min.times{|i| self[i + r.begin, c] = v[i]}
|
546
|
+
((v.size + r.begin)..r.entries.last).each {|i| self[i, c] = 0}
|
547
|
+
end
|
548
|
+
|
549
|
+
#
|
550
|
+
# Set a certain row with the values of a Vector
|
551
|
+
# m = Matrix.new(3, 3){|i, j| i * 3 + j + 1}
|
552
|
+
# m.row= 0, Vector[0, 0], 1..2
|
553
|
+
# m => 1 0 0
|
554
|
+
# 4 5 6
|
555
|
+
# 7 8 9
|
556
|
+
#
|
557
|
+
def row=(args)
|
558
|
+
i, val, range = MMatrix.id_vect_range(args, column_size)
|
559
|
+
row!(i)[range] = val
|
560
|
+
end
|
561
|
+
|
562
|
+
def norm(p = 2)
|
563
|
+
Vector::Norm.sqnorm(self, p) ** (Float(1)/p)
|
564
|
+
end
|
565
|
+
|
566
|
+
def norm_frobenius
|
567
|
+
norm
|
568
|
+
end
|
569
|
+
alias :normF :norm_frobenius
|
570
|
+
|
571
|
+
#
|
572
|
+
# Tests if the matrix is empty or not
|
573
|
+
#
|
574
|
+
def empty?
|
575
|
+
@rows.empty? if @rows
|
576
|
+
end
|
577
|
+
|
578
|
+
#
|
579
|
+
# Returns the row/s of matrix as a Matrix
|
580
|
+
#
|
581
|
+
def row2matrix(r)
|
582
|
+
a = self.send(:row, r).to_a
|
583
|
+
if r.is_a?(Range) and r.entries.size > 1
|
584
|
+
return Matrix[*a]
|
585
|
+
else
|
586
|
+
return Matrix[a]
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
590
|
+
#
|
591
|
+
# Returns the colomn/s of matrix as a Matrix
|
592
|
+
#
|
593
|
+
def column2matrix(c)
|
594
|
+
a = self.send(:column, c).to_a
|
595
|
+
if c.is_a?(Range) and c.entries.size > 1
|
596
|
+
return Matrix[*a]
|
597
|
+
else
|
598
|
+
return Matrix[*a.collect{|x| [x]}]
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
module LU
|
603
|
+
#
|
604
|
+
# Return the Gauss vector, MC, Golub, 3.2.1 Gauss Transformation, p94
|
605
|
+
#
|
606
|
+
def LU.gauss_vector(mat, k)
|
607
|
+
t = mat.column2matrix(k)
|
608
|
+
tk = t[k, 0]
|
609
|
+
(0..k).each{|i| t[i, 0] = 0}
|
610
|
+
return t if tk == 0
|
611
|
+
(k+1...mat.row_size).each{|i| t[i, 0] = t[i, 0].to_f / tk}
|
612
|
+
t
|
613
|
+
end
|
614
|
+
|
615
|
+
#
|
616
|
+
# Return the Gauss transformation matrix: M_k = I - tau * e_k^T
|
617
|
+
#
|
618
|
+
def LU.gauss(mat, k)
|
619
|
+
i = Matrix.I(mat.column_size)
|
620
|
+
tau = gauss_vector(mat, k)
|
621
|
+
e = i.row2matrix(k)
|
622
|
+
i - tau * e
|
623
|
+
end
|
624
|
+
|
625
|
+
#
|
626
|
+
# LU factorization: A = LU
|
627
|
+
# where L is lower triangular and U is upper triangular
|
628
|
+
#
|
629
|
+
def LU.factorization(mat)
|
630
|
+
u = mat.clone
|
631
|
+
n = u.column_size
|
632
|
+
i = Matrix.I(n)
|
633
|
+
l = i.clone
|
634
|
+
(n-1).times {|k|
|
635
|
+
mk = gauss(u, k)
|
636
|
+
u = mk * u # M_{n-1} * ... * M_1 * A = U
|
637
|
+
l += i - mk # L = M_1^{-1} * ... * M_{n-1}^{-1} = I + sum_{k=1}^{n-1} tau * e
|
638
|
+
}
|
639
|
+
return l, u
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
#
|
644
|
+
# Return the upper triangular matrix of LU factorization
|
645
|
+
# M_{n-1} * ... * M_1 * A = U
|
646
|
+
#
|
647
|
+
def U
|
648
|
+
LU.factorization(self)[1]
|
649
|
+
end
|
650
|
+
|
651
|
+
#
|
652
|
+
# Return the lower triangular matrix of LU factorization
|
653
|
+
# L = M_1^{-1} * ... * M_{n-1}^{-1} = I + sum_{k=1}^{n-1} tau * e
|
654
|
+
#
|
655
|
+
def L
|
656
|
+
LU.factorization(self)[0]
|
657
|
+
end
|
658
|
+
|
659
|
+
module Householder
|
660
|
+
#
|
661
|
+
# a QR factorization that uses Householder transformation
|
662
|
+
# Q^T * A = R
|
663
|
+
# MC, Golub & van Loan, pg 224, 5.2.1 Householder QR
|
664
|
+
#
|
665
|
+
def Householder.QR(mat)
|
666
|
+
h = []
|
667
|
+
a = mat.clone
|
668
|
+
m = a.row_size
|
669
|
+
n = a.column_size
|
670
|
+
n.times{|j|
|
671
|
+
v, beta = a[j..m - 1, j].house
|
672
|
+
|
673
|
+
h[j] = Matrix.diag(Matrix.I(j), Matrix.I(m-j)- beta * (v * v.t))
|
674
|
+
|
675
|
+
a[j..m-1, j..n-1] = (Matrix.I(m-j) - beta * (v * v.t)) * a[j..m-1, j..n-1]
|
676
|
+
a[(j+1)..m-1,j] = v[2..(m-j)] if j < m - 1 }
|
677
|
+
h
|
678
|
+
end
|
679
|
+
|
680
|
+
#
|
681
|
+
# From the essential part of Householder vector
|
682
|
+
# it returns the coresponding upper(U_j)/lower(V_j) matrix
|
683
|
+
#
|
684
|
+
def Householder.bidiagUV(essential, dim, beta)
|
685
|
+
v = Vector.concat(Vector[1], essential)
|
686
|
+
dimv = v.size
|
687
|
+
Matrix.diag(Matrix.I(dim - dimv), Matrix.I(dimv) - beta * (v * v.t) )
|
688
|
+
end
|
689
|
+
|
690
|
+
#
|
691
|
+
# Householder Bidiagonalization algorithm. MC, Golub, pg 252, Algorithm 5.4.2
|
692
|
+
# Returns the matrices U_B and V_B such that: U_B^T * A * V_B = B,
|
693
|
+
# where B is upper bidiagonal.
|
694
|
+
#
|
695
|
+
def Householder.bidiag(mat)
|
696
|
+
a = mat.clone
|
697
|
+
m = a.row_size
|
698
|
+
n = a.column_size
|
699
|
+
ub = Matrix.I(m)
|
700
|
+
vb = Matrix.I(n)
|
701
|
+
n.times{|j|
|
702
|
+
v, beta = a[j..m-1,j].house
|
703
|
+
a[j..m-1, j..n-1] = (Matrix.I(m-j) - beta * (v * v.t)) * a[j..m-1, j..n-1]
|
704
|
+
a[j+1..m-1, j] = v[1..(m-j-1)]
|
705
|
+
ub *= bidiagUV(a[j+1..m-1,j], m, beta) #Ub = U_1 * U_2 * ... * U_n
|
706
|
+
if j < n - 2
|
707
|
+
v, beta = (a[j, j+1..n-1]).house
|
708
|
+
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))
|
709
|
+
a[j, j+2..n-1] = v[1..n-j-2]
|
710
|
+
vb *= bidiagUV(a[j, j+2..n-1], n, beta) #Vb = V_1 * U_2 * ... * V_n-2
|
711
|
+
end }
|
712
|
+
return ub, vb
|
713
|
+
end
|
714
|
+
|
715
|
+
#
|
716
|
+
#Householder Reduction to Hessenberg Form
|
717
|
+
#
|
718
|
+
def Householder.toHessenberg(mat)
|
719
|
+
h = mat.clone
|
720
|
+
n = h.row_size
|
721
|
+
u0 = Matrix.I(n)
|
722
|
+
for k in (0...n - 2)
|
723
|
+
v, beta = h[k+1..n-1, k].house #the householder matrice part
|
724
|
+
houseV = Matrix.I(n-k-1) - beta * (v * v.t)
|
725
|
+
u0 *= Matrix.diag(Matrix.I(k+1), houseV)
|
726
|
+
h[k+1..n-1, k..n-1] = houseV * h[k+1..n-1, k..n-1]
|
727
|
+
h[0..n-1, k+1..n-1] = h[0..n-1, k+1..n-1] * houseV
|
728
|
+
end
|
729
|
+
return h, u0
|
730
|
+
end
|
731
|
+
|
732
|
+
|
733
|
+
end #end of Householder module
|
734
|
+
|
735
|
+
#
|
736
|
+
# Returns the upper bidiagonal matrix obtained with Householder Bidiagonalization algorithm
|
737
|
+
#
|
738
|
+
def bidiagonal
|
739
|
+
ub, vb = Householder.bidiag(self)
|
740
|
+
ub.t * self * vb
|
741
|
+
end
|
742
|
+
|
743
|
+
#
|
744
|
+
# Returns the orthogonal matrix Q of Householder QR factorization
|
745
|
+
# where Q = H_1 * H_2 * H_3 * ... * H_n,
|
746
|
+
#
|
747
|
+
def houseQ
|
748
|
+
h = Householder.QR(self)
|
749
|
+
q = h[0]
|
750
|
+
(1...h.size).each{|i| q *= h[i]}
|
751
|
+
q
|
752
|
+
end
|
753
|
+
|
754
|
+
#
|
755
|
+
# Returns the matrix R of Householder QR factorization
|
756
|
+
# R = H_n * H_n-1 * ... * H_1 * A is an upper triangular matrix
|
757
|
+
#
|
758
|
+
def houseR
|
759
|
+
h = Householder.QR(self)
|
760
|
+
r = self.clone
|
761
|
+
h.size.times{|i| r = h[i] * r}
|
762
|
+
r
|
763
|
+
end
|
764
|
+
|
765
|
+
#
|
766
|
+
# Modified Gram Schmidt QR factorization (MC, Golub, p. 232)
|
767
|
+
# A = Q_1 * R_1
|
768
|
+
#
|
769
|
+
def gram_schmidt
|
770
|
+
a = clone
|
771
|
+
n = column_size
|
772
|
+
m = row_size
|
773
|
+
q = Matrix.new(m, n){0}
|
774
|
+
r = Matrix.zero(n)
|
775
|
+
for k in 0...n
|
776
|
+
r[k,k] = a[0...m, k].norm
|
777
|
+
q[0...m, k] = a[0...m, k] / r[k, k]
|
778
|
+
for j in (k+1)...n
|
779
|
+
r[k, j] = q[0...m, k].t * a[0...m, j]
|
780
|
+
a[0...m, j] -= q[0...m, k] * r[k, j]
|
781
|
+
end
|
782
|
+
end
|
783
|
+
return q, r
|
784
|
+
end
|
785
|
+
|
786
|
+
#
|
787
|
+
# Returns the Q_1 matrix of Modified Gram Schmidt algorithm
|
788
|
+
# Q_1 has orthonormal columns
|
789
|
+
#
|
790
|
+
def gram_schmidtQ
|
791
|
+
gram_schmidt[0]
|
792
|
+
end
|
793
|
+
|
794
|
+
#
|
795
|
+
# Returns the R_1 upper triangular matrix of Modified Gram Schmidt algorithm
|
796
|
+
#
|
797
|
+
def gram_schmidtR
|
798
|
+
gram_schmidt[1]
|
799
|
+
end
|
800
|
+
|
801
|
+
|
802
|
+
module Givens
|
803
|
+
#
|
804
|
+
# Returns the values "c and s" of a Given rotation
|
805
|
+
# MC, Golub, pg 216, Alghorithm 5.1.3
|
806
|
+
#
|
807
|
+
def Givens.givens(a, b)
|
808
|
+
if b == 0
|
809
|
+
c = 0; s = 0
|
810
|
+
else
|
811
|
+
if b.abs > a.abs
|
812
|
+
tau = Float(-a)/b; s = 1/Math.sqrt(1+tau**2); c = s * tau
|
813
|
+
else
|
814
|
+
tau = Float(-b)/a; c = 1/Math.sqrt(1+tau**2); s = c * tau
|
815
|
+
end
|
816
|
+
end
|
817
|
+
return c, s
|
818
|
+
end
|
819
|
+
|
820
|
+
#
|
821
|
+
# a QR factorization using Givens rotation
|
822
|
+
# Computes the upper triangular matrix R and the orthogonal matrix Q
|
823
|
+
# where Q^t A = R (MC, Golub, p227 algorithm 5.2.2)
|
824
|
+
#
|
825
|
+
def Givens.QR(mat)
|
826
|
+
r = mat.clone
|
827
|
+
m = r.row_size
|
828
|
+
n = r.column_size
|
829
|
+
q = Matrix.I(m)
|
830
|
+
n.times{|j|
|
831
|
+
m-1.downto(j+1){|i|
|
832
|
+
c, s = givens(r[i - 1, j], r[i, j])
|
833
|
+
qt = Matrix.I(m); qt[i-1..i, i-1..i] = Matrix[[c, s],[-s, c]]
|
834
|
+
q *= qt
|
835
|
+
r[i-1..i, j..n-1] = Matrix[[c, -s],[s, c]] * r[i-1..i, j..n-1]}}
|
836
|
+
return r, q
|
837
|
+
end
|
838
|
+
|
839
|
+
end
|
840
|
+
|
841
|
+
#
|
842
|
+
# Returns the upper triunghiular matrix R of a Givens QR factorization
|
843
|
+
#
|
844
|
+
def givensR
|
845
|
+
Givens.QR(self)[0]
|
846
|
+
end
|
847
|
+
|
848
|
+
#
|
849
|
+
# Returns the orthogonal matrix Q of Givens QR factorization.
|
850
|
+
# Q = G_1 * ... * G_t where G_j is the j'th Givens rotation
|
851
|
+
# and 't' is the total number of rotations
|
852
|
+
#
|
853
|
+
def givensQ
|
854
|
+
Givens.QR(self)[1]
|
855
|
+
end
|
856
|
+
|
857
|
+
module Hessenberg
|
858
|
+
#
|
859
|
+
# the matrix must be an upper R^(n x n) Hessenberg matrix
|
860
|
+
#
|
861
|
+
def Hessenberg.QR(mat)
|
862
|
+
r = mat.clone
|
863
|
+
n = r.row_size
|
864
|
+
q = Matrix.I(n)
|
865
|
+
for j in (0...n-1)
|
866
|
+
c, s = Givens.givens(r[j,j], r[j+1, j])
|
867
|
+
cs = Matrix[[c, s], [-s, c]]
|
868
|
+
q *= Matrix.diag(Matrix.I(j), cs, Matrix.I(n - j - 2))
|
869
|
+
r[j..j+1, j..n-1] = cs.t * r[j..j+1, j..n-1]
|
870
|
+
end
|
871
|
+
return q, r
|
872
|
+
end
|
873
|
+
end
|
874
|
+
|
875
|
+
#
|
876
|
+
# Returns the orthogonal matrix Q of Hessenberg QR factorization
|
877
|
+
# Q = G_1 *...* G_(n-1) where G_j is the Givens rotation G_j = G(j, j+1, omega_j)
|
878
|
+
#
|
879
|
+
def hessenbergQ
|
880
|
+
Hessenberg.QR(self)[0]
|
881
|
+
end
|
882
|
+
|
883
|
+
#
|
884
|
+
# Returns the upper triunghiular matrix R of a Hessenberg QR factorization
|
885
|
+
#
|
886
|
+
def hessenbergR
|
887
|
+
Hessenberg.QR(self)[1]
|
888
|
+
end
|
889
|
+
|
890
|
+
#
|
891
|
+
# Return an upper Hessenberg matrix obtained with Householder reduction to Hessenberg Form algorithm
|
892
|
+
#
|
893
|
+
def hessenberg_form_H
|
894
|
+
Householder.toHessenberg(self)[0]
|
895
|
+
end
|
896
|
+
|
897
|
+
#
|
898
|
+
# The real Schur decomposition.
|
899
|
+
# The eigenvalues are aproximated in diagonal elements of the real Schur decomposition matrix
|
900
|
+
#
|
901
|
+
def realSchur(eps = 1.0e-10, steps = 100)
|
902
|
+
h = self.hessenberg_form_H
|
903
|
+
h1 = Matrix[]
|
904
|
+
i = 0
|
905
|
+
loop do
|
906
|
+
h1 = h.hessenbergR * h.hessenbergQ
|
907
|
+
break if Matrix.diag_in_delta?(h1, h, eps) or steps <= 0
|
908
|
+
h = h1.clone
|
909
|
+
steps -= 1
|
910
|
+
i += 1
|
911
|
+
end
|
912
|
+
h1
|
913
|
+
end
|
914
|
+
|
915
|
+
|
916
|
+
module Jacobi
|
917
|
+
#
|
918
|
+
# Returns the nurm of the off-diagonal element
|
919
|
+
#
|
920
|
+
def Jacobi.off(a)
|
921
|
+
n = a.row_size
|
922
|
+
sum = 0
|
923
|
+
n.times{|i| n.times{|j| sum += a[i, j]**2 if j != i}}
|
924
|
+
Math.sqrt(sum)
|
925
|
+
end
|
926
|
+
|
927
|
+
#
|
928
|
+
# Returns the index pair (p, q) with 1<= p < q <= n and A[p, q] is the maximum in absolute value
|
929
|
+
#
|
930
|
+
def Jacobi.max(a)
|
931
|
+
n = a.row_size
|
932
|
+
max = 0
|
933
|
+
p = 0
|
934
|
+
q = 0
|
935
|
+
n.times{|i|
|
936
|
+
((i+1)...n).each{|j|
|
937
|
+
val = a[i, j].abs
|
938
|
+
if val > max
|
939
|
+
max = val
|
940
|
+
p = i
|
941
|
+
q = j
|
942
|
+
end }}
|
943
|
+
return p, q
|
944
|
+
end
|
945
|
+
|
946
|
+
#
|
947
|
+
# Compute the cosine-sine pair (c, s) for the element A[p, q]
|
948
|
+
#
|
949
|
+
def Jacobi.sym_schur2(a, p, q)
|
950
|
+
if a[p, q] != 0
|
951
|
+
tau = Float(a[q, q] - a[p, p])/(2 * a[p, q])
|
952
|
+
if tau >= 0
|
953
|
+
t = 1./(tau + Math.sqrt(1 + tau ** 2))
|
954
|
+
else
|
955
|
+
t = -1./(-tau + Math.sqrt(1 + tau ** 2))
|
956
|
+
end
|
957
|
+
c = 1./Math.sqrt(1 + t ** 2)
|
958
|
+
s = t * c
|
959
|
+
else
|
960
|
+
c = 1
|
961
|
+
s = 0
|
962
|
+
end
|
963
|
+
return c, s
|
964
|
+
end
|
965
|
+
|
966
|
+
#
|
967
|
+
# Returns the Jacobi rotation matrix
|
968
|
+
#
|
969
|
+
def Jacobi.J(p, q, c, s, n)
|
970
|
+
j = Matrix.I(n)
|
971
|
+
j[p,p] = c; j[p, q] = s
|
972
|
+
j[q,p] = -s; j[q, q] = c
|
973
|
+
j
|
974
|
+
end
|
975
|
+
end
|
976
|
+
|
977
|
+
#
|
978
|
+
# Classical Jacobi 8.4.3 Golub & van Loan
|
979
|
+
#
|
980
|
+
def cJacobi(tol = 1.0e-10)
|
981
|
+
a = self.clone
|
982
|
+
n = row_size
|
983
|
+
v = Matrix.I(n)
|
984
|
+
eps = tol * a.normF
|
985
|
+
while Jacobi.off(a) > eps
|
986
|
+
p, q = Jacobi.max(a)
|
987
|
+
c, s = Jacobi.sym_schur2(a, p, q)
|
988
|
+
#print "\np:#{p} q:#{q} c:#{c} s:#{s}\n"
|
989
|
+
j = Jacobi.J(p, q, c, s, n)
|
990
|
+
a = j.t * a * j
|
991
|
+
v = v * j
|
992
|
+
end
|
993
|
+
return a, v
|
994
|
+
end
|
995
|
+
|
996
|
+
#
|
997
|
+
# Returns the aproximation matrix computed with Classical Jacobi algorithm.
|
998
|
+
# The aproximate eigenvalues values are in the diagonal of the matrix A.
|
999
|
+
#
|
1000
|
+
def cJacobiA(tol = 1.0e-10)
|
1001
|
+
cJacobi(tol)[0]
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
#
|
1005
|
+
# Returns a Vector with the eigenvalues aproximated values.
|
1006
|
+
# The eigenvalues are computed with the Classic Jacobi Algorithm.
|
1007
|
+
#
|
1008
|
+
def eigenvaluesJacobi
|
1009
|
+
a = cJacobiA
|
1010
|
+
Vector[*(0...row_size).collect{|i| a[i, i]}]
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
#
|
1014
|
+
# Returns the orthogonal matrix obtained with the Jacobi eigenvalue algorithm.
|
1015
|
+
# The columns of V are the eigenvector.
|
1016
|
+
#
|
1017
|
+
def cJacobiV(tol = 1.0e-10)
|
1018
|
+
cJacobi(tol)[1]
|
1019
|
+
end
|
1020
|
+
end
|
1021
|
+
|
1022
|
+
|