nmatrix-atlas 0.2.1 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -285,6 +285,152 @@ describe "math" do
285
285
  end
286
286
  end
287
287
 
288
+ NON_INTEGER_DTYPES.each do |dtype|
289
+ next if dtype == :object
290
+ context dtype do
291
+
292
+ it "calculates QR decomposition using factorize_qr for a square matrix" do
293
+
294
+ a = NMatrix.new(3, [12.0, -51.0, 4.0,
295
+ 6.0, 167.0, -68.0,
296
+ -4.0, 24.0, -41.0] , dtype: dtype)
297
+
298
+ q_solution = NMatrix.new([3,3], Q_SOLUTION_ARRAY_2, dtype: dtype)
299
+
300
+ r_solution = NMatrix.new([3,3], [-14.0, -21.0, 14,
301
+ 0.0, -175, 70,
302
+ 0.0, 0.0, -35] , dtype: dtype)
303
+
304
+ err = case dtype
305
+ when :float32, :complex64
306
+ 1e-4
307
+ when :float64, :complex128
308
+ 1e-13
309
+ end
310
+
311
+ begin
312
+ q,r = a.factorize_qr
313
+
314
+ expect(q).to be_within(err).of(q_solution)
315
+ expect(r).to be_within(err).of(r_solution)
316
+
317
+ rescue NotImplementedError
318
+ pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
319
+ end
320
+ end
321
+
322
+ it "calculates QR decomposition using factorize_qr for a tall and narrow rectangular matrix" do
323
+
324
+ a = NMatrix.new([4,2], [34.0, 21.0,
325
+ 23.0, 53.0,
326
+ 26.0, 346.0,
327
+ 23.0, 121.0] , dtype: dtype)
328
+
329
+ q_solution = NMatrix.new([4,4], Q_SOLUTION_ARRAY_1, dtype: dtype)
330
+
331
+ r_solution = NMatrix.new([4,2], [-53.75872022286244, -255.06559574252242,
332
+ 0.0, 269.34836526051555,
333
+ 0.0, 0.0,
334
+ 0.0, 0.0] , dtype: dtype)
335
+
336
+ err = case dtype
337
+ when :float32, :complex64
338
+ 1e-4
339
+ when :float64, :complex128
340
+ 1e-13
341
+ end
342
+
343
+ begin
344
+ q,r = a.factorize_qr
345
+
346
+ expect(q).to be_within(err).of(q_solution)
347
+ expect(r).to be_within(err).of(r_solution)
348
+
349
+ rescue NotImplementedError
350
+ pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
351
+ end
352
+ end
353
+
354
+ it "calculates QR decomposition using factorize_qr for a short and wide rectangular matrix" do
355
+
356
+ a = NMatrix.new([3,4], [123,31,57,81,92,14,17,36,42,34,11,28], dtype: dtype)
357
+
358
+ q_solution = NMatrix.new([3,3], Q_SOLUTION_ARRAY_3, dtype: dtype)
359
+
360
+ r_solution = NMatrix.new([3,4], R_SOLUTION_ARRAY, dtype: dtype)
361
+
362
+ err = case dtype
363
+ when :float32, :complex64
364
+ 1e-4
365
+ when :float64, :complex128
366
+ 1e-13
367
+ end
368
+
369
+ begin
370
+ q,r = a.factorize_qr
371
+
372
+ expect(q).to be_within(err).of(q_solution)
373
+ expect(r).to be_within(err).of(r_solution)
374
+
375
+ rescue NotImplementedError
376
+ pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
377
+ end
378
+ end
379
+
380
+ it "calculates QR decomposition such that A - QR ~ 0" do
381
+
382
+ a = NMatrix.new([3,3], [ 9.0, 0.0, 26.0,
383
+ 12.0, 0.0, -7.0,
384
+ 0.0, 4.0, 0.0] , dtype: dtype)
385
+
386
+ err = case dtype
387
+ when :float32, :complex64
388
+ 1e-4
389
+ when :float64, :complex128
390
+ 1e-13
391
+ end
392
+
393
+ begin
394
+ q,r = a.factorize_qr
395
+ a_expected = q.dot(r)
396
+
397
+ expect(a_expected).to be_within(err).of(a)
398
+
399
+ rescue NotImplementedError
400
+ pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
401
+ end
402
+ end
403
+
404
+
405
+ it "calculates the orthogonal matrix Q in QR decomposition" do
406
+
407
+ a = N.new([2,2], [34.0, 21, 23, 53] , dtype: dtype)
408
+
409
+ err = case dtype
410
+ when :float32, :complex64
411
+ 1e-4
412
+ when :float64, :complex128
413
+ 1e-13
414
+ end
415
+
416
+ begin
417
+ q,r = a.factorize_qr
418
+
419
+ #Q is orthogonal if Q x Q.transpose = I
420
+ product = q.dot(q.transpose)
421
+
422
+ expect(product[0,0]).to be_within(err).of(1)
423
+ expect(product[1,0]).to be_within(err).of(0)
424
+ expect(product[0,1]).to be_within(err).of(0)
425
+ expect(product[1,1]).to be_within(err).of(1)
426
+
427
+ rescue NotImplementedError
428
+ pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
429
+ end
430
+ end
431
+ end
432
+ end
433
+
288
434
  ALL_DTYPES.each do |dtype|
