def transform_permute_to_reshape(
    sorted_graph: List[Tensor], workdir: str = None
) -> List[Tensor]:
    """Convert permute to reshape wherever applicable.

    When permute op involves moving one or more dimensions with size
    1 around where the order of non-singular dimensions is preserved,
    it's basically a reshape op, i.e. the underlying memory layout
    does not change.

    If a permute op has a non-empty input tensor accessor, its original shape
    should be used to determine whether it can be converted to reshape.
    In this case the shape of the actual input tensor might not match the rank
    of the permutation (but the original shape does) - see the second
    example below.

    Examples:
        [256x5x1x32] -> [256x5x32x1] (with 0132) is a reshape
        [256x5x32] -> [256x5x1x32] (with 0132) is a reshape
        [256x1x5x1x32] -> [256x5x32x1x1] (with 02431) is a reshape
        [256x5x1x32] -> [256x32x5x1] (with 0312) is not a reshape

    Args:
        sorted_graph (List[Tensor]): input graph
        workdir (str, optional): current workdir for dumping debug info. Defaults to None.

    Returns:
        List[Tensor]: optimized graph
    """
    sorted_ops = graph_utils.get_sorted_ops(sorted_graph)

    has_modified = False
    for op in sorted_ops:
        if not _check_permute_to_reshape(op):
            continue

        has_modified = True

        permute_input = op._attrs["inputs"][0]
        permute_output = op._attrs["outputs"][0]
        output_shape = permute_output.shape()

        transform_utils.remove_dst_op_from_tensor(permute_input, op)

        reshape_op = reshape()
        reshape_output = reshape_op(permute_input, output_shape)

        transform_utils.replace_tensor(permute_output, reshape_output)

        sorted_graph.append(reshape_output)

    if has_modified:
        sorted_graph = toposort(sorted_graph)
        transform_utils.sanitize_sorted_graph(sorted_graph)
    return sorted_graph
Convert permute to reshape wherever applicable.

When permute op involves moving one or more dimensions with size
1 around where the order of non-singular dimensions is preserved,
it's basically a reshape op, i.e. the underlying memory layout
does not change.

If a permute op has a non-empty input tensor accessor, its original shape
should be used to determine whether it can be converted to reshape.
In this case the shape of the actual input tensor might not match the rank
of the permutation (but the original shape does) - see the second
example below.

Examples:
[256x5x1x32] -> [256x5x32x1] (with 0132) is a reshape
[256x5x32] -> [256x5x1x32] (with 0132) is a reshape
[256x1x5x1x32] -> [256x5x32x1x1] (with 02431) is a reshape
[256x5x1x32] -> [256x32x5x1] (with 0312) is not a reshape

Args:
sorted_graph (List[Tensor]): input graph
workdir (str, optional): current workdir for dumping debug info. Defaults to None.

Returns:
List[Tensor]: optimized graph
python/aitemplate/compiler/transform/transform_permute_to_reshape.py