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.
@@ -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
@@ -0,0 +1,11 @@
1
+ require 'gslng/backend'
2
+ require 'gslng/finalizer'
3
+
4
+ require 'gslng/vector'
5
+ require 'gslng/vector_view'
6
+ require 'gslng/matrix'
7
+ require 'gslng/matrix_view'
8
+
9
+ module GSLng
10
+ VERSION = "0.2.0"
11
+ end
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
@@ -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
data/test/test_gsl.rb CHANGED
@@ -6,4 +6,5 @@ require 'test/unit'
6
6
  # Add your testcases here
7
7
 
8
8
  require 'vector_test'
9
+ require 'matrix_test'
9
10