289
435
  next if dtype == :byte #doesn't work for unsigned types
290
436
  next if dtype == :object
@@ -332,6 +478,49 @@ describe "math" do
332
478
  end
333
479
  end
334
480
 
481
+ ALL_DTYPES.each do |dtype|
482
+ next if dtype == :byte #doesn't work for unsigned types
483
+ next if dtype == :object
484
+
485
+ context dtype do
486
+ err = case dtype
487
+ when :float32, :complex64
488
+ 1e-4
489
+ else #integer matrices will return :float64
490
+ 1e-13
491
+ end
492
+
493
+ it "should correctly find adjugate a matrix in place (bang)" do
494
+ a = NMatrix.new(:dense, 2, [2, 3, 3, 5], dtype)
495
+ b = NMatrix.new(:dense, 2, [5, -3, -3, 2], dtype)
496
+
497
+ if a.integer_dtype?
498
+ expect{a.adjugate!}.to raise_error(DataTypeError)
499
+ else
500
+ #should return adjugate as well as modifying a
501
+ r = a.adjugate!
502
+ expect(a).to be_within(err).of(b)
503
+ expect(r).to be_within(err).of(b)
504
+ end
505
+ end
506
+
507
+
508
+ it "should correctly find adjugate of a matrix out-of-place" do
509
+ a = NMatrix.new(:dense, 3, [-3, 2, -5, -1, 0, -2, 3, -4, 1], dtype)
510
+
511
+ if a.integer_dtype?
512
+ b = NMatrix.new(:dense, 3, [-8, 18, -4, -5, 12, -1, 4, -6, 2], :float64)
513
+ else
514
+ b = NMatrix.new(:dense, 3, [-8, 18, -4, -5, 12, -1, 4, -6, 2], dtype)
515
+ end
516
+
517
+ expect(a.adjoint).to be_within(err).of(b)
518
+ expect(a.adjugate).to be_within(err).of(b)
519
+ end
520
+
521
+ end
522
+ end
523
+
335
524
  # TODO: Get it working with ROBJ too
336
525
  [:byte,:int8,:int16,:int32,:int64,:float32,:float64].each do |left_dtype|
337
526
  [:byte,:int8,:int16,:int32,:int64,:float32,:float64].each do |right_dtype|
@@ -702,7 +891,7 @@ describe "math" do
702
891
  360, 96, 51, -14,
703
892
  448,-231,-24,-87,
704
893
  -1168, 595,234, 523],
705
- dtype: answer_dtype,
894
+ dtype: answer_dtype,
706
895
  stype: stype))
707
896
  end
708
897
 
@@ -757,7 +946,6 @@ describe "math" do
757
946
 
758
947
  context "determinants" do
759
948
  ALL_DTYPES.each do |dtype|
760
- next if dtype == :object
761
949
  context dtype do
762
950
  before do
