//  [python]
def _update_bwd(self, beta_init, bm_mat, llr, alpha_ta):
        """
        Run backward update from time t=k-1 to t=0.
        At each time t, computes beta_t-1 using beta_t and gamma_t.

        Returns llr for information bits for t=0,1,...,k-1
        """

        beta_next = beta_init
        llr_op_ta = tf.TensorArray(tf.float32,
                                  size=self._num_syms,
                                  dynamic_size=False,
                                  clear_after_read=False)
        beta_next = tf.cast(beta_next, tf.float32)

        # (bs, _Ns, _ni) matrix, by from state
        op_mask = tf.tile(self.trellis.op_by_fromnode[None,:,:],
                          [tf.shape(beta_init)[0],1,1])
        tonode_mask = tf.tile(self.trellis.to_nodes[None,:,:],
                              [tf.shape(beta_init)[0], 1, 1])

        ipbit_mat = tf.tile(tf.range(self._ni)[None, None, :],
                            [tf.shape(beta_init)[0], self._ns, 1])
        ipbitsign_mat = 1.0 - 2.0 * tf.cast(ipbit_mat, tf.float32)

        for t in tf.range(self._num_syms-1, -1, -1):
            bm_t = bm_mat[..., t]
            llr_t = 0.5 * llr[...,t][:, None,None]
            signed_half_llr = tf.math.multiply(
                tf.tile(llr_t,[1, self._ns, self._ni]), ipbitsign_mat)
            bm_byfromst = tf.gather(bm_t, op_mask, batch_dims=1)

            if self._algorithm in ['log', 'maxlog']:
                llr_byfromst = signed_half_llr
                gamma_byfromst = tf.math.add(llr_byfromst, bm_byfromst)
            else:
                llr_byfromst = tf.math.exp(signed_half_llr)
                gamma_byfromst = tf.multiply(llr_byfromst, bm_byfromst)

            beta_bytonode = tf.gather(beta_next, tonode_mask, batch_dims=1)

            if self._algorithm not in ['log', 'maxlog']:
                beta_gam_prod = tf.math.multiply(gamma_byfromst, beta_bytonode)
                beta_t = tf.math.reduce_sum(beta_gam_prod, axis=-1)
                beta_t_sum = tf.reduce_sum(beta_t, axis=-1)
                beta_t = tf.divide(beta_t, tf.tile(beta_t_sum[:,None],[1,self._ns]))
            elif self._algorithm == 'log':
                beta_gam_prod = gamma_byfromst + beta_bytonode
                beta_t = tf.math.reduce_logsumexp(beta_gam_prod, axis=-1, keepdims=False)
            else: #self._algorithm = 'maxlog'
                beta_gam_prod = gamma_byfromst + beta_bytonode
                beta_t = tf.math.reduce_max(beta_gam_prod, axis=-1)

            alph_t = alpha_ta.read(t)
            if self._algorithm not in ['log', 'maxlog']:
                llr_op_t0 = tf.math.multiply(
                                tf.math.multiply(alph_t, gamma_byfromst[...,0]),
                                            beta_bytonode[...,0])
                llr_op_t1 = tf.math.multiply(
                                tf.math.multiply(alph_t,gamma_byfromst[...,1]),
                                            beta_bytonode[...,1])
                llr_op_t = tf.math.log(tf.divide(tf.reduce_sum(llr_op_t0, axis=-1),
                                                tf.reduce_sum(llr_op_t1,axis=-1)))
            else:
                llr_op_t0 = alph_t + gamma_byfromst[...,0] + beta_bytonode[...,0]
                llr_op_t1 = alph_t + gamma_byfromst[...,1] + beta_bytonode[...,1]
                if self._algorithm == 'log':
                    llr_op_t = tf.math.subtract(
                        tf.math.reduce_logsumexp(llr_op_t0, axis=-1),
                        tf.math.reduce_logsumexp(llr_op_t1, axis=-1))
                else:
                    llr_op_t = tf.math.subtract(
                        tf.math.reduce_max(llr_op_t0, axis=-1),
                        tf.math.reduce_max(llr_op_t1, axis=-1))

            llr_op_ta = llr_op_ta.write(t, llr_op_t)
            beta_next = beta_t

        llr_op = tf.transpose(llr_op_ta.stack())
        return llr_op