def _get_dependency_graph(
    sorted_graph: List[Tensor], op_type: str
) -> OrderedDict[Operator, Set[Operator]]:
    """Get dependency graph: `G: {op: all the descendants of op with op_type}`.
       G only contains ops with type == op_type.
       The dependency doesn't necessarily need to be topologically sorted.
       It's helpful for debugging though. So let's keep it ordered.

    Args:
        sorted_graph (List[Tensor]): Topologically sorted graph
        op_type (str): The op type to be grouped

    Returns:
        OrderedDict[Operator, Set[Operator]]: The dependency graph.

    Algorithm:
    1) Do dfs to traverse the graph and collects descendants ops with type == op_type
       for every tensor, which is saved in `visited`.
    2) Go through `visited` and ony save the entries with parent op == op_type in
       the final dependency graph
    """
    visited = collections.OrderedDict()
    for tensor in sorted_graph:
        _dfs(tensor, op_type, visited)

    filtered = _filter_by_op_type(visited, op_type)

    return filtered
Get dependency graph: `G: {op: all the descendants of op with op_type}`.
       G only contains ops with type == op_type.
       The dependency doesn't necessarily need to be topologically sorted.
       It's helpful for debugging though. So let's keep it ordered.

    Args:
        sorted_graph (List[Tensor]): Topologically sorted graph
        op_type (str): The op type to be grouped

    Returns:
        OrderedDict[Operator, Set[Operator]]: The dependency graph.

    Algorithm:
    1) Do dfs to traverse the graph and collects descendants ops with type == op_type
       for every tensor, which is saved in `visited`.
    2) Go through `visited` and ony save the entries with parent op == op_type in
       the final dependency graph
    
python/aitemplate/compiler/transform/fuse_group_ops.py