def _fuse_group_ops_by_type(
    sorted_graph: List[Tensor], op_type: str, workdir: str = None
) -> List[Tensor]:
    """
    This pass groups gemm ops or layernorm ops together.

    For gemm ops, the supported op types are:
    - gemm_rcr
    - gemm_rcr_bias
    - gemm_rcr_bias_relu
    - gemm_rcr_bias_sigmoid.

    There are several conditions that the ops must meet to be grouped together:
    1) alignment requirement: all ops in a group must have the same ab_alignment.
       Minimal ab_alignment required is 2.
       TODO: experiment with grouping ops with different alignment together and use
       minimal alignment. Need to benchmark perf.
    2) ops must have the same type and alpha = 1.0. Only 2D gemms are supported.
    3) all gemm ops after a single split op will be grouped together into one group
       regardless of condition 4
    4) TODO: exclude gemm ops with large m/n/k with heuristics
    6) TODO: support dynamic batch dim. All dims must be static currently.

    For layernorm ops, the supported op types are:
    - layernorm
    - layernorm_sigmoid_mul

    The group conditions are
    1) all ops must have the same eps
    2) neither gamma or beta can be None
    3) the normalized_shape must have same rank
    4) all inputs must have the same rank and batch dimensions
    5) all inputs must have the same alignment according to _get_layernorm_alignment

    The overall algorithm is pretty simple. It takes two steps:
    1) find all groups of op to fuse together
    2) fuse them together
    Details of step 1 can be found in _group_ops_by_type
    """
    # if we didn't fuse any grouped ops, we simply return original sorted_graph
    if not _group_ops_by_type(sorted_graph, op_type, workdir):
        return sorted_graph

    sorted_graph = toposort(sorted_graph)
    sorted_graph = transform_utils.sanitize_sorted_graph(sorted_graph)
    return sorted_graph
This pass groups gemm ops or layernorm ops together.

For gemm ops, the supported op types are:
- gemm_rcr
- gemm_rcr_bias
- gemm_rcr_bias_relu
- gemm_rcr_bias_sigmoid.

There are several conditions that the ops must meet to be grouped together:
1) alignment requirement: all ops in a group must have the same ab_alignment.
Minimal ab_alignment required is 2.
TODO: experiment with grouping ops with different alignment together and use
minimal alignment. Need to benchmark perf.
2) ops must have the same type and alpha = 1.0. Only 2D gemms are supported.
3) all gemm ops after a single split op will be grouped together into one group
regardless of condition 4
4) TODO: exclude gemm ops with large m/n/k with heuristics
6) TODO: support dynamic batch dim. All dims must be static currently.

For layernorm ops, the supported op types are:
- layernorm
- layernorm_sigmoid_mul

The group conditions are
1) all ops must have the same eps
2) neither gamma or beta can be None
3) the normalized_shape must have same rank
4) all inputs must have the same rank and batch dimensions
5) all inputs must have the same alignment according to _get_layernorm_alignment

The overall algorithm is pretty simple. It takes two steps:
1) find all groups of op to fuse together
2) fuse them together
Details of step 1 can be found in _group_ops_by_type
python/aitemplate/compiler/transform/fuse_group_ops.py