matrix 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Matrix
4
+ VERSION = "0.3.0"
5
+ end
@@ -1,8 +1,15 @@
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 = "0.1.0"
12
+ spec.version = Matrix::VERSION
6
13
  spec.authors = ["Marc-Andre Lafortune"]
7
14
  spec.email = ["ruby-core@marc-andre.ca"]
8
15
 
@@ -10,8 +17,9 @@ Gem::Specification.new do |spec|
10
17
  spec.description = %q{An implementation of Matrix and Vector classes.}
11
18
  spec.homepage = "https://github.com/ruby/matrix"
12
19
  spec.license = "BSD-2-Clause"
20
+ spec.required_ruby_version = ">= 2.5.0"
13
21
 
14
- spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "lib/matrix.rb", "matrix.gemspec"]
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"]
15
23
  spec.bindir = "exe"
16
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
17
25
  spec.require_paths = ["lib"]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: matrix
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc-Andre Lafortune
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-12-04 00:00:00.000000000 Z
11
+ date: 2020-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -46,7 +46,6 @@ extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
48
  - ".gitignore"
49
- - ".travis.yml"
50
49
  - Gemfile
51
50
  - LICENSE.txt
52
51
  - README.md
@@ -54,6 +53,9 @@ files:
54
53
  - bin/console
55
54
  - bin/setup
56
55
  - lib/matrix.rb
56
+ - lib/matrix/eigenvalue_decomposition.rb
57
+ - lib/matrix/lup_decomposition.rb
58
+ - lib/matrix/version.rb
57
59
  - matrix.gemspec
58
60
  homepage: https://github.com/ruby/matrix
59
61
  licenses:
@@ -67,15 +69,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
67
69
  requirements:
68
70
  - - ">="
69
71
  - !ruby/object:Gem::Version
70
- version: '0'
72
+ version: 2.5.0
71
73
  required_rubygems_version: !ruby/object:Gem::Requirement
72
74
  requirements:
73
75
  - - ">="
74
76
  - !ruby/object:Gem::Version
75
77
  version: '0'
76
78
  requirements: []
77
- rubyforge_project:
78
- rubygems_version: 2.7.6
79
+ rubygems_version: 3.1.2
79
80
  signing_key:
80
81
  specification_version: 4
81
82
  summary: An implementation of Matrix and Vector classes.
@@ -1,4 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - ruby-head