pnmatrix 1.2.4 → 2.0.1

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.
@@ -196,11 +196,8 @@ class NMatrix
196
196
  i += 1
197
197
  end
198
198
 
199
- # A row vector should be stored as 1xN, not N
200
- #shape.unshift(1) if shape.size == 1
201
-
202
199
  # Then flatten the array.
203
- NMatrix.new(shape, params.flatten, options)
200
+ NMatrix.new(shape, params.flatten, **options)
204
201
  end
205
202
 
206
203
  #
@@ -230,7 +227,7 @@ class NMatrix
230
227
  # NMatrix.zeros([1, 5], dtype: :int32) # => 0 0 0 0 0
231
228
  #
232
229
  def zeros(shape, opts = {})
233
- NMatrix.new(shape, 0, {:dtype => :float64}.merge(opts))
230
+ NMatrix.new(shape, 0, **{:dtype => :float64}.merge(opts))
234
231
  end
235
232
  alias :zeroes :zeros
236
233
 
@@ -255,7 +252,7 @@ class NMatrix
255
252
  # 1 1 1
256
253
  #
257
254
  def ones(shape, opts={})
258
- NMatrix.new(shape, 1, {:dtype => :float64, :default => 1}.merge(opts))
255
+ NMatrix.new(shape, 1, **{:dtype => :float64}.merge(opts))
259
256
  end
260
257
 
261
258
  # call-seq:
@@ -268,7 +265,7 @@ class NMatrix
268
265
  # @return [NMatrix] a new nmatrix filled with ones.
269
266
  #
270
267
  def ones_like(nm)
271
- NMatrix.ones(nm.shape, dtype: nm.dtype, stype: nm.stype, capacity: nm.capacity, default: 1)
268
+ NMatrix.ones(nm.shape, dtype: nm.dtype, stype: nm.stype, capacity: nm.capacity)
272
269
  end
273
270
 
274
271
  # call-seq:
@@ -281,7 +278,7 @@ class NMatrix
281
278
  # @return [NMatrix] a new nmatrix filled with zeros.
282
279
  #
283
280
  def zeros_like(nm)
284
- NMatrix.zeros(nm.shape, dtype: nm.dtype, stype: nm.stype, capacity: nm.capacity, default: 0)
281
+ NMatrix.zeros(nm.shape, dtype: nm.dtype, stype: nm.stype, capacity: nm.capacity)
285
282
  end
286
283
 
287
284
  #
@@ -345,7 +342,7 @@ class NMatrix
345
342
  # 0.3333333333333333 0.25 0.2
346
343
  #
347
344
  def hilbert(shape, opts={})
348
- m = NMatrix.new([shape,shape], {:dtype => :float64}.merge(opts))
345
+ m = NMatrix.new([shape,shape], **{:dtype => :float64}.merge(opts))
349
346
  0.upto(shape - 1) do |i|
350
347
  0.upto(i) do |j|
351
348
  m[i,j] = 1.0 / (j + i + 1)
@@ -378,8 +375,8 @@ class NMatrix
378
375
  #
379
376
  def inv_hilbert(shape, opts={})
380
377
  opts = {:dtype => :float64}.merge(opts)
381
- m = NMatrix.new([shape,shape],opts)
382
- combination = NMatrix.new([2*shape,2*shape],opts)
378
+ m = NMatrix.new([shape,shape], **opts)
379
+ combination = NMatrix.new([2*shape,2*shape], **opts)
383
380
  #combinations refers to the combination of n things taken k at a time
384
381
  0.upto(2*shape-1) do |i|
385
382
  0.upto(i) do |j|
@@ -553,7 +550,7 @@ class NMatrix
553
550
  NMatrix.size(shape).times { |i| random_values << rng.rand(scale) }
554
551
  end
555
552
 
556
- NMatrix.new(shape, random_values, {:dtype => :float64, :stype => :dense}.merge(opts))
553
+ NMatrix.new(shape, random_values, **{:dtype => :float64, :stype => :dense}.merge(opts))
557
554
  end
558
555
  alias :rand :random
559
556
 
@@ -595,7 +592,7 @@ class NMatrix
595
592
  #
596
593
  def magic(shape, opts={})
597
594
  raise(ArgumentError, "shape of two is not allowed") if shape == 2
598
- nm = NMatrix.new([shape,shape], 0, {:dtype => :float64}.merge(opts))
595
+ nm = NMatrix.new([shape,shape], 0, **{:dtype => :float64}.merge(opts))
599
596
  if shape % 2 != 0
