Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions src/datajoint/blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,12 +474,30 @@ def pack_struct(self, array):
) # values

def read_cell_array(self):
"""deserialize MATLAB cell array"""
"""
Deserialize MATLAB cell array.

Handles edge cases from MATLAB:
- Empty cell arrays ({})
- Cell arrays with empty elements ({[], [], []})
- Nested arrays ({[1,2], [3,4,5]}) - ragged arrays
- Cell matrices with mixed content
"""
n_dims = self.read_value()
shape = self.read_value(count=n_dims)
n_elem = int(np.prod(shape))
result = [self.read_blob(n_bytes=self.read_value()) for _ in range(n_elem)]
return (self.squeeze(np.array(result).reshape(shape, order="F"), convert_to_scalar=False)).view(MatCell)

# Handle empty cell array
if n_elem == 0:
return np.empty(0, dtype=object).view(MatCell)

# Use object dtype to handle ragged/nested arrays without reshape errors.
# This avoids NumPy's array homogeneity requirements that cause failures
# with MATLAB cell arrays containing arrays of different sizes.
arr = np.empty(n_elem, dtype=object)
arr[:] = result
return self.squeeze(arr.reshape(shape, order="F"), convert_to_scalar=False).view(MatCell)

def pack_cell_array(self, array):
return (
Expand Down
68 changes: 68 additions & 0 deletions tests/integration/test_blob_matlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,71 @@ def test_iter(schema_blob_pop):
from_iter = {d["id"]: d for d in Blob()}
assert len(from_iter) == len(Blob())
assert from_iter[1]["blob"] == "character string"


def test_cell_array_with_nested_arrays():
"""
Test unpacking MATLAB cell arrays containing arrays of different sizes.
Regression test for issue #1098.
"""
# Create a cell array with nested arrays of different sizes (ragged)
cell = np.empty(2, dtype=object)
cell[0] = np.array([1, 2, 3])
cell[1] = np.array([4, 5, 6, 7, 8])
cell = cell.reshape((1, 2)).view(dj.MatCell)

# Pack and unpack
packed = pack(cell)
unpacked = unpack(packed)

# Should preserve structure
assert isinstance(unpacked, dj.MatCell)
assert unpacked.shape == (1, 2)
assert_array_equal(unpacked[0, 0], np.array([1, 2, 3]))
assert_array_equal(unpacked[0, 1], np.array([4, 5, 6, 7, 8]))


def test_cell_array_with_empty_elements():
"""
Test unpacking MATLAB cell arrays containing empty arrays.
Regression test for issue #1056.
"""
# Create a cell array with empty elements: {[], [], []}
cell = np.empty(3, dtype=object)
cell[0] = np.array([])
cell[1] = np.array([])
cell[2] = np.array([])
cell = cell.reshape((3, 1)).view(dj.MatCell)

# Pack and unpack
packed = pack(cell)
unpacked = unpack(packed)

# Should preserve structure
assert isinstance(unpacked, dj.MatCell)
assert unpacked.shape == (3, 1)
for i in range(3):
assert unpacked[i, 0].size == 0


def test_cell_array_mixed_empty_nonempty():
"""
Test unpacking MATLAB cell arrays with mixed empty and non-empty elements.
"""
# Create a cell array: {[1,2], [], [3,4,5]}
cell = np.empty(3, dtype=object)
cell[0] = np.array([1, 2])
cell[1] = np.array([])
cell[2] = np.array([3, 4, 5])
cell = cell.reshape((3, 1)).view(dj.MatCell)

# Pack and unpack
packed = pack(cell)
unpacked = unpack(packed)

# Should preserve structure
assert isinstance(unpacked, dj.MatCell)
assert unpacked.shape == (3, 1)
assert_array_equal(unpacked[0, 0], np.array([1, 2]))
assert unpacked[1, 0].size == 0
assert_array_equal(unpacked[2, 0], np.array([3, 4, 5]))
Loading