'Backpropagation of errors, output delta for previous layer, and calculate the update on
model params.
Arguments:
deltas (Tensor): error tensors for each time step of unrolling
alpha (float, optional): scale to apply to input for activation
gradient bprop.  Defaults to 1.0
beta (float, optional): scale to apply to output activation
gradient bprop.  Defaults to 0.0
Attributes:
dW_input (Tensor): input weight gradients
dW_recur (Tensor): recurrent weight gradients
db (Tensor): bias gradients
Returns:
Tensor: Backpropagated errors for each time step of model unrolling'

def bprop(self, deltas, alpha=1.0, beta=0.0):

    self.dW[:] = 0
    if (self.in_deltas is None):
        self.in_deltas = get_steps(deltas, self.out_shape)
        self.prev_in_deltas = (self.in_deltas[(-1):] + self.in_deltas[:(-1)])
    params = (self.r, self.z, self.hcan, self.rh_prev, self.h_prev_bprop, self.r_delta, self.z_delta, self.hcan_delta, self.rz_delta, self.rzhcan_delta, self.h_delta, self.in_deltas, self.prev_in_deltas)
    for (r, z, hcan, rh_prev, h_prev, r_delta, z_delta, hcan_delta, rz_delta, rzhcan_delta, h_delta, in_deltas, prev_in_deltas) in reversed(list(zip(*params))):
        hcan_delta[:] = ((self.activation.bprop(hcan) * in_deltas) * z)
        z_delta[:] = ((self.gate_activation.bprop(z) * in_deltas) * (hcan - h_prev))
        self.be.compound_dot(self.Whcan_recur.T, hcan_delta, r_delta)
        r_delta[:] = ((self.gate_activation.bprop(r) * r_delta) * h_prev)
        h_delta[:] = (in_deltas * (1 - z))
        self.be.compound_dot(self.Wrz_recur.T, rz_delta, h_delta, beta=1.0)
        self.be.compound_dot(self.Whcan_recur.T, hcan_delta, self.wrc_T_dc)
        h_delta[:] = (h_delta + (r * self.wrc_T_dc))
        if (h_prev != 0):
            self.be.compound_dot(rz_delta, h_prev.T, self.dWrz_recur, beta=1.0)
            self.be.compound_dot(hcan_delta, rh_prev.T, self.dWhcan_recur, beta=1.0)
        prev_in_deltas[:] = (prev_in_deltas + h_delta)
    self.be.compound_dot(self.rzhcan_delta_buffer, self.x.T, self.dW_input)
    self.db[:] = self.be.sum(self.rzhcan_delta_buffer, axis=1)
    if self.out_deltas_buffer:
        self.be.compound_dot(self.W_input.T, self.rzhcan_delta_buffer, self.out_deltas_buffer.reshape(self.nin, (-1)), alpha=alpha, beta=beta)
    self.final_hidden_error[:] = self.h_delta[0]
    return self.out_deltas_buffer