600
597
  MagicHelpers.odd_magic nm, shape
601
598
  elsif shape % 4 == 0
@@ -841,7 +838,7 @@ class NMatrix
841
838
  values = (0 ... NMatrix.size(shape)).to_a
842
839
 
843
840
  # It'll produce :int32, except if a dtype is provided.
844
- NMatrix.new(shape, values, {:stype => :dense}.merge(options))
841
+ NMatrix.new(shape, values, **{:stype => :dense}.merge(options))
845
842
  end
846
843
 
847
844
  {:bindgen => :byte, :indgen => :int64, :findgen => :float32, :dindgen => :float64,
@@ -969,7 +966,7 @@ module NVector #:nodoc:
969
966
  random_values = []
970
967
  size.times { |i| random_values << rng.rand }
971
968
 
972
- NMatrix.new([size,1], random_values, opts)
969
+ NMatrix.new([size,1], random_values, **opts)
973
970
  end
974
971
 
975
972
  #
@@ -27,13 +27,12 @@ class NMatrix
27
27
  # native IO. If you change the format, please make sure that native
28
28
  # IO can still understand NMatrix::VERSION.
29
29
  module VERSION #:nodoc:
30
- MAJOR = 1
31
- MINOR = 2
32
- TINY = 4
30
+ MAJOR = 2
31
+ MINOR = 0
32
+ TINY = 1
33
33
  #PRE = "a"
34
34
 
35
35
  STRING = [MAJOR, MINOR, TINY].compact.join(".")
36
36
  #STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
37
37
  end
38
38
  end
39
-
data/spec/io_spec.rb CHANGED
@@ -51,6 +51,7 @@ describe NMatrix::IO do
51
51
  end
52
52
 
53
53
  it "reads MATLAB .mat file containing a single square sparse matrix" do
54
+ skip "Mat5Reader was removed from pnmatrix" unless defined?(NMatrix::IO::Matlab::Mat5Reader)
54
55
  pending("not yet implemented for NMatrix-JRuby") if jruby?
55
56
  n = NMatrix::IO::Matlab.load_mat("spec/4x4_sparse.mat")
56
57
  expect(n[0,0]).to eq(2)
@@ -62,19 +63,20 @@ describe NMatrix::IO do
62
63
  end
63
64
 
64
65
  it "reads MATLAB .mat file containing a single dense integer matrix" do
66
+ skip "Mat5Reader was removed from pnmatrix" unless defined?(NMatrix::IO::Matlab::Mat5Reader)
65
67
  n = NMatrix::IO::Matlab.load_mat("spec/4x5_dense.mat")
66
68
  m = NMatrix.new([4,5], [16,17,18,19,20,15,14,13,12,11,6,7,8,9,10,5,4,3,2,1])
67
69
  expect(n).to eq(m)
68
70
  end
69
71
 
70
72
  it "reads MATLAB .mat file containing a single dense double matrix" do
73
+ skip "Mat5Reader was removed from pnmatrix" unless defined?(NMatrix::IO::Matlab::Mat5Reader)
71
74
  n = NMatrix::IO::Matlab.load_mat("spec/2x2_dense_double.mat")
72
75
  m = NMatrix.new(2, [1.1, 2.0, 3.0, 4.0], dtype: :float64)
73
76
  expect(n).to eq(m)
74
77
  end
75
78
 
76
79
  it "loads and saves MatrixMarket .mtx file containing a single large sparse double matrix" do
77
- pending "spec disabled because it's so slow"
78
80
  n = NMatrix::IO::Market.load("spec/utm5940.mtx")
79
81
  NMatrix::IO::Market.save(n, "spec/utm5940.saved.mtx")
80
82
  expect(`wc -l spec/utm5940.mtx`.split[0]).to eq(`wc -l spec/utm5940.saved.mtx`.split[0])
@@ -273,8 +273,9 @@ describe "NMatrix::LAPACK functions with internal implementations" do
273
273
  end
274
274
 
275
275
  expect(s).to be_within(err).of(s_true)
276
- expect(u).to be_within(err).of(u_true)
277
- expect(vt).to be_within(err).of(vt_true)
276
+ # Singular vectors are only defined up to sign (or complex phase).
277
+ expect(u.abs).to be_within(err).of(u_true.abs)
278
+ expect(vt.abs).to be_within(err).of(vt_true.abs)
278
279
 
279
280
  expect(s.dtype).to eq(a.abs_dtype)
280
281
  expect(u.dtype).to eq(dtype)
@@ -306,8 +307,9 @@ describe "NMatrix::LAPACK functions with internal implementations" do
306
307
  end
307
308
 
308
309
  expect(s).to be_within(err).of(s_true)
309
- expect(u).to be_within(err).of(u_true)
310
- expect(vt).to be_within(err).of(vt_true)
310
+ # Singular vectors are only defined up to sign (or complex phase).
311
+ expect(u.abs).to be_within(err).of(u_true.abs)
312
+ expect(vt.abs).to be_within(err).of(vt_true.abs)
311
313
  end
312
314
 
313
315
 
@@ -337,8 +339,9 @@ describe "NMatrix::LAPACK functions with internal implementations" do
337
339
  end
338
340
 
339
341
  expect(eigenvalues).to be_within(err).of(eigenvalues_true)
340
- expect(vr).to be_within(err).of(vr_true)
341
- expect(vl).to be_within(err).of(vl_true)
342
+ # Eigenvectors are only defined up to sign (or complex phase).
343
+ expect(vr.abs).to be_within(err).of(vr_true.abs)
344
+ expect(vl.abs).to be_within(err).of(vl_true.abs)
342
345
 
343
346
  expect(eigenvalues.dtype).to eq(NMatrix.upcast(dtype, :complex64))
344
347
  expect(vr.dtype).to eq(NMatrix.upcast(dtype, :complex64))
@@ -384,7 +387,8 @@ describe "NMatrix::LAPACK functions with internal implementations" do
384
387
  end
385
388
 
386
389
  expect(eigenvalues).to be_within(err).of(eigenvalues_true)
387
- expect(vr).to be_within(err).of(vr_true)
390
+ # Eigenvectors are only defined up to sign (or complex phase).
391
+ expect(vr.abs).to be_within(err).of(vr_true.abs)
388
392
  expect(vl).to be_within(err).of(vl_true)
389
393
 
390
394
  expect(eigenvalues.dtype).to eq(dtype)
@@ -441,7 +445,8 @@ describe "NMatrix::LAPACK functions with internal implementations" do
441
445
  end
442
446
 
443
447
  expect(eigenvalues).to be_within(err).of(eigenvalues_true)
444
- expect(vr).to be_within(err).of(vr_true)
448
+ # Eigenvectors are only defined up to sign (or complex phase).
449
+ expect(vr.abs).to be_within(err).of(vr_true.abs)
445
450
  end
446
451
  end
447
452
  end
data/spec/math_spec.rb CHANGED
@@ -82,8 +82,10 @@ describe "math" do
82
82
  end
83
83
 
84
84
  it "should correctly apply elementwise natural log" do
85
- expect(@m.log).to eq N.new(@size, [0, Math.log(2), Math.log(3), Math.log(4)],
86
- dtype: :float64, stype: stype)
85
+ expect(@m.log).to be_within(1e-14).of(
86
+ N.new(@size, [0, Math.log(2), Math.log(3), Math.log(4)],
87
+ dtype: :float64, stype: stype)
88
+ )
87
89
  end
88
90
 
89
91
  it "should correctly apply elementwise log with arbitrary base" do
@@ -264,6 +266,7 @@ describe "math" do
264
266
  expect(r).to eq(b)
265
267
  rescue NotImplementedError
266
268
  pending "potrf! not implemented without plugins"
269
+ raise
267
270
  end
268
271
  end
269
272
 
@@ -280,6 +283,7 @@ describe "math" do
280
283
  expect(r).to eq(b)
281
284
  rescue NotImplementedError
282
285
  pending "potrf! not implemented without plugins"
286
+ raise
283
287
  end
284
288
  end
285
289
 
@@ -295,6 +299,7 @@ describe "math" do
295
299
  expect(l).to eq(l_true)
296
300
  rescue NotImplementedError
297
301
  pending "potrf! not implemented without plugins"
302
+ raise
298
303
  end
299
304
  end
300
305
  end
@@ -325,11 +330,13 @@ describe "math" do
325
330
  begin
326
331
  q,r = a.factorize_qr
327
332
 
328
- expect(q).to be_within(err).of(q_solution)
329
- expect(r).to be_within(err).of(r_solution)
333
+ # QR factors are only defined up to matched column/row signs.
334
+ expect(q.abs).to be_within(err).of(q_solution.abs)
335
+ expect(r.abs).to be_within(err).of(r_solution.abs)
330
336
 
331
337
  rescue NotImplementedError
332
338
  pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
339
+ raise
333
340
  end
334
341
  end
335
342
 
@@ -359,11 +366,13 @@ describe "math" do
359
366
  begin
360
367
  q,r = a.factorize_qr
361
368
 
362
- expect(q).to be_within(err).of(q_solution)
363
- expect(r).to be_within(err).of(r_solution)
369
+ # QR factors are only defined up to matched column/row signs.
370
+ expect(q.abs).to be_within(err).of(q_solution.abs)
371
+ expect(r.abs).to be_within(err).of(r_solution.abs)
364
372
 
365
373
  rescue NotImplementedError
366
374
  pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
375
+ raise
367
376
  end
368
377
  end
369
378
 
@@ -387,11 +396,13 @@ describe "math" do
387
396
  begin
388
397
  q,r = a.factorize_qr
389
398
 
390
- expect(q).to be_within(err).of(q_solution)
391
- expect(r).to be_within(err).of(r_solution)
399
+ # QR factors are only defined up to matched column/row signs.
400
+ expect(q.abs).to be_within(err).of(q_solution.abs)
401
+ expect(r.abs).to be_within(err).of(r_solution.abs)
392
402
 
393
403
  rescue NotImplementedError
394
404
  pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
405
+ raise
395
406
  end
396
407
  end
397
408
 
@@ -416,6 +427,7 @@ describe "math" do
416
427
 
417
428
  rescue NotImplementedError
418
429
  pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
430
+ raise
419
431
  end
420
432
  end
421
433
 
@@ -444,6 +456,7 @@ describe "math" do
444
456
 
445
457
  rescue NotImplementedError
446
458
  pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
459
+ raise
447
460
  end
448
461
  end
449
462
  end
@@ -461,7 +474,7 @@ describe "math" do
461
474
  end
462
475
 
463
476
  it "should correctly invert a matrix in place (bang)" do
464
- pending("not yet implemented for :object dtype") if dtype == :object
477
+ pending("not yet implemented for :object dtype") if dtype == :object && defined?(NMatrix::LAPACKE)
465
478
  a = NMatrix.new(:dense, 5, [1, 8,-9, 7, 5,
466
479
  0, 1, 0, 4, 4,
467
480
  0, 0, 1, 2, 5,
@@ -529,6 +542,7 @@ describe "math" do
529
542
  expect(a.dot(a.pinv)).to be_within(err).of(b)
530
543
  rescue NotImplementedError
531
544
  pending "Suppressing a NotImplementedError when the atlas plugin is not available"
545
+ raise
532
546
  end
533
547
 
534
548
  else
@@ -539,6 +553,7 @@ describe "math" do
539
553
  expect(a.dot(a.pinv)).to be_within(err).of(b)
540
554
  rescue NotImplementedError
541
555
  pending "Suppressing a NotImplementedError when the atlas plugin is not available"
556
+ raise
542
557
  end
543
558
  end
544
559
  end
@@ -556,6 +571,7 @@ describe "math" do
556
571
  expect(b.dot(a.dot(b))).to be_within(err).of(b)
557
572
  rescue NotImplementedError
558
573
  pending "Suppressing a NotImplementedError when the atlas plugin is not available"
574
+ raise
559
575
  end
560
576
 
561
577
  else
@@ -567,6 +583,7 @@ describe "math" do
567
583
  expect(b.dot(a.dot(b))).to be_within(err).of(b)
568
584
  rescue NotImplementedError
569
585
  pending "Suppressing a NotImplementedError when the atlas plugin is not available"
586
+ raise
570
587
  end
571
588
  end
572
589
  end
@@ -1134,7 +1151,6 @@ describe "math" do
1134
1151
  expect(@c.det).to be_within(@err).of(-18)
1135
1152
  end
1136
1153
  it "computes the exact determinant of 2x2 matrix" do
1137
- pending("not yet implemented for :object dtype") if dtype == :object
1138
1154
  if dtype == :byte
1139
1155
  expect{@a.det_exact}.to raise_error(DataTypeError)
1140
1156
  else
@@ -1166,7 +1182,6 @@ describe "math" do
1166
1182
  end
1167
1183
 
1168
1184
  it "scales the matrix by a given factor and return the result" do
1169
- pending("not yet implemented for :object dtype") if dtype == :object
1170
1185
  if integer_dtype? dtype
1171
1186
  expect{@m.scale 2.0}.to raise_error(DataTypeError)
1172
1187
  else
@@ -1178,7 +1193,6 @@ describe "math" do
1178
1193
  end
1179
1194
 
1180
1195
  it "scales the matrix in place by a given factor" do
1181
- pending("not yet implemented for :object dtype") if dtype == :object
1182
1196
  if dtype == :int8
1183
1197
  expect{@m.scale! 2}.to raise_error(DataTypeError)
1184
1198
  else
@@ -1196,7 +1210,6 @@ describe "math" do
1196
1210
  context "matrix_norm" do
1197
1211
  ALL_DTYPES.each do |dtype|
1198
1212
  context dtype do
1199
- pending("not yet implemented for :object dtype") if dtype == :object
1200
1213
  before do
1201
1214
  @n = NMatrix.new([3,3], [-4,-3,-2,
1202
1215
  -1, 0, 1,
@@ -1215,6 +1228,7 @@ describe "math" do
1215
1228
 
1216
1229
  rescue NotImplementedError
1217
1230
  pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
1231
+ raise
1218
1232
  end
1219
1233
  end
1220
1234
  end
@@ -1240,6 +1254,7 @@ describe "math" do
1240
1254
  expect(@n.matrix_norm(-2)).to be_within(@matrix_norm_TOLERANCE).of(0.0)
1241
1255
  rescue NotImplementedError
1242
1256
  pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
1257
+ raise
1243
1258
  end
1244
1259
  expect(@n.matrix_norm(-1)).to eq(6)
1245
1260
  end
@@ -1293,6 +1308,7 @@ describe "math" do
1293
1308
 
1294
1309
  rescue NotImplementedError
1295
1310
  pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
1311
+ raise
1296
1312
  end
1297
1313
  end
1298
1314
 
@@ -1308,6 +1324,7 @@ describe "math" do
1308
1324
 
1309
1325
  rescue NotImplementedError
1310
1326
  pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
1327
+ raise
1311
1328
  end
1312
1329
  end
1313
1330
 
@@ -1323,6 +1340,7 @@ describe "math" do
1323
1340
 
1324
1341
  rescue NotImplementedError
1325
1342
  pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
1343
+ raise
1326
1344
  end
1327
1345
  end
1328
1346
 
@@ -1338,6 +1356,7 @@ describe "math" do
1338
1356
 
1339
1357
  rescue NotImplementedError
1340
1358
  pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
1359
+ raise
1341
1360
  end
1342
1361
  end
1343
1362
 
@@ -1353,6 +1372,7 @@ describe "math" do
1353
1372
 
1354
1373
  rescue NotImplementedError
1355
1374
  pending "Suppressing a NotImplementedError when the lapacke plugin is not available"
1375
+ raise
1356
1376
  end
1357
1377
  end
1358
1378
 
@@ -0,0 +1,181 @@
1
+ require 'spec_helper'
2
+
3
+ describe NMatrix do
4
+ def force_gc
5
+ 20.times do
6
+ Array.new(1_000) { |i| "gc-pressure-#{i}" }
7
+ GC.start
8
+ end
9
+ end
10
+
11
+ def build_dense_object_reference
12
+ values = Array.new(64 * 64) { |i| "dense-object-#{i}" }
13
+ matrix = NMatrix.new([64, 64], values, stype: :dense, dtype: :object)
14
+
15
+ matrix[50...64, 45...64]
16
+ end
17
+
18
+ def build_yale_object_reference
19
+ rng = Random.new(5678)
20
+ matrix = NMatrix.new([35, 35], nil, stype: :yale, dtype: :object, capacity: 1)
21
+
22
+ 700.times do |i|
23
+ matrix[rng.rand(35), rng.rand(35)] = "yale-object-#{i}"
24
+ end
25
+
26
+ matrix[3...30, 4...32]
27
+ end
28
+
29
+ def build_nested_yale_object_reference
30
+ rng = Random.new(6789)
31
+ matrix = NMatrix.new([45, 45], nil, stype: :yale, dtype: :object, capacity: 1)
32
+
33
+ 1_000.times do |i|
34
+ matrix[rng.rand(45), rng.rand(45)] = "nested-yale-object-#{i}"
35
+ end
36
+
37
+ matrix[5...40, 6...39][7...28, 8...30]
38
+ end
39
+
40
+ def build_list_object_reference
41
+ matrix = NMatrix.new([30, 30], nil, stype: :list, dtype: :object)
42
+
43
+ 30.times do |i|
44
+ matrix[i, (i * 7) % 30] = "list-object-#{i}"
45
+ end
46
+
47
+ matrix[4...28, 2...29]
48
+ end
49
+
50
+ def mutate_dense_object_matrix_without_external_references
51
+ rng = Random.new(555)
52
+ matrix = NMatrix.new([80, 80], nil, stype: :dense, dtype: :object)
53
+
54
+ 5_000.times do |i|
55
+ matrix[rng.rand(80), rng.rand(80)] = "mut-dense-#{i}"
56
+ GC.start if (i % 25).zero?
57
+ end
58
+
59
+ matrix
60
+ end
61
+
62
+ def mutate_yale_object_matrix_without_external_references
63
+ rng = Random.new(333)
64
+ matrix = NMatrix.new([80, 80], nil, stype: :yale, dtype: :object, capacity: 1)
65
+
66
+ 5_000.times do |i|
67
+ matrix[rng.rand(80), rng.rand(80)] = "mut-yale-#{i}"
68
+ GC.start if (i % 25).zero?
69
+ end
70
+
71
+ matrix
72
+ end
73
+
74
+ def mutate_list_object_matrix_without_external_references
75
+ rng = Random.new(888)
76
+ matrix = NMatrix.new([30, 30], nil, stype: :list, dtype: :object)
77
+
78
+ 500.times do |i|
79
+ matrix[rng.rand(30), rng.rand(30)] = "mut-list-#{i}"
80
+ GC.start if (i % 10).zero?
81
+ end
82
+
83
+ matrix
84
+ end
85
+
86
+ def touch_reference(reference)
87
+ reference.each_stored_with_indices do |value, _i, _j|
88
+ value.to_s.hash
89
+ end
90
+ end
91
+
92
+ specify do
93
+ 200.times do |i|
94
+ size = rand(500..1000)
95
+ m1 = NMatrix.new([size, size], 0, stype: :yale, dtype: :int32)
96
+ m2 = m1.clone
97
+
98
+ rand(1000).times do |j|
99
+ m1[rand(size), rand(size)] = 1
100
+ m2[rand(size), rand(size)] = 1
101
+ end
102
+
103
+ (m1*m2).det
104
+ end
105
+ end
106
+
107
+ specify "does not segfault when iterating a GC-stressed object Yale reference" do
108
+ reference = build_yale_object_reference
109
+
110
+ force_gc
111
+
112
+ touch_reference(reference)
113
+ end
114
+
115
+ specify "does not segfault when iterating a GC-stressed nested object Yale reference" do
116
+ reference = build_nested_yale_object_reference
117
+
118
+ force_gc
119
+
120
+ touch_reference(reference)
121
+ end
122
+
123
+ specify "does not segfault when iterating a GC-stressed object dense reference" do
124
+ reference = build_dense_object_reference
125
+
126
+ force_gc
127
+
128
+ reference.each_with_indices do |value, _i, _j|
129
+ value.to_s.hash
130
+ end
131
+ end
132
+
133
+ specify "does not segfault when iterating a GC-stressed object list reference" do
134
+ reference = build_list_object_reference
135
+
136
+ force_gc
137
+
138
+ touch_reference(reference)
139
+ end
140
+
141
+ specify "does not segfault after GC-stressed object dense mutation" do
142
+ matrix = mutate_dense_object_matrix_without_external_references
143
+
144
+ force_gc
145
+
146
+ matrix.each do |value|
147
+ value.to_s.hash
148
+ end
149
+ end
150
+
151
+ specify "does not segfault after GC-stressed object Yale mutation" do
152
+ matrix = mutate_yale_object_matrix_without_external_references
153
+
154
+ force_gc
155
+
156
+ touch_reference(matrix)
157
+ end
158
+
159
+ specify "does not segfault after GC-stressed object list mutation" do
160
+ matrix = mutate_list_object_matrix_without_external_references
161
+
162
+ force_gc
163
+
164
+ touch_reference(matrix)
165
+ end
166
+
167
+ specify "does not expose partially built Yale object storage during a cast" do
168
+ previous_gc_stress = GC.stress
169
+ GC.stress = true
170
+
171
+ current_stock = NMatrix.new([30, 1], 0, stype: :yale, dtype: :float64)
172
+ current_stock[3] = -4
173
+ current_stock[7] = 10
174
+ current_stock[11] = 50
175
+
176
+ quantity_made = (current_stock < 0).cast(dtype: :int32) * current_stock * -1
177
+ expect(quantity_made[3]).to eq(4)
178
+ ensure
179
+ GC.stress = previous_gc_stress
180
+ end
181
+ end