763
951
  @a = NMatrix.new([2,2], [1,2,
@@ -779,13 +967,19 @@ describe "math" do
779
967
  end
780
968
  end
781
969
  it "computes the determinant of 2x2 matrix" do
782
- expect(@a.det).to be_within(@err).of(-2)
970
+ if dtype != :object
971
+ expect(@a.det).to be_within(@err).of(-2)
972
+ end
783
973
  end
784
974
  it "computes the determinant of 3x3 matrix" do
785
- expect(@b.det).to be_within(@err).of(-8)
975
+ if dtype != :object
976
+ expect(@b.det).to be_within(@err).of(-8)
977
+ end
786
978
  end
787
979
  it "computes the determinant of 4x4 matrix" do
788
- expect(@c.det).to be_within(@err).of(-18)
980
+ if dtype != :object
981
+ expect(@c.det).to be_within(@err).of(-18)
982
+ end
789
983
  end
790
984
  it "computes the exact determinant of 2x2 matrix" do
791
985
  if dtype == :byte
@@ -804,4 +998,38 @@ describe "math" do
804
998
  end
805
999
  end
806
1000
  end
1001
+
1002
+ context "#scale and #scale!" do
1003
+ [:dense,:list,:yale].each do |stype|
1004
+ ALL_DTYPES.each do |dtype|
1005
+ next if dtype == :object
1006
+ context "for #{dtype}" do
1007
+ before do
1008
+ @m = NMatrix.new([3, 3], [0, 1, 2,
1009
+ 3, 4, 5,
1010
+ 6, 7, 8], stype: stype, dtype: dtype)
1011
+ end
1012
+ it "scales the matrix by a given factor and return the result" do
1013
+ if integer_dtype? dtype
1014
+ expect{@m.scale 2.0}.to raise_error(DataTypeError)
1015
+ else
1016
+ expect(@m.scale 2.0).to eq(NMatrix.new([3, 3], [0, 2, 4,
1017
+ 6, 8, 10,
1018
+ 12, 14, 16], stype: stype, dtype: dtype))
1019
+ end
1020
+ end
1021
+ it "scales the matrix in place by a given factor" do
1022
+ if dtype == :int8
1023
+ expect{@m.scale! 2}.to raise_error(DataTypeError)
1024
+ else
1025
+ @m.scale! 2
1026
+ expect(@m).to eq(NMatrix.new([3, 3], [0, 2, 4,
1027
+ 6, 8, 10,
1028
+ 12, 14, 16], stype: stype, dtype: dtype))
1029
+ end
1030
+ end
1031
+ end
1032
+ end
1033
+ end
1034
+ end
807
1035
  end
@@ -50,10 +50,45 @@ describe NMatrix do
50
50
  expect(m).to eq identity3
51
51
  end
52
52
 
53
+ it "hilbert() creates an hilbert matrix" do
54
+ m = NMatrix.hilbert(8)
55
+ expect(m[4, 0]).to be_within(0.000001).of(0.2)
56
+ expect(m[4, 1]).to be_within(0.000001).of(0.16666666666666666)
57
+ expect(m[4, 2]).to be_within(0.000001).of(0.14285714285714285)
58
+ expect(m[4, 3]).to be_within(0.000001).of(0.125)
59
+
60
+ m = NMatrix.hilbert(3)
61
+ hilbert3 = NMatrix.new([3, 3], [1.0, 0.5, 0.3333333333333333,\
62
+ 0.5, 0.3333333333333333, 0.25, 0.3333333333333333, 0.25, 0.2])
63
+ expect(m).to eq hilbert3
64
+ 0.upto(2) do |i|
65
+ 0.upto(2) do |j|
66
+ expect(m[i, j]).to be_within(0.000001).of(hilbert3[i,j])
67
+ end
68
+ end
69
+ end
70
+
71
+ it "inv_hilbert() creates an inverse hilbert matrix" do
72
+ m = NMatrix.inv_hilbert(6)
73
+ inv_hilbert6 = [3360.0, -88200.0, 564480.0, -1411200.0]
74
+ expect(m[2,0]).to be_within(0.000001).of(inv_hilbert6[0])
75
+ expect(m[2,1]).to be_within(0.000001).of(inv_hilbert6[1])
76
+ expect(m[2,2]).to be_within(0.000001).of(inv_hilbert6[2])
77
+ expect(m[2,3]).to be_within(0.000001).of(inv_hilbert6[3])
78
+
79
+ m = NMatrix.inv_hilbert(3)
80
+ inv_hilbert3 = NMatrix.new([3, 3], [ 9.0, -36.0, 30.0, -36.0, 192.0, -180.0, 30.0, -180.0, 180.0] )
81
+ 0.upto(2) do |i|
82
+ 0.upto(2) do |j|
83
+ expect(m[i, j]).to be_within(0.000001).of(inv_hilbert3[i,j])
84
+ end
85
+ end
86
+ end
87
+
53
88
  it "diag() creates a matrix with pre-supplied diagonal" do
54
89
  arr = [1,2,3,4]
55
90
  m = NMatrix.diag(arr)
56
- expect(m.is_a?(NMatrix)).to be_true
91
+ expect(m.is_a?(NMatrix)).to be true
57
92
  end
58
93
 
59
94
  it "diagonals() contains the seeded values on the diagonal" do
@@ -123,6 +158,112 @@ describe NMatrix do
123
158
  expect { NMatrix.random("not an array or integer") }.to raise_error
124
159
  end
125
160
  end
161
+
162
+ context "::magic" do
163
+
164
+ ALL_DTYPES.each do |dtype|
165
+ context dtype do
166
+ it "creates a matrix with numbers from 1 to n^n(n squared)" do
167
+ a = NMatrix.magic(3, dtype: dtype)
168
+ magic3 = NMatrix.new([3,3], [4, 9, 2, 3, 5, 7, 8, 1, 6], dtype: dtype)
169
+ expect(a).to eq magic3
170
+
171
+ b = NMatrix.magic(4, dtype: dtype)
172
+ magic4 = NMatrix.new([4,4], [1, 15, 14, 4, 12, 6, 7, 9, 8, 10, 11, 5, 13, 3, 2, 16], dtype: dtype)
173
+ expect(b).to eq magic4
174
+
175
+ c = NMatrix.magic(6, dtype: dtype)
176
+ magic6 = NMatrix.new([6,6], [31, 9, 2, 22, 27, 20, 3, 32, 7, 21, 23, 25, 35, 1, 6, 26, 19, 24, 4, 36, 29, 13, 18, 11, 30, 5, 34, 12, 14, 16, 8, 28, 33, 17, 10, 15], dtype: dtype)
177
+ expect(c).to eq magic6
178
+ end
179
+ end
180
+ end
181
+
182
+ it "shape of two is not allowed" do
183
+ expect { NMatrix.magic(2) }.to raise_error(ArgumentError)
184
+ end
185
+
186
+ it "Only accepts an integer as dimension" do
187
+ expect { NMatrix.magic(3.0) }.to raise_error(ArgumentError)
188
+ end
189
+ end
190
+
191
+ context "::linspace" do
192
+ it "creates a row vector when given only one shape parameter" do
193
+ v = NMatrix.linspace(1, 10, 4)
194
+ #Expect a row vector only
195
+ expect(v.shape.length).to eq(1)
196
+
197
+ ans = [1.0,4.0,7.0,10.0]
198
+
199
+ expect(v[0]).to be_within(0.000001).of(ans[0])
200
+ expect(v[1]).to be_within(0.000001).of(ans[1])
201
+ expect(v[2]).to be_within(0.000001).of(ans[2])
202
+ expect(v[3]).to be_within(0.000001).of(ans[3])
203
+ end
204
+
205
+ it "creates a matrix of input shape with each entry linearly spaced in row major order" do
206
+ v = NMatrix.linspace(1, Math::PI, [2,2])
207
+ expect(v.dtype).to eq(:float64)
208
+
209
+ ans = [1.0, 1.7138642072677612, 2.4277284145355225, 3.1415927410125732]
210
+
211
+ expect(v[0,0]).to be_within(0.000001).of(ans[0])
212
+ expect(v[0,1]).to be_within(0.000001).of(ans[1])
213
+ expect(v[1,0]).to be_within(0.000001).of(ans[2])
214
+ expect(v[1,1]).to be_within(0.000001).of(ans[3])
215
+ end
216
+ end
217
+
218
+ context "::logspace" do
219
+ it "creates a logarithmically spaced vector" do
220
+ v = NMatrix.logspace(1, 2, 10)
221
+
222
+ expect(v.shape.length).to eq(1)
223
+
224
+ #Unit test taken from Matlab R2015b output of logspace(1,2,10)
225
+ ans = [10.0000, 12.9155, 16.6810, 21.5443, 27.8256, 35.9381, 46.4159, 59.9484, 77.4264, 100.0000]
226
+
227
+ expect(v[0].round(4)).to be_within(0.000001).of(ans[0])
228
+ expect(v[1].round(4)).to be_within(0.000001).of(ans[1])
229
+ expect(v[2].round(4)).to be_within(0.000001).of(ans[2])
230
+ expect(v[3].round(4)).to be_within(0.000001).of(ans[3])
231
+ expect(v[4].round(4)).to be_within(0.000001).of(ans[4])
232
+ expect(v[5].round(4)).to be_within(0.000001).of(ans[5])
233
+ expect(v[6].round(4)).to be_within(0.000001).of(ans[6])
234
+ expect(v[7].round(4)).to be_within(0.000001).of(ans[7])
235
+ expect(v[8].round(4)).to be_within(0.000001).of(ans[8])
236
+ expect(v[9].round(4)).to be_within(0.000001).of(ans[9])
237
+ end
238
+
239
+ it "creates a logarithmically spaced vector bounded by Math::PI if :pi is pre-supplied" do
240
+ v = NMatrix.logspace(1, :pi, 7)
241
+
242
+ #Unit test taken from Matlab R2015b output of logspace(1,pi,10)
243
+ ans = [10.0000, 8.2450, 6.7980, 5.6050, 4.6213, 3.8103, 3.1416]
244
+
245
+ expect(v[0].round(4)).to be_within(0.000001).of(ans[0])
246
+ expect(v[1].round(4)).to be_within(0.000001).of(ans[1])
247
+ expect(v[2].round(4)).to be_within(0.000001).of(ans[2])
248
+ expect(v[3].round(4)).to be_within(0.000001).of(ans[3])
249
+ expect(v[4].round(4)).to be_within(0.000001).of(ans[4])
250
+ expect(v[5].round(4)).to be_within(0.000001).of(ans[5])
251
+ expect(v[6].round(4)).to be_within(0.000001).of(ans[6])
252
+ end
253
+
254
+ it "creates a matrix of input shape with each entry logarithmically spaced in row major order" do
255
+ v = NMatrix.logspace(1, 2, [3,2])
256
+
257
+ ans = [10.0, 15.8489, 25.1189, 39.8107, 63.0957, 100.0]
258
+
259
+ expect(v[0,0].round(4)).to be_within(0.000001).of(ans[0])
260
+ expect(v[0,1].round(4)).to be_within(0.000001).of(ans[1])
261
+ expect(v[1,0].round(4)).to be_within(0.000001).of(ans[2])
262
+ expect(v[1,1].round(4)).to be_within(0.000001).of(ans[3])
263
+ expect(v[2,0].round(4)).to be_within(0.000001).of(ans[4])
264
+ expect(v[2,1].round(4)).to be_within(0.000001).of(ans[5])
265
+ end
266
+ end
126
267
 
127
268
  it "seq() creates a matrix of integers, sequentially" do
128
269
  m = NMatrix.seq(2) # 2x2 matrix.
@@ -136,7 +277,6 @@ describe NMatrix do
136
277
  end
137
278
  end
138
279
 
139
-
140
280
  it "indgen() creates a matrix of integers as well as seq()" do
141
281
  m = NMatrix.indgen(2) # 2x2 matrix.
142
282
  value = 0
@@ -189,19 +329,19 @@ describe NMatrix do
189
329
  it "column() returns a NMatrix" do
190
330
  m = NMatrix.random(3)
191
331
 
192
- expect(m.column(2).is_a?(NMatrix)).to be_true
332
+ expect(m.column(2).is_a?(NMatrix)).to be true
193
333
  end
194
334
 
195
335
  it "row() returns a NMatrix" do
196
336
  m = NMatrix.random(3)
197
337
 
198
- expect(m.row(2).is_a?(NMatrix)).to be_true
338
+ expect(m.row(2).is_a?(NMatrix)).to be true
199
339
  end
200
340
 
201
341
  it "diagonals() creates an NMatrix" do
202
342
  arr = [1,2,3,4]
203
343
  m = NMatrix.diagonals(arr)
204
- expect(m.is_a?(NMatrix)).to be_true
344
+ expect(m.is_a?(NMatrix)).to be true
205
345
  end
206
346
 
207
347
  it "diagonals() contains the seeded values on the diagonal" do