@@ -196,7 +196,7 @@ def _matlab_array_types():
196196 matlab .single : np .float32 ,
197197 matlab .logical : np .bool ,
198198 matlab .uint64 : np .uint64 ,
199- matlab .uint32 : np .uint32 ,
199+ matlab .uint32 : np .uint64 ,
200200 matlab .uint16 : np .uint16 ,
201201 matlab .uint8 : np .uint8 ,
202202 matlab .int64 : np .int64 ,
@@ -244,32 +244,28 @@ def from_any(cls, other):
244244 type__ = other ["type__" ]
245245
246246 if type__ == "structarray" :
247- # Matlab returns a list of dictionaries in data__
247+ # MPython returns a list of dictionaries in data__
248248 # and the array shape in size__.
249249 return Struct ._from_runtime (other )
250250
251251 elif type__ == "cell" :
252- # Matlab returns a list of dictionaries in data__
252+ # MPython returns a list of dictionaries in data__
253253 # and the array shape in size__.
254254 return Cell ._from_runtime (other )
255255
256256 elif type__ == "object" :
257- # Matlab returns the object's fields serialized
257+ # MPython returns the object's fields serialized
258258 # in a dictionary.
259259 return MatlabClass ._from_runtime (other )
260260
261261 elif type__ == "sparse" :
262- # Matlab returns a dense version of the array in data__.
263- if sparse :
264- return SparseArray ._from_runtime (other )
265- else :
266- data = np .asarray (other ['data__' ], dtype = np .double )
267- return data .view (Array )
262+ # MPython returns the coordinates and values in a dict.
263+ return SparseArray ._from_runtime (other )
268264
269265 elif type__ == "char" :
270266 # Character array that is not a row vector
271267 # (row vector are converted to str automatically)
272- # Matlab returns all rows in a (F-ordered) cell in data__
268+ # MPython returns all rows in a (F-ordered) cell in data__
273269 # Let's use the cell constructor to return a cellstr.
274270 # -> A cellstr is a column vector, not a row vector
275271 size = np .asarray (other ["size__" ]).tolist ()[0 ]
@@ -349,7 +345,7 @@ def _to_runtime(cls, obj):
349345 return obj
350346
351347 elif sparse and isinstance (obj , sparse .coo_array ):
352- return dict ( type__ = 'sparse' , data__ = obj . todense () )
348+ return SparseArray . from_any ( obj ). _as_runtime ( )
353349
354350 else :
355351 # TODO: do we want to raise if the type is not supported by matlab?
@@ -1069,123 +1065,6 @@ def _return_delayed(self, index):
10691065 return super ().__getitem__ (index )
10701066
10711067
1072- # ----------------------------------------------------------------------
1073- # SparseArray
1074- # ----------------------------------------------------------------------
1075-
1076-
1077- if sparse :
1078-
1079- class WrappedSparseArray (sparse .sparray , AnyWrappedArray ):
1080- """Base class for sparse arrays."""
1081-
1082- def to_dense (self ) -> "Array" :
1083- return Array .from_any (super ().to_dense ())
1084-
1085- class SparseArray (sparse .coo_array , WrappedSparseArray ):
1086- """
1087- Matlab sparse arrays.
1088-
1089- ```python
1090- # Instantiate from size
1091- SparseArray(N, M, ...)
1092- SparseArray([N, M, ...])
1093- SparseArray.from_shape([N, M, ...])
1094-
1095- # Instantiate from existing sparse or dense array
1096- SparseArray(other_array)
1097- SparseArray.from_any(other_array)
1098-
1099- # Other options
1100- SparseArray(..., dtype=None, *, copy=None)
1101- ```
1102-
1103- !!! warning
1104- Lists or vectors of integers can be interpreted as shapes
1105- or as dense arrays to copy. They are interpreted as shapes
1106- by the `SparseArray` constructor. To ensure that they are
1107- interpreted as dense arrays to copy, use `SparseArray.from_any`.
1108- """
1109-
1110- def __init__ (self , * args , ** kwargs ) -> "SparseArray" :
1111- mode , arg , kwargs = self ._parse_args (* args , ** kwargs )
1112- if mode == "shape" :
1113- return super ().__init__ (shape = arg , ** kwargs )
1114- else :
1115- if not isinstance (arg , (np .ndarray , sparse .sparray )):
1116- arg = np .asanyarray (arg )
1117- return super ().__init__ (arg , ** kwargs )
1118-
1119- def _as_runtime (self ) -> dict :
1120- data = super ().todense ()
1121- return dict (type__ = 'sparse' , data__ = data )
1122-
1123- @classmethod
1124- def _from_runtime (cls , dictobj : dict ) -> "SparseArray" :
1125- if dictobj ['type__' ] != 'sparse' :
1126- raise ValueError ("Not a matlab sparse matrix" )
1127- return cls .from_any (dictobj ['data__' ])
1128-
1129- @classmethod
1130- def from_shape (cls , shape = tuple (), ** kwargs ) -> "Array" :
1131- """
1132- Build an array of a given shape.
1133-
1134- Parameters
1135- ----------
1136- shape : list[int]
1137- Shape of the new array.
1138-
1139- Other Parameters
1140- ----------------
1141- dtype : np.dtype | None, default='double'
1142- Target data type.
1143-
1144- Returns
1145- -------
1146- array : SparseArray
1147- New array.
1148- """
1149- return cls (list (shape ), ** kwargs )
1150-
1151- @classmethod
1152- def from_any (cls , other , ** kwargs ) -> "SparseArray" :
1153- """
1154- Convert an array-like object to a numeric array.
1155-
1156- Parameters
1157- ----------
1158- other : ArrayLike
1159- object to convert.
1160-
1161- Other Parameters
1162- ----------------
1163- dtype : np.dtype | None, default=None
1164- Target data type. Guessed if `None`.
1165- copy : bool | None, default=None
1166- Whether to copy the underlying data.
1167- * `True` : the object is copied;
1168- * `None` : the the object is copied only if needed;
1169- * `False`: raises a `ValueError` if a copy cannot be avoided.
1170-
1171- Returns
1172- -------
1173- array : SparseArray
1174- Converted array.
1175- """
1176- copy = kwargs .pop ("copy" , None )
1177- inp = other
1178- if not isinstance (other , sparse .sparray ):
1179- other = np .asanyarray (other , ** kwargs )
1180- other = cls (other , ** kwargs )
1181- other = _spcopy_if_needed (other , inp , copy )
1182- return other
1183-
1184-
1185- else :
1186- WrappedSparseArray = SparseArray = None
1187-
1188-
11891068# ----------------------------------------------------------------------
11901069# Array
11911070# ----------------------------------------------------------------------
@@ -1421,6 +1300,179 @@ def __repr__(self):
14211300 return super ().__repr__ ()
14221301
14231302
1303+ # ----------------------------------------------------------------------
1304+ # SparseArray
1305+ # ----------------------------------------------------------------------
1306+
1307+
1308+ class _SparseMixin :
1309+ """Methods common to the scipy.sparse and dense backends."""
1310+
1311+ def _as_runtime (self ) -> dict :
1312+ indices = self .nonzero ()
1313+ values = self [indices ].reshape ([- 1 , 1 ])
1314+ indices = np .stack (indices , - 1 )
1315+ indices += 1
1316+ size = np .array ([[* np .shape (self )]])
1317+ return dict (
1318+ type__ = 'sparse' ,
1319+ size__ = size ,
1320+ indices__ = indices ,
1321+ values__ = values ,
1322+ )
1323+
1324+ @classmethod
1325+ def _from_runtime (cls , dictobj : dict ) -> "SparseArray" :
1326+ if dictobj ['type__' ] != 'sparse' :
1327+ raise ValueError ("Not a matlab sparse matrix" )
1328+ size = np .array (dictobj ['size__' ], dtype = np .uint64 ).ravel ()
1329+ size = size .tolist ()
1330+ dtype = _matlab_array_types ()[type (dictobj ['values__' ])]
1331+ obj = cls .from_shape (size , dtype = dtype )
1332+ indices = np .asarray (dictobj ['indices__' ], dtype = np .long ) - 1
1333+ values = np .asarray (dictobj ['values__' ], dtype = dtype ).ravel ()
1334+ obj [tuple (indices .T )] = values
1335+ return obj
1336+
1337+
1338+ if sparse :
1339+
1340+ class WrappedSparseArray (sparse .sparray , AnyWrappedArray ):
1341+ """Base class for sparse arrays."""
1342+
1343+ def to_dense (self ) -> "Array" :
1344+ return Array .from_any (super ().to_dense ())
1345+
1346+ class SparseArray (sparse .coo_array , _SparseMixin , WrappedSparseArray ):
1347+ """
1348+ Matlab sparse arrays (scipy.sparse backend).
1349+
1350+ ```python
1351+ # Instantiate from size
1352+ SparseArray(N, M, ...)
1353+ SparseArray([N, M, ...])
1354+ SparseArray.from_shape([N, M, ...])
1355+
1356+ # Instantiate from existing sparse or dense array
1357+ SparseArray(other_array)
1358+ SparseArray.from_any(other_array)
1359+
1360+ # Other options
1361+ SparseArray(..., dtype=None, *, copy=None)
1362+ ```
1363+
1364+ !!! warning
1365+ Lists or vectors of integers can be interpreted as shapes
1366+ or as dense arrays to copy. They are interpreted as shapes
1367+ by the `SparseArray` constructor. To ensure that they are
1368+ interpreted as dense arrays to copy, usse `SparseArray.from_any`.
1369+ """
1370+
1371+ def __init__ (self , * args , ** kwargs ) -> None :
1372+ mode , arg , kwargs = self ._parse_args (* args , ** kwargs )
1373+ if mode == "shape" :
1374+ return super ().__init__ (shape = arg , ** kwargs )
1375+ else :
1376+ if not isinstance (arg , (np .ndarray , sparse .sparray )):
1377+ arg = np .asanyarray (arg )
1378+ return super ().__init__ (arg , ** kwargs )
1379+
1380+ @classmethod
1381+ def from_shape (cls , shape = tuple (), ** kwargs ) -> "Array" :
1382+ """
1383+ Build an array of a given shape.
1384+
1385+ Parameters
1386+ ----------
1387+ shape : list[int]
1388+ Shape of the new array.
1389+
1390+ Other Parameters
1391+ ----------------
1392+ dtype : np.dtype | None, default='double'
1393+ Target data type.
1394+
1395+ Returns
1396+ -------
1397+ array : SparseArray
1398+ New array.
1399+ """
1400+ return cls (list (shape ), ** kwargs )
1401+
1402+ @classmethod
1403+ def from_any (cls , other , ** kwargs ) -> "SparseArray" :
1404+ """
1405+ Convert an array-like object to a numeric array.
1406+
1407+ Parameters
1408+ ----------
1409+ other : ArrayLike
1410+ object to convert.
1411+
1412+ Other Parameters
1413+ ----------------
1414+ dtype : np.dtype | None, default=None
1415+ Target data type. Guessed if `None`.
1416+ copy : bool | None, default=None
1417+ Whether to copy the underlying data.
1418+ * `True` : the object is copied;
1419+ * `None` : the the object is copied only if needed;
1420+ * `False`: raises a `ValueError` if a copy cannot be avoided.
1421+
1422+ Returns
1423+ -------
1424+ array : SparseArray
1425+ Converted array.
1426+ """
1427+ copy = kwargs .pop ("copy" , None )
1428+ inp = other
1429+ if not isinstance (other , sparse .sparray ):
1430+ other = np .asanyarray (other , ** kwargs )
1431+ other = cls (other , ** kwargs )
1432+ other = _spcopy_if_needed (other , inp , copy )
1433+ return other
1434+
1435+ else :
1436+ warnings .warn (
1437+ "Since scipy.sparse is not available, sparse matrices "
1438+ "will be implemented as dense matrices, which can lead to "
1439+ "unsubstainable memory usage. If this is an issue, install "
1440+ "scipy in your python environment."
1441+ )
1442+
1443+ class SparseArray (_SparseMixin , Array ):
1444+ """
1445+ Matlab sparse arrays (dense backend).
1446+
1447+ ```python
1448+ # Instantiate from size
1449+ SparseArray(N, M, ...)
1450+ SparseArray([N, M, ...])
1451+ SparseArray.from_shape([N, M, ...])
1452+
1453+ # Instantiate from existing sparse or dense array
1454+ SparseArray(other_array)
1455+ SparseArray.from_any(other_array)
1456+
1457+ # Other options
1458+ SparseArray(..., dtype=None, *, copy=None)
1459+ ```
1460+
1461+ !!! warning
1462+ Lists or vectors of integers can be interpreted as shapes
1463+ or as dense arrays to copy. They are interpreted as shapes
1464+ by the `SparseArray` constructor. To ensure that they are
1465+ interpreted as dense arrays to copy, usse `SparseArray.from_any`.
1466+
1467+ !!! note
1468+ This is not really a sparse array, but a dense array that gets
1469+ converted to a sparse array when passed to matlab.
1470+ """
1471+
1472+ def to_dense (self ) -> "Array" :
1473+ return np .ndarray .view (self , Array )
1474+
1475+
14241476# ----------------------------------------------------------------------
14251477# Cell
14261478# ----------------------------------------------------------------------
@@ -1749,7 +1801,7 @@ def _as_runtime(self) -> dict:
17491801 def _from_runtime (cls , objdict : dict ) -> "Cell" :
17501802 if objdict ['type__' ] != 'cell' :
17511803 raise TypeError ('objdict is not a cell' )
1752- size = np .array (objdict ['size__' ], dtype = np .uint32 ).ravel ()
1804+ size = np .array (objdict ['size__' ], dtype = np .uint64 ).ravel ()
17531805 data = np .fromiter (objdict ['data__' ], dtype = object )
17541806 data = data .reshape (size [::- 1 ]).transpose ()
17551807 try :
@@ -2376,7 +2428,7 @@ def _as_runtime(self) -> dict:
23762428 def _from_runtime (cls , objdict : dict ) -> "Struct" :
23772429 if objdict ['type__' ] != 'structarray' :
23782430 raise TypeError ('objdict is not a structarray' )
2379- size = np .array (objdict ['size__' ], dtype = np .uint32 ).ravel ()
2431+ size = np .array (objdict ['size__' ], dtype = np .uint64 ).ravel ()
23802432 data = np .array (objdict ['data__' ], dtype = object )
23812433 data = data .reshape (size )
23822434 try :
0 commit comments