def _get_binary_op_bcast_shape(lhs_shape, rhs_shape):
    """Get the shape after binary broadcasting.

    We will strictly follow the broadcasting rule in numpy.

    Parameters
    ----------
    lhs_shape : tuple
    rhs_shape : tuple

    Returns
    -------
    ret_shape : tuple
    """
    ret_shape = []
    if len(lhs_shape) > len(rhs_shape):
        lhs_shape, rhs_shape = rhs_shape, lhs_shape
    for ptr in range(len(rhs_shape)):
        if ptr < len(lhs_shape):
            l_val, r_val = lhs_shape[len(lhs_shape) - 1 - ptr], \
                           rhs_shape[len(rhs_shape) - 1 - ptr]
            assert(l_val == 1 or r_val == 1 or l_val == r_val),\
                "Shape is NOT broadcastable, lhs=%s, rhs=%s"\
                %(str(lhs_shape), str(rhs_shape))
            ret_shape.append(max(l_val, r_val))
        else:
            ret_shape.append(rhs_shape[len(rhs_shape) - 1 - ptr])
    ret_shape = ret_shape[::-1]
    return ret_shape