def _fuse_group_gemm_reshape_cat(sorted_graph: List[Tensor]) -> List[Tensor]:
    """
    This pass fuses strided_op + view_op + concat patterns. It mainly performs the
    following transformations:
      (1) updates the strided_op's output_accessors with new stride information;
      (2) updates concat op's input_masks and inputs;
      (3) removes the view_op from the graph
    Crrently, group_gemm is the only supported strided_op, and reshape and
    unsqueeze are the supported view_op(s).
    """
    sorted_ops = graph_utils.get_sorted_ops(sorted_graph)

    visited_cat_ops = set()
    for op in sorted_ops:
        # TODO: add support to other strided ops such as elementwise ops
        if not _is_strided_group_gemm(op):
            continue
        strided_op = op
        for idx, output_tensor in enumerate(strided_op._attrs["outputs"]):
            # Find the next strided_op + reshape + cat.
            if len(output_tensor.dst_ops()) != 1:
                continue
            next_op = list(output_tensor.dst_ops())[0]

            # TODO: add flatten later
            if next_op._attrs["op"] not in ["reshape", "unsqueeze"]:
                continue

            view_op = next_op
            reshape_output_tensor = view_op._attrs["outputs"][0]
            # Let's keep it simple for now. We might be able to support
            # the case where reshape's output is used by multiple ops.
            if len(reshape_output_tensor.dst_ops()) > 1:
                continue
            # skip dynamic shape
            if not shape_utils.all_static_dimensions(
                reshape_output_tensor._attrs["shape"]
            ):
                continue

            if not reshape_output_tensor.dst_ops():
                continue

            next_op = list(reshape_output_tensor.dst_ops())[0]

            if next_op._attrs["op"] != "concatenate":
                continue
            cat_op = next_op

            if not _is_valid_for_fusion(strided_op, cat_op, idx):
                continue

            # Update "outputs" of the strided_op, and "input_masks" of the cat op.
            # Note that we do not update the "inputs" of the cat op here, to make sure
            # that we could find input indices correctly for other tensors.

            cat_dim = cat_op._attrs["concat_dim"]
            if not transform_strided_ops_utils.cat_split_dim_is_static(cat_op, cat_dim):
                continue

            reshape_to_shape = reshape_output_tensor._attrs["shape"]
            if cat_dim >= len(reshape_to_shape):
                continue

            if cat_op not in visited_cat_ops:
                # We can have output_list = some_op()(x, y, z); y = concatenate()(output_list).
                # Depending on whether we create a copy or not in concatenate(),
                # the cat inputs and some_op outputs may point to the same list.
                # This copy adds extra safety to make sure that it doesn't happen.
                cat_op._attrs["inputs"] = cat_op._attrs["inputs"].copy()
            visited_cat_ops.add(cat_op)

            cat_idx = cat_op.get_tensor_index(reshape_output_tensor)

            offset = 0
            orig_idx = cat_op.get_original_index(cat_idx)

            # We compute the offset with original inputs because the current inputs
            # may have been modified by other transformations. For example,
            # _fuse_strided_op_and_cat may remove some input tensors.
            for orig_i in range(orig_idx):
                input_tensor = cat_op._attrs["original_inputs"][orig_i]
                input_tensor_shape = input_tensor._attrs["shape"]
                assert cat_dim < len(input_tensor_shape), (
                    f"Expected cat_dim to be less than the length of input_tensor_shape, "
                    f"but got cat_dim: {cat_dim}, and input_tensor_shape: {input_tensor_shape}"
                )
                assert shape_utils.all_static_dimensions(input_tensor_shape, cat_dim), (
                    f"Expected input_tensor_shape[{cat_dim}:] are all static dimensions, "
                    f"but got: {input_tensor_shape}"
                )

                strided_dim_values = [
                    dim._attrs["values"][0] for dim in input_tensor_shape[cat_dim:]
                ]
                offset += functools.reduce(lambda t1, t2: t1 * t2, strided_dim_values)

            cat_op.remove_input_at(cat_idx)
            cat_output = cat_op._attrs["outputs"][0]
            cat_output_shape = cat_output._attrs["shape"]
            # cat_output_shape[cat_dim:] is guaranteed to be static by the
            # cat_split_dim_is_static above
            strided_dim_values = [
                dim._attrs["values"][0] for dim in cat_output_shape[cat_dim:]
            ]
            dim_value = functools.reduce(lambda t1, t2: t1 * t2, strided_dim_values)
            new_tensor_shape = cat_output_shape[:cat_dim] + [IntImm(dim_value)]
            new_tensor = Tensor(shape=new_tensor_shape, dtype=cat_output.dtype())
            strided_op._attrs["output_accessors"][idx].update_base_tensor(
                new_tensor, cat_dim, offset
            )

            cat_output._attrs["src_ops"].add(strided_op)

            transform_utils.remove_view_op_from_sorted_graph(view_op)

            output_tensor = strided_op._attrs["outputs"][idx]
            strided_op._attrs["outputs"][idx] = cat_output
            transform_utils.remove_tensor_from_sorted_graph(output_tensor)

    return transform_utils.sanitize_sorted_graph(sorted_graph)

    This pass fuses strided_op + view_op + concat patterns. It mainly performs the
    following transformations:
      (1) updates the strided_op's output_accessors with new stride information;
      (2) updates concat op's input_masks and inputs;
      (3) removes the view_op from the graph
    Crrently, group_gemm is the only supported strided_op, and reshape and
    unsqueeze are the supported view_op(s).
    
python/aitemplate/compiler/transform/transform_strided_ops.py