예제 #1
0
def is_identifiable(G, X, Y, print_hedge=False):
    """Return if the causal effect of variables X on variables Y in G
    is identifiable based on algorithm ID in Shpitser and Pearl 2008."""
    C = c_components
    X = _set(X)
    Y = _set(Y)
    V = observable_nodes(G)
    Gprime = G.subgraph(G.nodes - X)

    if X == set(): return True  # Line 1

    if V - An(G, Y) - Y != set():  # Line 2
        return is_identifiable(ancestral_graph(G, Y), X & An(G, Y), Y)

    W = (V - X) - An(do_X(G, X), Y) - Y
    if W != set(): return is_identifiable(G, X | W, Y)  # Line 3

    Sk = C(Gprime)
    if len(Sk) > 1:  # Line 4
        return all(is_identifiable(G, V - S, S) for S in Sk)
    else:
        S = Sk[0]
        if any(c == V for c in C(G)):  # Line 5
            if print_hedge: print(f'Hedge at ({V}, {V&S})')
            return False
        if S in C(G):  # Line 6
            return True
        else:  # Line 7
            Sprime = [c for c in C(G) if S < c][0]
            return is_identifiable(G.subgraph(Sprime | Y), X & Sprime, Y)
예제 #2
0
def backdoor_criterion_search(DAG, X, Y):
    """Return all sets of nodes satisfying the backdoor criterion from X to Y in DAG."""
    possible_nodes = DAG.nodes() - De(DAG, X) - _set(X) - _set(Y)
    return [
        set(z) for z in powerset(possible_nodes)
        if meets_backdoor_criterion(DAG, X, Y, z)
    ]
예제 #3
0
def d_separated(DAG, X, Y, Z):
    """Return if Z d-separates X and Y in the DAG."""
    X, Y, Z = _set(X), _set(Y), _set(Z)
    ancestral = ancestral_graph(
        DAG, X | Y | Z)  # Take ancestral graph of nodes in question..
    moral = moral_graph(ancestral)  # Moralize & disorient...
    moral_without_givens = moral.subgraph(moral.nodes - Z)  # Remove givens...
    # Any paths between X and Y?
    return not any(
        [nx.has_path(moral_without_givens, x, y) for x in X for y in Y])
예제 #4
0
def specific_adjustment_formula(DAG, X, Y, Z, S):
    """An expression for the z-specific causal effect of X on Y.
    Z U S must satisfy the front-door criterion."""

    assert meets_backdoor_criterion(
        DAG, X, Y,
        _set(Z) | _set(S)), 'Z does not meet backdoor criterion'

    x, y, z, s = map(val, (X, Y, Z, S))
    if len(s) > 0: sub_s = '{' + str(s) + '}'
    specific_formula = f'\sum_{sub_s}{P(y, given=(x, s, z))}{P(s, given=z)}'

    return P(y, given=z, do=x) + '=' + specific_formula
예제 #5
0
def meets_backdoor_criterion(DAG, X, Y, Z):
    """Return if Z satisfies the backdoor criterion from X to Y in DAG."""
    return all((
        # i) No node in Z is a descendant of X
        not De(DAG, X) & _set(Z),
        # ii) Z d-separates all backdoor paths between X and Y
        d_separated(backdoor_graph(DAG, X), X, Y, Z)))
예제 #6
0
파일: viz.py 프로젝트: BrennanBarker/oyster
def d_sep_graphs(DAG, X, Y, Z, pos=None):
    """Visualize d-separation."""
    X, Y, Z = _set(X), _set(Y), _set(Z)
    a = ancestral_graph(DAG, X | Y | Z)
    m = moral_graph(a)
    mwoz = m.subgraph(m.nodes - Z)

    if not pos: pos = ex.pos.get(DAG, None)  # Try to find a matching pos

    f, axs = plt.subplots(1, 3, constrained_layout=True)
    draw(a, title='Ancestral', pos=pos, ax=axs[0], _show_axis_lines=True)
    draw(m, title='Moral', pos=pos, ax=axs[1], _show_axis_lines=True)
    draw(mwoz,
         title='Without Givens',
         pos=pos,
         ax=axs[2],
         _show_axis_lines=True)
예제 #7
0
def frontdoor_criterion_search(DAG, X, Y):
    """Return all sets of nodes that satisfy the backdoor criterion from X to Y in DAG."""
    return [
        set(Z) for Z in powerset(DAG.nodes - _set(X) - _set(Y))
        if meets_frontdoor_criterion(DAG, X, Y, Z)
    ]
예제 #8
0
def d_separator_search(DAG, X, Y):
    """Return d_separators for X and Y in DAG."""
    return [
        set(z) for z in powerset(DAG.nodes - _set(X) - _set(Y))
        if d_separated(DAG, X, Y, z)
    ]
예제 #9
0
def joint(DAG, do=[]):
    do = _set(do)
    return P(val(DAG.nodes - do), do=val(do))
예제 #10
0
def joint_factorization(DAG, do=[], hidden=[]):
    """Return an expression for the joint probability distribution
    factorized according to the relationships encoded in DAG."""
    if do: DAG = do_X(DAG, do)
    factors = [P(val(V), given=pa(DAG, V)) for V in DAG.nodes - _set(do)]
    return joint(DAG, do=do) + '=' + product(factors)
예제 #11
0
def val(Variables):
    """Represent a variable as a value"""
    return ','.join(sorted(_set(Variables))).lower()
예제 #12
0
def are_nonadjacent(G, nodes):
    for node in nodes:
        return all(other not in G.to_undirected()[node]
                   for other in _set(nodes) - _set(node))
예제 #13
0
 def wrapper(DAG, nodes):
     return set.union(set(), *(set(f(DAG, node)) for node in _set(nodes)))
예제 #14
0
 def wrapper(DAG, nodes):
     return set.intersection(*(set(f(DAG, node)) for node in _set(nodes)))
예제 #15
0
def do_X(DAG, X):
    """Return the subgraph of DAG with arrows into nodes X removed."""
    return nx.subgraph_view(DAG, filter_edge=lambda a, b: b not in _set(X))
예제 #16
0
def backdoor_graph(DAG, X):
    """Return the subgraph of DAG with arrows from nodes X removed."""
    return nx.subgraph_view(DAG, filter_edge=lambda a, b: a not in _set(X))