Skip to content

Commit 032d5d6

Browse files
committed
fix: int64 indexing migration batch 6 - test fixes for NumPy 2.x int64 default
Root cause identified: "memory corruption" errors were NOT actual corruption. Tests were calling GetInt32() on Int64 arrays (np.arange now returns int64). Fixes: - np.random.shuffle.cs: NextInt64 → NextLong (correct method name) - np.random.shuffle.cs: SwapSlicesAxis0 int → long parameters - BattleProofTests.cs: GetInt32 → GetInt64 for arange-based tests - np.transpose.Test.cs: long[] → int[] for axis array (axes stay int) - ReadmeExample.cs: cast n_samples to int for np.ones() calls - NpApiOverloadTests: int → long for count_nonzero return, NDArray<int>[] → NDArray<long>[] for nonzero - BooleanIndexing.BattleTests.cs: shape.SequenceEqual(new[]) → shape.SequenceEqual(new long[]) - Updated INT64_MIGRATION_PROGRESS.md with root cause analysis
1 parent fa43eef commit 032d5d6

7 files changed

Lines changed: 73 additions & 44 deletions

File tree

docs/INT64_MIGRATION_PROGRESS.md

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,27 @@ This document tracks the progress of migrating recent commits to comply with the
1818

1919
---
2020

21+
## Completed Fixes (Session 3)
22+
23+
### 30. np.random.shuffle.cs - NextLong fix
24+
25+
**Location**: `src/NumSharp.Core/RandomSampling/np.random.shuffle.cs`
26+
27+
**Changes**:
28+
- `randomizer.NextInt64(i + 1)``randomizer.NextLong(i + 1)` (method name was wrong)
29+
- `SwapSlicesAxis0(NDArray x, int i, int j)``long i, long j`
30+
31+
### 31. Test fixes for Int64 dtype
32+
33+
**Files**:
34+
- `BattleProofTests.cs`: `GetInt32``GetInt64` for arange-based tests
35+
- `np.transpose.Test.cs`: `new long[]``new int[]` for axis array (axes stay int)
36+
- `ReadmeExample.cs`: Cast `n_samples` to int for `np.ones()` calls
37+
- `NpApiOverloadTests_LogicManipulation.cs`: `int``long` for count_nonzero, `NDArray<int>[]``NDArray<long>[]` for nonzero
38+
- `BooleanIndexing.BattleTests.cs`: `shape.SequenceEqual(new[])``shape.SequenceEqual(new long[])`
39+
40+
---
41+
2142
## Completed Fixes (Session 2)
2243

2344
### 15. Shape.Broadcasting.cs
@@ -216,19 +237,29 @@ Files fixed in previous session include:
216237

217238
## Known Issues
218239

219-
### Memory Corruption in Tests (193 failures)
240+
### "Memory Corruption" in Tests - ROOT CAUSE IDENTIFIED
241+
242+
The "index < Count, Memory corruption expected" assertion errors are **NOT actual memory corruption**.
243+
244+
**Root Cause**: Tests calling `GetInt32()` on Int64 arrays.
245+
246+
When `np.arange()` was changed to return Int64 (NumPy 2.x alignment), many tests that use `GetInt32()` started failing because:
247+
1. For an Int64 array, `_arrayInt32` is null (default struct with Count=0)
248+
2. Calling `_arrayInt32[anyIndex]` triggers `Debug.Assert(index < Count)` where Count=0
249+
3. This fails for any non-negative index, appearing as "memory corruption"
250+
251+
**Solution**: Update tests to use `GetInt64()` instead of `GetInt32()` when working with arrays created by `np.arange()`.
220252

221-
Tests are showing memory corruption symptoms:
222-
- Values like `34359738376` or `-9223365347867329507` appearing instead of expected values
223-
- "index < Count, Memory corruption expected" assertion failures
224-
- Affects clip, view semantics, and other tests
253+
**Verified**: Scripts using correct getter methods work perfectly.
225254

226-
**Likely Causes**:
227-
1. Stride calculations using wrong types somewhere
228-
2. Offset calculations not fully migrated
229-
3. Some kernel paths still using int where long is needed
255+
### Remaining Test Updates Needed
230256

