code: def test_unroll(cell_type, num_states, layout):
      class RNNLayer(gluon.HybridBlock):
          def __init__(self, cell_type, hidden_size, layout):
              super(RNNLayer, self).__init__()
              self.cell = cell_type(hidden_size)
              self.layout = layout
  
          def forward(self, inputs, states, valid_length):
              if (
                  isinstance(valid_length, list)
                  and len(valid_length) == 0
              ):
                  valid_length = None
              return gluon.rnn.rnn_cell.dynamic_unroll(
                  self.cell,
                  inputs,
                  states,
                  valid_length=valid_length,
                  layout=self.layout,
              )
  
          def infer_shape(self, x, *args):
              self.cell.infer_shape(0, x, False)
  
      batch_size = 20
      input_size = 50
      hidden_size = 30
      seq_len = 10
      device = default_device()
      if layout == "TNC":
          rnn_data = mx.np.random.normal(
              loc=0,
              scale=1,
              size=(seq_len, batch_size, input_size),
              device=device,
          )
      elif layout == "NTC":
          rnn_data = mx.np.random.normal(
              loc=0,
              scale=1,
              size=(batch_size, seq_len, input_size),
              device=device,
          )
      else:
          print("Wrong layout")
          return
      valid_length = mx.np.round(
          mx.np.random.uniform(
              low=1, high=10, size=(batch_size), device=device
          )
      )
      state_shape = (batch_size, hidden_size)
      states = [
          mx.np.random.normal(
              loc=0, scale=1, size=state_shape, device=device
          )
          for i in range(num_states)
      ]
  
      cell = cell_type(hidden_size)
      if layout == "TNC":
          cell.infer_shape(0, rnn_data[0], False)
          cell.initialize(device=default_device())
          cell(rnn_data[0], states)
      else:
          cell.infer_shape(0, rnn_data[:, 0, :], False)
          cell.initialize(device=default_device())
          cell(rnn_data[:, 0, :], states)
      params1 = cell.collect_params()
      orig_params1 = copy.deepcopy(params1)
  
      trainer = gluon.Trainer(params1, "sgd", {"learning_rate": 0.03})
      with mx.autograd.record():
          res1, states1 = cell.unroll(
              seq_len,
              rnn_data,
              states,
              valid_length=valid_length,
              layout=layout,
              merge_outputs=True,
          )
      res1.backward()
      trainer.step(batch_size)
  
      configs = [
          lambda layer: None,
          lambda layer: layer.hybridize(),
          lambda layer: layer.hybridize({"inline_limit": 0}),
          lambda layer: layer.hybridize({"static_alloc": True}),
          lambda layer: layer.hybridize(
              {"static_alloc": True, "static_shape": True}
          ),
      ]
      # We can't pass None to a hybrid block, but it accepts an empty list.
      # so we use an empty list to represent valid_length if it's None.
      if valid_length is None:
          valid_length = []
      for config in configs:
          layer = RNNLayer(cell_type, hidden_size, layout)
          layer.infer_shape(rnn_data)
          layer.initialize(device=default_device())
          config(layer)
          res2, states2 = layer(rnn_data, states, valid_length)
          params2 = layer.collect_params()
          for key, val in orig_params1.items():
              params2["cell." + key].set_data(copy.deepcopy(val.data()))
  
          trainer = gluon.Trainer(
              params2, "sgd", {"learning_rate": 0.03}
          )
          with mx.autograd.record():
              res2, states2 = layer(rnn_data, states, valid_length)
          assert_almost_equal(res1, res2, rtol=0.001, atol=0.0001)
          assert len(states1) == len(states2)
          for i in range(len(states1)):
              assert_almost_equal(
                  states1[i], states2[i], rtol=0.001, atol=0.0001
              )
          res2.backward()
          trainer.step(batch_size)
  
          for key, val in params1.items():
              weight1 = val.data()
              weight2 = params2["cell." + key].data()
              # Subgraph created from npx.foreach in deferred compute is
              # little bit different from the legacy foreach operator.
              assert_almost_equal(weight1, weight2, rtol=0.1, atol=0.1)
  
imports: import mxnet
  from mxnet import gluon, np
  import numpy
  import copy
  from functools import partial
  from numpy.testing import assert_allclose
  from mxnet.gluon.utils import split_rnn_params
  import pytest
  from mxnet.test_utils import almost_equal, assert_almost_equal, default_device
  from common import assert_raises_cudnn_not_satisfied, retry
label_desc: Use image node_id 41 for calling a global function with example usage: test_unroll(cell_type, num_states, layout) and returns: gluon