pnmatrix 1.2.4
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/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
|