matrix 0.1.0 → 0.4.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.
- checksums.yaml +4 -4
- data/lib/matrix.rb +299 -180
- data/lib/matrix/eigenvalue_decomposition.rb +882 -0
- data/lib/matrix/lup_decomposition.rb +219 -0
- data/lib/matrix/version.rb +5 -0
- data/matrix.gemspec +12 -7
- metadata +12 -44
- data/.gitignore +0 -9
- data/.travis.yml +0 -4
- data/Gemfile +0 -6
- data/README.md +0 -41
- data/Rakefile +0 -10
- data/bin/console +0 -14
- data/bin/setup +0 -8
@@ -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/matrix.gemspec
CHANGED
@@ -1,21 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
begin
|
4
|
+
require_relative "lib/matrix/version"
|
5
|
+
rescue LoadError
|
6
|
+
# for Ruby core repository
|
7
|
+
require_relative "version"
|
8
|
+
end
|
9
|
+
|
3
10
|
Gem::Specification.new do |spec|
|
4
11
|
spec.name = "matrix"
|
5
|
-
spec.version =
|
12
|
+
spec.version = Matrix::VERSION
|
6
13
|
spec.authors = ["Marc-Andre Lafortune"]
|
7
14
|
spec.email = ["ruby-core@marc-andre.ca"]
|
8
15
|
|
9
16
|
spec.summary = %q{An implementation of Matrix and Vector classes.}
|
10
17
|
spec.description = %q{An implementation of Matrix and Vector classes.}
|
11
18
|
spec.homepage = "https://github.com/ruby/matrix"
|
12
|
-
spec.
|
19
|
+
spec.licenses = ["Ruby", "BSD-2-Clause"]
|
20
|
+
spec.required_ruby_version = ">= 2.5.0"
|
13
21
|
|
14
|
-
spec.files = ["
|
22
|
+
spec.files = ["LICENSE.txt", "lib/matrix.rb", "lib/matrix/eigenvalue_decomposition.rb", "lib/matrix/lup_decomposition.rb", "lib/matrix/version.rb", "matrix.gemspec"]
|
15
23
|
spec.bindir = "exe"
|
16
|
-
spec.executables =
|
24
|
+
spec.executables = []
|
17
25
|
spec.require_paths = ["lib"]
|
18
|
-
|
19
|
-
spec.add_development_dependency "bundler"
|
20
|
-
spec.add_development_dependency "rake"
|
21
26
|
end
|
metadata
CHANGED
@@ -1,43 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: matrix
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marc-Andre Lafortune
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
11
|
+
date: 2021-06-18 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
41
13
|
description: An implementation of Matrix and Vector classes.
|
42
14
|
email:
|
43
15
|
- ruby-core@marc-andre.ca
|
@@ -45,21 +17,18 @@ executables: []
|
|
45
17
|
extensions: []
|
46
18
|
extra_rdoc_files: []
|
47
19
|
files:
|
48
|
-
- ".gitignore"
|
49
|
-
- ".travis.yml"
|
50
|
-
- Gemfile
|
51
20
|
- LICENSE.txt
|
52
|
-
- README.md
|
53
|
-
- Rakefile
|
54
|
-
- bin/console
|
55
|
-
- bin/setup
|
56
21
|
- lib/matrix.rb
|
22
|
+
- lib/matrix/eigenvalue_decomposition.rb
|
23
|
+
- lib/matrix/lup_decomposition.rb
|
24
|
+
- lib/matrix/version.rb
|
57
25
|
- matrix.gemspec
|
58
26
|
homepage: https://github.com/ruby/matrix
|
59
27
|
licenses:
|
28
|
+
- Ruby
|
60
29
|
- BSD-2-Clause
|
61
30
|
metadata: {}
|
62
|
-
post_install_message:
|
31
|
+
post_install_message:
|
63
32
|
rdoc_options: []
|
64
33
|
require_paths:
|
65
34
|
- lib
|
@@ -67,16 +36,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
67
36
|
requirements:
|
68
37
|
- - ">="
|
69
38
|
- !ruby/object:Gem::Version
|
70
|
-
version:
|
39
|
+
version: 2.5.0
|
71
40
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
41
|
requirements:
|
73
42
|
- - ">="
|
74
43
|
- !ruby/object:Gem::Version
|
75
44
|
version: '0'
|
76
45
|
requirements: []
|
77
|
-
|
78
|
-
|
79
|
-
signing_key:
|
46
|
+
rubygems_version: 3.3.0.dev
|
47
|
+
signing_key:
|
80
48
|
specification_version: 4
|
81
49
|
summary: An implementation of Matrix and Vector classes.
|
82
50
|
test_files: []
|
data/.gitignore
DELETED
data/.travis.yml
DELETED
data/Gemfile
DELETED
data/README.md
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
# Matrix
|
2
|
-
|
3
|
-
An implementation of `Matrix` and `Vector` classes.
|
4
|
-
|
5
|
-
The `Matrix` class represents a mathematical matrix. It provides methods for creating matrices, operating on them arithmetically and algebraically, and determining their mathematical properties (trace, rank, inverse, determinant).
|
6
|
-
|
7
|
-
The `Vector` class represents a mathematical vector, which is useful in its own right, and also constitutes a row or column of a `Matrix`.
|
8
|
-
|
9
|
-
## Installation
|
10
|
-
|
11
|
-
Add this line to your application's Gemfile:
|
12
|
-
|
13
|
-
```ruby
|
14
|
-
gem 'matrix'
|
15
|
-
```
|
16
|
-
|
17
|
-
And then execute:
|
18
|
-
|
19
|
-
$ bundle
|
20
|
-
|
21
|
-
Or install it yourself as:
|
22
|
-
|
23
|
-
$ gem install matrix
|
24
|
-
|
25
|
-
## Usage
|
26
|
-
|
27
|
-
TODO: Write usage instructions here
|
28
|
-
|
29
|
-
## Development
|
30
|
-
|
31
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
32
|
-
|
33
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
34
|
-
|
35
|
-
## Contributing
|
36
|
-
|
37
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/matrix.
|
38
|
-
|
39
|
-
## License
|
40
|
-
|
41
|
-
The gem is available as open source under the terms of the [2-Clause BSD License](https://opensource.org/licenses/BSD-2-Clause).
|
data/Rakefile
DELETED
data/bin/console
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "bundler/setup"
|
4
|
-
require "matrix"
|
5
|
-
|
6
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
8
|
-
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
12
|
-
|
13
|
-
require "irb"
|
14
|
-
IRB.start(__FILE__)
|