Repository: ray-project/ray

Issue:
[rllib] utils.debug.summarize() dies on empty arrays
# System information
- **OS Platform and Distribution (e.g., Linux Ubuntu 16.04)**: Ubuntu 18.04
- **Ray installed from (source or binary)**: binary (pypi)
- **Ray version**: 0.7.1 & 0.7.2
- **Python version**: 3.6
- **Exact command to reproduce**: N/A

### Describe the problem

I'm running rllib on an environment that returns weird zero-length observations. RLLib chokes when it tries to summarise them because the arrays don't have a min/max/mean. Example traceback below.

### Source code / logs

```
2019-07-16 14:10:09,054 ERROR trial_runner.py:487 -- Error processing event.
Traceback (most recent call last):
  File "/home/sam/.virtualenvs/weird-env/lib/python3.6/site-packages/ray/tune/trial_runner.py", line 436, in _process_trial
    result = self.trial_executor.fetch_result(trial)
…snip…
  File "/home/sam/.virtualenvs/weird-env/lib/python3.6/site-packages/ray/rllib/evaluation/sampler.py", line 308, in _env_runner
    summarize(unfiltered_obs)))
  File "/home/sam/.virtualenvs/weird-env/lib/python3.6/site-packages/ray/rllib/utils/debug.py", line 65, in summarize
    return _printer.pformat(_summarize(obj))
  File "/home/sam/.virtualenvs/weird-env/lib/python3.6/site-packages/ray/rllib/utils/debug.py", line 70, in _summarize
    return {k: _summarize(v) for k, v in obj.items()}
…snip…
  File "/home/sam/.virtualenvs/weird-env/lib/python3.6/site-packages/ray/rllib/utils/debug.py", line 87, in _summarize
    obj.shape, obj.dtype, round(float(np.min(obj)), 3),
  File "/home/sam/.virtualenvs/weird-env/lib/python3.6/site-packages/numpy/core/fromnumeric.py", line 2618, in amin
    initial=initial)
  File "/home/sam/.virtualenvs/weird-env/lib/python3.6/site-packages/numpy/core/fromnumeric.py", line 86, in _wrapreduction
    return ufunc.reduce(obj, axis, dtype, out, **passkwargs)
ValueError: zero-size array to reduction operation minimum which has no identity

```


Discussion:

Resolving patch:
diff --git a/python/ray/rllib/utils/debug.py b/python/ray/rllib/utils/debug.py
--- a/python/ray/rllib/utils/debug.py
+++ b/python/ray/rllib/utils/debug.py
@@ -78,7 +78,10 @@ def _summarize(obj):
     elif isinstance(obj, tuple):
         return tuple(_summarize(x) for x in obj)
     elif isinstance(obj, np.ndarray):
-        if obj.dtype == np.object:
+        if obj.size == 0:
+            return _StringValue("np.ndarray({}, dtype={})".format(
+                obj.shape, obj.dtype))
+        elif obj.dtype == np.object:
             return _StringValue("np.ndarray({}, dtype={}, head={})".format(
                 obj.shape, obj.dtype, _summarize(obj[0])))
         else:
diff --git a/python/ray/rllib/utils/memory.py b/python/ray/rllib/utils/memory.py
--- a/python/ray/rllib/utils/memory.py
+++ b/python/ray/rllib/utils/memory.py
@@ -56,7 +56,11 @@ def aligned_array(size, dtype, align=64):
     empty = np.empty(n + (align - 1), dtype=np.uint8)
     data_align = empty.ctypes.data % align
     offset = 0 if data_align == 0 else (align - data_align)
-    output = empty[offset:offset + n].view(dtype)
+    if n == 0:
+        # stop np from optimising out empty slice reference
+        output = empty[offset:offset + 1][0:0].view(dtype)
+    else:
+        output = empty[offset:offset + n].view(dtype)
 
     assert len(output) == size, len(output)
     assert output.ctypes.data % align == 0, output.ctypes.data


Test patch: