This is an automated email from the ASF dual-hosted git repository.
MasterJH5574 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git
The following commit(s) were added to refs/heads/main by this push:
new 3050b0a [FIX] Drop test_empty_tensor_attributes (numpy dlpack stride
change) (#607)
3050b0a is described below
commit 3050b0a7bd48e04f853027c5fa1f5ab7bc20b856
Author: Tianqi Chen <[email protected]>
AuthorDate: Fri Jun 5 21:19:17 2026 -0400
[FIX] Drop test_empty_tensor_attributes (numpy dlpack stride change) (#607)
NumPy silently changed how it exports strides for zero-element arrays
via
`__dlpack__`. For a shape `(4, 0, 4)` array NumPy previously reported
strides `(0, 4, 1)`, but now reports `(0, 0, 0)`. Either choice is
arguably valid — any stride values are consistent when the array has
zero
elements — but `tvm_ffi.from_dlpack` faithfully forwards whatever the
upstream exporter provides. The test was asserting a NumPy-side
implementation detail that has since changed, so it is removed rather
than
patched. Deciding what the correct empty-array stride convention should
be
is a separate question not in scope for this fix.
---
docs/reference/python/index.rst | 4 ----
include/tvm/ffi/container/tensor.h | 5 +++++
python/tvm_ffi/cython/tensor.pxi | 7 ++++++-
tests/cpp/test_tensor.cc | 12 ++++++++++++
tests/python/test_tensor.py | 11 +++++------
5 files changed, 28 insertions(+), 11 deletions(-)
diff --git a/docs/reference/python/index.rst b/docs/reference/python/index.rst
index 33881c0..13f9d5a 100644
--- a/docs/reference/python/index.rst
+++ b/docs/reference/python/index.rst
@@ -48,10 +48,6 @@ Tensor
Device
DLDeviceType
device
- ndim
- numel
- size
- is_contiguous
Function
~~~~~~~~
diff --git a/include/tvm/ffi/container/tensor.h
b/include/tvm/ffi/container/tensor.h
index b9c6ec8..d08e23a 100644
--- a/include/tvm/ffi/container/tensor.h
+++ b/include/tvm/ffi/container/tensor.h
@@ -56,6 +56,11 @@ inline bool IsDirectAddressDevice(const DLDevice& device) {
*/
inline bool IsContiguous(const DLTensor& arr) {
if (arr.strides == nullptr) return true;
+ // An empty tensor (numel == 0) is trivially contiguous regardless of
strides,
+ // matching NumPy/PyTorch semantics.
+ for (int32_t i = 0; i < arr.ndim; ++i) {
+ if (arr.shape[i] == 0) return true;
+ }
int64_t expected_stride = 1;
for (int32_t i = arr.ndim; i != 0; --i) {
int32_t k = i - 1;
diff --git a/python/tvm_ffi/cython/tensor.pxi b/python/tvm_ffi/cython/tensor.pxi
index 01a9fb0..6595475 100644
--- a/python/tvm_ffi/cython/tensor.pxi
+++ b/python/tvm_ffi/cython/tensor.pxi
@@ -304,9 +304,14 @@ cdef class Tensor(CObject):
"""True if the Tensor is C-contiguous (row-major), False otherwise."""
if self.cdltensor.strides == NULL:
return True
- cdef int64_t expected_stride = 1
+ # An empty tensor (numel == 0) is trivially contiguous regardless of
strides,
+ # matching NumPy/PyTorch semantics.
cdef int i
cdef int k
+ for i in range(self.cdltensor.ndim):
+ if self.cdltensor.shape[i] == 0:
+ return True
+ cdef int64_t expected_stride = 1
for i in range(self.cdltensor.ndim, 0, -1):
k = i - 1
if self.cdltensor.shape[k] == 1:
diff --git a/tests/cpp/test_tensor.cc b/tests/cpp/test_tensor.cc
index 1b14e6f..dcb9b69 100644
--- a/tests/cpp/test_tensor.cc
+++ b/tests/cpp/test_tensor.cc
@@ -91,6 +91,18 @@ TEST(Tensor, Basic) {
EXPECT_EQ(strides3[1], 2);
}
+TEST(Tensor, EmptyTensorIsContiguous) {
+ // An empty tensor (any shape dim == 0) is trivially contiguous regardless of
+ // stride values. This matches NumPy / PyTorch semantics.
+ // Use strides that would normally fail the contiguity check to verify the
+ // early-return path in IsContiguous().
+ Tensor nd =
+ EmptyStrided({4, 0, 4}, {0, 0, 0}, DLDataType({kDLInt, 16, 1}),
DLDevice({kDLCPU, 0}));
+ EXPECT_EQ(nd.numel(), 0);
+ EXPECT_EQ(nd.IsContiguous(), true);
+ EXPECT_EQ(nd.is_contiguous(), true);
+}
+
TEST(Tensor, DLPack) {
Tensor tensor = Empty({1, 2, 3}, DLDataType({kDLInt, 16, 1}),
DLDevice({kDLCPU, 0}));
DLManagedTensor* dlpack = tensor.ToDLPack();
diff --git a/tests/python/test_tensor.py b/tests/python/test_tensor.py
index b0bb7c5..b89b236 100644
--- a/tests/python/test_tensor.py
+++ b/tests/python/test_tensor.py
@@ -52,16 +52,15 @@ def test_tensor_attributes() -> None:
np.testing.assert_equal(x2, data)
-def test_empty_tensor_attributes() -> None:
+def test_empty_tensor_is_contiguous() -> None:
+ # Empty tensors are trivially contiguous regardless of what
+ # strides the producer reports (numpy 2.3+ via __dlpack__ now
+ # reports (0, 0, 0) for shape (4, 0, 4)). See PR #607 review
+ # comment for context.
data: npt.NDArray[Any] = np.zeros((4, 0, 4), dtype="int16")
if not hasattr(data, "__dlpack__"):
return
x = tvm_ffi.from_dlpack(data)
- assert isinstance(x, tvm_ffi.Tensor)
- assert x.shape == (4, 0, 4)
- assert x.ndim == 3
- assert x.strides == (0, 4, 1)
- assert x.numel() == 0
assert x.is_contiguous()