pnmatrix 1.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/ext/nmatrix/binary_format.txt +53 -0
- data/ext/nmatrix/data/complex.h +388 -0
- data/ext/nmatrix/data/data.cpp +274 -0
- data/ext/nmatrix/data/data.h +651 -0
- data/ext/nmatrix/data/meta.h +64 -0
- data/ext/nmatrix/data/ruby_object.h +386 -0
- data/ext/nmatrix/extconf.rb +70 -0
- data/ext/nmatrix/math/asum.h +99 -0
- data/ext/nmatrix/math/cblas_enums.h +36 -0
- data/ext/nmatrix/math/cblas_templates_core.h +507 -0
- data/ext/nmatrix/math/gemm.h +241 -0
- data/ext/nmatrix/math/gemv.h +178 -0
- data/ext/nmatrix/math/getrf.h +255 -0
- data/ext/nmatrix/math/getrs.h +121 -0
- data/ext/nmatrix/math/imax.h +82 -0
- data/ext/nmatrix/math/laswp.h +165 -0
- data/ext/nmatrix/math/long_dtype.h +62 -0
- data/ext/nmatrix/math/magnitude.h +54 -0
- data/ext/nmatrix/math/math.h +751 -0
- data/ext/nmatrix/math/nrm2.h +165 -0
- data/ext/nmatrix/math/rot.h +117 -0
- data/ext/nmatrix/math/rotg.h +106 -0
- data/ext/nmatrix/math/scal.h +71 -0
- data/ext/nmatrix/math/trsm.h +336 -0
- data/ext/nmatrix/math/util.h +162 -0
- data/ext/nmatrix/math.cpp +1368 -0
- data/ext/nmatrix/nm_memory.h +60 -0
- data/ext/nmatrix/nmatrix.cpp +285 -0
- data/ext/nmatrix/nmatrix.h +476 -0
- data/ext/nmatrix/ruby_constants.cpp +151 -0
- data/ext/nmatrix/ruby_constants.h +106 -0
- data/ext/nmatrix/ruby_nmatrix.c +3130 -0
- data/ext/nmatrix/storage/common.cpp +77 -0
- data/ext/nmatrix/storage/common.h +183 -0
- data/ext/nmatrix/storage/dense/dense.cpp +1096 -0
- data/ext/nmatrix/storage/dense/dense.h +129 -0
- data/ext/nmatrix/storage/list/list.cpp +1628 -0
- data/ext/nmatrix/storage/list/list.h +138 -0
- data/ext/nmatrix/storage/storage.cpp +730 -0
- data/ext/nmatrix/storage/storage.h +99 -0
- data/ext/nmatrix/storage/yale/class.h +1139 -0
- data/ext/nmatrix/storage/yale/iterators/base.h +143 -0
- data/ext/nmatrix/storage/yale/iterators/iterator.h +131 -0
- data/ext/nmatrix/storage/yale/iterators/row.h +450 -0
- data/ext/nmatrix/storage/yale/iterators/row_stored.h +140 -0
- data/ext/nmatrix/storage/yale/iterators/row_stored_nd.h +169 -0
- data/ext/nmatrix/storage/yale/iterators/stored_diagonal.h +124 -0
- data/ext/nmatrix/storage/yale/math/transpose.h +110 -0
- data/ext/nmatrix/storage/yale/yale.cpp +2074 -0
- data/ext/nmatrix/storage/yale/yale.h +203 -0
- data/ext/nmatrix/types.h +55 -0
- data/ext/nmatrix/util/io.cpp +279 -0
- data/ext/nmatrix/util/io.h +115 -0
- data/ext/nmatrix/util/sl_list.cpp +627 -0
- data/ext/nmatrix/util/sl_list.h +144 -0
- data/ext/nmatrix/util/util.h +78 -0
- data/lib/nmatrix/blas.rb +378 -0
- data/lib/nmatrix/cruby/math.rb +744 -0
- data/lib/nmatrix/enumerate.rb +253 -0
- data/lib/nmatrix/homogeneous.rb +241 -0
- data/lib/nmatrix/io/fortran_format.rb +138 -0
- data/lib/nmatrix/io/harwell_boeing.rb +221 -0
- data/lib/nmatrix/io/market.rb +263 -0
- data/lib/nmatrix/io/point_cloud.rb +189 -0
- data/lib/nmatrix/jruby/decomposition.rb +24 -0
- data/lib/nmatrix/jruby/enumerable.rb +13 -0
- data/lib/nmatrix/jruby/error.rb +4 -0
- data/lib/nmatrix/jruby/math.rb +501 -0
- data/lib/nmatrix/jruby/nmatrix_java.rb +840 -0
- data/lib/nmatrix/jruby/operators.rb +283 -0
- data/lib/nmatrix/jruby/slice.rb +264 -0
- data/lib/nmatrix/lapack_core.rb +181 -0
- data/lib/nmatrix/lapack_plugin.rb +44 -0
- data/lib/nmatrix/math.rb +953 -0
- data/lib/nmatrix/mkmf.rb +100 -0
- data/lib/nmatrix/monkeys.rb +137 -0
- data/lib/nmatrix/nmatrix.rb +1172 -0
- data/lib/nmatrix/rspec.rb +75 -0
- data/lib/nmatrix/shortcuts.rb +1163 -0
- data/lib/nmatrix/version.rb +39 -0
- data/lib/nmatrix/yale_functions.rb +118 -0
- data/lib/nmatrix.rb +28 -0
- data/spec/00_nmatrix_spec.rb +892 -0
- data/spec/01_enum_spec.rb +196 -0
- data/spec/02_slice_spec.rb +407 -0
- data/spec/03_nmatrix_monkeys_spec.rb +80 -0
- data/spec/2x2_dense_double.mat +0 -0
- data/spec/4x4_sparse.mat +0 -0
- data/spec/4x5_dense.mat +0 -0
- data/spec/blas_spec.rb +215 -0
- data/spec/elementwise_spec.rb +311 -0
- data/spec/homogeneous_spec.rb +100 -0
- data/spec/io/fortran_format_spec.rb +88 -0
- data/spec/io/harwell_boeing_spec.rb +98 -0
- data/spec/io/test.rua +9 -0
- data/spec/io_spec.rb +159 -0
- data/spec/lapack_core_spec.rb +482 -0
- data/spec/leakcheck.rb +16 -0
- data/spec/math_spec.rb +1363 -0
- data/spec/nmatrix_yale_resize_test_associations.yaml +2802 -0
- data/spec/nmatrix_yale_spec.rb +286 -0
- data/spec/rspec_monkeys.rb +56 -0
- data/spec/rspec_spec.rb +35 -0
- data/spec/shortcuts_spec.rb +474 -0
- data/spec/slice_set_spec.rb +162 -0
- data/spec/spec_helper.rb +172 -0
- data/spec/stat_spec.rb +214 -0
- data/spec/test.pcd +20 -0
- data/spec/utm5940.mtx +83844 -0
- metadata +295 -0
@@ -0,0 +1,253 @@
|
|
1
|
+
#--
|
2
|
+
# = NMatrix
|
3
|
+
#
|
4
|
+
# A linear algebra library for scientific computation in Ruby.
|
5
|
+
# NMatrix is part of SciRuby.
|
6
|
+
#
|
7
|
+
# NMatrix was originally inspired by and derived from NArray, by
|
8
|
+
# Masahiro Tanaka: http://narray.rubyforge.org
|
9
|
+
#
|
10
|
+
# == Copyright Information
|
11
|
+
#
|
12
|
+
# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
|
13
|
+
# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
|
14
|
+
#
|
15
|
+
# Please see LICENSE.txt for additional copyright notices.
|
16
|
+
#
|
17
|
+
# == Contributing
|
18
|
+
#
|
19
|
+
# By contributing source code to SciRuby, you agree to be bound by
|
20
|
+
# our Contributor Agreement:
|
21
|
+
#
|
22
|
+
# * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
|
23
|
+
#
|
24
|
+
# == enumerate.rb
|
25
|
+
#
|
26
|
+
# Enumeration methods for NMatrix
|
27
|
+
#++
|
28
|
+
|
29
|
+
class NMatrix
|
30
|
+
include Enumerable
|
31
|
+
|
32
|
+
##
|
33
|
+
# call-seq:
|
34
|
+
# each -> Enumerator
|
35
|
+
#
|
36
|
+
# Enumerate through the matrix. @see Enumerable#each
|
37
|
+
#
|
38
|
+
# For dense, this actually calls a specialized each iterator (in C). For yale and list, it relies upon
|
39
|
+
# #each_with_indices (which is about as fast as reasonably possible for C code).
|
40
|
+
def each &bl
|
41
|
+
if self.stype == :dense
|
42
|
+
self.__dense_each__(&bl)
|
43
|
+
elsif block_given?
|
44
|
+
self.each_with_indices(&bl)
|
45
|
+
else # Handle case where no block is given
|
46
|
+
Enumerator.new do |yielder|
|
47
|
+
self.each_with_indices do |params|
|
48
|
+
yielder.yield params
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# call-seq:
|
56
|
+
# flat_map -> Enumerator
|
57
|
+
# flat_map { |elem| block } -> Array
|
58
|
+
#
|
59
|
+
# Maps using Enumerator (returns an Array or an Enumerator)
|
60
|
+
alias_method :flat_map, :map
|
61
|
+
|
62
|
+
##
|
63
|
+
# call-seq:
|
64
|
+
# map -> Enumerator
|
65
|
+
# map { |elem| block } -> NMatrix
|
66
|
+
#
|
67
|
+
# Returns an NMatrix if a block is given. For an Array, use #flat_map
|
68
|
+
#
|
69
|
+
# Note that #map will always return an :object matrix, because it has no way of knowing
|
70
|
+
# how to handle operations on the different dtypes.
|
71
|
+
#
|
72
|
+
def map(&bl)
|
73
|
+
return enum_for(:map) unless block_given?
|
74
|
+
# NMatrix-jruby currently supports only doubles
|
75
|
+
cp = jruby? ? self : self.cast(dtype: :object)
|
76
|
+
cp.map!(&bl)
|
77
|
+
cp
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# call-seq:
|
82
|
+
# map! -> Enumerator
|
83
|
+
# map! { |elem| block } -> NMatrix
|
84
|
+
#
|
85
|
+
# Maps in place.
|
86
|
+
# @see #map
|
87
|
+
#
|
88
|
+
def map!
|
89
|
+
return enum_for(:map!) unless block_given?
|
90
|
+
iterated = false
|
91
|
+
self.each_stored_with_indices do |e, *i|
|
92
|
+
iterated = true
|
93
|
+
self[*i] = (yield e)
|
94
|
+
end
|
95
|
+
#HACK: if there's a single element in a non-dense matrix, it won't iterate and
|
96
|
+
#won't change the default value; this ensures that it does get changed.
|
97
|
+
unless iterated then
|
98
|
+
self.each_with_indices do |e, *i|
|
99
|
+
self[*i] = (yield e)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
#
|
106
|
+
# call-seq:
|
107
|
+
# each_rank() -> NMatrix
|
108
|
+
# each_rank() { |rank| block } -> NMatrix
|
109
|
+
# each_rank(dimen) -> Enumerator
|
110
|
+
# each_rank(dimen) { |rank| block } -> NMatrix
|
111
|
+
#
|
112
|
+
# Generic for @each_row, @each_col
|
113
|
+
#
|
114
|
+
# Iterate through each rank by reference.
|
115
|
+
#
|
116
|
+
# @param [Fixnum] dimen the rank being iterated over.
|
117
|
+
#
|
118
|
+
def each_rank(dimen=0, get_by=:reference)
|
119
|
+
return enum_for(:each_rank, dimen, get_by) unless block_given?
|
120
|
+
(0...self.shape[dimen]).each do |idx|
|
121
|
+
yield self.rank(dimen, idx, get_by)
|
122
|
+
end
|
123
|
+
self
|
124
|
+
end
|
125
|
+
alias :each_along_dim :each_rank
|
126
|
+
|
127
|
+
#
|
128
|
+
# call-seq:
|
129
|
+
# each_row { |row| block } -> NMatrix
|
130
|
+
#
|
131
|
+
# Iterate through each row, referencing it as an NMatrix slice.
|
132
|
+
def each_row(get_by=:reference)
|
133
|
+
return enum_for(:each_row, get_by) unless block_given?
|
134
|
+
(0...self.shape[0]).each do |i|
|
135
|
+
yield self.row(i, get_by)
|
136
|
+
end
|
137
|
+
self
|
138
|
+
end
|
139
|
+
|
140
|
+
#
|
141
|
+
# call-seq:
|
142
|
+
# each_column { |column| block } -> NMatrix
|
143
|
+
#
|
144
|
+
# Iterate through each column, referencing it as an NMatrix slice.
|
145
|
+
def each_column(get_by=:reference)
|
146
|
+
return enum_for(:each_column, get_by) unless block_given?
|
147
|
+
(0...self.shape[1]).each do |j|
|
148
|
+
yield self.column(j, get_by)
|
149
|
+
end
|
150
|
+
self
|
151
|
+
end
|
152
|
+
|
153
|
+
#
|
154
|
+
# call-seq:
|
155
|
+
# each_layer -> { |column| block } -> ...
|
156
|
+
#
|
157
|
+
# Iterate through each layer, referencing it as an NMatrix slice.
|
158
|
+
#
|
159
|
+
# Note: If you have a 3-dimensional matrix, the first dimension contains rows,
|
160
|
+
# the second contains columns, and the third contains layers.
|
161
|
+
def each_layer(get_by=:reference)
|
162
|
+
return enum_for(:each_layer, get_by) unless block_given?
|
163
|
+
(0...self.shape[2]).each do |k|
|
164
|
+
yield self.layer(k, get_by)
|
165
|
+
end
|
166
|
+
self
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
#
|
171
|
+
# call-seq:
|
172
|
+
# each_stored_with_index -> Enumerator
|
173
|
+
#
|
174
|
+
# Allow iteration across a vector NMatrix's stored values. See also @each_stored_with_indices
|
175
|
+
#
|
176
|
+
def each_stored_with_index(&block)
|
177
|
+
raise(NotImplementedError, "only works for dim 2 vectors") unless self.dim <= 2
|
178
|
+
return enum_for(:each_stored_with_index) unless block_given?
|
179
|
+
|
180
|
+
self.each_stored_with_indices do |v, i, j|
|
181
|
+
if shape[0] == 1
|
182
|
+
yield(v,j)
|
183
|
+
elsif shape[1] == 1
|
184
|
+
yield(v,i)
|
185
|
+
else
|
186
|
+
method_missing(:each_stored_with_index, &block)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
self
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
##
|
194
|
+
# call-seq:
|
195
|
+
# inject_rank() -> Enumerator
|
196
|
+
# inject_rank(dimen) -> Enumerator
|
197
|
+
# inject_rank(dimen, initial) -> Enumerator
|
198
|
+
# inject_rank(dimen, initial, dtype) -> Enumerator
|
199
|
+
# inject_rank() { |elem| block } -> NMatrix
|
200
|
+
# inject_rank(dimen) { |elem| block } -> NMatrix
|
201
|
+
# inject_rank(dimen, initial) { |elem| block } -> NMatrix
|
202
|
+
# inject_rank(dimen, initial, dtype) { |elem| block } -> NMatrix
|
203
|
+
#
|
204
|
+
# Reduces an NMatrix using a supplied block over a specified dimension.
|
205
|
+
# The block should behave the same way as for Enumerable#reduce.
|
206
|
+
#
|
207
|
+
# @param [Integer] dimen the dimension being reduced
|
208
|
+
# @param [Numeric] initial the initial value for the reduction
|
209
|
+
# (i.e. the usual parameter to Enumerable#reduce). Supply nil or do not
|
210
|
+
# supply this argument to have it follow the usual Enumerable#reduce
|
211
|
+
# behavior of using the first element as the initial value.
|
212
|
+
# @param [Symbol] dtype if non-nil/false, forces the accumulated result to have this dtype
|
213
|
+
# @return [NMatrix] an NMatrix with the same number of dimensions as the
|
214
|
+
# input, but with the input dimension now having size 1. Each element
|
215
|
+
# is the result of the reduction at that position along the specified
|
216
|
+
# dimension.
|
217
|
+
#
|
218
|
+
def inject_rank(dimen=0, initial=nil, dtype=nil)
|
219
|
+
|
220
|
+
raise(RangeError, "requested dimension (#{dimen}) does not exist (shape: #{shape})") if dimen > self.dim
|
221
|
+
|
222
|
+
return enum_for(:inject_rank, dimen, initial, dtype) unless block_given?
|
223
|
+
|
224
|
+
new_shape = shape.dup
|
225
|
+
new_shape[dimen] = 1
|
226
|
+
|
227
|
+
first_as_acc = false
|
228
|
+
|
229
|
+
if initial then
|
230
|
+
acc = NMatrix.new(new_shape, initial, :dtype => dtype || self.dtype, stype: self.stype)
|
231
|
+
else
|
232
|
+
each_rank(dimen) do |sub_mat|
|
233
|
+
acc = (sub_mat.is_a?(NMatrix) and !dtype.nil? and dtype != self.dtype) ? sub_mat.cast(self.stype, dtype) : sub_mat
|
234
|
+
break
|
235
|
+
end
|
236
|
+
first_as_acc = true
|
237
|
+
end
|
238
|
+
|
239
|
+
each_rank(dimen) do |sub_mat|
|
240
|
+
if first_as_acc
|
241
|
+
first_as_acc = false
|
242
|
+
next
|
243
|
+
end
|
244
|
+
acc = yield(acc, sub_mat)
|
245
|
+
end
|
246
|
+
|
247
|
+
acc
|
248
|
+
end
|
249
|
+
|
250
|
+
alias :reduce_along_dim :inject_rank
|
251
|
+
alias :inject_along_dim :inject_rank
|
252
|
+
|
253
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
#--
|
2
|
+
# = NMatrix
|
3
|
+
#
|
4
|
+
# A linear algebra library for scientific computation in Ruby.
|
5
|
+
# NMatrix is part of SciRuby.
|
6
|
+
#
|
7
|
+
# NMatrix was originally inspired by and derived from NArray, by
|
8
|
+
# Masahiro Tanaka: http://narray.rubyforge.org
|
9
|
+
#
|
10
|
+
# == Copyright Information
|
11
|
+
#
|
12
|
+
# SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
|
13
|
+
# NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
|
14
|
+
#
|
15
|
+
# Please see LICENSE.txt for additional copyright notices.
|
16
|
+
#
|
17
|
+
# == Contributing
|
18
|
+
#
|
19
|
+
# By contributing source code to SciRuby, you agree to be bound by
|
20
|
+
# our Contributor Agreement:
|
21
|
+
#
|
22
|
+
# * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
|
23
|
+
#
|
24
|
+
# == homogeneous.rb
|
25
|
+
#
|
26
|
+
# This file contains optional shortcuts for generating homogeneous
|
27
|
+
# transformations.
|
28
|
+
#
|
29
|
+
#++
|
30
|
+
|
31
|
+
class NMatrix
|
32
|
+
class << self
|
33
|
+
#
|
34
|
+
# call-seq:
|
35
|
+
# x_rotation(angle_in_radians) -> NMatrix
|
36
|
+
# x_rotation(angle_in_radians, dtype: dtype) -> NMatrix
|
37
|
+
# y_rotation(angle_in_radians) -> NMatrix
|
38
|
+
# y_rotation(angle_in_radians, dtype: dtype) -> NMatrix
|
39
|
+
# z_rotation(angle_in_radians) -> NMatrix
|
40
|
+
# z_rotation(angle_in_radians, dtype: dtype) -> NMatrix
|
41
|
+
#
|
42
|
+
# Generate a 4x4 homogeneous transformation matrix representing a rotation
|
43
|
+
# about the x, y, or z axis respectively.
|
44
|
+
#
|
45
|
+
# * *Arguments* :
|
46
|
+
# - +angle_in_radians+ -> The angle of rotation in radians.
|
47
|
+
# - +dtype+ -> (optional) Default is +:float64+
|
48
|
+
# * *Returns* :
|
49
|
+
# - A homogeneous transformation matrix consisting of a single rotation.
|
50
|
+
#
|
51
|
+
# Examples:
|
52
|
+
#
|
53
|
+
# NMatrix.x_rotation(Math::PI.quo(6)) # =>
|
54
|
+
# 1.0 0.0 0.0 0.0
|
55
|
+
# 0.0 0.866025 -0.499999 0.0
|
56
|
+
# 0.0 0.499999 0.866025 0.0
|
57
|
+
# 0.0 0.0 0.0 1.0
|
58
|
+
#
|
59
|
+
#
|
60
|
+
# NMatrix.x_rotation(Math::PI.quo(6), dtype: :float32) # =>
|
61
|
+
# 1.0 0.0 0.0 0.0
|
62
|
+
# 0.0 0.866025 -0.5 0.0
|
63
|
+
# 0.0 0.5 0.866025 0.0
|
64
|
+
# 0.0 0.0 0.0 1.0
|
65
|
+
#
|
66
|
+
def x_rotation angle_in_radians, opts={}
|
67
|
+
c = Math.cos(angle_in_radians)
|
68
|
+
s = Math.sin(angle_in_radians)
|
69
|
+
NMatrix.new(4, [1.0, 0.0, 0.0, 0.0,
|
70
|
+
0.0, c, -s, 0.0,
|
71
|
+
0.0, s, c, 0.0,
|
72
|
+
0.0, 0.0, 0.0, 1.0], {dtype: :float64}.merge(opts))
|
73
|
+
end
|
74
|
+
|
75
|
+
def y_rotation angle_in_radians, opts={}
|
76
|
+
c = Math.cos(angle_in_radians)
|
77
|
+
s = Math.sin(angle_in_radians)
|
78
|
+
NMatrix.new(4, [ c, 0.0, s, 0.0,
|
79
|
+
0.0, 1.0, 0.0, 0.0,
|
80
|
+
-s, 0.0, c, 0.0,
|
81
|
+
0.0, 0.0, 0.0, 1.0], {dtype: :float64}.merge(opts))
|
82
|
+
end
|
83
|
+
|
84
|
+
def z_rotation angle_in_radians, opts={}
|
85
|
+
c = Math.cos(angle_in_radians)
|
86
|
+
s = Math.sin(angle_in_radians)
|
87
|
+
NMatrix.new(4, [ c, -s, 0.0, 0.0,
|
88
|
+
s, c, 0.0, 0.0,
|
89
|
+
0.0, 0.0, 1.0, 0.0,
|
90
|
+
0.0, 0.0, 0.0, 1.0], {dtype: :float64}.merge(opts))
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
#
|
95
|
+
# call-seq:
|
96
|
+
# translation(x, y, z) -> NMatrix
|
97
|
+
# translation([x,y,z]) -> NMatrix
|
98
|
+
# translation(translation_matrix) -> NMatrix
|
99
|
+
# translation(translation_matrix) -> NMatrix
|
100
|
+
# translation(translation, dtype: dtype) -> NMatrix
|
101
|
+
# translation(x, y, z, dtype: dtype) -> NMatrix
|
102
|
+
#
|
103
|
+
# Generate a 4x4 homogeneous transformation matrix representing a translation.
|
104
|
+
#
|
105
|
+
# * *Returns* :
|
106
|
+
# - A homogeneous transformation matrix consisting of a translation.
|
107
|
+
#
|
108
|
+
# Examples:
|
109
|
+
#
|
110
|
+
# NMatrix.translation(4.0,5.0,6.0) # =>
|
111
|
+
# 1.0 0.0 0.0 4.0
|
112
|
+
# 0.0 1.0 0.0 5.0
|
113
|
+
# 0.0 0.0 1.0 6.0
|
114
|
+
# 0.0 0.0 0.0 1.0
|
115
|
+
#
|
116
|
+
# NMatrix.translation(4.0,5.0,6.0, dtype: :int64) # =>
|
117
|
+
# 1 0 0 4
|
118
|
+
# 0 1 0 5
|
119
|
+
# 0 0 1 6
|
120
|
+
# 0 0 0 1
|
121
|
+
# NMatrix.translation(4,5,6) # =>
|
122
|
+
# 1 0 0 4
|
123
|
+
# 0 1 0 5
|
124
|
+
# 0 0 1 6
|
125
|
+
# 0 0 0 1
|
126
|
+
#
|
127
|
+
def translation *args
|
128
|
+
xyz = args.shift if args.first.is_a?(NMatrix) || args.first.is_a?(Array)
|
129
|
+
default_dtype = xyz.respond_to?(:dtype) ? xyz.dtype : NMatrix.guess_dtype(xyz)
|
130
|
+
opts = {dtype: default_dtype}
|
131
|
+
opts = opts.merge(args.pop) if args.size > 0 && args.last.is_a?(Hash)
|
132
|
+
xyz ||= args
|
133
|
+
|
134
|
+
n = if args.size > 0
|
135
|
+
NMatrix.eye(4, opts)
|
136
|
+
else
|
137
|
+
NMatrix.eye(4, opts)
|
138
|
+
end
|
139
|
+
n[0..2,3] = xyz
|
140
|
+
n
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
#
|
145
|
+
# call-seq:
|
146
|
+
# quaternion -> NMatrix
|
147
|
+
#
|
148
|
+
# Find the quaternion for a 3D rotation matrix.
|
149
|
+
#
|
150
|
+
# Code borrowed from: http://courses.cms.caltech.edu/cs171/quatut.pdf
|
151
|
+
#
|
152
|
+
# * *Returns* :
|
153
|
+
# - A length-4 NMatrix representing the corresponding quaternion.
|
154
|
+
#
|
155
|
+
# Examples:
|
156
|
+
#
|
157
|
+
# n.quaternion # => [1, 0, 0, 0]
|
158
|
+
#
|
159
|
+
def quaternion
|
160
|
+
raise(ShapeError, "Expected square matrix") if self.shape[0] != self.shape[1]
|
161
|
+
raise(ShapeError, "Expected 3x3 rotation (or 4x4 homogeneous) matrix") if self.shape[0] > 4 || self.shape[0] < 3
|
162
|
+
|
163
|
+
q = NMatrix.new([4], dtype: self.dtype == :float32 ? :float32: :float64)
|
164
|
+
rotation_trace = self[0,0] + self[1,1] + self[2,2]
|
165
|
+
if rotation_trace >= 0
|
166
|
+
self_w = self.shape[0] == 4 ? self[3,3] : 1.0
|
167
|
+
root_of_homogeneous_trace = Math.sqrt(rotation_trace + self_w)
|
168
|
+
q[0] = root_of_homogeneous_trace * 0.5
|
169
|
+
s = 0.5 / root_of_homogeneous_trace
|
170
|
+
q[1] = (self[2,1] - self[1,2]) * s
|
171
|
+
q[2] = (self[0,2] - self[2,0]) * s
|
172
|
+
q[3] = (self[1,0] - self[0,1]) * s
|
173
|
+
else
|
174
|
+
h = 0
|
175
|
+
h = 1 if self[1,1] > self[0,0]
|
176
|
+
h = 2 if self[2,2] > self[h,h]
|
177
|
+
|
178
|
+
case_macro = Proc.new do |i,j,k,ii,jj,kk|
|
179
|
+
qq = NMatrix.new([4], dtype: :float64)
|
180
|
+
self_w = self.shape[0] == 4 ? self[3,3] : 1.0
|
181
|
+
s = Math.sqrt( (self[ii,ii] - (self[jj,jj] + self[kk,kk])) + self_w)
|
182
|
+
qq[i] = s*0.5
|
183
|
+
s = 0.5 / s
|
184
|
+
qq[j] = (self[ii,jj] + self[jj,ii]) * s
|
185
|
+
qq[k] = (self[kk,ii] + self[ii,kk]) * s
|
186
|
+
qq[0] = (self[kk,jj] - self[jj,kk]) * s
|
187
|
+
qq
|
188
|
+
end
|
189
|
+
|
190
|
+
case h
|
191
|
+
when 0
|
192
|
+
q = case_macro.call(1,2,3, 0,1,2)
|
193
|
+
when 1
|
194
|
+
q = case_macro.call(2,3,1, 1,2,0)
|
195
|
+
when 2
|
196
|
+
q = case_macro.call(3,1,2, 2,0,1)
|
197
|
+
end
|
198
|
+
|
199
|
+
self_w = self.shape[0] == 4 ? self[3,3] : 1.0
|
200
|
+
if self_w != 1
|
201
|
+
s = 1.0 / Math.sqrt(self_w)
|
202
|
+
q[0] *= s
|
203
|
+
q[1] *= s
|
204
|
+
q[2] *= s
|
205
|
+
q[3] *= s
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
q
|
210
|
+
end
|
211
|
+
|
212
|
+
#
|
213
|
+
# call-seq:
|
214
|
+
# angle_vector -> [angle, about_vector]
|
215
|
+
#
|
216
|
+
# Find the angle vector for a quaternion. Assumes the quaternion has unit length.
|
217
|
+
#
|
218
|
+
# Source: http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/
|
219
|
+
#
|
220
|
+
# * *Returns* :
|
221
|
+
# - An angle (in radians) describing the rotation about the +about_vector+.
|
222
|
+
# - A length-3 NMatrix representing the corresponding quaternion.
|
223
|
+
#
|
224
|
+
# Examples:
|
225
|
+
#
|
226
|
+
# q.angle_vector # => [1, 0, 0, 0]
|
227
|
+
#
|
228
|
+
def angle_vector
|
229
|
+
raise(ShapeError, "Expected length-4 vector or matrix (quaternion)") if self.shape[0] != 4
|
230
|
+
raise("Expected unit quaternion") if self[0] > 1
|
231
|
+
|
232
|
+
xyz = NMatrix.new([3], dtype: self.dtype)
|
233
|
+
|
234
|
+
angle = 2 * Math.acos(self[0])
|
235
|
+
s = Math.sqrt(1.0 - self[0]*self[0])
|
236
|
+
|
237
|
+
xyz[0..2] = self[1..3]
|
238
|
+
xyz /= s if s >= 0.001 # avoid divide by zero
|
239
|
+
return [angle, xyz]
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
#--
|
2
|
+
# = NMatrix
|
3
|
+
#
|
4
|
+
# A linear algebra library for scientific computation in Ruby.
|
5
|
+
# NMatrix is part of SciRuby.
|
6
|
+
#
|
7
|
+
# NMatrix was originally inspired by and derived from NArray, by
|
8
|
+
# Masahiro Tanaka: http://narray.rubyforge.org
|
9
|
+
#
|
10
|
+
# == Copyright Information
|
11
|
+
#
|
12
|
+
# SciRuby is Copyright (c) 2010 - 2016, Ruby Science Foundation
|
13
|
+
# NMatrix is Copyright (c) 2012 - 2016, John Woods and the Ruby Science Foundation
|
14
|
+
#
|
15
|
+
# Please see LICENSE.txt for additional copyright notices.
|
16
|
+
#
|
17
|
+
# == Contributing
|
18
|
+
#
|
19
|
+
# By contributing source code to SciRuby, you agree to be bound by
|
20
|
+
# our Contributor Agreement:
|
21
|
+
#
|
22
|
+
# * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
|
23
|
+
#
|
24
|
+
# == io/matlab/fortran_format.rb
|
25
|
+
#
|
26
|
+
# A parser for making sense of FORTRAN formats.
|
27
|
+
# => Only handles R (real), F (float) and E (exponential) format codes.
|
28
|
+
#++
|
29
|
+
|
30
|
+
class NMatrix
|
31
|
+
module IO
|
32
|
+
module FortranFormat
|
33
|
+
|
34
|
+
# Class for reading strings in FORTRAN format for specifying attributes
|
35
|
+
# of numerical data in a file. Supports F (float), E (exponential) and
|
36
|
+
# R (real).
|
37
|
+
#
|
38
|
+
# == Usage
|
39
|
+
#
|
40
|
+
# p = NMatrix::IO::FortranFormat::Reader.new("(16I5)")
|
41
|
+
# v = p.parse
|
42
|
+
# puts v #=> { :format_code => "INT_ID",
|
43
|
+
# #=> :repeat => 16,
|
44
|
+
# #=> :field_width => 5 }
|
45
|
+
class Reader
|
46
|
+
|
47
|
+
# Accepts a string in FORTRAN format and initializes the
|
48
|
+
# NMatrix::IO::FortranFormat::Reader object for further parsing of the
|
49
|
+
# data.
|
50
|
+
#
|
51
|
+
# == Arguments
|
52
|
+
#
|
53
|
+
# * +string+ - FORTRAN format string to be parsed.
|
54
|
+
def initialize string
|
55
|
+
@string = string
|
56
|
+
end
|
57
|
+
|
58
|
+
# Parses the FORTRAN format string passed in initialize and returns
|
59
|
+
# a hash of the results.
|
60
|
+
#
|
61
|
+
# == Result Hash Format
|
62
|
+
#
|
63
|
+
# Take note that some of the below parameters may be absent in the hash
|
64
|
+
# depending on the type of string being parsed.
|
65
|
+
#
|
66
|
+
# * +:format_code+ - A string containing the format code of the read data.
|
67
|
+
# Can be "INT_ID", "FP_ID" or "EXP_ID"
|
68
|
+
# * +:repeat+ - Number of times this format will repeat in a line.
|
69
|
+
# * +:field_width+ - Width of the numerical part of the number.
|
70
|
+
# * +:post_decimal_width+ - Width of the numerals after the decimal point.
|
71
|
+
# * +:exponent_width+ - Width of exponent part of the number.
|
72
|
+
def parse
|
73
|
+
raise(IOError, "Left or right parentheses missing") \
|
74
|
+
if parentheses_missing? # change tests to handle 'raise' not return
|
75
|
+
|
76
|
+
@result = {}
|
77
|
+
@string = @string[1..-2]
|
78
|
+
|
79
|
+
if valid_fortran_format?
|
80
|
+
load_result
|
81
|
+
else
|
82
|
+
raise(IOError, "Invalid FORTRAN format specified. Only Integer, Float or Exponential acceptable.")
|
83
|
+
end
|
84
|
+
|
85
|
+
@result
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
def parentheses_missing?
|
90
|
+
true if @string[0] != '(' or @string[-1] != ')'
|
91
|
+
end
|
92
|
+
|
93
|
+
# Changing any of the following regular expressions can lead to disaster
|
94
|
+
def valid_fortran_format?
|
95
|
+
@mdata = @string.match(/\A(\d*)(I)(\d+)\z/) # check for integer format
|
96
|
+
@mdata = @string.match(/\A(\d*)(F)(\d+)\.(\d+)\z/) \
|
97
|
+
if @mdata.nil? # check for floating point if not integer
|
98
|
+
@mdata = @string.match(/\A(\d*)(E)(\d+)\.(\d+)(E)?(\d*)\z/) \
|
99
|
+
if @mdata.nil? # check for exponential format if not floating point
|
100
|
+
|
101
|
+
@mdata
|
102
|
+
end
|
103
|
+
|
104
|
+
def load_result
|
105
|
+
if @mdata.to_a.include? "I"
|
106
|
+
create_integer_hash
|
107
|
+
elsif @mdata.to_a.include? "F"
|
108
|
+
create_float_hash
|
109
|
+
else
|
110
|
+
create_exp_hash
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def create_integer_hash
|
115
|
+
@result[:format_code] = "INT_ID"
|
116
|
+
@result[:repeat] = @mdata[1].to_i if !@mdata[1].empty?
|
117
|
+
@result[:field_width] = @mdata[3].to_i
|
118
|
+
end
|
119
|
+
|
120
|
+
def create_float_hash
|
121
|
+
@result[:format_code] = "FP_ID"
|
122
|
+
@result[:repeat] = @mdata[1].to_i if !@mdata[1].empty?
|
123
|
+
@result[:field_width] = @mdata[3].to_i
|
124
|
+
@result[:post_decimal_width] = @mdata[4].to_i
|
125
|
+
end
|
126
|
+
|
127
|
+
def create_exp_hash
|
128
|
+
@result[:format_code] = "EXP_ID"
|
129
|
+
@result[:repeat] = @mdata[1].to_i if !@mdata[1].empty?
|
130
|
+
@result[:field_width] = @mdata[3].to_i
|
131
|
+
@result[:post_decimal_width] = @mdata[4].to_i
|
132
|
+
@result[:exponent_width] = @mdata[6].to_i if !@mdata[6].empty?
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|