matrix 0.1.0 → 0.4.2

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.4.2"
5
+ 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 = "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
 
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.license = "BSD-2-Clause"
19
+ spec.licenses = ["Ruby", "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 = ["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 = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
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.1.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: 2018-12-04 00:00:00.000000000 Z
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: '0'
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
- rubyforge_project:
78
- rubygems_version: 2.7.6
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
@@ -1,9 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
data/.travis.yml DELETED
@@ -1,4 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - ruby-head
data/Gemfile DELETED
@@ -1,6 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
-
5
- # Specify your gem's dependencies in matrix.gemspec
6
- gemspec
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
@@ -1,10 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
3
-
4
- Rake::TestTask.new(:test) do |t|
5
- t.libs << "test" << "test/lib"
6
- t.libs << "lib"
7
- t.test_files = FileList['test/**/test_*.rb']
8
- end
9
-
10
- task :default => [:test]
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__)