matrix 0.0.1 → 0.4.1
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.
- checksums.yaml +5 -5
- data/.gitignore +9 -17
- data/Gemfile +3 -1
- data/LICENSE.txt +20 -20
- data/README.md +29 -13
- data/Rakefile +9 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/matrix.rb +2491 -3
- data/lib/matrix/eigenvalue_decomposition.rb +882 -0
- data/lib/matrix/lup_decomposition.rb +219 -0
- data/lib/matrix/version.rb +4 -2
- data/matrix.gemspec +20 -14
- metadata +28 -24
@@ -0,0 +1,219 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
class Matrix
|
3
|
+
# Adapted from JAMA: http://math.nist.gov/javanumerics/jama/
|
4
|
+
|
5
|
+
#
|
6
|
+
# For an m-by-n matrix A with m >= n, the LU decomposition is an m-by-n
|
7
|
+
# unit lower triangular matrix L, an n-by-n upper triangular matrix U,
|
8
|
+
# and a m-by-m permutation matrix P so that L*U = P*A.
|
9
|
+
# If m < n, then L is m-by-m and U is m-by-n.
|
10
|
+
#
|
11
|
+
# The LUP decomposition with pivoting always exists, even if the matrix is
|
12
|
+
# singular, so the constructor will never fail. The primary use of the
|
13
|
+
# LU decomposition is in the solution of square systems of simultaneous
|
14
|
+
# linear equations. This will fail if singular? returns true.
|
15
|
+
#
|
16
|
+
|
17
|
+
class LUPDecomposition
|
18
|
+
# Returns the lower triangular factor +L+
|
19
|
+
|
20
|
+
include Matrix::ConversionHelper
|
21
|
+
|
22
|
+
def l
|
23
|
+
Matrix.build(@row_count, [@column_count, @row_count].min) do |i, j|
|
24
|
+
if (i > j)
|
25
|
+
@lu[i][j]
|
26
|
+
elsif (i == j)
|
27
|
+
1
|
28
|
+
else
|
29
|
+
0
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the upper triangular factor +U+
|
35
|
+
|
36
|
+
def u
|
37
|
+
Matrix.build([@column_count, @row_count].min, @column_count) do |i, j|
|
38
|
+
if (i <= j)
|
39
|
+
@lu[i][j]
|
40
|
+
else
|
41
|
+
0
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the permutation matrix +P+
|
47
|
+
|
48
|
+
def p
|
49
|
+
rows = Array.new(@row_count){Array.new(@row_count, 0)}
|
50
|
+
@pivots.each_with_index{|p, i| rows[i][p] = 1}
|
51
|
+
Matrix.send :new, rows, @row_count
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns +L+, +U+, +P+ in an array
|
55
|
+
|
56
|
+
def to_ary
|
57
|
+
[l, u, p]
|
58
|
+
end
|
59
|
+
alias_method :to_a, :to_ary
|
60
|
+
|
61
|
+
# Returns the pivoting indices
|
62
|
+
|
63
|
+
attr_reader :pivots
|
64
|
+
|
65
|
+
# Returns +true+ if +U+, and hence +A+, is singular.
|
66
|
+
|
67
|
+
def singular?
|
68
|
+
@column_count.times do |j|
|
69
|
+
if (@lu[j][j] == 0)
|
70
|
+
return true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
false
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns the determinant of +A+, calculated efficiently
|
77
|
+
# from the factorization.
|
78
|
+
|
79
|
+
def det
|
80
|
+
if (@row_count != @column_count)
|
81
|
+
raise Matrix::ErrDimensionMismatch
|
82
|
+
end
|
83
|
+
d = @pivot_sign
|
84
|
+
@column_count.times do |j|
|
85
|
+
d *= @lu[j][j]
|
86
|
+
end
|
87
|
+
d
|
88
|
+
end
|
89
|
+
alias_method :determinant, :det
|
90
|
+
|
91
|
+
# Returns +m+ so that <tt>A*m = b</tt>,
|
92
|
+
# or equivalently so that <tt>L*U*m = P*b</tt>
|
93
|
+
# +b+ can be a Matrix or a Vector
|
94
|
+
|
95
|
+
def solve b
|
96
|
+
if (singular?)
|
97
|
+
raise Matrix::ErrNotRegular, "Matrix is singular."
|
98
|
+
end
|
99
|
+
if b.is_a? Matrix
|
100
|
+
if (b.row_count != @row_count)
|
101
|
+
raise Matrix::ErrDimensionMismatch
|
102
|
+
end
|
103
|
+
|
104
|
+
# Copy right hand side with pivoting
|
105
|
+
nx = b.column_count
|
106
|
+
m = @pivots.map{|row| b.row(row).to_a}
|
107
|
+
|
108
|
+
# Solve L*Y = P*b
|
109
|
+
@column_count.times do |k|
|
110
|
+
(k+1).upto(@column_count-1) do |i|
|
111
|
+
nx.times do |j|
|
112
|
+
m[i][j] -= m[k][j]*@lu[i][k]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
# Solve U*m = Y
|
117
|
+
(@column_count-1).downto(0) do |k|
|
118
|
+
nx.times do |j|
|
119
|
+
m[k][j] = m[k][j].quo(@lu[k][k])
|
120
|
+
end
|
121
|
+
k.times do |i|
|
122
|
+
nx.times do |j|
|
123
|
+
m[i][j] -= m[k][j]*@lu[i][k]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
Matrix.send :new, m, nx
|
128
|
+
else # same algorithm, specialized for simpler case of a vector
|
129
|
+
b = convert_to_array(b)
|
130
|
+
if (b.size != @row_count)
|
131
|
+
raise Matrix::ErrDimensionMismatch
|
132
|
+
end
|
133
|
+
|
134
|
+
# Copy right hand side with pivoting
|
135
|
+
m = b.values_at(*@pivots)
|
136
|
+
|
137
|
+
# Solve L*Y = P*b
|
138
|
+
@column_count.times do |k|
|
139
|
+
(k+1).upto(@column_count-1) do |i|
|
140
|
+
m[i] -= m[k]*@lu[i][k]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
# Solve U*m = Y
|
144
|
+
(@column_count-1).downto(0) do |k|
|
145
|
+
m[k] = m[k].quo(@lu[k][k])
|
146
|
+
k.times do |i|
|
147
|
+
m[i] -= m[k]*@lu[i][k]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
Vector.elements(m, false)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def initialize a
|
155
|
+
raise TypeError, "Expected Matrix but got #{a.class}" unless a.is_a?(Matrix)
|
156
|
+
# Use a "left-looking", dot-product, Crout/Doolittle algorithm.
|
157
|
+
@lu = a.to_a
|
158
|
+
@row_count = a.row_count
|
159
|
+
@column_count = a.column_count
|
160
|
+
@pivots = Array.new(@row_count)
|
161
|
+
@row_count.times do |i|
|
162
|
+
@pivots[i] = i
|
163
|
+
end
|
164
|
+
@pivot_sign = 1
|
165
|
+
lu_col_j = Array.new(@row_count)
|
166
|
+
|
167
|
+
# Outer loop.
|
168
|
+
|
169
|
+
@column_count.times do |j|
|
170
|
+
|
171
|
+
# Make a copy of the j-th column to localize references.
|
172
|
+
|
173
|
+
@row_count.times do |i|
|
174
|
+
lu_col_j[i] = @lu[i][j]
|
175
|
+
end
|
176
|
+
|
177
|
+
# Apply previous transformations.
|
178
|
+
|
179
|
+
@row_count.times do |i|
|
180
|
+
lu_row_i = @lu[i]
|
181
|
+
|
182
|
+
# Most of the time is spent in the following dot product.
|
183
|
+
|
184
|
+
kmax = [i, j].min
|
185
|
+
s = 0
|
186
|
+
kmax.times do |k|
|
187
|
+
s += lu_row_i[k]*lu_col_j[k]
|
188
|
+
end
|
189
|
+
|
190
|
+
lu_row_i[j] = lu_col_j[i] -= s
|
191
|
+
end
|
192
|
+
|
193
|
+
# Find pivot and exchange if necessary.
|
194
|
+
|
195
|
+
p = j
|
196
|
+
(j+1).upto(@row_count-1) do |i|
|
197
|
+
if (lu_col_j[i].abs > lu_col_j[p].abs)
|
198
|
+
p = i
|
199
|
+
end
|
200
|
+
end
|
201
|
+
if (p != j)
|
202
|
+
@column_count.times do |k|
|
203
|
+
t = @lu[p][k]; @lu[p][k] = @lu[j][k]; @lu[j][k] = t
|
204
|
+
end
|
205
|
+
k = @pivots[p]; @pivots[p] = @pivots[j]; @pivots[j] = k
|
206
|
+
@pivot_sign = -@pivot_sign
|
207
|
+
end
|
208
|
+
|
209
|
+
# Compute multipliers.
|
210
|
+
|
211
|
+
if (j < @row_count && @lu[j][j] != 0)
|
212
|
+
(j+1).upto(@row_count-1) do |i|
|
213
|
+
@lu[i][j] = @lu[i][j].quo(@lu[j][j])
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
data/lib/matrix/version.rb
CHANGED
data/matrix.gemspec
CHANGED
@@ -1,23 +1,29 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
require_relative "lib/matrix/version"
|
5
|
+
rescue LoadError
|
6
|
+
# for Ruby core repository
|
7
|
+
require_relative "version"
|
8
|
+
end
|
5
9
|
|
6
10
|
Gem::Specification.new do |spec|
|
7
11
|
spec.name = "matrix"
|
8
12
|
spec.version = Matrix::VERSION
|
9
|
-
spec.authors = ["
|
10
|
-
spec.email = ["
|
11
|
-
|
12
|
-
spec.summary = %q{
|
13
|
-
spec.
|
14
|
-
spec.
|
13
|
+
spec.authors = ["Marc-Andre Lafortune"]
|
14
|
+
spec.email = ["ruby-core@marc-andre.ca"]
|
15
|
+
|
16
|
+
spec.summary = %q{An implementation of Matrix and Vector classes.}
|
17
|
+
spec.description = %q{An implementation of Matrix and Vector classes.}
|
18
|
+
spec.homepage = "https://github.com/ruby/matrix"
|
19
|
+
spec.licenses = ["Ruby", "BSD-2-Clause"]
|
20
|
+
spec.required_ruby_version = ">= 2.5.0"
|
15
21
|
|
16
|
-
spec.files =
|
17
|
-
spec.
|
18
|
-
spec.
|
22
|
+
spec.files = [".gitignore", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "lib/matrix.rb", "lib/matrix/eigenvalue_decomposition.rb", "lib/matrix/lup_decomposition.rb", "lib/matrix/version.rb", "matrix.gemspec"]
|
23
|
+
spec.bindir = "exe"
|
24
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
25
|
spec.require_paths = ["lib"]
|
20
26
|
|
21
|
-
spec.add_development_dependency "bundler"
|
27
|
+
spec.add_development_dependency "bundler"
|
22
28
|
spec.add_development_dependency "rake"
|
23
29
|
end
|
metadata
CHANGED
@@ -1,80 +1,84 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: matrix
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
8
|
-
autorequire:
|
9
|
-
bindir:
|
7
|
+
- Marc-Andre Lafortune
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
-
description:
|
41
|
+
description: An implementation of Matrix and Vector classes.
|
42
42
|
email:
|
43
|
-
-
|
43
|
+
- ruby-core@marc-andre.ca
|
44
44
|
executables: []
|
45
45
|
extensions: []
|
46
46
|
extra_rdoc_files: []
|
47
47
|
files:
|
48
|
-
- .gitignore
|
48
|
+
- ".gitignore"
|
49
49
|
- Gemfile
|
50
50
|
- LICENSE.txt
|
51
51
|
- README.md
|
52
52
|
- Rakefile
|
53
|
+
- bin/console
|
54
|
+
- bin/setup
|
53
55
|
- lib/matrix.rb
|
56
|
+
- lib/matrix/eigenvalue_decomposition.rb
|
57
|
+
- lib/matrix/lup_decomposition.rb
|
54
58
|
- lib/matrix/version.rb
|
55
59
|
- matrix.gemspec
|
56
|
-
homepage:
|
60
|
+
homepage: https://github.com/ruby/matrix
|
57
61
|
licenses:
|
58
|
-
-
|
62
|
+
- Ruby
|
63
|
+
- BSD-2-Clause
|
59
64
|
metadata: {}
|
60
|
-
post_install_message:
|
65
|
+
post_install_message:
|
61
66
|
rdoc_options: []
|
62
67
|
require_paths:
|
63
68
|
- lib
|
64
69
|
required_ruby_version: !ruby/object:Gem::Requirement
|
65
70
|
requirements:
|
66
|
-
- -
|
71
|
+
- - ">="
|
67
72
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
73
|
+
version: 2.5.0
|
69
74
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
75
|
requirements:
|
71
|
-
- -
|
76
|
+
- - ">="
|
72
77
|
- !ruby/object:Gem::Version
|
73
78
|
version: '0'
|
74
79
|
requirements: []
|
75
|
-
|
76
|
-
|
77
|
-
signing_key:
|
80
|
+
rubygems_version: 3.2.3
|
81
|
+
signing_key:
|
78
82
|
specification_version: 4
|
79
|
-
summary:
|
83
|
+
summary: An implementation of Matrix and Vector classes.
|
80
84
|
test_files: []
|