Repository: Theano/Theano

Issue:
Implement tensor.min() for bool and uint*
Currently, it raises NotImplementedError.
It could use tensor.max(), like for float and int, or maybe in that case actually use a "minimum" reduction.

See [here](https://github.com/Theano/Theano/blob/372ae95ef9aae402b7f426d159585d26556eb0a1/theano/tensor/basic.py#L1734)


Discussion:

Resolving patch:
diff --git a/theano/tensor/basic.py b/theano/tensor/basic.py
--- a/theano/tensor/basic.py
+++ b/theano/tensor/basic.py
@@ -1715,6 +1715,12 @@ def min(x, axis=None, keepdims=False):
     str_x_type = str(x.dtype)
     if str_x_type.startswith('float') or str_x_type in int_dtypes:
         return -max(-x, axis=axis, keepdims=keepdims)
+    elif str_x_type in uint_dtypes:
+        itype = np.iinfo(x.dtype)
+        max_val = np.array(itype.max, dtype=itype.dtype)
+        return max_val - max(max_val - x, axis=axis, keepdims=keepdims)
+    elif str_x_type == 'bool':
+        return ~max(~x, axis=axis, keepdims=keepdims)
     else:
         # Be careful about unsigned integers, complex
         raise NotImplementedError()
@@ -1740,6 +1746,11 @@ def argmin(x, axis=None, keepdims=False):
     str_x_type = str(x.dtype)
     if str_x_type.startswith('float') or str_x_type in int_dtypes:
         return argmax(-x, axis=axis, keepdims=keepdims)
+    elif str_x_type in uint_dtypes:
+        itype = np.iinfo(x.dtype)
+        return argmax(itype.max - x, axis=axis, keepdims=keepdims)
+    elif str_x_type == 'bool':
+        return argmax(~x, axis=axis, keepdims=keepdims)
     else:
         # Be careful about unsigned integers, complex
         raise NotImplementedError()


Test patch:
diff --git a/theano/tensor/tests/test_basic.py b/theano/tensor/tests/test_basic.py
--- a/theano/tensor/tests/test_basic.py
+++ b/theano/tensor/tests/test_basic.py
@@ -3401,6 +3401,24 @@ def test_grad_argmax(self):
         except TypeError:
             pass
 
+    def test_uint(self):
+        for dtype in ('uint8', 'uint16', 'uint32', 'uint64'):
+            itype = np.iinfo(dtype)
+            data = np.array([itype.min + 3, itype.min, itype.max - 5, itype.max], dtype)
+            n = as_tensor_variable(data)
+            i = eval_outputs(argmin(n))
+            self.assertEqual(i, 1)
+            i = eval_outputs(argmax(n))
+            self.assertEqual(i, 3)
+
+    def test_bool(self):
+        data = np.array([True, False], 'bool')
+        n = as_tensor_variable(data)
+        i = eval_outputs(argmin(n))
+        self.assertEqual(i, 1)
+        i = eval_outputs(argmax(n))
+        self.assertEqual(i, 0)
+
 
 class T_min_max(unittest.TestCase):
     def setUp(self):
@@ -3586,6 +3604,28 @@ def _grad_list(self):
         # check_grad_max(data, eval_outputs(grad(max_and_argmax(n,
         # axis=1)[0], n)),axis=1)
 
+    def test_uint(self):
+        for dtype in ('uint8', 'uint16', 'uint32', 'uint64'):
+            itype = np.iinfo(dtype)
+            data = np.array([itype.min + 3, itype.min, itype.max - 5, itype.max], dtype)
+            n = as_tensor_variable(data)
+            self.assertEqual(min(n).dtype, dtype)
+            i = eval_outputs(min(n))
+            self.assertEqual(i, itype.min)
+            self.assertEqual(max(n).dtype, dtype)
+            i = eval_outputs(max(n))
+            self.assertEqual(i, itype.max)
+
+    def test_bool(self):
+        data = np.array([True, False], 'bool')
+        n = as_tensor_variable(data)
+        self.assertEqual(min(n).dtype, 'bool')
+        i = eval_outputs(min(n))
+        self.assertEqual(i, False)
+        self.assertEqual(max(n).dtype, 'bool')
+        i = eval_outputs(max(n))
+        self.assertEqual(i, True)
+
 
 def test_basic_allclose():
     # This was raised by a user in https://github.com/Theano/Theano/issues/2975