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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef1ecaaf8a71faee9a7cef26bc35fae50230224856ab5dd6186cee93e5cca69d
4
- data.tar.gz: f2e5e5944ad4832ef4ad4db05f6e94142fefb780e19282d94ece2a055cf447a0
3
+ metadata.gz: a3c8bcc6ac3a9e0e97953992655b299b74a737aa5e2e208fae45144cfdaa09d8
4
+ data.tar.gz: 0460e3eb44c6f67e22d1171e9929ebf949c69ddb6f9a9355894944006ffd7366
5
5
  SHA512:
6
- metadata.gz: 3fcf1506aa63ccbba57208e358102ae0dc537cd80b530e4b359b07d01a7cb1da11261dfd6d7028224175ae0f38014ec0373b99fc339caa2b952ad48c0fb26fd7
7
- data.tar.gz: c7a9eb8b65719571120a6a122721e1eaadc240164e182a5095ff45018453a90f4cc48f99ef219305827beace8839bdebb0d27d35dfb5f64ec64f54d5406a8910
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://travis-ci.org/ruby-numo/numo-linalg.svg?branch=master)](https://travis-ci.org/ruby-numo/numo-linalg)
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 desgin allows you to change backend libraries without re-compiling.
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
- ## Acknowledgement
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]
@@ -1,5 +1,5 @@
1
- COGEN=ruby gen/cogen.rb -l
2
- GENDEPS=gen/*.rb tmpl/*.c
1
+ COGEN=<%= "ruby #{__dir__}/gen/cogen.rb -l" %>
2
+ GENDEPS=<%= "#{__dir__}/gen/*.rb #{__dir__}/tmpl/*.c" %>
3
3
 
4
4
  <%
5
5
  srcs = %w[s d c z].map{|c| [c, "blas_#{c}.c"]}
@@ -36,5 +36,5 @@ elsif have_header("windows.h")
36
36
  end
37
37
 
38
38
  create_site_conf
39
- create_depend
39
+ create_depend(__dir__)
40
40
  create_makefile('numo/linalg/blas')
@@ -1,5 +1,5 @@
1
- COGEN=ruby gen/cogen.rb -l
2
- GENDEPS=gen/*.rb tmpl/*.c
1
+ COGEN=<%= "ruby #{__dir__}/gen/cogen.rb -l" %>
2
+ GENDEPS=<%= "#{__dir__}/gen/*.rb #{__dir__}/tmpl/*.c" %>
3
3
 
4
4
  <%
5
5
  srcs = %w[s d c z].map{|c| [c, "lapack_#{c}.c"]}
@@ -36,5 +36,5 @@ elsif have_header("windows.h")
36
36
  exit(1) unless have_func("LoadLibrary")
37
37
  end
38
38
 
39
- create_depend
39
+ create_depend(__dir__)
40
40
  create_makefile('numo/linalg/lapack')
@@ -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 = File.join(Dir.pwd, "depend")
75
+ dep_path = "#{base_dir}/depend"
76
76
  File.open(dep_path, "w") do |dep|
77
- dep_erb_path = File.join(Dir.pwd, "depend.erb")
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/openblas/lib', '/opt/openblas/lib64', '/usr/local/opt/openblas/lib']
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
- case RbConfig::CONFIG['host_os']
60
- when /mswin|msys|mingw|cygwin/
61
- 'dll'
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
- [l.to_sym, lib_dirs.map { |d| "#{d}/lib#{l}.#{lib_ext}" }
73
- .keep_if { |f| File.exist?(f) }.first]
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
@@ -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,*args)
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
- send(fn,*args)
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,*args)
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
- send(fn,*args)
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
- Blas.call(:gemv, b, a, trans:'t')
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
- Blas.call(:gemv, a, b)
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
- Blas.call(:gemm, a, b)
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
- if true
1008
- resids = (x[false,n..-1,true].abs**2).sum(axis:-2)
1009
- else
1010
- resids = x[false,0,true].new_zeros
1011
- mask = rank.eq(n)
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)
@@ -1,5 +1,5 @@
1
1
  module Numo
2
2
  module Linalg
3
- VERSION = "0.1.3"
3
+ VERSION = "0.1.7"
4
4
  end
5
5
  end
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", "~> 1.3"
24
- spec.add_development_dependency "rake", "~> 12.0"
25
- spec.add_development_dependency "rspec", "~> 3.0"
26
- spec.add_runtime_dependency "numo-narray", ">= 0.9.0.7"
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.3
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: 2018-08-23 00:00:00.000000000 Z
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: '1.3'
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: '1.3'
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: '12.0'
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: '12.0'
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: '3.0'
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: '3.0'
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.0.7
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.0.7
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
- rubyforge_project:
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