def add_gru(self, name, W_h, W_x, b, hidden_size, input_size,
            input_names, output_names, activation = 'TANH', inner_activation = 'SIGMOID_HARD',
            output_all = False, reverse_input = False):
        """
        Add a Gated-Recurrent Unit (GRU) layer to the model.

        Parameters
        ----------
        name: str
            The name of this layer.
        W_h: [numpy.array]
            List of recursion weight matrices. The ordering is [R_z, R_r, R_o],
            where R_z, R_r and R_o are weight matrices at update gate, reset gate and output gate.
            The shapes of these matrices are (hidden_size, hidden_size).
        W_x: [numpy.array]
            List of input weight matrices. The ordering is [W_z, W_r, W_o],
            where W_z, W_r, and W_o are weight matrices at update gate, reset gate and output gate.
            The shapes of these matrices are (hidden_size, input_size).
        b: [numpy.array] | None
            List of biases of the GRU layer. The ordering is [b_z, b_r, b_o],
            where b_z, b_r, b_o are biases at update gate, reset gate and output gate.
            If None, biases are ignored. Otherwise the shapes of the biases are (hidden_size, ).
        hidden_size: int
            Number of hidden units. This is equal to the number of channels of output shape.
        input_size: int
            Number of the number of channels of input shape.
        activation: str
            Activation function used at the output gate. Can be one of the following option:
            ['RELU', 'TANH', 'SIGMOID', 'SCALED_TANH', 'SIGMOID_HARD', 'LINEAR'].
            Defaults to 'TANH'.
            See add_activation for more detailed description.
        inner_activation: str
            Inner activation function used at update and reset gates. Can be one of the following option:
            ['RELU', 'TANH', 'SIGMOID', 'SCALED_TANH', 'SIGMOID_HARD', 'LINEAR'].
            Defaults to 'SIGMOID_HARD'.
            See add_activation for more detailed description.
        input_names: [str]
            The input blob name list of this layer, in the order of [x, h_input].
        output_names: [str]
            The output blob name list of this layer, in the order of [y, h_output].
        output_all: boolean
            Whether the recurrent layer should output at every time step.

            - If False, the output is the result after the final state update.
            - If True, the output is a sequence, containing outputs at all time steps.
        reverse_input: boolean
            Whether the recurrent layer should process the input sequence in the reverse order.

            - If False, the input sequence order is not reversed.
            - If True, the input sequence order is reversed.

        See Also
        --------
        add_activation, add_simple_rnn, add_unilstm, add_bidirlstm
        """
        spec = self.spec
        nn_spec = self.nn_spec

        # Add a new Layer
        spec_layer = nn_spec.layers.add()
        spec_layer.name = name

        for name in input_names:
            spec_layer.input.append(name)
        for name in output_names:
            spec_layer.output.append(name)
        spec_layer_params = spec_layer.gru

        # set the parameters
        spec_layer_params.inputVectorSize = input_size
        spec_layer_params.outputVectorSize = hidden_size
        if b is not None:
            spec_layer_params.hasBiasVectors = True
        spec_layer_params.sequenceOutput = output_all
        spec_layer_params.reverseInput = reverse_input

        activation_f = spec_layer_params.activations.add()
        activation_g = spec_layer_params.activations.add()
        _set_recurrent_activation(activation_f, inner_activation)
        _set_recurrent_activation(activation_g, activation)

        # Write the weights
        R_z, R_r, R_o = W_h
        W_z, W_r, W_o = W_x

        spec_layer_params.updateGateWeightMatrix.floatValue.extend(map(float, W_z.flatten()))
        spec_layer_params.resetGateWeightMatrix.floatValue.extend(map(float, W_r.flatten()))
        spec_layer_params.outputGateWeightMatrix.floatValue.extend(map(float, W_o.flatten()))

        spec_layer_params.updateGateRecursionMatrix.floatValue.extend(map(float, R_z.flatten()))
        spec_layer_params.resetGateRecursionMatrix.floatValue.extend(map(float, R_r.flatten()))
        spec_layer_params.outputGateRecursionMatrix.floatValue.extend(map(float, R_o.flatten()))

        if b is not None:
            b_z, b_r, b_o = b
            spec_layer_params.updateGateBiasVector.floatValue.extend(map(float, b_z.flatten()))
            spec_layer_params.resetGateBiasVector.floatValue.extend(map(float, b_r.flatten()))
            spec_layer_params.outputGateBiasVector.floatValue.extend(map(float, b_o.flatten()))