class_docstr: Gated Recurrent Unit (GRU) Layer.
  
  Parameters
  ----------
  {BaseRNNLayer.n_units}
  
  {BaseRNNLayer.only_return_final}
  
  input_weights : Initializer, ndarray
      Weight parameters for input connection.
      Defaults to :class:`HeNormal() <neupy.init.HeNormal>`.
  
  hidden_weights : Initializer, ndarray
      Weight parameters for hidden connection.
      Defaults to :class:`HeNormal() <neupy.init.HeNormal>`.
  
  biases : Initializer, ndarray
      Bias parameters for all gates.
      Defaults to :class:`Constant(0) <neupy.init.Constant>`.
  
  resetgate : function
      Activation function for the reset gate.
      Defaults to ``tf.nn.sigmoid``.
  
  updategate : function
      Activation function for the update gate.
      Defaults to ``tf.nn.sigmoid``.
  
  hidden_update : function
      Activation function for the hidden state update.
      Defaults to ``tf.tanh``.
  
  learn_init : bool
      If ``True``, make ``hidden_init`` trainable variable.
      Defaults to ``False``.
  
  hidden_init : array-like, Tensorfow variable, scalar or Initializer
      Initializer for initial hidden state (:math:`h_0`).
      Defaults to :class:`Constant(0) <neupy.init.Constant>`.
  
  backwards : bool
      If ``True``, process the sequence backwards and then reverse the
      output again such that the output from the layer is always
      from :math:`x_1` to :math:`x_n`. Defaults to ``False``.
  
  unroll_scan : bool
      If ``True`` the recursion is unrolled instead of using scan.
      For some graphs this gives a significant speed up but it
      might also consume more memory. When ``unroll_scan=True``,
      backpropagation always includes the full sequence, so
      ``n_gradient_steps`` must be set to ``-1`` and the input
      sequence length must be known at compile time (i.e.,
      cannot be given as ``None``). Defaults to ``False``.
  
  {BaseLayer.name}
  
  Notes
  -----
  Code was adapted from the
  `Lasagne <https://github.com/Lasagne/Lasagne>`_ library.
  
  Examples
  --------
  Sequence classification
  
  >>> from neupy.layers import *
  >>>
  >>> n_time_steps = 40
  >>> n_categories = 20
  >>> embedded_size = 10
  >>>
  >>> network = join(
  ...     Input(n_time_steps),
  ...     Embedding(n_categories, embedded_size),
  ...     GRU(20),
  ...     Sigmoid(1),
  ... )
  >>> network
  (?, 40) -> [... 4 layers ...] -> (?, 1)
code: def output(self, input, **kwargs):
      # Because scan iterates over the first dimension we
      # dimshuffle to (n_time_steps, n_samples, n_features)
      input = tf.transpose(input, [1, 0, 2])
  
      # Create single recurrent computation step function
      # input_n is the n'th vector of the input
      def one_gru_step(states, input_n):
          with tf.name_scope("gru-cell"):
              (hid_previous,) = states
              input_n = (
                  tf.matmul(input_n, self.input_weights) + self.biases
              )
  
              # Compute W_{hr} h_{t - 1}, W_{hu} h_{t - 1},
              # and W_{hc} h_{t - 1}
              hid_input = tf.matmul(hid_previous, self.hidden_weights)
  
              if self.gradient_clipping != 0:
                  input_n = clip_gradient(
                      input_n, self.gradient_clipping
                  )
                  hid_input = clip_gradient(
                      hid_input, self.gradient_clipping
                  )
  
              hid_resetgate, hid_updategate, hid_hidden = tf.split(
                  hid_input, 3, axis=1
              )
  
              in_resetgate, in_updategate, in_hidden = tf.split(
                  input_n, 3, axis=1
              )
  
              # Reset and update gates
              resetgate = self.resetgate(hid_resetgate + in_resetgate)
              updategate = self.updategate(
                  hid_updategate + in_updategate
              )
  
              # Compute W_{xc}x_t + r_t \odot (W_{hc} h_{t - 1})
              hidden_update = in_hidden + resetgate * hid_hidden
  
              if self.gradient_clipping != 0:
                  hidden_update = clip_gradient(
                      hidden_update, self.gradient_clipping
                  )
  
              hidden_update = self.hidden_update(hidden_update)
  
              # Compute (1 - u_t)h_{t - 1} + u_t c_t
              return [
                  hid_previous
                  - updategate * (hid_previous - hidden_update)
              ]
  
      input_shape = tf.shape(input)
      n_samples = input_shape[1]  # batch dim has been moved
      hidden_init = tf.tile(self.hidden_init, (n_samples, 1))
      sequence = input
  
      if self.backwards:
          sequence = tf.reverse(sequence, axis=[0])
  
      if self.unroll_scan:
          # Explicitly unroll the recurrence instead of using scan
          hid_out = unroll_scan(
              fn=one_gru_step,
              sequence=sequence,
              outputs_info=[hidden_init],
          )
      else:
          (hid_out,) = tf.scan(
              fn=one_gru_step,
              elems=sequence,
              initializer=[hidden_init],
              name="gru-scan",
          )
  
      # When it is requested that we only return the final sequence step,
      # we need to slice it out immediately after scan is applied
      if self.only_return_final:
          return hid_out[-1]
  
      # if scan is backward reverse the output
      if self.backwards:
          hid_out = tf.reverse(hid_out, axis=[0])
  
      # dimshuffle back to (n_samples, n_time_steps, n_features))
      hid_out = tf.transpose(hid_out, [1, 0, 2])
      return hid_out
  
imports: import types
  import tensorflow
  from neupy import init
  from neupy.utils import tensorflow_session
  from neupy.exceptions import LayerConnectionError
  from neupy.core.properties import IntProperty, Property, NumberProperty, ParameterProperty
  from .base import BaseLayer
label_desc: Use image node_id 3 for calling the GRU obj's underlying member method code with example usage: obj.output(input) and returns: hid_out, hid_out, list