kmat 0.0.3
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 +7 -0
- data/.gitattributes +3 -0
- data/.gitignore +15 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile +4 -0
- data/LICENSE.md +675 -0
- data/README.md +224 -0
- data/Rakefile +26 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ext/kmat/arith/binary.c +1121 -0
- data/ext/kmat/arith/logical.c +332 -0
- data/ext/kmat/arith/math.c +34 -0
- data/ext/kmat/arith/statistics.c +173 -0
- data/ext/kmat/arith/unary.c +165 -0
- data/ext/kmat/auto_collect.rb +118 -0
- data/ext/kmat/elementwise_function.rb +149 -0
- data/ext/kmat/extconf.rb +75 -0
- data/ext/kmat/id.txt +80 -0
- data/ext/kmat/id_sym.rb +40 -0
- data/ext/kmat/km_util.h +97 -0
- data/ext/kmat/kmat.h +96 -0
- data/ext/kmat/lapack_headers/blas.h +354 -0
- data/ext/kmat/lapack_headers/lapacke.h +19455 -0
- data/ext/kmat/lapack_headers/lapacke_config.h +119 -0
- data/ext/kmat/lapack_headers/lapacke_mangling.h +17 -0
- data/ext/kmat/lapack_headers/lapacke_utils.h +579 -0
- data/ext/kmat/linalg/dla.c +1629 -0
- data/ext/kmat/linalg/linalg.c +267 -0
- data/ext/kmat/linalg/norm.c +727 -0
- data/ext/kmat/linalg/vla.c +102 -0
- data/ext/kmat/linalg/working.c +240 -0
- data/ext/kmat/main.c +95 -0
- data/ext/kmat/smat/accessor.c +719 -0
- data/ext/kmat/smat/array.c +108 -0
- data/ext/kmat/smat/boxmuller.c +72 -0
- data/ext/kmat/smat/constructer.c +302 -0
- data/ext/kmat/smat/convert.c +375 -0
- data/ext/kmat/smat/elem.c +171 -0
- data/ext/kmat/smat/fund.c +702 -0
- data/ext/kmat/smat/share.c +427 -0
- data/ext/kmat/smat/smat.c +530 -0
- data/ext/kmat/smat/sort.c +1156 -0
- data/ext/kmat/sym.txt +34 -0
- data/kmat.gemspec +46 -0
- data/lib/kmat.rb +20 -0
- data/lib/kmat/accessor.rb +164 -0
- data/lib/kmat/arith.rb +189 -0
- data/lib/kmat/linalg.rb +279 -0
- data/lib/kmat/logical.rb +150 -0
- data/lib/kmat/misc.rb +122 -0
- data/lib/kmat/random.rb +106 -0
- data/lib/kmat/statistics.rb +98 -0
- data/lib/kmat/version.rb +3 -0
- metadata +156 -0
data/ext/kmat/sym.txt
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
a
|
2
|
+
all
|
3
|
+
b
|
4
|
+
bool
|
5
|
+
boolean
|
6
|
+
c
|
7
|
+
col
|
8
|
+
column
|
9
|
+
complex
|
10
|
+
d
|
11
|
+
double
|
12
|
+
each
|
13
|
+
each_with_index2
|
14
|
+
f
|
15
|
+
float
|
16
|
+
full
|
17
|
+
i
|
18
|
+
infinity
|
19
|
+
int
|
20
|
+
integer
|
21
|
+
mmap
|
22
|
+
mmap_with_index2
|
23
|
+
n
|
24
|
+
none
|
25
|
+
o
|
26
|
+
object
|
27
|
+
one
|
28
|
+
r
|
29
|
+
random
|
30
|
+
row
|
31
|
+
serial
|
32
|
+
v
|
33
|
+
value
|
34
|
+
z
|
data/kmat.gemspec
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "kmat/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "kmat"
|
8
|
+
spec.version = Mat::VERSION
|
9
|
+
spec.authors = ["KAZOON"]
|
10
|
+
spec.email = ["cycloawaodorin+gem@gmail.com"]
|
11
|
+
spec.license = "GPL-3.0"
|
12
|
+
|
13
|
+
spec.summary = %q{Kmat is a Ruby gem for matrix operations. Kmat uses BLAS/LAPACK as back-end.}
|
14
|
+
#spec.description = %q{TODO: Write a longer description or delete this line.}
|
15
|
+
spec.homepage = "https://github.com/cycloawaodorin/"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
#spec.metadata["allowed_push_host"] = "http://localhost"
|
21
|
+
|
22
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
23
|
+
spec.metadata["source_code_uri"] = "https://github.com/cycloawaodorin/kmat"
|
24
|
+
spec.metadata["changelog_uri"] = "https://github.com/cycloawaodorin/kmat/blob/master/CHANGELOG.md"
|
25
|
+
else
|
26
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
27
|
+
"public gem pushes."
|
28
|
+
end
|
29
|
+
|
30
|
+
# Specify which files should be added to the gem when it is released.
|
31
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
32
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
33
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
34
|
+
end
|
35
|
+
spec.bindir = "exe"
|
36
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
37
|
+
spec.require_paths = ["lib"]
|
38
|
+
spec.extensions = ["ext/kmat/extconf.rb"]
|
39
|
+
|
40
|
+
spec.add_development_dependency "bundler", ">= 2.0"
|
41
|
+
spec.add_development_dependency "rake", ">= 10.0"
|
42
|
+
spec.add_development_dependency "rake-compiler"
|
43
|
+
spec.add_development_dependency "pry", ">= 0.12.2"
|
44
|
+
|
45
|
+
spec.required_ruby_version = '>= 2.6.0'
|
46
|
+
end
|
data/lib/kmat.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "kmat/version"
|
2
|
+
|
3
|
+
class Mat
|
4
|
+
include Enumerable
|
5
|
+
end
|
6
|
+
|
7
|
+
class << Mat
|
8
|
+
private def _define_dup_escaped_method(method)
|
9
|
+
Mat.class_eval("def #{method}(*args, &block); self.dup.#{method}!(*args, &block); end")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
require "kmat/kmat"
|
14
|
+
require "kmat/arith"
|
15
|
+
require "kmat/accessor"
|
16
|
+
require "kmat/linalg"
|
17
|
+
require "kmat/logical"
|
18
|
+
require "kmat/misc"
|
19
|
+
require "kmat/random"
|
20
|
+
require "kmat/statistics"
|
@@ -0,0 +1,164 @@
|
|
1
|
+
class Mat
|
2
|
+
def ===(other)
|
3
|
+
if other.kind_of?(Mat)
|
4
|
+
self == other
|
5
|
+
else
|
6
|
+
false
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](*idx)
|
11
|
+
if block_given?
|
12
|
+
tmp = self.bracket(*idx)
|
13
|
+
ret = yield(tmp)
|
14
|
+
tmp.__send__(:_kill)
|
15
|
+
ret
|
16
|
+
else
|
17
|
+
self.bracket(*idx)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def temp
|
22
|
+
ret = yield(self)
|
23
|
+
_kill
|
24
|
+
ret
|
25
|
+
end
|
26
|
+
|
27
|
+
# for example, A.repmat(2, 3) returns [A, A, A: A, A, A]
|
28
|
+
def repmat(row_repeat, col_repeat)
|
29
|
+
ri = Mat.new(row_size()*row_repeat, 1, :int) do |i, j|
|
30
|
+
i % row_size()
|
31
|
+
end
|
32
|
+
ci = Mat.new(col_size()*col_repeat, 1, :int) do |i, j|
|
33
|
+
i % col_size()
|
34
|
+
end
|
35
|
+
self[ri, ci] do |m|
|
36
|
+
m.dup
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# extend `other' to fit the shape of self
|
41
|
+
# this is similar to Python's numpy broadcasting
|
42
|
+
def broadcast(other, vt=nil)
|
43
|
+
if other.kind_of?(Mat)
|
44
|
+
if other.row_size == 1
|
45
|
+
if other.col_size == 1
|
46
|
+
other.repmat(row_size(), col_size())
|
47
|
+
elsif other.col_size == self.col_size
|
48
|
+
other.repmat(self.row_size, 1)
|
49
|
+
else
|
50
|
+
raise MismatchedDimensionError, "can't broadcast from (#{other.size.join(', ')}) to (#{self.size.join(', ')})"
|
51
|
+
end
|
52
|
+
elsif other.row_size == self.row_size
|
53
|
+
if other.col_size == 1
|
54
|
+
other.repmat(1, col_size())
|
55
|
+
elsif other.col_size == self.col_size
|
56
|
+
other
|
57
|
+
else
|
58
|
+
raise MismatchedDimensionError, "can't broadcast from (#{self.size.join(', ')}) to (#{other.size.join(', ')})"
|
59
|
+
end
|
60
|
+
else
|
61
|
+
raise MismatchedDimensionError, "can't broadcast from (#{self.size.join(', ')}) to (#{other.size.join(', ')})"
|
62
|
+
end
|
63
|
+
else
|
64
|
+
vt = vtype() if vt.nil?
|
65
|
+
Mat.new(row_size(), col_size(), vt).fill(other)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# replace self by repeated `src'
|
70
|
+
# self will be left-top of infinitly repeated `src'
|
71
|
+
def replace_rep(src)
|
72
|
+
ri = Mat.new(row_size(), 1, :int) do |i, j|
|
73
|
+
i % src.row_size
|
74
|
+
end
|
75
|
+
ci = Mat.new(col_size(), 1, :int) do |i, j|
|
76
|
+
i % src.col_size
|
77
|
+
end
|
78
|
+
src[ri, ci] do |m|
|
79
|
+
self.copy_from(m)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# flip row, column or both axis
|
84
|
+
# `dim' Symbol specifies fliping axis (:row, :col, :both, :none are available)
|
85
|
+
def flip(dim=:both)
|
86
|
+
dim = dim.to_sym if dim.respond_to?(:to_sym)
|
87
|
+
m, n = *shape()
|
88
|
+
case dim
|
89
|
+
when :both, :b
|
90
|
+
ri = Mat.new(m, 1, :int) do |i, j|
|
91
|
+
m-i-1
|
92
|
+
end
|
93
|
+
ci = Mat.new(n, 1, :int) do |i, j|
|
94
|
+
n-i-1
|
95
|
+
end
|
96
|
+
self[ri, ci]
|
97
|
+
when :row, :r
|
98
|
+
ri = Mat.new(m, 1, :int) do |i, j|
|
99
|
+
m-i-1
|
100
|
+
end
|
101
|
+
self[ri, nil]
|
102
|
+
when :col, :c
|
103
|
+
ci = Mat.new(n, 1, :int) do |i, j|
|
104
|
+
n-i-1
|
105
|
+
end
|
106
|
+
self[nil, ci]
|
107
|
+
when :none, :n
|
108
|
+
self[]
|
109
|
+
else
|
110
|
+
raise ArgumentError, "unknown axis symbol #{dim.inspect}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def diag(k=0)
|
115
|
+
_diag_ul(k)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class << Mat
|
120
|
+
# for example, Mat.blocks([a, b], [c, d]) returns a single matrix of [a, b; c, d]
|
121
|
+
def blocks(*blocks)
|
122
|
+
vstack(*blocks.map do |row|
|
123
|
+
hstack(*row)
|
124
|
+
end)
|
125
|
+
end
|
126
|
+
|
127
|
+
def vstack(*mats)
|
128
|
+
m, n = *mats[0].shape
|
129
|
+
1.upto(mats.size-1) do |i|
|
130
|
+
mat = mats[i]
|
131
|
+
raise MismatchedDimensionError, 'column-sizes must be the same' if mat.col_size != n
|
132
|
+
m += mat.row_size
|
133
|
+
end
|
134
|
+
ret = Mat.new(m, n, mats[0].vtype)
|
135
|
+
k = 0;
|
136
|
+
n = Mat.irange(n)
|
137
|
+
mats.each do |mat|
|
138
|
+
ret[Mat.new(mat.row_size, 1, :int) do |i, j|
|
139
|
+
i+k
|
140
|
+
end, n] = mat
|
141
|
+
k += mat.row_size
|
142
|
+
end
|
143
|
+
ret
|
144
|
+
end
|
145
|
+
|
146
|
+
def hstack(*mats)
|
147
|
+
m, n = *mats[0].shape
|
148
|
+
1.upto(mats.size-1) do |i|
|
149
|
+
mat = mats[i]
|
150
|
+
raise MismatchedDimensionError, 'row-sizes must be the same' if mat.row_size != m
|
151
|
+
n += mat.col_size
|
152
|
+
end
|
153
|
+
ret = Mat.new(m, n, mats[0].vtype)
|
154
|
+
k = 0
|
155
|
+
m = Mat.irange(m)
|
156
|
+
mats.each do |mat|
|
157
|
+
ret[m, Mat.new(mat.col_size, 1, :int) do |i, j|
|
158
|
+
i+k
|
159
|
+
end] = mat
|
160
|
+
k += mat.col_size
|
161
|
+
end
|
162
|
+
ret
|
163
|
+
end
|
164
|
+
end
|
data/lib/kmat/arith.rb
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
class Mat
|
2
|
+
# to define Numeric * Mat as Mat#scalar(Numeric) remaining Mat * Mat is undefined,
|
3
|
+
# this returns non‐standard value
|
4
|
+
def coerce(other)
|
5
|
+
if caller.first[/`([^']*)'/, 1] == '*'
|
6
|
+
[self, other]
|
7
|
+
else
|
8
|
+
raise TypeError, "Mat can't be coerced into #{other.class}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
def add(other)
|
12
|
+
if other.kind_of?(Mat)
|
13
|
+
self.dup.add!(broadcast(other))
|
14
|
+
else
|
15
|
+
self.dup.s_add!(other)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
alias :+ :add
|
19
|
+
def sub(other)
|
20
|
+
if other.kind_of?(Mat)
|
21
|
+
self.dup.sub!(broadcast(other))
|
22
|
+
else
|
23
|
+
self.dup.s_sub!(other)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
alias :- :sub
|
27
|
+
def e_mul(other)
|
28
|
+
if other.kind_of?(Mat)
|
29
|
+
self.dup.e_mul!(broadcast(other))
|
30
|
+
else
|
31
|
+
self.dup.s_mul!(other)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
def e_div(other)
|
35
|
+
if other.kind_of?(Mat)
|
36
|
+
self.dup.e_div!(broadcast(other))
|
37
|
+
else
|
38
|
+
self.dup.s_div!(other)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
module MatrixProductOperator
|
42
|
+
refine Mat do
|
43
|
+
def *(other)
|
44
|
+
if other.kind_of?(Mat)
|
45
|
+
mprod(other)
|
46
|
+
else
|
47
|
+
self.dup.s_mul!(other)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
def /(other)
|
51
|
+
if other.kind_of?(Mat)
|
52
|
+
over(other)
|
53
|
+
else
|
54
|
+
self.dup.s_div!(other)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
def *(other)
|
60
|
+
if other.kind_of?(Mat)
|
61
|
+
raise ArgumentError, "Mat#* is available only for scalar multiplication. To use Mat#*(Mat) for matrix product, call `using Mat::MatrixProductOperator'"
|
62
|
+
else
|
63
|
+
self.dup.s_mul!(other)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
def scalar(alpha)
|
67
|
+
self.dup.s_mul!(alpha)
|
68
|
+
end
|
69
|
+
alias :"scalar!" :"s_mul!"
|
70
|
+
def /(other)
|
71
|
+
if other.kind_of?(Mat)
|
72
|
+
raise ArgumentError, "Mat#/ is available only for scalar multiplication with reciprocal. To use Mat#/(Mat) as an alias of Mat#over, call `using Mat::MatrixProductOperator`"
|
73
|
+
else
|
74
|
+
self.dup.s_div!(other)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def add_times(other, alpha)
|
79
|
+
if other.kind_of?(Mat)
|
80
|
+
self.dup.add_times!(broadcast(other), alpha)
|
81
|
+
else
|
82
|
+
self.dup.s_mul!(other*alpha)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def maximum(other)
|
87
|
+
if other.kind_of?(Mat)
|
88
|
+
self.dup.maximum!(broadcast(other))
|
89
|
+
else
|
90
|
+
self.dup.s_maximum!(other)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
def minimum(other)
|
94
|
+
if other.kind_of?(Mat)
|
95
|
+
self.dup.minimum!(broadcast(other))
|
96
|
+
else
|
97
|
+
self.dup.s_minimum!(other)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def pow(other)
|
102
|
+
if other.kind_of?(Mat)
|
103
|
+
self.dup.pow!(broadcast(other))
|
104
|
+
else
|
105
|
+
self.dup.s_pow!(other)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
def rpow(other)
|
109
|
+
other.dup.pow!(other.broadcast(self))
|
110
|
+
end
|
111
|
+
def **(other)
|
112
|
+
case other
|
113
|
+
when Integer
|
114
|
+
ret, temp = self.dup, self.dup
|
115
|
+
ret.eye
|
116
|
+
if 0 <= other
|
117
|
+
a = self.dup
|
118
|
+
else
|
119
|
+
a = self.inv
|
120
|
+
other = -other
|
121
|
+
end
|
122
|
+
loop do
|
123
|
+
if other % 2 == 1
|
124
|
+
temp.mprod!(ret, a)
|
125
|
+
temp, ret = ret, temp
|
126
|
+
end
|
127
|
+
other = other.div(2)
|
128
|
+
break if other == 0
|
129
|
+
temp.mprod!(a, a)
|
130
|
+
temp, a = a, temp
|
131
|
+
end
|
132
|
+
ret
|
133
|
+
when Float
|
134
|
+
if self.symmetry?
|
135
|
+
v, d = self.symmetrize.sym_evd
|
136
|
+
d.diag.s_pow!(other)
|
137
|
+
foo = v.mprod(d)
|
138
|
+
d.mprod!(foo, v.t!)
|
139
|
+
else
|
140
|
+
raise UncomputableMatrixError, "cannot compute float power of non-symmetric matrcies"
|
141
|
+
end
|
142
|
+
when Rational
|
143
|
+
self ** other.to_f
|
144
|
+
else
|
145
|
+
raise ArgumentError, "Mat powered by #{other.class} is not supported"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def hypot(other)
|
150
|
+
if other.kind_of?(Mat)
|
151
|
+
self.dup.hypot!(broadcast(other))
|
152
|
+
else
|
153
|
+
self.dup.s_hypot!(other)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
class << Mat
|
159
|
+
def max(*args)
|
160
|
+
case args.size
|
161
|
+
when 0
|
162
|
+
nil
|
163
|
+
when 1
|
164
|
+
args[0]
|
165
|
+
else
|
166
|
+
ret = args[0].dup
|
167
|
+
1.upto(args.size-1) { |i|
|
168
|
+
ret.maximum!(args[i])
|
169
|
+
}
|
170
|
+
ret
|
171
|
+
end
|
172
|
+
end
|
173
|
+
alias :maximum :max
|
174
|
+
def min(*args)
|
175
|
+
case args.size
|
176
|
+
when 0
|
177
|
+
nil
|
178
|
+
when 1
|
179
|
+
args[0]
|
180
|
+
else
|
181
|
+
ret = args[0].dup
|
182
|
+
1.upto(args.size-1) { |i|
|
183
|
+
ret.minimum!(args[i])
|
184
|
+
}
|
185
|
+
ret
|
186
|
+
end
|
187
|
+
end
|
188
|
+
alias :minimum :min
|
189
|
+
end
|