nmatrix-atlas 0.2.1 → 0.2.3

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.
@@ -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