(grad_op_tree, x, be)
Reverse broadcast from shape(grad_op_tree) to shape(x)

        Arguments:
            grad_op_tree (OpTreeNode or Tensor): The OpTreeNode to broadcast.
            x (OpTreeNode or Tensor): Provides the dimension to be broadcasted to.
            be: (Backend): The backend to be used.

        Returns:
            OpTreeNode or Tensor: The broadcasted result.
Reverse broadcast from shape(grad_op_tree) to shape(x)
Reverse broadcast from shape ( grad_op_tree ) to shape ( x )
def _unbroadcast(grad_op_tree, x, be):
        """
        Reverse broadcast from shape(grad_op_tree) to shape(x)

        Arguments:
            grad_op_tree (OpTreeNode or Tensor): The OpTreeNode to broadcast.
            x (OpTreeNode or Tensor): Provides the dimension to be broadcasted to.
            be: (Backend): The backend to be used.

        Returns:
            OpTreeNode or Tensor: The broadcasted result.
        """

        if (not grad_op_tree) or (not x):
            return grad_op_tree
        if type(x) in _scalar_types:
            return 0.
        in_shape = x.shape
        out_shape = grad_op_tree.shape

        if in_shape == out_shape:
            return grad_op_tree
        elif len(in_shape) == 2 and len(out_shape) == 2:
            # broadcasts
            if in_shape == (1, 1):
                # [1 * 1] -> [m * n]
                return be.sum(grad_op_tree)
            elif in_shape[0] == out_shape[0] and in_shape[1] == 1:
                # [m * 1] -> [m * n]
                return be.sum(grad_op_tree, axis=1)
            elif in_shape[0] == 1 and in_shape[1] == out_shape[1]:
                # [1 * n] -> [m * n]
                return be.sum(grad_op_tree, axis=0)
            # reductions
            elif ((out_shape[0] == in_shape[0] and out_shape[1] == 1) or
                  (out_shape[0] == 1 and out_shape[1] == in_shape[1])):
                # TODO cleaner way to broadcast
                return 0 * x + grad_op_tree
            else:
                return NotImplemented
        else:
            return NotImplemented
def _unbroadcast ( grad_op_tree , x , be ) : if ( not grad_op_tree ) or ( not x ) : return grad_op_tree if type ( x ) in _scalar_types : return 0. in_shape = x . shape out_shape = grad_op_tree . shape if in_shape == out_shape : return grad_op_tree elif len ( in_shape ) == 2 and len ( out_shape ) == 2 : # broadcasts if in_shape == ( 1 , 1 ) : # [1 * 1] -> [m * n] return be . sum ( grad_op_tree ) elif in_shape [ 0 ] == out_shape [ 0 ] and in_shape [ 1 ] == 1 : # [m * 1] -> [m * n] return be . sum ( grad_op_tree , axis = 1 ) elif in_shape [ 0 ] == 1 and in_shape [ 1 ] == out_shape [ 1 ] : # [1 * n] -> [m * n] return be . sum ( grad_op_tree , axis = 0 ) # reductions elif ( ( out_shape [ 0 ] == in_shape [ 0 ] and out_shape [ 1 ] == 1 ) or ( out_shape [ 0 ] == 1 and out_shape [ 1 ] == in_shape [ 1 ] ) ) : # TODO cleaner way to broadcast return 0 * x + grad_op_tree else : return NotImplemented else : return NotImplemented