ruby-gsl-ng 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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