numo-linalg 0.1.3 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -7
- data/Rakefile +17 -0
- data/ext/numo/linalg/blas/depend.erb +2 -2
- data/ext/numo/linalg/blas/extconf.rb +1 -1
- data/ext/numo/linalg/lapack/depend.erb +2 -2
- data/ext/numo/linalg/lapack/extconf.rb +1 -1
- data/ext/numo/linalg/mkmf_linalg.rb +3 -3
- data/lib/numo/linalg/autoloader.rb +63 -6
- data/lib/numo/linalg/function.rb +149 -15
- data/lib/numo/linalg/version.rb +1 -1
- data/numo-linalg.gemspec +5 -4
- data/spec/linalg/autoloader_spec.rb +0 -17
- data/spec/linalg/function/expm_spec.rb +36 -0
- data/spec/linalg/function/null_space_spec.rb +41 -0
- data/spec/linalg/function/orth_spec.rb +43 -0
- metadata +37 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3c8bcc6ac3a9e0e97953992655b299b74a737aa5e2e208fae45144cfdaa09d8
|
4
|
+
data.tar.gz: 0460e3eb44c6f67e22d1171e9929ebf949c69ddb6f9a9355894944006ffd7366
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 918dc720d8d187174bf1bc04b88567b2cc6268c961b53187060ce25ff20e1b38050a58e3ed29d0993a1c4a1637193e0891272ee8628a19b7883b7136905b581c
|
7
|
+
data.tar.gz: a2eeefea7aca430c1ba6dc4d777fc87a5c12e22cd27ad5d9a53de0ce7a19b4e2e4f67add3cc1d45cb8989ea2073fb87cc85c6cfeb5c4df1a346de258a47f947c
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Numo::Linalg : Linear Algebra library with BLAS/LAPACK binding to Numo::NArray
|
2
2
|
|
3
3
|
[![Binder](http://mybinder.org/badge.svg)](http://mybinder.org/repo/ruby-numo/numo-linalg)
|
4
|
-
[![Build Status](https://
|
4
|
+
[![Build Status](https://github.com/ruby-numo/numo-linalg/workflows/build/badge.svg)](https://github.com/ruby-numo/numo-linalg/actions)
|
5
5
|
|
6
6
|
[GitHub](https://github.com/ruby-numo/numo-linalg) |
|
7
7
|
[RubyGems](https://rubygems.org/gems/numo-linalg)
|
@@ -11,15 +11,15 @@ Under development!
|
|
11
11
|
## Introduction
|
12
12
|
|
13
13
|
This is a binding of BLAS/LAPACK for Numo::NArray using dynamic linking loader.
|
14
|
-
This
|
14
|
+
This design allows you to change backend libraries without re-compiling.
|
15
15
|
|
16
|
-
### [Numo::Linalg API](http://ruby-numo.github.io/linalg/yard/Numo/Linalg.html)
|
16
|
+
### [Numo::Linalg API](http://ruby-numo.github.io/numo-linalg/yard/Numo/Linalg.html)
|
17
17
|
|
18
18
|
* Matrix and vector products
|
19
19
|
* dot, matmul
|
20
20
|
* Decomposition
|
21
21
|
* lu, lu\_fact, lu\_inv, lu\_solve, ldl, cholesky, cho\_fact, cho\_inv, cho\_solve,
|
22
|
-
qr, svd, svdvals
|
22
|
+
qr, svd, svdvals, orth, null_space
|
23
23
|
* Matrix eigenvalues
|
24
24
|
* eig, eigh, eigvals, eigvalsh
|
25
25
|
* Norms and other numbers
|
@@ -29,8 +29,8 @@ This desgin allows you to change backend libraries without re-compiling.
|
|
29
29
|
|
30
30
|
### Low-level modules
|
31
31
|
|
32
|
-
* [Numo::Linalg::Blas](http://ruby-numo.github.io/linalg/yard/Numo/Linalg/Blas.html) - Low-level BLAS functions
|
33
|
-
* [Numo::Linalg::Lapack](http://ruby-numo.github.io/linalg/yard/Numo/Linalg/Lapack.html) - Low-level LAPACK functions
|
32
|
+
* [Numo::Linalg::Blas](http://ruby-numo.github.io/numo-linalg/yard/Numo/Linalg/Blas.html) - Low-level BLAS functions
|
33
|
+
* [Numo::Linalg::Lapack](http://ruby-numo.github.io/numo-linalg/yard/Numo/Linalg/Lapack.html) - Low-level LAPACK functions
|
34
34
|
|
35
35
|
## Installation
|
36
36
|
|
@@ -82,7 +82,7 @@ require "numo/linalg"
|
|
82
82
|
* Makoto Kishimoto
|
83
83
|
* Atsushi Tatsuma
|
84
84
|
|
85
|
-
##
|
85
|
+
## Acknowledgments
|
86
86
|
|
87
87
|
* This work is partly supported by 2016 Ruby Association Grant.
|
88
88
|
|
data/Rakefile
CHANGED
@@ -16,3 +16,20 @@ end
|
|
16
16
|
task :cleandoc do
|
17
17
|
sh "rm -r yard .yardoc"
|
18
18
|
end
|
19
|
+
|
20
|
+
require 'rake/extensiontask'
|
21
|
+
Rake::ExtensionTask.new 'numo/linalg/blas'
|
22
|
+
Rake::ExtensionTask.new 'numo/linalg/lapack'
|
23
|
+
|
24
|
+
CLOBBER.include('lib/numo/linalg/site_conf.rb')
|
25
|
+
task :compile do
|
26
|
+
site_conf = Dir['./tmp/**/numo/linalg/blas/**/lib/site_conf.rb'].first
|
27
|
+
cp site_conf, 'lib/numo/linalg'
|
28
|
+
end
|
29
|
+
|
30
|
+
require 'rspec/core/rake_task'
|
31
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
32
|
+
t.rspec_opts = '--color --format documentation --require spec_helper'
|
33
|
+
end
|
34
|
+
|
35
|
+
task :default => [:clobber, :compile, :spec]
|
@@ -69,12 +69,12 @@ def find_libnarray_a
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
-
def create_depend
|
72
|
+
def create_depend(base_dir)
|
73
73
|
require 'erb'
|
74
74
|
message "creating depend\n"
|
75
|
-
dep_path =
|
75
|
+
dep_path = "#{base_dir}/depend"
|
76
76
|
File.open(dep_path, "w") do |dep|
|
77
|
-
dep_erb_path =
|
77
|
+
dep_erb_path = "#{base_dir}/depend.erb"
|
78
78
|
File.open(dep_erb_path, "r") do |dep_erb|
|
79
79
|
erb = ERB.new(dep_erb.read)
|
80
80
|
erb.filename = dep_erb_path
|
@@ -19,15 +19,23 @@ module Numo
|
|
19
19
|
# @return [String] name of loaded backend library (mkl/openblas/lapack)
|
20
20
|
def load_library
|
21
21
|
mkl_dirs = ['/opt/intel/lib', '/opt/intel/lib64', '/opt/intel/mkl/lib', '/opt/intel/mkl/lib64']
|
22
|
-
openblas_dirs = ['/opt/
|
22
|
+
openblas_dirs = ['/opt/OpenBLAS/lib', '/opt/OpenBLAS/lib64', '/opt/openblas/lib', '/opt/openblas/lib64',
|
23
|
+
'/usr/local/opt/openblas/lib']
|
23
24
|
atlas_dirs = ['/opt/atlas/lib', '/opt/atlas/lib64',
|
24
25
|
'/usr/lib/atlas', '/usr/lib64/atlas', '/usr/local/opt/atlas/lib']
|
25
26
|
lapacke_dirs = ['/opt/lapack/lib', '/opt/lapack/lib64', '/opt/local/lib/lapack',
|
26
27
|
'/usr/local/opt/lapack/lib']
|
27
28
|
opt_dirs = ['/opt/local/lib', '/opt/local/lib64', '/opt/lib', '/opt/lib64']
|
28
29
|
base_dirs = ['/usr/local/lib', '/usr/local/lib64', '/usr/lib', '/usr/lib64']
|
30
|
+
base_dirs.concat(Dir["/usr/lib/#{RbConfig::CONFIG['host_cpu']}-*"])
|
29
31
|
base_dirs.unshift(*ENV['LD_LIBRARY_PATH'].split(':')) unless ENV['LD_LIBRARY_PATH'].nil?
|
30
32
|
|
33
|
+
select_dirs(base_dirs)
|
34
|
+
select_dirs(opt_dirs)
|
35
|
+
select_dirs(lapacke_dirs)
|
36
|
+
select_dirs(atlas_dirs)
|
37
|
+
select_dirs(mkl_dirs)
|
38
|
+
|
31
39
|
mkl_libs = find_mkl_libs([*base_dirs, *opt_dirs, *mkl_dirs])
|
32
40
|
openblas_libs = find_openblas_libs([*base_dirs, *opt_dirs, *openblas_dirs])
|
33
41
|
atlas_libs = find_atlas_libs([*base_dirs, *opt_dirs, *atlas_dirs, *lapacke_dirs])
|
@@ -56,9 +64,14 @@ module Numo
|
|
56
64
|
end
|
57
65
|
|
58
66
|
def detect_library_extension
|
59
|
-
|
60
|
-
|
61
|
-
|
67
|
+
# Ruby >= 2.5 provides SOEXT in rbconfig
|
68
|
+
so_ext = RbConfig::CONFIG["SOEXT"]
|
69
|
+
return so_ext if so_ext
|
70
|
+
|
71
|
+
return 'dll' if windows?
|
72
|
+
|
73
|
+
# For Ruby < 2.5, we use RUBY_PLATFORM
|
74
|
+
case RUBY_PLATFORM
|
62
75
|
when /darwin|mac os/
|
63
76
|
'dylib'
|
64
77
|
else
|
@@ -66,11 +79,55 @@ module Numo
|
|
66
79
|
end
|
67
80
|
end
|
68
81
|
|
82
|
+
def windows?
|
83
|
+
case RUBY_PLATFORM
|
84
|
+
when /mswin|msys|mingw|cygwin/
|
85
|
+
true
|
86
|
+
else
|
87
|
+
false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def select_dirs(dirs)
|
92
|
+
dirs.select!{|d| Dir.exist?(d)}
|
93
|
+
end
|
94
|
+
|
69
95
|
def find_libs(lib_names, lib_dirs)
|
70
96
|
lib_ext = detect_library_extension
|
71
97
|
lib_arr = lib_names.map do |l|
|
72
|
-
|
73
|
-
|
98
|
+
x = nil
|
99
|
+
if windows?
|
100
|
+
# On Windows, try to search the default DLL search path at first
|
101
|
+
[
|
102
|
+
"lib#{l}.#{lib_ext}",
|
103
|
+
"lib#{l}64.#{lib_ext}"
|
104
|
+
].each do |filename|
|
105
|
+
begin
|
106
|
+
Fiddle.dlopen(filename).close
|
107
|
+
rescue Fiddle::DLError
|
108
|
+
x = nil
|
109
|
+
else
|
110
|
+
x = filename
|
111
|
+
break
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
if x.nil?
|
116
|
+
# Search in the candidate directories
|
117
|
+
lib_dirs.find do |d|
|
118
|
+
x = Dir.glob("#{d}/lib#{l}{,64}.#{lib_ext}{,.*}").find do |lib|
|
119
|
+
begin
|
120
|
+
Fiddle.dlopen(lib).close
|
121
|
+
rescue Fiddle::DLError
|
122
|
+
false
|
123
|
+
else
|
124
|
+
true
|
125
|
+
end
|
126
|
+
end
|
127
|
+
break if x
|
128
|
+
end
|
129
|
+
end
|
130
|
+
[l.to_sym, x]
|
74
131
|
end
|
75
132
|
Hash[*lib_arr.flatten]
|
76
133
|
end
|
data/lib/numo/linalg/function.rb
CHANGED
@@ -12,12 +12,19 @@ module Numo; module Linalg
|
|
12
12
|
# defined from data-types of arguments.
|
13
13
|
# @param [Symbol] func function name without BLAS char.
|
14
14
|
# @param args arguments passed to Blas function.
|
15
|
+
# @param kwargs keyword arguments passed to Blas function.
|
15
16
|
# @example
|
16
17
|
# c = Numo::Linalg::Blas.call(:gemm, a, b)
|
17
|
-
def self.call(func
|
18
|
+
def self.call(func, *args, **kwargs)
|
18
19
|
fn = (Linalg.blas_char(*args) + func.to_s).to_sym
|
19
20
|
fn = FIXNAME[fn] || fn
|
20
|
-
|
21
|
+
if kwargs.empty?
|
22
|
+
# This conditional branch is necessary to prevent ArgumentError
|
23
|
+
# that occurs in Ruby 2.6 or earlier.
|
24
|
+
send(fn, *args)
|
25
|
+
else
|
26
|
+
send(fn, *args, **kwargs)
|
27
|
+
end
|
21
28
|
end
|
22
29
|
|
23
30
|
end
|
@@ -34,12 +41,19 @@ module Numo; module Linalg
|
|
34
41
|
# defined from data-types of arguments.
|
35
42
|
# @param [Symbol,String] func function name without BLAS char.
|
36
43
|
# @param args arguments passed to Lapack function.
|
44
|
+
# @param kwargs keyword arguments passed to Lapack function.
|
37
45
|
# @example
|
38
46
|
# s = Numo::Linalg::Lapack.call(:gesv, a)
|
39
|
-
def self.call(func
|
47
|
+
def self.call(func, *args, **kwargs)
|
40
48
|
fn = (Linalg.blas_char(*args) + func.to_s).to_sym
|
41
49
|
fn = FIXNAME[fn] || fn
|
42
|
-
|
50
|
+
if kwargs.empty?
|
51
|
+
# This conditional branch is necessary to prevent ArgumentError
|
52
|
+
# that occurs in Ruby 2.6 or earlier.
|
53
|
+
send(fn, *args)
|
54
|
+
else
|
55
|
+
send(fn, *args, **kwargs)
|
56
|
+
end
|
43
57
|
end
|
44
58
|
|
45
59
|
end
|
@@ -89,14 +103,58 @@ module Numo; module Linalg
|
|
89
103
|
func = blas_char(a, b) =~ /c|z/ ? :dotu : :dot
|
90
104
|
Blas.call(func, a, b)
|
91
105
|
else
|
92
|
-
|
106
|
+
if b.contiguous?
|
107
|
+
trans = 't'
|
108
|
+
else
|
109
|
+
if b.fortran_contiguous?
|
110
|
+
trans = 'n'
|
111
|
+
b = b.transpose
|
112
|
+
else
|
113
|
+
trans = 't'
|
114
|
+
b = b.dup
|
115
|
+
end
|
116
|
+
end
|
117
|
+
Blas.call(:gemv, b, a, trans:trans)
|
93
118
|
end
|
94
119
|
else
|
95
120
|
case b.ndim
|
96
121
|
when 1
|
97
|
-
|
122
|
+
if a.contiguous?
|
123
|
+
trans = 'n'
|
124
|
+
else
|
125
|
+
if a.fortran_contiguous?
|
126
|
+
trans = 't'
|
127
|
+
a = a.transpose
|
128
|
+
else
|
129
|
+
trans = 'n'
|
130
|
+
a = a.dup
|
131
|
+
end
|
132
|
+
end
|
133
|
+
Blas.call(:gemv, a, b, trans:trans)
|
98
134
|
else
|
99
|
-
|
135
|
+
if a.contiguous?
|
136
|
+
transa = 'n'
|
137
|
+
else
|
138
|
+
if a.fortran_contiguous?
|
139
|
+
transa = 't'
|
140
|
+
a = a.transpose
|
141
|
+
else
|
142
|
+
transa = 'n'
|
143
|
+
a = a.dup
|
144
|
+
end
|
145
|
+
end
|
146
|
+
if b.contiguous?
|
147
|
+
transb = 'n'
|
148
|
+
else
|
149
|
+
if b.fortran_contiguous?
|
150
|
+
transb='t'
|
151
|
+
b = b.transpose
|
152
|
+
else
|
153
|
+
transb='n'
|
154
|
+
b = b.dup
|
155
|
+
end
|
156
|
+
end
|
157
|
+
Blas.call(:gemm, a, b, transa:transa, transb:transb)
|
100
158
|
end
|
101
159
|
end
|
102
160
|
end
|
@@ -296,6 +354,42 @@ module Numo; module Linalg
|
|
296
354
|
end
|
297
355
|
end
|
298
356
|
|
357
|
+
# Computes an orthonormal basis for the range of matrix A.
|
358
|
+
#
|
359
|
+
# @param a [Numo::NArray] m-by-n matrix A (>= 2-dimensional NArray).
|
360
|
+
# @param rcond [Float] (optional)
|
361
|
+
# rcond is used to determine the effective rank of A.
|
362
|
+
# Singular values `s[i] <= rcond * s.max` are treated as zero.
|
363
|
+
# If rcond < 0, machine precision is used instead.
|
364
|
+
# @return [Numo::NArray] The orthonormal basis for the range of matrix A.
|
365
|
+
|
366
|
+
def orth(a, rcond: -1)
|
367
|
+
raise NArray::ShapeError, '2-d array is required' if a.ndim < 2
|
368
|
+
s, u, = svd(a)
|
369
|
+
tol = s.max * (rcond.nil? || rcond < 0 ? a.class::EPSILON * a.shape.max : rcond)
|
370
|
+
k = (s > tol).count
|
371
|
+
u[true, 0...k]
|
372
|
+
end
|
373
|
+
|
374
|
+
# Computes an orthonormal basis for the null space of matrix A.
|
375
|
+
#
|
376
|
+
# @param a [Numo::NArray] m-by-n matrix A (>= 2-dimensional NArray).
|
377
|
+
# @param rcond [Float] (optional)
|
378
|
+
# rcond is used to determine the effective rank of A.
|
379
|
+
# Singular values `s[i] <= rcond * s.max` are treated as zero.
|
380
|
+
# If rcond < 0, machine precision is used instead.
|
381
|
+
# @return [Numo::NArray] The orthonormal basis for the null space of matrix A.
|
382
|
+
|
383
|
+
def null_space(a, rcond: -1)
|
384
|
+
raise NArray::ShapeError, '2-d array is required' if a.ndim < 2
|
385
|
+
s, _u, vh = svd(a)
|
386
|
+
tol = s.max * (rcond.nil? || rcond < 0 ? a.class::EPSILON * a.shape.max : rcond)
|
387
|
+
k = (s > tol).count
|
388
|
+
return a.class.new if k == vh.shape[0]
|
389
|
+
r = vh[k..-1, true].transpose.dup
|
390
|
+
blas_char(vh) =~ /c|z/ ? r.conj : r
|
391
|
+
end
|
392
|
+
|
299
393
|
# Computes an LU factorization of a M-by-N matrix A
|
300
394
|
# using partial pivoting with row interchanges.
|
301
395
|
#
|
@@ -1004,14 +1098,11 @@ module Numo; module Linalg
|
|
1004
1098
|
when n
|
1005
1099
|
resids = (x[n..-1,true].abs**2).sum(axis:0)
|
1006
1100
|
when NArray
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
# NArray does not suppurt this yet.
|
1013
|
-
resids[mask,true] = (x[mask,n..-1,true].abs**2).sum(axis:-2)
|
1014
|
-
end
|
1101
|
+
resids = (x[false,n..-1,true].abs**2).sum(axis:-2)
|
1102
|
+
## NArray does not suppurt this yet.
|
1103
|
+
# resids = x[false,0,true].new_zeros
|
1104
|
+
# mask = rank.eq(n)
|
1105
|
+
# resids[mask,true] = (x[mask,n..-1,true].abs**2).sum(axis:-2)
|
1015
1106
|
end
|
1016
1107
|
end
|
1017
1108
|
x = x[false,0...n,true]
|
@@ -1083,6 +1174,49 @@ module Numo; module Linalg
|
|
1083
1174
|
end
|
1084
1175
|
end
|
1085
1176
|
|
1177
|
+
# Compute the matrix exponential using Pade approximation method.
|
1178
|
+
#
|
1179
|
+
# @param a [Numo::NArray] square matrix (>= 2-dimensinal NArray)
|
1180
|
+
# @param ord [Integer] order of approximation
|
1181
|
+
# @return [Numo::NArray]
|
1182
|
+
# @example
|
1183
|
+
# a = Numo::Linalg.expm(Numo::DFloat.zeros([2,2]))
|
1184
|
+
# => Numo::DFloat#shape=[2,2]
|
1185
|
+
# [[1, 0],
|
1186
|
+
# [0, 1]]
|
1187
|
+
# b = Numo::Linalg.expm(Numo::DFloat[[1, 2], [-1, 3]] * Complex::I)
|
1188
|
+
# => Numo::DComplex#shape=[2,2]
|
1189
|
+
# [[0.426459+1.89218i, -2.13721-0.978113i],
|
1190
|
+
# [1.06861+0.489056i, -1.71076+0.914063i]]
|
1191
|
+
|
1192
|
+
def expm(a, ord=8)
|
1193
|
+
raise NArray::ShapeError, 'matrix a is not square matrix' if a.shape[0] != a.shape[1]
|
1194
|
+
|
1195
|
+
inf_norm = norm(a, 'inf')
|
1196
|
+
n_squarings = inf_norm.zero? ? 0 : [0, Math.log2(inf_norm).ceil.to_i].max
|
1197
|
+
a = a / (2**n_squarings)
|
1198
|
+
|
1199
|
+
sz_mat = a.shape[0]
|
1200
|
+
c = 1
|
1201
|
+
s = -1
|
1202
|
+
x = Numo::DFloat.eye(sz_mat)
|
1203
|
+
n = Numo::DFloat.eye(sz_mat)
|
1204
|
+
d = Numo::DFloat.eye(sz_mat)
|
1205
|
+
|
1206
|
+
(1..ord).each do |k|
|
1207
|
+
c *= (ord - k + 1).fdiv((2 * ord - k + 1) * k)
|
1208
|
+
x = a.dot(x)
|
1209
|
+
cx = c * x
|
1210
|
+
n += cx
|
1211
|
+
d += s * cx
|
1212
|
+
s *= -1
|
1213
|
+
end
|
1214
|
+
|
1215
|
+
res = solve(d, n)
|
1216
|
+
n_squarings.times { res = res.dot(res) }
|
1217
|
+
res
|
1218
|
+
end
|
1219
|
+
|
1086
1220
|
# @!visibility private
|
1087
1221
|
def _make_complex_eigvecs(w, vin) # :nodoc:
|
1088
1222
|
v = w.class.cast(vin)
|
data/lib/numo/linalg/version.rb
CHANGED
data/numo-linalg.gemspec
CHANGED
@@ -20,8 +20,9 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.extensions = ["ext/numo/linalg/blas/extconf.rb",
|
21
21
|
"ext/numo/linalg/lapack/extconf.rb"]
|
22
22
|
|
23
|
-
spec.add_development_dependency "bundler"
|
24
|
-
spec.add_development_dependency "rake"
|
25
|
-
spec.add_development_dependency "
|
26
|
-
spec.
|
23
|
+
spec.add_development_dependency "bundler"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "rake-compiler"
|
26
|
+
spec.add_development_dependency "rspec"
|
27
|
+
spec.add_runtime_dependency "numo-narray", ">= 0.9.1.4"
|
27
28
|
end
|
@@ -7,21 +7,4 @@ RSpec.describe Numo::Linalg::Autoloader do
|
|
7
7
|
expect { described_class.load_library }.to_not raise_error
|
8
8
|
expect(described_class.libs).to_not be_nil
|
9
9
|
end
|
10
|
-
|
11
|
-
describe 'private method' do
|
12
|
-
let(:autoloader) do
|
13
|
-
class DummyLoader
|
14
|
-
include Numo::Linalg::Autoloader
|
15
|
-
end
|
16
|
-
DummyLoader.new
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'finds BLAS/LAPACK libraries' do
|
20
|
-
lapack_libs = autoloader.send(:find_lapack_libs, ['/usr/lib', '/usr/local/lib', '/usr/local/opt/lapack/lib'])
|
21
|
-
expect(lapack_libs[:blas]).to_not be_nil
|
22
|
-
expect(lapack_libs[:cblas]).to_not be_nil
|
23
|
-
expect(lapack_libs[:lapack]).to_not be_nil
|
24
|
-
expect(lapack_libs[:lapacke]).to_not be_nil
|
25
|
-
end
|
26
|
-
end
|
27
10
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Numo::Linalg do
|
6
|
+
describe 'det' do
|
7
|
+
it 'raises ShapeError given a rectangular matrix as matrix A' do
|
8
|
+
expect { described_class.expm(Numo::DFloat.new(2, 3).rand) }.to raise_error(Numo::NArray::ShapeError)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'calculates matrix exponential of matrix without full eigenvectors' do
|
12
|
+
e = Math.exp(1)
|
13
|
+
err = (described_class.expm(Numo::DFloat[[1, 2], [0, 1]]) - Numo::DFloat[[e, 2 * e], [0, e]]).abs.sum(1).max
|
14
|
+
expect(err).to be < 1e-6
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'calculates matrix exponential for matrix with large norm' do
|
18
|
+
tmp = Numo::DFloat[[-0.0995741, 0.0746806], [-0.199148, 0.149361]]
|
19
|
+
err = (described_class.expm(Numo::DFloat[[-147, 72], [-192, 93]]) - tmp).abs.sum(1).max
|
20
|
+
expect(err).to be < 1e-6
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'calculates matrix exponential for diagonal matrix' do
|
24
|
+
err = (described_class.expm(Numo::DFloat[2, 3].diag) - Numo::NMath.exp(Numo::DFloat[2, 3]).diag).abs.sum(1).max
|
25
|
+
expect(err).to be < 1e-6
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'returns identity matrix when given zero matrix' do
|
29
|
+
expect(described_class.expm(Numo::DFloat.zeros([2, 2]))).to eq(Numo::DFloat.eye(2))
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'returns complex matrix when given complex matrix' do
|
33
|
+
expect(described_class.expm(rand_rect_complex_mat(2, 2))).to be_a(Numo::DComplex)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Numo::Linalg do
|
6
|
+
describe 'null_space' do
|
7
|
+
let(:r) { 2 }
|
8
|
+
let(:m) { 6 }
|
9
|
+
let(:n) { 4 }
|
10
|
+
let(:mat_a) { rand_rect_real_mat(m, r).dot(rand_rect_real_mat(r, n)) }
|
11
|
+
let(:mat_b) { rand_rect_complex_mat(m, r).dot(rand_rect_complex_mat(r, n)) }
|
12
|
+
let(:mat_c) { mat_a + 1.0e-6 * Numo::DFloat.new(m, n).rand }
|
13
|
+
|
14
|
+
it 'raises ShapeError given a vector' do
|
15
|
+
expect { described_class.lu(Numo::DFloat.new(3).rand) }.to raise_error(Numo::NArray::ShapeError)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'calculates an orthonormal basis for the null space of a real matrix' do
|
19
|
+
basis = described_class.null_space(mat_a)
|
20
|
+
expect(basis.shape[0]).to eq(n)
|
21
|
+
expect(basis.shape[1]).to eq(r)
|
22
|
+
expect((basis.transpose.dot(basis) - Numo::DFloat.eye(r)).abs.max).to be < ERR_TOL
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'calculates an orthonormal basis for the null space of a complex matrix' do
|
26
|
+
basis = described_class.null_space(mat_b)
|
27
|
+
expect(basis.shape[0]).to eq(n)
|
28
|
+
expect(basis.shape[1]).to eq(r)
|
29
|
+
expect((basis.transpose.conj.dot(basis) - Numo::DFloat.eye(r)).abs.max).to be < ERR_TOL
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'calculates an orthonormal basis according to the given rcond value' do
|
33
|
+
basis = described_class.null_space(mat_c, rcond: 1.0e-4)
|
34
|
+
expect(basis.shape[0]).to eq(n)
|
35
|
+
expect(basis.shape[1]).to eq(r)
|
36
|
+
expect((basis.transpose.dot(basis) - Numo::DFloat.eye(r)).abs.max).to be < ERR_TOL
|
37
|
+
basis = described_class.null_space(mat_c, rcond: 1.0e-8)
|
38
|
+
expect(basis.shape).to be_empty
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe Numo::Linalg do
|
6
|
+
describe 'orth' do
|
7
|
+
let(:r) { 2 }
|
8
|
+
let(:m) { 6 }
|
9
|
+
let(:n) { 3 }
|
10
|
+
let(:mat_a) { rand_rect_real_mat(m, r).dot(rand_rect_real_mat(r, n)) }
|
11
|
+
let(:mat_b) { rand_rect_complex_mat(m, r).dot(rand_rect_complex_mat(r, n)) }
|
12
|
+
let(:mat_c) { mat_a + 1.0e-6 * Numo::DFloat.new(m, n).rand }
|
13
|
+
|
14
|
+
it 'raises ShapeError given a vector' do
|
15
|
+
expect { described_class.lu(Numo::DFloat.new(3).rand) }.to raise_error(Numo::NArray::ShapeError)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'calculates an orthonormal basis for the range of a real matrix' do
|
19
|
+
basis = described_class.orth(mat_a)
|
20
|
+
expect(basis.shape[0]).to eq(m)
|
21
|
+
expect(basis.shape[1]).to eq(r)
|
22
|
+
expect((basis.transpose.dot(basis) - Numo::DFloat.eye(r)).abs.max).to be < ERR_TOL
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'calculates an orthonormal basis for the range of a complex matrix' do
|
26
|
+
basis = described_class.orth(mat_b)
|
27
|
+
expect(basis.shape[0]).to eq(m)
|
28
|
+
expect(basis.shape[1]).to eq(r)
|
29
|
+
expect((basis.transpose.conj.dot(basis) - Numo::DFloat.eye(r)).abs.max).to be < ERR_TOL
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'calculates an orthonormal basis according to the given rcond value' do
|
33
|
+
basis = described_class.orth(mat_c, rcond: 1.0e-4)
|
34
|
+
expect(basis.shape[0]).to eq(m)
|
35
|
+
expect(basis.shape[1]).to eq(r)
|
36
|
+
expect((basis.transpose.dot(basis) - Numo::DFloat.eye(r)).abs.max).to be < ERR_TOL
|
37
|
+
basis = described_class.orth(mat_c, rcond: 1.0e-8)
|
38
|
+
expect(basis.shape[0]).to eq(m)
|
39
|
+
expect(basis.shape[1]).to eq(n)
|
40
|
+
expect((basis.transpose.dot(basis) - Numo::DFloat.eye(n)).abs.max).to be < ERR_TOL
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: numo-linalg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Masahiro TANAKA
|
@@ -9,64 +9,78 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2021-07-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - "
|
18
|
+
- - ">="
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '
|
20
|
+
version: '0'
|
21
21
|
type: :development
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- - "
|
25
|
+
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: '
|
27
|
+
version: '0'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: rake
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- - "
|
32
|
+
- - ">="
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '
|
34
|
+
version: '0'
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- - "
|
39
|
+
- - ">="
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: '
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rake-compiler
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
42
56
|
- !ruby/object:Gem::Dependency
|
43
57
|
name: rspec
|
44
58
|
requirement: !ruby/object:Gem::Requirement
|
45
59
|
requirements:
|
46
|
-
- - "
|
60
|
+
- - ">="
|
47
61
|
- !ruby/object:Gem::Version
|
48
|
-
version: '
|
62
|
+
version: '0'
|
49
63
|
type: :development
|
50
64
|
prerelease: false
|
51
65
|
version_requirements: !ruby/object:Gem::Requirement
|
52
66
|
requirements:
|
53
|
-
- - "
|
67
|
+
- - ">="
|
54
68
|
- !ruby/object:Gem::Version
|
55
|
-
version: '
|
69
|
+
version: '0'
|
56
70
|
- !ruby/object:Gem::Dependency
|
57
71
|
name: numo-narray
|
58
72
|
requirement: !ruby/object:Gem::Requirement
|
59
73
|
requirements:
|
60
74
|
- - ">="
|
61
75
|
- !ruby/object:Gem::Version
|
62
|
-
version: 0.9.
|
76
|
+
version: 0.9.1.4
|
63
77
|
type: :runtime
|
64
78
|
prerelease: false
|
65
79
|
version_requirements: !ruby/object:Gem::Requirement
|
66
80
|
requirements:
|
67
81
|
- - ">="
|
68
82
|
- !ruby/object:Gem::Version
|
69
|
-
version: 0.9.
|
83
|
+
version: 0.9.1.4
|
70
84
|
description: Ruby/Numo Linear Algebra library with interface to BLAS/LAPACK.
|
71
85
|
email:
|
72
86
|
- masa16.tanaka@gmail.com
|
@@ -174,6 +188,7 @@ files:
|
|
174
188
|
- spec/linalg/function/eigh_spec.rb
|
175
189
|
- spec/linalg/function/eigvals_spec.rb
|
176
190
|
- spec/linalg/function/eigvalsh_spec.rb
|
191
|
+
- spec/linalg/function/expm_spec.rb
|
177
192
|
- spec/linalg/function/inv_spec.rb
|
178
193
|
- spec/linalg/function/ldl_spec.rb
|
179
194
|
- spec/linalg/function/lstsq_spec.rb
|
@@ -185,6 +200,8 @@ files:
|
|
185
200
|
- spec/linalg/function/matrix_power_spec.rb
|
186
201
|
- spec/linalg/function/matrix_rank_spec.rb
|
187
202
|
- spec/linalg/function/norm_spec.rb
|
203
|
+
- spec/linalg/function/null_space_spec.rb
|
204
|
+
- spec/linalg/function/orth_spec.rb
|
188
205
|
- spec/linalg/function/pinv_spec.rb
|
189
206
|
- spec/linalg/function/qr_spec.rb
|
190
207
|
- spec/linalg/function/slogdet_spec.rb
|
@@ -211,8 +228,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
211
228
|
- !ruby/object:Gem::Version
|
212
229
|
version: '0'
|
213
230
|
requirements: []
|
214
|
-
|
215
|
-
rubygems_version: 2.7.3
|
231
|
+
rubygems_version: 3.1.2
|
216
232
|
signing_key:
|
217
233
|
specification_version: 4
|
218
234
|
summary: Ruby/Numo Linear Algebra library with BLAS/LAPACK
|
@@ -229,6 +245,7 @@ test_files:
|
|
229
245
|
- spec/linalg/function/eigh_spec.rb
|
230
246
|
- spec/linalg/function/eigvals_spec.rb
|
231
247
|
- spec/linalg/function/eigvalsh_spec.rb
|
248
|
+
- spec/linalg/function/expm_spec.rb
|
232
249
|
- spec/linalg/function/inv_spec.rb
|
233
250
|
- spec/linalg/function/ldl_spec.rb
|
234
251
|
- spec/linalg/function/lstsq_spec.rb
|
@@ -240,6 +257,8 @@ test_files:
|
|
240
257
|
- spec/linalg/function/matrix_power_spec.rb
|
241
258
|
- spec/linalg/function/matrix_rank_spec.rb
|
242
259
|
- spec/linalg/function/norm_spec.rb
|
260
|
+
- spec/linalg/function/null_space_spec.rb
|
261
|
+
- spec/linalg/function/orth_spec.rb
|
243
262
|
- spec/linalg/function/pinv_spec.rb
|
244
263
|
- spec/linalg/function/qr_spec.rb
|
245
264
|
- spec/linalg/function/slogdet_spec.rb
|