'Backward propagation of errors through bi-directional recurrent layer.
Arguments:
deltas (Tensor): tensors containing the errors for
each step of model unrolling.
shape: (output_size, sequence_length * batch_size)
Returns:
Tensor: back propagated errors for each step of time unrolling
for each mini-batch element
shape: (input_size, sequence_length * batch_size)'

def bprop(self, error, alpha=1.0, beta=1.0):

    if (self.in_deltas_f is None):
        self.in_deltas_f = get_steps(error[:self.nout], self.o_shape)
        self.prev_in_deltas = (self.in_deltas_f[(-1):] + self.in_deltas_f[:(-1)])
    if (self.in_deltas_b is None):
        self.in_deltas_b = get_steps(error[self.nout:], self.o_shape)
        self.next_in_deltas = (self.in_deltas_b[1:] + self.in_deltas_b[:1])
    self.out_deltas_buffer[:] = 0
    self.be.compound_rnn_unroll_bprop(self.W_recur_f.T, self.prev_in_deltas, self.in_deltas_f, self.h_f, self.nout, self.nsteps, self.nsteps, self.activation, True)
    self.be.compound_rnn_unroll_bprop(self.W_recur_b.T, self.next_in_deltas, self.in_deltas_b, self.h_b, self.nout, self.nsteps, self.nsteps, self.activation, False)
    in_deltas_all_f = error[:self.nout]
    in_deltas_cur_f = in_deltas_all_f[:, self.be.bsz:]
    h_prev_all = self.h_buffer_f[:, :(- self.be.bsz)]
    self.be.compound_dot(in_deltas_cur_f, h_prev_all.T, self.dW_recur_f)
    in_deltas_all_b = error[self.nout:]
    in_deltas_cur_b = in_deltas_all_b[:, :(- self.be.bsz)]
    h_next_all = self.h_buffer_b[:, self.be.bsz:]
    self.be.compound_dot(in_deltas_cur_b, h_next_all.T, self.dW_recur_b)
    self._bprop_bn(error, self.h_ff_buffer)
    self.be.compound_dot(in_deltas_all_f, self.x_f_v.T, self.dW_input_f)
    self.db_f[:] = self.be.sum(in_deltas_all_f, axis=1)
    if self.out_deltas_buffer_f:
        self.be.compound_dot(self.W_input_f.T, in_deltas_all_f, self.out_deltas_buffer_f_v, alpha=alpha, beta=beta)
    self.be.compound_dot(in_deltas_all_b, self.x_b_v.T, self.dW_input_b)
    self.db_b[:] = self.be.sum(in_deltas_all_b, axis=1)
    if self.out_deltas_buffer_b:
        self.be.compound_dot(self.W_input_b.T, in_deltas_all_b, self.out_deltas_buffer_b_v, alpha=alpha, beta=(beta if self.split_inputs else 1.0))
    return self.out_deltas_buffer
