Skip to content

Commit dbbada9

Browse files
committed
fix permutation inference
1 parent 9a731da commit dbbada9

2 files changed

Lines changed: 28 additions & 15 deletions

File tree

python/pyarrow/array.pxi

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4795,12 +4795,13 @@ cdef class FixedShapeTensorArray(ExtensionArray):
47954795

47964796
arrow_type = from_numpy_dtype(obj.dtype)
47974797
shape = np.take(obj.shape, permutation)
4798+
permutation = _invert_permutation(permutation[1:] - 1)
47984799
values = np.ravel(obj, order="K")
47994800

48004801
return ExtensionArray.from_storage(
48014802
fixed_shape_tensor(arrow_type, shape[1:],
48024803
dim_names=dim_names,
4803-
permutation=permutation[1:] - 1),
4804+
permutation=permutation),
48044805
FixedSizeListArray.from_arrays(values, shape[1:].prod())
48054806
)
48064807

@@ -4998,15 +4999,20 @@ def _infer_uniform_shape(shape_rows, ndim):
49984999
return inferred
49995000

50005001

5002+
def _invert_permutation(permutation):
5003+
return [int(x) for x in
5004+
np.argsort(np.array(permutation, dtype=np.int64), kind="stable")]
5005+
5006+
50015007
def _permutation_from_strides(arr):
5002-
"""Infer the dimension permutation from array strides.
5008+
"""Infer the logical-to-physical permutation from array strides.
50035009
50045010
Note: for arrays with size-1 dimensions, the inferred permutation
50055011
may be unreliable since size-1 strides are unconstrained. Callers
50065012
should skip permutation validation for such arrays.
50075013
"""
5008-
return [int(x) for x in
5009-
(-np.array(arr.strides, dtype=np.int64)).argsort(kind="stable")]
5014+
return _invert_permutation(
5015+
(-np.array(arr.strides, dtype=np.int64)).argsort(kind="stable"))
50105016

50115017

50125018
cdef class VariableShapeTensorArray(ExtensionArray):
@@ -5070,7 +5076,8 @@ cdef class VariableShapeTensorArray(ExtensionArray):
50705076
dim_names : tuple or list of strings, default None
50715077
Explicit names to tensor dimensions.
50725078
permutation : tuple or list of integers, default None
5073-
Physical permutation for all input arrays. If None, inferred from strides.
5079+
Logical-to-physical permutation for all input arrays. If None,
5080+
inferred from strides.
50745081
uniform_shape : tuple or list of integers or None, default None
50755082
Optional known uniform dimensions in physical order. If None, inferred.
50765083
@@ -5151,8 +5158,9 @@ cdef class VariableShapeTensorArray(ExtensionArray):
51515158
(f"obj[{i}] has permutation {ndarray_permutation_list}; "
51525159
f"expected {list(normalized_permutation)}"))
51535160

5161+
physical_to_logical = _invert_permutation(normalized_permutation)
51545162
shape_rows = [
5155-
[int(x) for x in np.take(arr.shape, normalized_permutation)]
5163+
[int(x) for x in np.take(arr.shape, physical_to_logical)]
51565164
for arr in arrays
51575165
]
51585166

@@ -5173,10 +5181,10 @@ cdef class VariableShapeTensorArray(ExtensionArray):
51735181
if arr.size > 0:
51745182
raveled = np.ravel(arr, order="K")
51755183
physical_shape = tuple(
5176-
np.take(arr.shape, normalized_permutation))
5184+
np.take(arr.shape, physical_to_logical))
51775185
reconstructed = raveled.reshape(physical_shape)
5178-
inv_perm = list(np.argsort(normalized_permutation))
5179-
reconstructed_logical = np.transpose(reconstructed, inv_perm)
5186+
reconstructed_logical = np.transpose(reconstructed,
5187+
normalized_permutation)
51805188
if not np.array_equal(reconstructed_logical, arr):
51815189
raise ValueError(
51825190
"Array memory layout is incompatible with variable "

python/pyarrow/tests/test_extension_type.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1723,6 +1723,14 @@ def test_tensor_array_from_numpy(np_type_str):
17231723
assert tensor_array_from_numpy.type.dim_names is None
17241724
assert tensor_array_from_numpy.to_tensor() == pa.Tensor.from_numpy(arr)
17251725

1726+
arr = np.arange(48, dtype=np.dtype(np_type_str)).reshape((2, 2, 3, 4))
1727+
arr = np.transpose(arr, (0, 2, 3, 1))
1728+
tensor_array_from_numpy = pa.FixedShapeTensorArray.from_numpy_ndarray(arr)
1729+
assert tensor_array_from_numpy.type.shape == [2, 3, 4]
1730+
assert tensor_array_from_numpy.type.permutation == [1, 2, 0]
1731+
assert tensor_array_from_numpy.type.dim_names is None
1732+
assert tensor_array_from_numpy.to_tensor() == pa.Tensor.from_numpy(arr)
1733+
17261734
arr = flat_arr.reshape(1, 2, 3, 2)
17271735
result = pa.FixedShapeTensorArray.from_numpy_ndarray(arr)
17281736
expected = np.array(
@@ -1902,12 +1910,9 @@ def test_variable_shape_tensor_array_from_numpy(value_type):
19021910
arr = np.arange(24, dtype=value_type).reshape((2, 3, 4))
19031911
arr = np.transpose(arr, (2, 0, 1))
19041912
result = pa.VariableShapeTensorArray.from_numpy_ndarray([arr])
1905-
assert result.type.permutation == [1, 2, 0]
1906-
expected_tensor_view = np.transpose(arr, np.argsort(result.type.permutation))
1907-
tensor = result[0].to_tensor()
1908-
assert list(tensor.shape) == list(expected_tensor_view.shape)
1909-
result_ndarray = result[0].to_numpy()
1910-
assert list(result_ndarray.shape) == list(expected_tensor_view.shape)
1913+
assert result.type.permutation == [2, 0, 1]
1914+
assert result[0].to_tensor() == pa.Tensor.from_numpy(arr)
1915+
np.testing.assert_array_equal(result[0].to_numpy(), arr)
19111916

19121917
arr = np.array([1, 2, 3, 4], dtype=value_type)
19131918
result = pa.VariableShapeTensorArray.from_numpy_ndarray([arr])

0 commit comments

Comments
 (0)