(jsn)
return cluster2root, cluster2leaf
For each cluster (Pipeline, OperatorChoice, or higher-order IndividualOp), get two representatives (first-order IndividualOps).

    Lale visualizes composite operators using graphviz clusters. To
    visualize an edge to (from) a cluster, we need to tell graphviz a
    representative in that cluster for the edge to connect to (from).

    Parameters
    ----------
    jsn:
        JSON representation of a Lale pipeline.
        The steps of all sub-pipelines must be topologically ordered.

    Returns
    -------
    reps:
        Two dictionaries from cluster id fields to node id fields.
        Nodes in the first dictionary are roots that are furthest left
        in the visualization, suitable for incoming edges.
        Nodes in the second dictionary are roots that are furthest
        right in the visualization, suitable for outgoing edges.
For each cluster (Pipeline, OperatorChoice, or higher-order IndividualOp), get two representatives (first-order IndividualOps).
For each cluster ( Pipeline OperatorChoice or higher - order IndividualOp ) get two representatives ( first - order IndividualOps ) .
def _get_cluster2reps(jsn) -> Tuple[Dict[str, str], Dict[str, str]]:
    """For each cluster (Pipeline, OperatorChoice, or higher-order IndividualOp), get two representatives (first-order IndividualOps).

    Lale visualizes composite operators using graphviz clusters. To
    visualize an edge to (from) a cluster, we need to tell graphviz a
    representative in that cluster for the edge to connect to (from).

    Parameters
    ----------
    jsn:
        JSON representation of a Lale pipeline.
        The steps of all sub-pipelines must be topologically ordered.

    Returns
    -------
    reps:
        Two dictionaries from cluster id fields to node id fields.
        Nodes in the first dictionary are roots that are furthest left
        in the visualization, suitable for incoming edges.
        Nodes in the second dictionary are roots that are furthest
        right in the visualization, suitable for outgoing edges.
    """
    cluster2root: Dict[str, str] = {}
    cluster2leaf: Dict[str, str] = {}
    node2depth: Dict[str, int] = {}

    def populate(uid: str, jsn, depth: int, clusters: List[str]) -> int:
        kind: str = lale.json_operator.json_op_kind(jsn)
        if kind == "Pipeline" or jsn["class"] == _LALE_SKL_PIPELINE:
            step2idx: Dict[str, int] = {}
            for step_idx, step_uid in enumerate(jsn["steps"].keys()):
                step2idx[step_uid] = step_idx
            if kind == "Pipeline":
                edges = jsn["edges"]
            else:
                names = list(jsn["steps"].keys())
                edges = [[names[i], names[i + 1]] for i in range(len(names) - 1)]
            for tail, head in edges:
                assert (
                    step2idx[tail] < step2idx[head]
                ), f"steps {tail} and {head} are not in topological order"
            node2preds: Dict[str, List[str]] = {
                step: [] for step in jsn["steps"].keys()
            }
            for tail, head in edges:
                node2preds[head].append(tail)
            more_clusters = [uid, *clusters]
            d_max = depth
            for step_uid, step_jsn in jsn["steps"].items():
                d_root = max(
                    [node2depth[p] for p in node2preds[step_uid]], default=depth
                )
                d_leaf = populate(step_uid, step_jsn, d_root, more_clusters)
                d_max = max(d_max, d_leaf)
        elif kind == "OperatorChoice" or "steps" in jsn:
            more_clusters = [uid, *clusters]
            d_max = depth
            for step_uid, step_jsn in jsn["steps"].items():
                d_leaf = populate(step_uid, step_jsn, depth, more_clusters)
                d_max = max(d_max, d_leaf)
        else:
            assert kind == "IndividualOp"
            d_max = depth + 1
            for cluster in clusters:
                if (
                    cluster not in cluster2root
                    or node2depth[cluster2root[cluster]] > d_max
                ):
                    cluster2root[cluster] = uid
                if (
                    cluster not in cluster2leaf
                    or node2depth[cluster2leaf[cluster]] < d_max
                ):
                    cluster2leaf[cluster] = uid
        node2depth[uid] = d_max
        return d_max

    populate("(root)", jsn, 0, [])
    return cluster2root, cluster2leaf
def _get_cluster2reps ( jsn ) -> Tuple [ Dict [ str , str ] , Dict [ str , str ] ] : cluster2root : Dict [ str , str ] = { } cluster2leaf : Dict [ str , str ] = { } node2depth : Dict [ str , int ] = { } def populate ( uid : str , jsn , depth : int , clusters : List [ str ] ) -> int : kind : str = lale . json_operator . json_op_kind ( jsn ) if kind == "Pipeline" or jsn [ "class" ] == _LALE_SKL_PIPELINE : step2idx : Dict [ str , int ] = { } for step_idx , step_uid in enumerate ( jsn [ "steps" ] . keys ( ) ) : step2idx [ step_uid ] = step_idx if kind == "Pipeline" : edges = jsn [ "edges" ] else : names = list ( jsn [ "steps" ] . keys ( ) ) edges = [ [ names [ i ] , names [ i + 1 ] ] for i in range ( len ( names ) - 1 ) ] for tail , head in edges : assert ( step2idx [ tail ] < step2idx [ head ] ) , f"steps {tail} and {head} are not in topological order" node2preds : Dict [ str , List [ str ] ] = { step : [ ] for step in jsn [ "steps" ] . keys ( ) } for tail , head in edges : node2preds [ head ] . append ( tail ) more_clusters = [ uid , * clusters ] d_max = depth for step_uid , step_jsn in jsn [ "steps" ] . items ( ) : d_root = max ( [ node2depth [ p ] for p in node2preds [ step_uid ] ] , default = depth ) d_leaf = populate ( step_uid , step_jsn , d_root , more_clusters ) d_max = max ( d_max , d_leaf ) elif kind == "OperatorChoice" or "steps" in jsn : more_clusters = [ uid , * clusters ] d_max = depth for step_uid , step_jsn in jsn [ "steps" ] . items ( ) : d_leaf = populate ( step_uid , step_jsn , depth , more_clusters ) d_max = max ( d_max , d_leaf ) else : assert kind == "IndividualOp" d_max = depth + 1 for cluster in clusters : if ( cluster not in cluster2root or node2depth [ cluster2root [ cluster ] ] > d_max ) : cluster2root [ cluster ] = uid if ( cluster not in cluster2leaf or node2depth [ cluster2leaf [ cluster ] ] < d_max ) : cluster2leaf [ cluster ] = uid node2depth [ uid ] = d_max return d_max populate ( "(root)" , jsn , 0 , [ ] ) return cluster2root , cluster2leaf