ruby-gsl-ng 0.1.0 → 0.2.0
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.
- data/History.txt +6 -1
- data/Manifest.txt +12 -4
- data/README.txt +5 -2
- data/Rakefile +1 -0
- data/ext/extconf.rb +2 -0
- data/ext/gslng_extensions.cpp +77 -0
- data/lib/gslng/backend.rb +19 -0
- data/lib/gslng/backend_components/error_handling.rb +12 -0
- data/lib/gslng/backend_components/matrix.rb +73 -0
- data/lib/{gsl/backend.rb → gslng/backend_components/vector.rb} +24 -32
- data/lib/gslng/finalizer.rb +5 -0
- data/lib/gslng/matrix.rb +406 -0
- data/lib/gslng/matrix_view.rb +39 -0
- data/lib/gslng/vector.rb +353 -0
- data/lib/gslng/vector_view.rb +39 -0
- data/lib/gslng.rb +11 -0
- data/test/benchmark.rb +49 -0
- data/test/matrix_test.rb +105 -0
- data/test/test_gsl.rb +1 -0
- data/test/vector_test.rb +72 -39
- metadata +17 -9
- data/lib/gsl/finalizer.rb +0 -5
- data/lib/gsl/vector.rb +0 -236
- data/lib/gsl.rb +0 -8
data/lib/gslng/vector.rb
ADDED
@@ -0,0 +1,353 @@
|
|
1
|
+
module GSLng
|
2
|
+
# A fixed-size n-dimensional vector.
|
3
|
+
#
|
4
|
+
# =Notes
|
5
|
+
# * #each is implemented through calls to #[], which can be relatively slow (compared to direct C pointer access)
|
6
|
+
# for big Vectors. There's a faster version (#fast_each) that can be used when there's not return value expected from the #each call.
|
7
|
+
# * Since this class includes Enumerable, and Enumerable's methods call #each, certain methods are redefined (like #max and #min)
|
8
|
+
# so they use #fast_each instead. Thus, any other Enumerable's method not defined here will be slower.
|
9
|
+
# * Some functions (like #sum, #dot, and others) use BLAS functions (through GSLng's CBLAS interface).
|
10
|
+
# * In contrary to Array, operators [] and []= will raise an exception when accessing out-of-bounds elements.
|
11
|
+
# * Operator * multiplies two vectors element-by-element. To perform a dot product use the ^ operator instead (or the #dot method).
|
12
|
+
# * Operands are coerced to vectors so you can do vector + scalar, etc. (see #coerce)
|
13
|
+
#
|
14
|
+
class Vector
|
15
|
+
include Enumerable
|
16
|
+
|
17
|
+
attr_reader :ptr # :nodoc:
|
18
|
+
attr_reader :size # Vector size
|
19
|
+
|
20
|
+
#--------------------- constructors -------------------------#
|
21
|
+
|
22
|
+
# Create a Vector of size n. If zero is true, the vector is initialized with zeros.
|
23
|
+
# Otherwise, the vector will contain garbage.
|
24
|
+
# You can optionally pass a block, in which case #map_index! will be called with it (i.e.: it works like Array.new).
|
25
|
+
def initialize(n, zero = false)
|
26
|
+
@size = n
|
27
|
+
@ptr = (zero ? GSLng.backend::gsl_vector_calloc(n) : GSLng.backend::gsl_vector_alloc(n))
|
28
|
+
GSLng.set_finalizer(self, :gsl_vector_free, @ptr)
|
29
|
+
if (block_given?) then self.map_index!(&Proc.new) end
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize_copy(other) #:nodoc:
|
33
|
+
ObjectSpace.undefine_finalizer(self) # TODO: ruby bug?
|
34
|
+
|
35
|
+
@size = other.size
|
36
|
+
@ptr = GSLng.backend::gsl_vector_alloc(other.size)
|
37
|
+
GSLng.set_finalizer(self, :gsl_vector_free, @ptr)
|
38
|
+
|
39
|
+
GSLng.backend::gsl_vector_memcpy(@ptr, other.ptr)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Same as Vector.new(n, true)
|
43
|
+
def Vector.zero(n); Vector.new(n, true) end
|
44
|
+
|
45
|
+
# Create a vector from an Array.
|
46
|
+
def Vector.from_array(array)
|
47
|
+
if (array.empty?) then raise "Can't create empty vector" end
|
48
|
+
Vector.new(array.size) {|i| array[i]}
|
49
|
+
end
|
50
|
+
|
51
|
+
# Creates a Vector from an Array (see #from_array) or a Range. For example:
|
52
|
+
# Vector[1,2,3]
|
53
|
+
# Vector[1..3]
|
54
|
+
def Vector.[](*args)
|
55
|
+
array = (args.size == 1 && Range === args[0] ? args[0].to_a : args)
|
56
|
+
Vector.from_array(array)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Generates a Vector of n random numbers between 0 and 1.
|
60
|
+
# NOTE: This simply uses Kernel::rand
|
61
|
+
def Vector.random(n)
|
62
|
+
Vector.new(n).map!{|x| Kernel::rand}
|
63
|
+
end
|
64
|
+
class << self; alias_method :rand, :random end
|
65
|
+
|
66
|
+
#--------------------- setting values -------------------------#
|
67
|
+
|
68
|
+
# Set all values to v
|
69
|
+
def all!(v); GSLng.backend::gsl_vector_set_all(self.ptr, v); return self end
|
70
|
+
alias_method :set!, :all!
|
71
|
+
alias_method :fill!, :all!
|
72
|
+
|
73
|
+
# Set all values to zero
|
74
|
+
def zero!; GSLng.backend::gsl_vector_set_zero(self.ptr); return self end
|
75
|
+
|
76
|
+
# Set all values to zero, except the i-th element, which is set to 1
|
77
|
+
def basis!(i); GSLng.backend::gsl_vector_set_basis(self.ptr, i); return self end
|
78
|
+
|
79
|
+
#--------------------- operators -------------------------#
|
80
|
+
|
81
|
+
# Add (element-by-element) other to self
|
82
|
+
def add!(other)
|
83
|
+
case other
|
84
|
+
when Numeric; GSLng.backend::gsl_vector_add_constant(self.ptr, other.to_f)
|
85
|
+
when Vector; GSLng.backend::gsl_vector_add(self.ptr, other.ptr)
|
86
|
+
else
|
87
|
+
x,y = other.coerce(self)
|
88
|
+
x.add!(y)
|
89
|
+
end
|
90
|
+
return self
|
91
|
+
end
|
92
|
+
|
93
|
+
# Substract (element-by-element) other from self
|
94
|
+
def substract!(other)
|
95
|
+
case other
|
96
|
+
when Numeric; GSLng.backend::gsl_vector_add_constant(self.ptr, -other.to_f)
|
97
|
+
when Vector; GSLng.backend::gsl_vector_sub(self.ptr, other.ptr)
|
98
|
+
else
|
99
|
+
x,y = other.coerce(self)
|
100
|
+
x.sub!(y)
|
101
|
+
end
|
102
|
+
return self
|
103
|
+
end
|
104
|
+
alias_method :sub!, :substract!
|
105
|
+
|
106
|
+
# Multiply (element-by-element) other with self
|
107
|
+
def multiply!(other)
|
108
|
+
case other
|
109
|
+
when Numeric; GSLng.backend::gsl_blas_dscal(other.to_f, self.ptr)
|
110
|
+
when Vector; GSLng.backend::gsl_vector_mul(self.ptr, other.ptr)
|
111
|
+
else
|
112
|
+
x,y = other.coerce(self)
|
113
|
+
x.mul!(y)
|
114
|
+
end
|
115
|
+
return self
|
116
|
+
end
|
117
|
+
alias_method :mul!, :multiply!
|
118
|
+
|
119
|
+
# Divide (element-by-element) self by other
|
120
|
+
def divide!(other)
|
121
|
+
case other
|
122
|
+
when Numeric; GSLng.backend::gsl_blas_dscal(1.0 / other, self.ptr)
|
123
|
+
when Vector; GSLng.backend::gsl_vector_div(self.ptr, other.ptr)
|
124
|
+
else
|
125
|
+
x,y = other.coerce(self)
|
126
|
+
x.div!(y)
|
127
|
+
end
|
128
|
+
return self
|
129
|
+
end
|
130
|
+
alias_method :div!, :divide!
|
131
|
+
|
132
|
+
# Element-by-element addition
|
133
|
+
def +(other); self.dup.add!(other) end
|
134
|
+
|
135
|
+
# Element-by-element substraction
|
136
|
+
def -(other); self.dup.sub!(other) end
|
137
|
+
|
138
|
+
# Element-by-element product
|
139
|
+
def *(other)
|
140
|
+
case other
|
141
|
+
when Numeric; self.dup.mul!(other)
|
142
|
+
when Vector; self.dup.mul!(other)
|
143
|
+
else
|
144
|
+
x,y = other.coerce(self)
|
145
|
+
x * y
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Element-by-element division
|
150
|
+
def /(other); self.dup.div!(other) end
|
151
|
+
|
152
|
+
#--------------------- other math -------------------------#
|
153
|
+
|
154
|
+
# Dot product between self and other (uses BLAS's ddot)
|
155
|
+
def dot(other)
|
156
|
+
out = FFI::Buffer.new(:double)
|
157
|
+
GSLng.backend::gsl_blas_ddot(self.ptr, other.ptr, out)
|
158
|
+
return out[0].get_double(0)
|
159
|
+
end
|
160
|
+
alias_method :^, :dot
|
161
|
+
|
162
|
+
# Norm 2 of the vector (uses BLAS's dnrm2)
|
163
|
+
def norm; GSLng.backend::gsl_blas_dnrm2(self.ptr) end
|
164
|
+
alias_method :length, :norm
|
165
|
+
|
166
|
+
# Returns the sum of all elements (uses BLAS's dasum)
|
167
|
+
def sum; GSLng.backend::gsl_blas_dasum(self.ptr) end
|
168
|
+
|
169
|
+
# Optimized version of: self += other * alpha (where alpha is a Numeric). Uses BLAS's daxpy.
|
170
|
+
def mul_add(other, alpha); GSLng.backend::gsl_blas_daxpy(alpha, other.ptr, self.ptr); return self end
|
171
|
+
|
172
|
+
#--------------------- misc -------------------------#
|
173
|
+
|
174
|
+
# Reverse the order of elements
|
175
|
+
def reverse!; GSLng.backend::gsl_vector_reverse(self.ptr); return self end
|
176
|
+
|
177
|
+
# Swap the i-th element with the j-th element
|
178
|
+
def swap(i,j); GSLng.backend::gsl_vector_swap_elements(self.ptr, i, j); return self end
|
179
|
+
|
180
|
+
def sort!; GSLng.backend::gsl_sort_vector(self.ptr); return self end
|
181
|
+
def sort; self.dup.sort! end
|
182
|
+
|
183
|
+
# Copy other's values into self
|
184
|
+
def copy(other); GSLng.backend::gsl_vector_memcpy(self.ptr, other.ptr); return self end
|
185
|
+
|
186
|
+
#--------------------- set/get -------------------------#
|
187
|
+
|
188
|
+
# Access the i-th element (*NOTE*: throws exception if out-of-bounds).
|
189
|
+
# If _index_ is negative, it counts from the end (-1 is the last element).
|
190
|
+
# TODO: support ranges
|
191
|
+
def [](index)
|
192
|
+
GSLng.backend::gsl_vector_get(self.ptr, (index < 0 ? @size + index : index))
|
193
|
+
end
|
194
|
+
|
195
|
+
# Set the i-th element (*NOTE*: throws exception if out-of-bounds).
|
196
|
+
# If _index_ is negative, it counts from the end (-1 is the last element).
|
197
|
+
# TODO: support ranges
|
198
|
+
def []=(index, value)
|
199
|
+
GSLng.backend::gsl_vector_set(self.ptr, (index < 0 ? @size + index : index), value.to_f)
|
200
|
+
end
|
201
|
+
|
202
|
+
# Create a Vector::View from this Vector.
|
203
|
+
# If _size_ is nil, it is computed automatically from _offset_ and _stride_
|
204
|
+
def view(offset = 0, size = nil, stride = 1)
|
205
|
+
if (stride <= 0) then raise 'stride must be positive' end
|
206
|
+
|
207
|
+
if (size.nil?)
|
208
|
+
size = @size - offset
|
209
|
+
k,m = size.divmod(stride)
|
210
|
+
size = k + (m == 0 ? 0 : 1)
|
211
|
+
end
|
212
|
+
View.new(self, offset, size, stride)
|
213
|
+
end
|
214
|
+
alias_method :subvector_view, :view
|
215
|
+
|
216
|
+
# Shorthand for #subvector_view(..).to_vector.
|
217
|
+
def subvector(*args); subvector_view(*args).to_vector end
|
218
|
+
|
219
|
+
#--------------------- predicate methods -------------------------#
|
220
|
+
|
221
|
+
# if all elements are zero
|
222
|
+
def zero?; GSLng.backend::gsl_vector_isnull(self.ptr) == 1 ? true : false end
|
223
|
+
|
224
|
+
# if all elements are strictly positive (>0)
|
225
|
+
def positive?; GSLng.backend::gsl_vector_ispos(self.ptr) == 1 ? true : false end
|
226
|
+
|
227
|
+
#if all elements are strictly negative (<0)
|
228
|
+
def negative?; GSLng.backend::gsl_vector_isneg(self.ptr) == 1 ? true : false end
|
229
|
+
|
230
|
+
# if all elements are non-negative (>=0)
|
231
|
+
def nonnegative?; GSLng.backend::gsl_vector_isnonneg(self.ptr) == 1 ? true : false end
|
232
|
+
|
233
|
+
#--------------------- min/max -------------------------#
|
234
|
+
|
235
|
+
# Return maximum element of vector
|
236
|
+
def max; GSLng.backend::gsl_vector_max(self.ptr) end
|
237
|
+
|
238
|
+
# Return minimum element of vector
|
239
|
+
def min; GSLng.backend::gsl_vector_min(self.ptr) end
|
240
|
+
|
241
|
+
# Same as Array#minmax
|
242
|
+
def minmax
|
243
|
+
min = FFI::Buffer.new(:double)
|
244
|
+
max = FFI::Buffer.new(:double)
|
245
|
+
GSLng.backend::gsl_vector_minmax(self.ptr, min, max)
|
246
|
+
return [min[0].get_float64(0),max[0].get_float64(0)]
|
247
|
+
end
|
248
|
+
|
249
|
+
# Same as #minmax, but returns the indices to the elements
|
250
|
+
def minmax_index
|
251
|
+
min = FFI::Buffer.new(:size_t)
|
252
|
+
max = FFI::Buffer.new(:size_t)
|
253
|
+
GSLng.backend::gsl_vector_minmax_index(self.ptr, min, max)
|
254
|
+
#return [min[0].get_size_t(0),max[0].get_size_t(0)]
|
255
|
+
return [min[0].get_ulong(0),max[0].get_ulong(0)]
|
256
|
+
end
|
257
|
+
|
258
|
+
# Same as #min, but returns the index to the element
|
259
|
+
def min_index; GSLng.backend::gsl_vector_min_index(self.ptr) end
|
260
|
+
|
261
|
+
# Same as #max, but returns the index to the element
|
262
|
+
def max_index; GSLng.backend::gsl_vector_max_index(self.ptr) end
|
263
|
+
|
264
|
+
#--------------------- block handling -------------------------#
|
265
|
+
|
266
|
+
# Yields the block for each element in the Vector
|
267
|
+
def each # :yield: obj
|
268
|
+
@size.times {|i| yield(self[i])}
|
269
|
+
end
|
270
|
+
|
271
|
+
# Same as #each, but faster. The catch is that this method returns nothing.
|
272
|
+
def fast_each(&block) #:yield: obj
|
273
|
+
GSLng.backend::gsl_vector_each(self.ptr, block)
|
274
|
+
end
|
275
|
+
|
276
|
+
def fast_each_with_index(&block) #:yield: obj,i
|
277
|
+
GSLng.backend::gsl_vector_each_with_index(self.ptr, block)
|
278
|
+
end
|
279
|
+
|
280
|
+
# Efficient map! implementation
|
281
|
+
def map!(&block); GSLng.backend::gsl_vector_map(self.ptr, block); return self end
|
282
|
+
|
283
|
+
# Alternate version of #map!, in this case the block receives the index as a parameter.
|
284
|
+
def map_index!(&block); GSLng.backend::gsl_vector_map_index(self.ptr, block); return self end
|
285
|
+
|
286
|
+
# See #map!. Returns a Vector.
|
287
|
+
def map(&block); self.dup.map!(block) end
|
288
|
+
|
289
|
+
#--------------------- conversions -------------------------#
|
290
|
+
|
291
|
+
# Same as Array#join
|
292
|
+
def join(sep = $,)
|
293
|
+
s = ''
|
294
|
+
GSLng.backend::gsl_vector_each(self.ptr, lambda {|e| s += (s.empty?() ? e.to_s : "#{sep}#{e}")})
|
295
|
+
return s
|
296
|
+
end
|
297
|
+
|
298
|
+
# Coerces _other_ to be a Vector. If _other_ is a scalar (a Numeric) a vector of the same size as self
|
299
|
+
# will be created with all values set to _other_.
|
300
|
+
def coerce(other)
|
301
|
+
case other
|
302
|
+
when Vector
|
303
|
+
[ other, self ]
|
304
|
+
when Numeric
|
305
|
+
[ Vector.new(@size).set!(other), self ]
|
306
|
+
else
|
307
|
+
raise TypeError, "Can't coerce #{other.class} into #{self.class}"
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
# Convert to String (same format as Array#to_s):
|
312
|
+
# Vector[1,2,3].to_s => "[1.0, 2.0, 3.0]"
|
313
|
+
def to_s
|
314
|
+
"[" + self.join(', ') + "]"
|
315
|
+
end
|
316
|
+
|
317
|
+
def inspect #:nodoc:
|
318
|
+
"#{self}:Vector"
|
319
|
+
end
|
320
|
+
|
321
|
+
# Convert to Array
|
322
|
+
def to_a
|
323
|
+
Array.new(@size) {|i| self[i]}
|
324
|
+
end
|
325
|
+
|
326
|
+
# Create a row matrix from this vector
|
327
|
+
def to_matrix
|
328
|
+
m = Matrix.new(1, @size)
|
329
|
+
GSLng.backend::gsl_matrix_set_row(m.ptr, 0, self.ptr)
|
330
|
+
return m
|
331
|
+
end
|
332
|
+
alias_method :to_row, :to_matrix
|
333
|
+
|
334
|
+
# Create a column matrix from this vector
|
335
|
+
def transpose
|
336
|
+
m = Matrix.new(@size, 1)
|
337
|
+
GSLng.backend::gsl_matrix_set_col(m.ptr, 0, self.ptr)
|
338
|
+
return m
|
339
|
+
end
|
340
|
+
alias_method :to_column, :transpose
|
341
|
+
|
342
|
+
#--------------------- equality -------------------------#
|
343
|
+
|
344
|
+
# Element-by-element comparison. Admits comparing to Array.
|
345
|
+
def ==(other)
|
346
|
+
if (self.size != other.size) then return false end
|
347
|
+
self.fast_each_with_index do |elem,i|
|
348
|
+
if (elem != other[i]) then return false end
|
349
|
+
end
|
350
|
+
return true
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module GSLng
|
2
|
+
class Vector
|
3
|
+
# A View of a Vector.
|
4
|
+
#
|
5
|
+
# Views reference an existing Vector and can be used to access parts of it without having to copy
|
6
|
+
# it entirely. You can treat a View just like a Vector.
|
7
|
+
# But note that modifying elements of a View will modify the elements of the original vector.
|
8
|
+
#
|
9
|
+
class View < Vector
|
10
|
+
attr_reader :owner # The Vector owning the data this View uses
|
11
|
+
|
12
|
+
def initialize(owner, offset, size, stride = 1) #:nodoc:
|
13
|
+
@owner = owner
|
14
|
+
@size = size
|
15
|
+
if (stride == 1) then @ptr = GSLng.backend::gsl_vector_subvector2(owner.ptr, offset, size)
|
16
|
+
else @ptr = GSLng.backend::gsl_vector_subvector_with_stride2(owner.ptr, offset, stride, size) end
|
17
|
+
GSLng.set_finalizer(self, :gsl_vector_free, @ptr)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns a Vector (*NOT* a View) copied from this view. In other words,
|
21
|
+
# you'll get a Vector which you can modify without modifying #owner elements
|
22
|
+
def dup
|
23
|
+
v = Vector.new(@size)
|
24
|
+
GSLng.backend::gsl_vector_memcpy(v.ptr, @ptr)
|
25
|
+
return v
|
26
|
+
end
|
27
|
+
alias_method :clone, :dup
|
28
|
+
alias_method :to_vector, :dup
|
29
|
+
|
30
|
+
def view #:nodoc:
|
31
|
+
raise "Can't create a View from a View"
|
32
|
+
end
|
33
|
+
|
34
|
+
def inspect #:nodoc:
|
35
|
+
"#{self}:VectorView"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/gslng.rb
ADDED
data/test/benchmark.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/ruby -I../lib -I../ext
|
2
|
+
require 'benchmark'
|
3
|
+
require 'gslng'
|
4
|
+
require 'rbgsl'
|
5
|
+
require 'narray'
|
6
|
+
include Benchmark
|
7
|
+
|
8
|
+
n = 100
|
9
|
+
size = 5000
|
10
|
+
|
11
|
+
puts "This is GSLng #{GSLng::VERSION}"; puts
|
12
|
+
|
13
|
+
puts "Vector#each vs Vector#fast_each - vector of #{size} elements"
|
14
|
+
bm do |x|
|
15
|
+
v = GSLng::Vector.zero(size)
|
16
|
+
x.report("each :") {n.times {s = 0; v.each do |e| s += e end}}
|
17
|
+
x.report("fast_each :") {n.times {s = 0; v.fast_each do |e| s += e end}}
|
18
|
+
end
|
19
|
+
|
20
|
+
n = 500
|
21
|
+
size = 50000
|
22
|
+
|
23
|
+
puts "Norm (BLAS) - vector of #{size} elements"
|
24
|
+
bm do |x|
|
25
|
+
v = GSLng::Vector.random(size)
|
26
|
+
gv = GSL::Vector.alloc(v.to_a)
|
27
|
+
x.report("rb-gsl :") {n.times {gv.dnrm2}}
|
28
|
+
x.report("GSLng :") {n.times {v.norm}}
|
29
|
+
end
|
30
|
+
|
31
|
+
n=5000
|
32
|
+
size = 5000
|
33
|
+
puts "Vector product - two vectors of #{size} elements"
|
34
|
+
bm do |x|
|
35
|
+
v,v2 = GSLng::Vector.random(size),GSLng::Vector.random(size)
|
36
|
+
gv,gv2 = GSL::Vector.alloc(v.to_a),GSL::Vector.alloc(v2.to_a)
|
37
|
+
x.report("rb-gsl :") {n.times {gv.mul!(gv2)}}
|
38
|
+
x.report("GSLng :") {n.times {v.mul!(v2)}}
|
39
|
+
end
|
40
|
+
|
41
|
+
n=500
|
42
|
+
size = 5000
|
43
|
+
puts "Sort - vector of #{size} elements"
|
44
|
+
bm do |x|
|
45
|
+
v = GSLng::Vector.random(size)
|
46
|
+
gv = GSL::Vector.alloc(v.to_a)
|
47
|
+
x.report("rb-gsl :") {n.times {v.sort!}}
|
48
|
+
x.report("GSLng :") {n.times {gv.sort!}}
|
49
|
+
end
|
data/test/matrix_test.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# To change this template, choose Tools | Templates
|
2
|
+
# and open the template in the editor.
|
3
|
+
|
4
|
+
$:.unshift File.join(File.dirname(__FILE__),'..','lib')
|
5
|
+
$:.unshift File.join(File.dirname(__FILE__),'..','ext')
|
6
|
+
|
7
|
+
require 'test/unit'
|
8
|
+
require 'gslng'
|
9
|
+
|
10
|
+
include GSLng
|
11
|
+
|
12
|
+
class TestMatrix < Test::Unit::TestCase
|
13
|
+
def test_initialize
|
14
|
+
assert_equal([5,5], Matrix.new(5,5).size)
|
15
|
+
assert_equal([5,5], Matrix.zero(5,5).size)
|
16
|
+
assert(Matrix.zero(5,5).zero?)
|
17
|
+
assert_nothing_raised { Matrix.random(5,5) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_to_s
|
21
|
+
assert_equal("[0.0 0.0 0.0]", Matrix[0, 0, 0].to_s)
|
22
|
+
assert_equal("[0.0 1.0 2.0]", Matrix[0,1,2].to_s)
|
23
|
+
assert_equal("[1.0 2.0 3.0;\n 2.0 3.0 4.0]", Matrix[[1,2,3],[2,3,4]].to_s)
|
24
|
+
assert_equal("0.0 0.0 0.0", Matrix[0, 0, 0].join(' '))
|
25
|
+
assert_equal("1.0 2.0 3.0 2.0 3.0 4.0", Matrix[[1,2,3],[2,3,4]].join(' '))
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_equal
|
29
|
+
assert_equal(Matrix[0, 0, 0], Matrix[0, 0, 0])
|
30
|
+
assert_equal(Matrix[[1,2,3],[2,3,4]], Matrix[[1,2,3],[2,3,4]])
|
31
|
+
m = Matrix[[1,2,3],[2,3,4]]
|
32
|
+
assert_equal(m, m.dup)
|
33
|
+
assert_equal(Matrix[0,1,2], Matrix[0...3])
|
34
|
+
assert_equal(Matrix[[0,1,2],[1,2,3]], Matrix[0...3,1...4])
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_set_get
|
38
|
+
m = Matrix[[1,2,3],[2,3,4]]
|
39
|
+
m[0,0] = 3
|
40
|
+
assert_equal(Matrix[[3,2,3],[2,3,4]], m)
|
41
|
+
assert_equal(2, m[1,0])
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_complex_get
|
45
|
+
m = Matrix[[1,2,3],[2,3,4]]
|
46
|
+
assert_equal(m, m[:*,:*])
|
47
|
+
assert_equal(Vector[1, 2, 3], m[0,:*])
|
48
|
+
assert_equal(Vector[2, 3, 4], m[1,:*])
|
49
|
+
assert_equal(Vector[1, 2], m[:*,0])
|
50
|
+
assert_equal(Vector[2, 3], m[:*,1])
|
51
|
+
assert_equal(Vector[3, 4], m[:*,2])
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_complex_set
|
55
|
+
m = Matrix[[1,2,3],[2,3,4]]
|
56
|
+
m[0,:*] = 1
|
57
|
+
assert_equal(Matrix[[1,1,1],[2,3,4]], m)
|
58
|
+
m[0,:*] = Vector[1,2,4]
|
59
|
+
assert_equal(Matrix[[1,2,4],[2,3,4]], m)
|
60
|
+
|
61
|
+
m[:*,0] = 1
|
62
|
+
assert_equal(Matrix[[1,2,4],[1,3,4]], m)
|
63
|
+
m[:*,0] = Vector[1,2]
|
64
|
+
assert_equal(Matrix[[1,2,4],[2,3,4]], m)
|
65
|
+
|
66
|
+
m[:*,:*] = 1
|
67
|
+
assert_equal(Matrix[[1,1,1],[1,1,1]], m)
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_operators
|
71
|
+
assert_equal(Matrix[[1,4,3],[2,4,4]],Matrix[[1,2,3],[2,3,4]] + Matrix[[0,2,0],[0,1,0]])
|
72
|
+
assert_equal(Matrix[[1.5,2.5,3.5],[2.5,3.5,4.5]],Matrix[[1,2,3],[2,3,4]] + 0.5)
|
73
|
+
assert_equal(Matrix[[1.5,2.5,3.5],[2.5,3.5,4.5]],0.5 + Matrix[[1,2,3],[2,3,4]])
|
74
|
+
|
75
|
+
assert_equal(Matrix[[0,2,0],[0,4,0],[0,6,0]],Matrix[1,2,3].transpose * Vector[0,2,0])
|
76
|
+
assert_equal(Matrix[[4],[6]],Matrix[[1,2,3],[2,3,4]] * Vector[0,2,0].transpose)
|
77
|
+
assert_equal(Matrix[4, 6],Vector[0,2,0] * Matrix[[1,2],[2,3],[4,5]])
|
78
|
+
assert_equal(Matrix[[3,6],[9,12]],Matrix[[1,2],[3,4]] * 3)
|
79
|
+
|
80
|
+
assert_equal(Matrix[[4,6],[2,3]],Matrix[[0,2,0],[0,1,0]] * Matrix[[1,2],[2,3],[4,5]])
|
81
|
+
assert_equal(Matrix[[0,4,0],[0,1,0]],Matrix[[0,2,0],[0,1,0]] ^ Matrix[[0,2,0],[0,1,0]])
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_swaps
|
85
|
+
m = Matrix[[1,2,3],[2,3,4],[3,4,5]]
|
86
|
+
assert_equal(Matrix[[2,3,4],[1,2,3],[3,4,5]], m.swap_rows(0,1))
|
87
|
+
assert_equal(Matrix[[3,2,4],[2,1,3],[4,3,5]], m.swap_columns(0,1))
|
88
|
+
assert_equal(Matrix[[3,2,4],[2,1,3],[4,3,5]], m.swap_rowcol(0,0))
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_view
|
92
|
+
m,view = nil,nil
|
93
|
+
assert_nothing_raised {
|
94
|
+
m = Matrix[[1,2,3],[2,3,4]]
|
95
|
+
view = m.view
|
96
|
+
view[0,0] = 3
|
97
|
+
}
|
98
|
+
assert_equal(Matrix[[3,2,3],[2,3,4]], m)
|
99
|
+
assert_equal(Matrix[[3,2,3],[2,3,4]], view)
|
100
|
+
assert_equal(Matrix[3,4], m.view(1, 1))
|
101
|
+
assert_equal(Matrix[3,2], m.view(0, 0, nil, 1).transpose)
|
102
|
+
assert_equal(Matrix[[3],[2]], m.column_view(0))
|
103
|
+
assert_equal(Matrix[3,2,3], m.row_view(0))
|
104
|
+
end
|
105
|
+
end
|