Repository: chainer/chainer

Issue:
Add chainerx.ndarray.item
Background: #5797, #6007.


Discussion:

Resolving patch:
diff --git a/chainerx/_docs/array.py b/chainerx/_docs/array.py
--- a/chainerx/_docs/array.py
+++ b/chainerx/_docs/array.py
@@ -248,6 +248,19 @@ def binary_op(name, s):
 Returns ``True`` if the gradient will be set after backprop.
 
 See the note on :meth:`require_grad` for details.
+""")
+
+    _docs.set_doc(
+        ndarray.item,
+        """item()
+Copies an element of an array to a standard Python scalar and returns it.
+
+Returns:
+    z:
+        A copy of the specified element of the array as a suitable Python
+        scalar.
+
+.. seealso:: :func:`numpy.item`
 """)
 
     _docs.set_doc(


Test patch:
diff --git a/tests/chainerx_tests/unit_tests/test_array.py b/tests/chainerx_tests/unit_tests/test_array.py
--- a/tests/chainerx_tests/unit_tests/test_array.py
+++ b/tests/chainerx_tests/unit_tests/test_array.py
@@ -1,3 +1,5 @@
+import math
+
 import numpy
 import pytest
 
@@ -42,6 +44,81 @@ def test_init_with_device(shape, dtype_spec, device):
     _check_array(array, dtype_spec, shape, device=device)
 
 
+@pytest.mark.parametrize('value', [
+    0, 1, -1, 0.1, 0.9, -0.1, -0.9, 1.1, -1.1, 1.9, -
+    1.9, True, False, float('inf'), -float('inf'), float('nan'), -0.0
+])
+@pytest.mark.parametrize('shape', [
+    (), (1,), (1, 1, 1)
+])
+@pytest.mark.parametrize_device(['native:0', 'cuda:0'])
+def test_cast_scalar(device, value, shape, dtype):
+    np_dtype = numpy.dtype(dtype)
+    try:
+        np_value = np_dtype.type(value)
+    except (ValueError, OverflowError):
+        return
+
+    a_np = numpy.asarray([np_value], dtype).reshape(shape)
+    a_chx = chainerx.array(a_np)
+
+    def should_cast_succeed(typ):
+        try:
+            typ(np_value)
+            return True
+        except (ValueError, OverflowError):
+            return False
+
+    # Cast to float
+    if should_cast_succeed(float):
+        assert type(float(a_chx)) is float
+        if math.isnan(float(a_np)):
+            assert math.isnan(float(a_chx))
+        else:
+            assert float(a_np) == float(a_chx)
+    # Cast to int
+    if should_cast_succeed(int):
+        assert type(int(a_chx)) is int
+        assert int(a_np) == int(a_chx)
+    # Cast to bool
+    if should_cast_succeed(bool):
+        assert type(bool(a_chx)) is bool
+        assert bool(a_np) == bool(a_chx)
+
+    # item()
+    item_actual = a_chx.item()
+    np_dtype = numpy.dtype(dtype)
+    item_expected = np_dtype.type(value).item()
+    assert isinstance(item_actual, type(item_expected))
+    assert (
+        (numpy.isnan(item_actual) and numpy.isnan(item_expected))
+        or item_actual == item_expected)
+
+
+@pytest.mark.parametrize('shape', [
+    (0,), (1, 0), (2,), (1, 2), (2, 3),
+])
+@pytest.mark.parametrize_device(['native:0', 'cuda:0'])
+def test_cast_scalar_invalid(device, shape):
+    dtype = chainerx.float32
+
+    a = chainerx.ones(shape, dtype)
+    with pytest.raises(chainerx.DimensionError):
+        float(a)
+
+    a = chainerx.ones(shape, dtype)
+    with pytest.raises(chainerx.DimensionError):
+        int(a)
+
+    a = chainerx.ones(shape, dtype)
+    with pytest.raises(chainerx.DimensionError):
+        bool(a)
+
+    a = chainerx.ones(shape, dtype)
+    with pytest.raises(chainerx.DimensionError):
+        a.item()
+
+
 def test_to_device():
     a = chainerx.ones((2,), chainerx.float32, device="native:0")
     dst_device = chainerx.get_device("native:1")