def _orient_edges(self, skel_graph, sep_set):
    'Orient edges in a skeleton graph to estimate the causal DAG, or CPDAG.\n\n        Uses the separation sets to orient edges via conditional independence\n        testing.\n\n        Parameters\n        ----------\n        skel_graph : nx.Graph\n            A skeleton graph. If ``None``, then will initialize PC using a\n            complete graph. By default None.\n        sep_set : _type_\n            _description_\n        '
    dag = skel_graph.to_directed()
    node_ids = skel_graph.nodes()
    for (i, j) in combinations(node_ids, 2):
        adj_i = set(dag.successors(i))
        if (j in adj_i):
            continue
        adj_j = set(dag.successors(j))
        if (i in adj_j):
            continue
        if (sep_set[i][j] is None):
            continue
        common_k = (adj_i & adj_j)
        for k in common_k:
            if (k not in sep_set[i][j]):
                if dag.has_edge(k, i):
                    dag.remove_edge(k, i)
                if dag.has_edge(k, j):
                    dag.remove_edge(k, j)
    old_dag = dag.copy()
    while True:
        for (i, j) in combinations(node_ids, 2):
            if _has_both_edges(dag, i, j):
                for k in dag.predecessors(i):
                    if dag.has_edge(i, k):
                        continue
                    if _has_any_edge(dag, k, j):
                        continue
                    dag.remove_edge(j, i)
                    break
            if _has_both_edges(dag, i, j):
                succs_i = set()
                for k in dag.successors(i):
                    if (not dag.has_edge(k, i)):
                        succs_i.add(k)
                preds_j = set()
                for k in dag.predecessors(j):
                    if (not dag.has_edge(j, k)):
                        preds_j.add(k)
                if (len((succs_i & preds_j)) > 0):
                    dag.remove_edge(j, i)
            if _has_both_edges(dag, i, j):
                adj_i = set()
                for k in dag.successors(i):
                    if dag.has_edge(k, i):
                        adj_i.add(k)
                for (k, l) in combinations(adj_i, 2):
                    if _has_any_edge(dag, k, l):
                        continue
                    if (dag.has_edge(j, k) or (not dag.has_edge(k, j))):
                        continue
                    if (dag.has_edge(j, l) or (not dag.has_edge(l, j))):
                        continue
                    dag.remove_edge(j, i)
                    break
        if nx.is_isomorphic(dag, old_dag):
            break
        old_dag = dag.copy()
    return dag
Orient edges in a skeleton graph to estimate the causal DAG, or CPDAG.

Uses the separation sets to orient edges via conditional independence
testing.

Parameters
----------
skel_graph : nx.Graph
    A skeleton graph. If ``None``, then will initialize PC using a
    complete graph. By default None.
sep_set : _type_
    _description_
def _orient_edges(self, skel_graph, sep_set):
    'Orient edges in a skeleton graph to estimate the causal DAG, or CPDAG.\n\n        Uses the separation sets to orient edges via conditional independence\n        testing.\n\n        Parameters\n        ----------\n        skel_graph : nx.Graph\n            A skeleton graph. If ``None``, then will initialize PC using a\n            complete graph. By default None.\n        sep_set : _type_\n            _description_\n        '
    dag = skel_graph.to_directed()
    node_ids = skel_graph.nodes()
    for (i, j) in combinations(node_ids, 2):
        adj_i = set(dag.successors(i))
        if (j in adj_i):
            continue
        adj_j = set(dag.successors(j))
        if (i in adj_j):
            continue
        if (sep_set[i][j] is None):
            continue
        common_k = (adj_i & adj_j)
        for k in common_k:
            if (k not in sep_set[i][j]):
                if dag.has_edge(k, i):
                    dag.remove_edge(k, i)
                if dag.has_edge(k, j):
                    dag.remove_edge(k, j)
    old_dag = dag.copy()
    while True:
        for (i, j) in combinations(node_ids, 2):
            if _has_both_edges(dag, i, j):
                for k in dag.predecessors(i):
                    if dag.has_edge(i, k):
                        continue
                    if _has_any_edge(dag, k, j):
                        continue
                    dag.remove_edge(j, i)
                    break
            if _has_both_edges(dag, i, j):
                succs_i = set()
                for k in dag.successors(i):
                    if (not dag.has_edge(k, i)):
                        succs_i.add(k)
                preds_j = set()
                for k in dag.predecessors(j):
                    if (not dag.has_edge(j, k)):
                        preds_j.add(k)
                if (len((succs_i & preds_j)) > 0):
                    dag.remove_edge(j, i)
            if _has_both_edges(dag, i, j):
                adj_i = set()
                for k in dag.successors(i):
                    if dag.has_edge(k, i):
                        adj_i.add(k)
                for (k, l) in combinations(adj_i, 2):
                    if _has_any_edge(dag, k, l):
                        continue
                    if (dag.has_edge(j, k) or (not dag.has_edge(k, j))):
                        continue
                    if (dag.has_edge(j, l) or (not dag.has_edge(l, j))):
                        continue
                    dag.remove_edge(j, i)
                    break
        if nx.is_isomorphic(dag, old_dag):
            break
        old_dag = dag.copy()
    return dag