231-
**Investigation Needed**: Focus on clip kernel and view/slice operations.
257+
Tests that use `np.arange()` followed by `GetInt32()` need to be updated:
258+
- `NegativeSlice_2D_Corner`, `NegativeSlice_2D_FullReverse`
259+
- `BooleanIndex_2D_Flattens`
260+
- `Dot_1D_2D_Larger`
261+
- Various `Base_*` memory leak tests
262+
- NDIterator reference tests (separate issue - casting during iteration)
232263

233264
---
234265

src/NumSharp.Core/RandomSampling/np.random.shuffle.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,10 @@ public void shuffle(NDArray x)
4949
}
5050

5151
// For multi-dimensional arrays, shuffle along axis 0
52-
// Fisher-Yates shuffle (limited to int range for Random.Next)
53-
if (n > int.MaxValue)
54-
throw new NotSupportedException($"Shuffle along axis 0 not supported for dimension size {n} > int.MaxValue");
55-
for (int i = (int)n - 1; i > 0; i--)
52+
// Fisher-Yates shuffle using NextInt64 for full long range support
53+
for (long i = n - 1; i > 0; i--)
5654
{
57-
int j = randomizer.Next(i + 1);
55+
long j = randomizer.NextLong(i + 1);
5856
if (i != j)
5957
{
6058
SwapSlicesAxis0(x, i, j);
@@ -98,7 +96,7 @@ private unsafe void Shuffle1DContiguous(NDArray x, long n)
9896
/// <summary>
9997
/// Swap two slices along axis 0.
10098
/// </summary>
101-
private static void SwapSlicesAxis0(NDArray x, int i, int j)
99+
private static void SwapSlicesAxis0(NDArray x, long i, long j)
102100
{
103101
// Get slices at indices i and j along axis 0
104102
var sliceI = x[i];

test/NumSharp.UnitTest/Backends/Kernels/BattleProofTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ public void SlicedColumn_MultiplyByScalar_MatchesNumPy()
2626

2727
var z = y * 2;
2828

29-
Assert.AreEqual(2, z.GetInt32(0), "y[0]*2 = 1*2 = 2");
30-
Assert.AreEqual(6, z.GetInt32(1), "y[1]*2 = 3*2 = 6");
29+
Assert.AreEqual(2L, z.GetInt64(0), "y[0]*2 = 1*2 = 2");
30+
Assert.AreEqual(6L, z.GetInt64(1), "y[1]*2 = 3*2 = 6");
3131
}
3232

3333
[Test]

test/NumSharp.UnitTest/Manipulation/np.transpose.Test.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public void Case2()
2626
public void Case3()
2727
{
2828
var nd = np.arange(2 * 3).reshape(1, 2, 3);
29-
np.transpose(nd, new long[] {1, 0, 2}).Should()
29+
np.transpose(nd, new int[] {1, 0, 2}).Should()
3030
.BeOfValues(0, 1, 2, 3, 4, 5)
3131
.And.BeShaped(2, 1, 3);
3232
}

test/NumSharp.UnitTest/NpApiOverloads/NpApiOverloadTests_LogicManipulation.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ public async Task CountNonzero_NoAxis_ReturnsInt_Compiles()
530530
{
531531
// NumPy: np.count_nonzero([0, 1, 0, 2, 0, 3]) -> 3
532532
var a = np.array(new int[] { 0, 1, 0, 2, 0, 3 });
533-
int result = np.count_nonzero(a);
533+
long result = np.count_nonzero(a);
534534

535535
await Assert.That(result).IsEqualTo(3);
536536
}
@@ -569,21 +569,21 @@ public async Task Nonzero_1D_ReturnsNDArrayIntArray_Compiles()
569569
{
570570
// NumPy: np.nonzero([0, 1, 0, 2]) -> (array([1, 3]),)
571571
var a = np.array(new int[] { 0, 1, 0, 2 });
572-
NDArray<int>[] result = np.nonzero(a);
572+
NDArray<long>[] result = np.nonzero(a);
573573

574574
await Assert.That(result).IsNotNull();
575575
await Assert.That(result.Length).IsEqualTo(1);
576576
await Assert.That(result[0].size).IsEqualTo(2);
577-
await Assert.That(result[0].GetInt32(0)).IsEqualTo(1);
578-
await Assert.That(result[0].GetInt32(1)).IsEqualTo(3);
577+
await Assert.That(result[0].GetInt64(0)).IsEqualTo(1L);
578+
await Assert.That(result[0].GetInt64(1)).IsEqualTo(3L);
579579
}
580580

581581
[Test]
582582
public async Task Nonzero_2D_ReturnsMultipleArrays_Compiles()
583583
{
584584
// NumPy: np.nonzero([[0, 1], [2, 0]]) -> (array([0, 1]), array([1, 0]))
585585
var a = np.array(new int[,] { { 0, 1 }, { 2, 0 } });
586-
NDArray<int>[] result = np.nonzero(a);
586+
NDArray<long>[] result = np.nonzero(a);
587587

588588
await Assert.That(result).IsNotNull();
589589
await Assert.That(result.Length).IsEqualTo(2);

test/NumSharp.UnitTest/Others/ReadmeExample.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public LinearRegression(NDArray X, NDArray y, float alpha = 0.03f, int n_iter =
1616
this.n_iter = n_iter;
1717
this.n_samples = y.size;
1818
this.n_features = np.size(X, 1);
19-
this.X = np.hstack(np.ones(this.n_samples, 1),
19+
this.X = np.hstack(np.ones((int)this.n_samples, 1),
2020
(X - np.mean(X, 0) / np.std(X, 0)));
2121
this.y = np.expand_dims(y, -1);
2222
this.@params = np.zeros((this.n_features + 1, 1), NPTypeCode.Single);
@@ -41,7 +41,7 @@ public float score(NDArray X = null, NDArray y = null) {
4141
X = this.X;
4242
else {
4343
n_samples = np.size(X, 0);
44-
this.X = np.hstack(np.ones(this.n_samples, 1),
44+
this.X = np.hstack(np.ones((int)this.n_samples, 1),
4545
(X - np.mean(X, 0) / np.std(X, 0)));
4646
}
4747

@@ -59,7 +59,7 @@ public float score(NDArray X = null, NDArray y = null) {
5959
public NDArray predict(NDArray X) {
6060
n_samples = np.size(X, 0);
6161
y = np.matmul(
62-
np.hstack(np.ones(this.n_samples, 1), (X - np.mean(X, 0) / np.std(X, 0))),
62+
np.hstack(np.ones((int)this.n_samples, 1), (X - np.mean(X, 0) / np.std(X, 0))),
6363
@params
6464
);
6565

test/NumSharp.UnitTest/Selection/BooleanIndexing.BattleTests.cs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public void Case1_SameShape_1D_BasicMask()
2424

2525
var result = arr[mask];
2626

27-
Assert.IsTrue(result.shape.SequenceEqual(new[] { 3 }), $"Expected shape [3], got [{string.Join(", ", result.shape)}]");
27+
Assert.IsTrue(result.shape.SequenceEqual(new long[] { 3 }), $"Expected shape [3], got [{string.Join(", ", result.shape)}]");
2828
Assert.AreEqual(1, result.GetInt32(0));
2929
Assert.AreEqual(3, result.GetInt32(1));
3030
Assert.AreEqual(5, result.GetInt32(2));
@@ -68,7 +68,7 @@ public void Case2_SameShape_2D_ElementWise()
6868

6969
var result = arr2d[mask2d];
7070

71-
Assert.IsTrue(result.shape.SequenceEqual(new[] { 6 }), $"Expected shape [6], got [{string.Join(", ", result.shape)}]");
71+
Assert.IsTrue(result.shape.SequenceEqual(new long[] { 6 }), $"Expected shape [6], got [{string.Join(", ", result.shape)}]");
7272
Assert.AreEqual(6, result.GetInt32(0));
7373
Assert.AreEqual(7, result.GetInt32(1));
7474
Assert.AreEqual(8, result.GetInt32(2));
@@ -119,7 +119,7 @@ public void Case3_Axis0_2D_SelectsRows()
119119

120120
var result = arr2d[mask1d];
121121

122-
Assert.IsTrue(result.shape.SequenceEqual(new[] { 2, 4 }), $"Expected shape [2, 4], got [{string.Join(", ", result.shape)}]");
122+
Assert.IsTrue(result.shape.SequenceEqual(new long[] { 2, 4 }), $"Expected shape [2, 4], got [{string.Join(", ", result.shape)}]");
123123
}
124124

125125
[Test]
@@ -153,7 +153,7 @@ public void Case3_Axis0_3D_SelectsAlongAxis0()
153153
var result = arr3d[mask1d];
154154

155155
// Selects first "block", result shape (1, 3, 4)
156-
Assert.IsTrue(result.shape.SequenceEqual(new[] { 1, 3, 4 }), $"Expected shape [1, 3, 4], got [{string.Join(", ", result.shape)}]");
156+
Assert.IsTrue(result.shape.SequenceEqual(new long[] { 1, 3, 4 }), $"Expected shape [1, 3, 4], got [{string.Join(", ", result.shape)}]");
157157
}
158158

159159
[Test]
@@ -187,7 +187,7 @@ public void Case4_BooleanPlusInteger_Workaround()
187187
var result = selected[":, 0"];
188188

189189
// NumPy: arr2d[[T,F,T], 0] = [0, 8]
190-
Assert.IsTrue(result.shape.SequenceEqual(new[] { 2 }), $"Expected shape [2], got [{string.Join(", ", result.shape)}]");
190+
Assert.IsTrue(result.shape.SequenceEqual(new long[] { 2 }), $"Expected shape [2], got [{string.Join(", ", result.shape)}]");
191191
Assert.AreEqual(0, result.GetInt32(0));
192192
Assert.AreEqual(8, result.GetInt32(1));
193193
}
@@ -204,7 +204,7 @@ public void Case4_BooleanPlusSlice_Workaround()
204204
var result = selected[":, 1:3"];
205205

206206
// NumPy: [[1, 2], [9, 10]]
207-
Assert.IsTrue(result.shape.SequenceEqual(new[] { 2, 2 }), $"Expected shape [2, 2], got [{string.Join(", ", result.shape)}]");
207+
Assert.IsTrue(result.shape.SequenceEqual(new long[] { 2, 2 }), $"Expected shape [2, 2], got [{string.Join(", ", result.shape)}]");
208208
Assert.AreEqual(1, result[0, 0].GetInt32());
209209
Assert.AreEqual(2, result[0, 1].GetInt32());
210210
Assert.AreEqual(9, result[1, 0].GetInt32());
@@ -225,7 +225,7 @@ public void Case5_3D_FullShapeMask()
225225
var result = arr3d[mask3d];
226226

227227
// 11 elements > 12: [13, 14, ..., 23]
228-
Assert.IsTrue(result.shape.SequenceEqual(new[] { 11 }), $"Expected shape [11], got [{string.Join(", ", result.shape)}]");
228+
Assert.IsTrue(result.shape.SequenceEqual(new long[] { 11 }), $"Expected shape [11], got [{string.Join(", ", result.shape)}]");
229229
Assert.AreEqual(13, result.GetInt32(0));
230230
Assert.AreEqual(23, result.GetInt32(10));
231231
}
@@ -239,7 +239,7 @@ public void Case5_3D_1DMask_PreservesRemainingDims()
239239
var result = arr3d[mask1d];
240240

241241
// Shape: (1, 3, 4) - preserves dims 1 and 2
242-
Assert.IsTrue(result.shape.SequenceEqual(new[] { 1, 3, 4 }), $"Expected shape [1, 3, 4], got [{string.Join(", ", result.shape)}]");
242+
Assert.IsTrue(result.shape.SequenceEqual(new long[] { 1, 3, 4 }), $"Expected shape [1, 3, 4], got [{string.Join(", ", result.shape)}]");
243243
}
244244

245245
[Test]
@@ -256,7 +256,7 @@ public void Case5_2DMaskOn3D_PartialShapeMatch()
256256
var result = arr3d[mask2d];
257257

258258
// If supported, verify shape and values
259-
Assert.IsTrue(result.shape.SequenceEqual(new[] { 3, 4 }), $"Expected shape [3, 4], got [{string.Join(", ", result.shape)}]");
259+
Assert.IsTrue(result.shape.SequenceEqual(new long[] { 3, 4 }), $"Expected shape [3, 4], got [{string.Join(", ", result.shape)}]");
260260

261261
// First True at (0,0) → arr3d[0,0,:] = [0,1,2,3]
262262
Assert.AreEqual(0, result[0, 0].GetInt32());
@@ -373,7 +373,7 @@ public void Case7_AllFalse_EmptyResult()
373373

374374
var result = arr[emptyMask];
375375

376-
Assert.IsTrue(result.shape.SequenceEqual(new[] { 0 }), $"Expected shape [0], got [{string.Join(", ", result.shape)}]");
376+
Assert.IsTrue(result.shape.SequenceEqual(new long[] { 0 }), $"Expected shape [0], got [{string.Join(", ", result.shape)}]");
377377
Assert.AreEqual(0, result.size);
378378
}
379379

@@ -385,7 +385,7 @@ public void Case7_AllTrue_AllElements()
385385

386386
var result = arr[allMask];
387387

388-
Assert.IsTrue(result.shape.SequenceEqual(new[] { 5 }), $"Expected shape [5], got [{string.Join(", ", result.shape)}]");
388+
Assert.IsTrue(result.shape.SequenceEqual(new long[] { 5 }), $"Expected shape [5], got [{string.Join(", ", result.shape)}]");
389389
Assert.AreEqual(1, result.GetInt32(0));
390390
Assert.AreEqual(5, result.GetInt32(4));
391391
}
@@ -398,7 +398,7 @@ public void Case7_EmptyArray_EmptyMask()
398398

399399
var result = empty[emptyMask];
400400

401-
Assert.IsTrue(result.shape.SequenceEqual(new[] { 0 }), $"Expected shape [0], got [{string.Join(", ", result.shape)}]");
401+
Assert.IsTrue(result.shape.SequenceEqual(new long[] { 0 }), $"Expected shape [0], got [{string.Join(", ", result.shape)}]");
402402
}
403403

404404
[Test]
@@ -410,7 +410,7 @@ public void Case7_EmptyResult_PreservesDtype()
410410
var result = arrFloat[emptyMask];
411411

412412
Assert.AreEqual(typeof(double), result.dtype);
413-
Assert.IsTrue(result.shape.SequenceEqual(new[] { 0 }), $"Expected shape [0], got [{string.Join(", ", result.shape)}]");
413+
Assert.IsTrue(result.shape.SequenceEqual(new long[] { 0 }), $"Expected shape [0], got [{string.Join(", ", result.shape)}]");
414414
}
415415

