Repository: scipy/scipy

Issue:
savemat discards dimension information if any dimension is zero
My issue is about `scipy.io.savemat`

#### Reproducing code example:
```
scipy.io.savemat(r'D:/temp/temp.mat', {'data': np.full((10, 0), np.nan)})
assert scipy.io.loadmat(r'D:/temp/temp.mat')['data'].shape == (10, 0)
```

#### Error message:
This fails because the loaded `data` variable has size `(0, 0)`

This is a problem with `savemat` but not `loadmat` because if I use Matlab to write out a `10x0` matrix it loads just fine:
```
scipy.io.loadmat(r'D:/temp/temp-from-matlab.mat')['data'].shape == (10, 0)
```

You can create this example file as follows:
```
$ cat >temp.inp <<'EOF'
TUFUTEFCIDUuMCBNQVQtZmlsZSwgUGxhdGZvcm06IFBDV0lONjQsIENyZWF0ZWQgb246IE1vbiBK
YW4gIDQgMDk6NDc6MzQgMjAyMSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAAAAAAAAAAAAABSU0PAAAAJQAAAHic42NgYDAAYjYg5oDSIMAK5XNB+YwMLAwpiSWJTFA+ACQM
Ags=
EOF

$ base64 --decode temp.inp > temp-from-matlab.mat
```

A similar bug with empty arrays in `savemat` was fixed in #6096, maybe this is related.

#### Scipy/Numpy/Python version information:
```
1.5.2 1.19.2 sys.version_info(major=3, minor=7, micro=9, releaselevel='final', serial=0)
```


Discussion:

Resolving patch:
diff --git a/scipy/io/matlab/miobase.py b/scipy/io/matlab/miobase.py
--- a/scipy/io/matlab/miobase.py
+++ b/scipy/io/matlab/miobase.py
@@ -294,11 +294,11 @@ def matdims(arr, oned_as='column'):
     """
     shape = arr.shape
     if shape == ():  # scalar
-        return (1,1)
-    if functools.reduce(operator.mul, shape) == 0:  # zero elememts
-        return (0,) * np.max([arr.ndim, 2])
+        return (1, 1)
     if len(shape) == 1:  # 1D
-        if oned_as == 'column':
+        if shape[0] == 0:
+            return (0, 0)
+        elif oned_as == 'column':
             return shape + (1,)
         elif oned_as == 'row':
             return (1,) + shape


Test patch:
diff --git a/scipy/io/matlab/tests/test_mio.py b/scipy/io/matlab/tests/test_mio.py
--- a/scipy/io/matlab/tests/test_mio.py
+++ b/scipy/io/matlab/tests/test_mio.py
@@ -948,6 +948,13 @@ def test_logical_out_type():
     assert_equal(var.dtype.type, np.uint8)
 
 
+def test_roundtrip_zero_dimensions():
+    stream = BytesIO()
+    savemat(stream, {'d':np.empty((10, 0))})
+    d = loadmat(stream)
+    assert d['d'].shape == (10, 0)
+
+
 def test_mat4_3d():
     # test behavior when writing 3-D arrays to matlab 4 files
     stream = BytesIO()
diff --git a/scipy/io/matlab/tests/test_miobase.py b/scipy/io/matlab/tests/test_miobase.py
--- a/scipy/io/matlab/tests/test_miobase.py
+++ b/scipy/io/matlab/tests/test_miobase.py
@@ -19,8 +19,9 @@ def test_matdims():
     # 3d array, rowish vector
     assert_equal(matdims(np.array([[[2,3]]])), (1, 1, 2))
     assert_equal(matdims(np.array([])), (0, 0))  # empty 1-D array
-    assert_equal(matdims(np.array([[]])), (0, 0))  # empty 2-D array
-    assert_equal(matdims(np.array([[[]]])), (0, 0, 0))  # empty 3-D array
+    assert_equal(matdims(np.array([[]])), (1, 0))  # empty 2-D array
+    assert_equal(matdims(np.array([[[]]])), (1, 1, 0))  # empty 3-D array
+    assert_equal(matdims(np.empty((1, 0, 1))), (1, 0, 1))  # empty 3-D array
     # Optional argument flips 1-D shape behavior.
     assert_equal(matdims(np.array([1,2]), 'row'), (1, 2))  # 1-D array, 2 elements
     # The argument has to make sense though