Repository: chainer/chainer

Issue:
link.LSTM.to_cpu (resp. to_gpu) does not move state variables to CPU (resp. GPU).
When `link.LSTM` is move to CPU (resp. GPU), its state variables should also be moved to CPU(resp. GPU). But it does not.

```
In [39]: l = chainer.links.LSTM(10, 10)

In [40]: l.xp
Out[40]: <module 'numpy' from '/home/delta/.pyenv/versions/pyenv-2.7.9/lib/python2.7/site-packages/numpy/__init__.pyc'>

In [41]: x = chainer.Variable(numpy.random.uniform(-1, 1, (5, 10)).astype(numpy.float32))

In [42]: l(x)
Out[42]: <variable at 0x7fe900a88d90>

In [43]: type(l.h.data)
Out[43]: numpy.ndarray

In [44]: l.to_gpu()
Out[44]: <chainer.links.connection.lstm.LSTM at 0x7fe900a42a10>

In [45]: l.xp
Out[45]: <module 'cupy' from 'cupy/__init__.pyc'>

In [46]: type(l.h.data)
Out[46]: numpy.ndarray

In [47]: type(l.c.data)
Out[47]: numpy.ndarray
```

The output of [46] and [47] should be `cupy.ndarray`



Discussion:

Resolving patch:
diff --git a/chainer/links/connection/lstm.py b/chainer/links/connection/lstm.py
--- a/chainer/links/connection/lstm.py
+++ b/chainer/links/connection/lstm.py
@@ -35,6 +35,20 @@ def __init__(self, in_size, out_size):
         self.state_size = out_size
         self.reset_state()
 
+    def to_cpu(self):
+        super(LSTM, self).to_cpu()
+        if self.c is not None:
+            self.c.to_cpu()
+        if self.h is not None:
+            self.h.to_cpu()
+
+    def to_gpu(self, device=None):
+        super(LSTM, self).to_gpu(device)
+        if self.c is not None:
+            self.c.to_gpu(device)
+        if self.h is not None:
+            self.h.to_gpu(device)
+
     def reset_state(self):
         """Resets the internal state.
 


Test patch:
diff --git a/tests/chainer_tests/links_tests/connection_tests/test_lstm.py b/tests/chainer_tests/links_tests/connection_tests/test_lstm.py
--- a/tests/chainer_tests/links_tests/connection_tests/test_lstm.py
+++ b/tests/chainer_tests/links_tests/connection_tests/test_lstm.py
@@ -57,4 +57,94 @@ def test_forward_gpu(self):
         self.check_forward(cuda.to_gpu(self.x))
 
 
+class TestLSSTMRestState(unittest.TestCase):
+
+    def setUp(self):
+        self.link = links.LSTM(5, 7)
+        self.x = chainer.Variable(
+            numpy.random.uniform(-1, 1, (3, 5)).astype(numpy.float32))
+
+    def check_state(self):
+        self.assertIsNone(self.link.c)
+        self.assertIsNone(self.link.h)
+        self.link(self.x)
+        self.assertIsNotNone(self.link.c)
+        self.assertIsNotNone(self.link.h)
+
+    def test_state_cpu(self):
+        self.check_state()
+
+    @attr.gpu
+    def test_state_gpu(self):
+        self.link.to_gpu()
+        self.x.to_gpu()
+        self.check_state()
+
+    def check_reset_state(self):
+        self.link(self.x)
+        self.link.reset_state()
+        self.assertIsNone(self.link.c)
+        self.assertIsNone(self.link.h)
+
+    def test_reset_state_cpu(self):
+        self.check_reset_state()
+
+    @attr.gpu
+    def test_reset_state_gpu(self):
+        self.link.to_gpu()
+        self.x.to_gpu()
+        self.check_reset_state()
+
+
+class TestLSTMToCPUToGPU(unittest.TestCase):
+
+    def setUp(self):
+        self.link = links.LSTM(5, 7)
+        self.x = chainer.Variable(
+            numpy.random.uniform(-1, 1, (3, 5)).astype(numpy.float32))
+
+    def check_to_cpu(self, s):
+        self.link.to_cpu()
+        self.assertIsInstance(s.data, self.link.xp.ndarray)
+        self.link.to_cpu()
+        self.assertIsInstance(s.data, self.link.xp.ndarray)
+
+    def test_to_cpu_cpu(self):
+        self.link(self.x)
+        self.check_to_cpu(self.link.c)
+        self.check_to_cpu(self.link.h)
+
+    @attr.gpu
+    def test_to_cpu_gpu(self):
+        self.link.to_gpu()
+        self.x.to_gpu()
+        self.link(self.x)
+        self.check_to_cpu(self.link.c)
+        self.check_to_cpu(self.link.h)
+
+    def check_to_cpu_to_gpu(self, s):
+        self.link.to_gpu()
+        self.assertIsInstance(s.data, self.link.xp.ndarray)
+        self.link.to_gpu()
+        self.assertIsInstance(s.data, self.link.xp.ndarray)
+        self.link.to_cpu()
+        self.assertIsInstance(s.data, self.link.xp.ndarray)
+        self.link.to_gpu()
+        self.assertIsInstance(s.data, self.link.xp.ndarray)
+
+    @attr.gpu
+    def test_to_cpu_to_gpu_cpu(self):
+        self.link(self.x)
+        self.check_to_cpu_to_gpu(self.link.c)
+        self.check_to_cpu_to_gpu(self.link.h)
+
+    @attr.gpu
+    def test_to_cpu_to_gpu_gpu(self):
+        self.link.to_gpu()
+        self.x.to_gpu()
+        self.link(self.x)
+        self.check_to_cpu_to_gpu(self.link.c)
+        self.check_to_cpu_to_gpu(self.link.h)
+
+
 testing.run_module(__name__, __file__)