Related to #28790 and #29820, which report spurious FPE warnings from matmul on Apple Silicon as "annoying but harmless". On our workload the same code path silently corrupts an unrelated live array. Filing separately because the impact class is different.
Environment
- macOS 26.6.2 (build 25G83), Apple M5 Pro, arm64
- Python 3.14.4 (Homebrew), numpy 2.2.0,
numpy.show_config() → blas name: accelerate
- scipy 1.17.1 (held constant throughout)
1. The known symptom reproduces in three lines
import numpy as np, warnings
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
np.identity(15) @ np.identity(15)
print([str(x.message) for x in w])
# ['divide by zero encountered in matmul', 'overflow encountered in matmul',
# 'invalid value encountered in matmul'] — result is correct
2. The corruption
In a scientific pipeline we have a float64 array of shape (255985,) — an owning copy, never assigned to anywhere in our source. Across a loop that calls scipy.spatial.ConvexHull and then all_pts @ hull_eqs[:, :3].T + hull_eqs[:, 3], that array's contents change between iterations. At the affected sites all inputs and outputs of the matmul are finite (|hull_eqs| <= 28.4, |signed| <= 55.8), so the reported overflow is impossible arithmetically and indicates access beyond the buffer.
Downstream this changed a boolean selection mask, hence a point set, hence a convex hull, hence an aggregate — moving a reported financial figure by +24% (33,977,903 -> 42,130,944) with no error and no warning other than the FPE noise above.
3. It behaves as heap-layout dependence
Adding a statement that merely holds references to the arrays — _keepalive = (a_f, c_f, d_f, v_f), computing nothing — makes the corruption vanish and restores the correct result. So does reversing an unrelated loop. Any instrumentation that closes over the array suppresses it, which made it self-defeating to measure.
4. Version isolation
Holding scipy at 1.17.1 and moving numpy alone:
| numpy |
identity-matmul warnings |
our result |
| 2.2.0 |
3 warnings |
corrupt |
| 2.3.3 |
none |
correct |
| 2.3.4 |
none |
correct |
Under 2.3.4 the result is byte-identical across clean / keepalive / reversed-loop builds. We have pinned numpy>=2.3.4.
Question: was the out-of-bounds write fixed deliberately between 2.2.0 and 2.3.3, or incidentally? If the underlying Accelerate integration issue persists in any form, the harmless-warnings framing in #28790/#29820 understates the risk — this silently changed a number that people make decisions on.
Related to #28790 and #29820, which report spurious FPE warnings from
matmulon Apple Silicon as "annoying but harmless". On our workload the same code path silently corrupts an unrelated live array. Filing separately because the impact class is different.Environment
numpy.show_config()→blas name: accelerate1. The known symptom reproduces in three lines
2. The corruption
In a scientific pipeline we have a
float64array of shape(255985,)— an owning copy, never assigned to anywhere in our source. Across a loop that callsscipy.spatial.ConvexHulland thenall_pts @ hull_eqs[:, :3].T + hull_eqs[:, 3], that array's contents change between iterations. At the affected sites all inputs and outputs of the matmul are finite (|hull_eqs| <= 28.4,|signed| <= 55.8), so the reported overflow is impossible arithmetically and indicates access beyond the buffer.Downstream this changed a boolean selection mask, hence a point set, hence a convex hull, hence an aggregate — moving a reported financial figure by +24% (33,977,903 -> 42,130,944) with no error and no warning other than the FPE noise above.
3. It behaves as heap-layout dependence
Adding a statement that merely holds references to the arrays —
_keepalive = (a_f, c_f, d_f, v_f), computing nothing — makes the corruption vanish and restores the correct result. So does reversing an unrelated loop. Any instrumentation that closes over the array suppresses it, which made it self-defeating to measure.4. Version isolation
Holding scipy at 1.17.1 and moving numpy alone:
Under 2.3.4 the result is byte-identical across clean / keepalive / reversed-loop builds. We have pinned
numpy>=2.3.4.Question: was the out-of-bounds write fixed deliberately between 2.2.0 and 2.3.3, or incidentally? If the underlying Accelerate integration issue persists in any form, the harmless-warnings framing in #28790/#29820 understates the risk — this silently changed a number that people make decisions on.