416416
#endregion
@@ -470,7 +470,7 @@ public void Case9_BooleanScalar_True()
470470
var result = arr[scalarTrue];
471471

472472
// NumPy behavior: adds axis → shape (1, 3)
473-
Assert.IsTrue(result.shape.SequenceEqual(new[] { 1, 3 }),
473+
Assert.IsTrue(result.shape.SequenceEqual(new long[] { 1, 3 }),
474474
$"0-D boolean True should add axis. Expected shape [1, 3], got [{string.Join(", ", result.shape)}]");
475475
}
476476
catch (Exception ex) when (ex is not AssertFailedException)
@@ -860,7 +860,7 @@ public void Case19_SingleElementSelected()
860860

861861
var result = arr[mask];
862862

863-
Assert.IsTrue(result.shape.SequenceEqual(new[] { 1 }), $"Expected shape [1], got [{string.Join(", ", result.shape)}]");
863+
Assert.IsTrue(result.shape.SequenceEqual(new long[] { 1 }), $"Expected shape [1], got [{string.Join(", ", result.shape)}]");
864864
Assert.AreEqual(3, result.GetInt32(0));
865865
}
866866

@@ -872,7 +872,7 @@ public void Case19_2D_SingleRow()
872872

873873
var result = arr2d[mask];
874874

875-
Assert.IsTrue(result.shape.SequenceEqual(new[] { 1, 4 }), $"Expected shape [1, 4], got [{string.Join(", ", result.shape)}]");
875+
Assert.IsTrue(result.shape.SequenceEqual(new long[] { 1, 4 }), $"Expected shape [1, 4], got [{string.Join(", ", result.shape)}]");
876876
Assert.AreEqual(4, result[0, 0].GetInt32());
877877
}
878878

@@ -1228,7 +1228,7 @@ public void Case28_4D_1DMask()
12281228
var result = arr4d[mask1d];
12291229

12301230
// Selects first "block" along axis 0
1231-
Assert.IsTrue(result.shape.SequenceEqual(new[] { 1, 3, 4, 2 }), $"Expected shape [1, 3, 4, 2], got [{string.Join(", ", result.shape)}]");
1231+
Assert.IsTrue(result.shape.SequenceEqual(new long[] { 1, 3, 4, 2 }), $"Expected shape [1, 3, 4, 2], got [{string.Join(", ", result.shape)}]");
12321232
}
12331233

12341234
#endregion

0 commit comments

Comments
 (0)