def test_asia_graph_dsep(asia_graph):
    """Example-based test of d-separation for asia_graph."""
    assert nx.d_separated(
        asia_graph, {"asia", "smoking"}, {"dyspnea", "xray"}, {"bronchitis", "either"}
    )
    assert nx.d_separated(
        asia_graph, {"tuberculosis", "cancer"}, {"bronchitis"}, {"smoking", "xray"}
    )
Exemplo n.º 2
0
def test_cyclic_graphs_raise_error():
    """
    Test that cycle graphs should cause erroring.

    This is because PGMs assume a directed acyclic graph.
    """
    with pytest.raises(nx.NetworkXError):
        g = nx.cycle_graph(3, nx.DiGraph)
        nx.d_separated(g, {0}, {1}, {2})
Exemplo n.º 3
0
def test_undirected_graphs_are_not_supported():
    """
    Test that undirected graphs are not supported.

    d-separation does not apply in the case of undirected graphs.
    """
    with pytest.raises(nx.NetworkXNotImplemented):
        g = nx.path_graph(3, nx.Graph)
        nx.d_separated(g, {0}, {1}, {2})
Exemplo n.º 4
0
def test_markov_condition(graph):
    """Test that the Markov condition holds for each PGM graph."""
    for node in graph.nodes:
        parents = set(graph.predecessors(node))
        non_descendants = graph.nodes - nx.descendants(graph,
                                                       node) - {node} - parents
        assert nx.d_separated(graph, {node}, non_descendants, parents)
Exemplo n.º 5
0
    def d_separated(self,
                    x: Sequence[str],
                    y: Sequence[str],
                    s: Optional[Sequence[str]] = None):
        """
        Checks if all variables in X are d-separated from all variables in Y by the variables in S.

        Parameters
        ----------
        x: Sequence,
            First set of nodes in DAG.

        y: Sequence,
            Second set of nodes in DAG.

        s: Sequence (optional),
            Set of conditioning nodes in DAG.

        Returns
        -------
        bool,
            A boolean indicating whether x is d-separated from y by s.
        """
        return nx.d_separated(self.dag, set(x), set(y),
                              set(s) if s is not None else set())
Exemplo n.º 6
0
    def is_d_separated(self,
                       X: Iterable[str],
                       Y: Iterable[str],
                       Z: Optional[Iterable[str]] = None) -> bool:
        X, Y, Z = _as_set(X), _as_set(Y), _as_set(Z)

        if X & Z or Y & Z:
            return True

        return nx.d_separated(self._G, X, Y, Z)
Exemplo n.º 7
0
 def computeDependencies(self, order):
     deps = []
     nodes = list(self.g.nodes())
     nodes.sort()
     #print('nodes = ', nodes, ', order = ', order)
     cNodes = self.getCombinations(nodes, order)
     for i in range(len(nodes)):
         node1 = nodes[i]
         if not self.rvDict[node1].isObserved:
             continue
         for j in range(i, len(nodes)):
             node2 = nodes[j]
             if node1 == node2 or not self.rvDict[node2].isObserved:
                 continue
             isAdjacent = self.isAdjacent(node1, node2)
             isSeparated = not isAdjacent and networkx.d_separated(
                 self.g, {node1}, {node2}, {})
             dep = self.makeDependency(node1, node2, None, not isSeparated)
             deps.append(dep)
             for c in cNodes:
                 #print('cNodes = ', cNodes)
                 if node1 in c or node2 in c:
                     continue
                 # Verify that every member of c is observed.  If not, we skip this combo.
                 allObserved = True
                 for m in c:
                     if not self.rvDict[m].isObserved:
                         allObserved = False
                         break
                 if not allObserved:
                     continue
                 isSeparated = not isAdjacent and networkx.d_separated(
                     self.g, {node1}, {node2}, set(c))
                 dep = self.makeDependency(node1, node2, list(c),
                                           not isSeparated)
                 deps.append(dep)
     #print('deps = ', deps)
     return deps
Exemplo n.º 8
0
def test_fork_graph_dsep(fork_graph):
    """Example-based test of d-separation for fork_graph."""
    assert nx.d_separated(fork_graph, {1}, {2}, {0})
    assert not nx.d_separated(fork_graph, {1}, {2}, {})
Exemplo n.º 9
0
def test_path_graph_dsep(path_graph):
    """Example-based test of d-separation for path_graph."""
    assert nx.d_separated(path_graph, {0}, {2}, {1})
    assert not nx.d_separated(path_graph, {0}, {2}, {})
Exemplo n.º 10
0
def test_invalid_nodes_raise_error(asia_graph):
    """
    Test that graphs that have invalid nodes passed in raise errors.
    """
    with pytest.raises(nx.NodeNotFound):
        nx.d_separated(asia_graph, {0}, {1}, {2})
Exemplo n.º 11
0
def test_asia_graph_dsep(asia_graph):
    """Example-based test of d-separation for asia_graph."""
    assert nx.d_separated(asia_graph, {'asia', 'smoking'}, {'dyspnea', 'xray'},
                          {'bronchitis', 'either'})
    assert nx.d_separated(asia_graph, {'tuberculosis', 'cancer'},
                          {'bronchitis'}, {'smoking', 'xray'})
Exemplo n.º 12
0
def test_naive_bayes_dsep(naive_bayes_graph):
    """Example-based test of d-separation for naive_bayes_graph."""
    for u, v in combinations(range(1, 5), 2):
        assert nx.d_separated(naive_bayes_graph, {u}, {v}, {0})
        assert not nx.d_separated(naive_bayes_graph, {u}, {v}, {})
Exemplo n.º 13
0
def test_collider_graph_dsep(collider_graph):
    """Example-based test of d-separation for collider_graph."""
    assert nx.d_separated(collider_graph, {0}, {1}, {})
    assert not nx.d_separated(collider_graph, {0}, {1}, {2})
Exemplo n.º 14
0
    def findFrontdoorBlockingSet(self, source, target):
        cacheKey = (source, target)
        if cacheKey in self.fdCache.keys():
            return self.fdCache[cacheKey]
        backdoorSet = self.findBackdoorBlockingSet(source, target)
        maxBlocking = 2
        bSet = []

        # Create a graph view that removes the direct link from the source to the destination
        def includeEdge(s, d):
            #print('source, dest = ', s, d)
            if s == source and d == target:
                return False
            return True

        pathNodes = {}
        vg = networkx.subgraph_view(self.g, filter_edge=includeEdge)
        # Use that view to find all indirect paths from source to dest
        paths0 = networkx.all_simple_paths(vg, source, target)
        paths = [path for path in paths0]
        #print('paths = ', paths)
        if len(paths) == 0:
            # No indirect paths
            return []
        for path in paths:
            #print('path = ', path)
            # Remove the first and last node of the path -- always the source and target
            intermediates = path[1:-1]
            #print('int = ', intermediates)
            for i in intermediates:
                if i not in pathNodes:
                    pathNodes[i] = 1
                else:
                    pathNodes[i] += 1
        pathTups = []
        # First look for single node solutions
        for node in pathNodes.keys():
            cnt = pathNodes[node]
            outTup = (cnt, node)
            pathTups.append(outTup)

        # Sort the nodes in descending order of the number of paths containing it
        pathTups.sort()
        pathTups.reverse()
        combos = [(tup[1], ) for tup in pathTups]
        #print('pathNodes = ', pathNodes.keys())
        # Now add any multiple field combinations.  Order is not significant here.
        multiCombos = self.getCombinations(pathNodes.keys(),
                                           maxBlocking,
                                           minOrder=2)
        combos += multiCombos
        #print('combos = ', combos)
        for nodeSet in combos:
            testSet = set(list(nodeSet) + list(backdoorSet))
            #print('testSet = ', list(testSet))
            if networkx.d_separated(vg, {source}, {target}, testSet):
                bSet = list(nodeSet)
                break

        print('FDblocking = ', bSet)
        self.fdCache[cacheKey] = bSet
        return bSet
Exemplo n.º 15
0
    def findBackdoorBlockingSet(self, source, target):
        """ Find the minimal set of nodes that block all backdoor paths from source
            to target.
        """
        cacheKey = (source, target)
        if cacheKey in self.bdCache.keys():
            return self.bdCache[cacheKey]
        maxBlocking = 3
        bSet = []
        # find all paths from parents of source to target.
        parents = self.getParents(source)

        #print('parents = ', parents)
        # Create a graph view that removes the links from the source to its parents
        def includeEdge(s, d):
            #print('source, dest = ', s, d)
            if d == source:
                return False
            return True

        pathNodes = {}
        vg = networkx.subgraph_view(self.g, filter_edge=includeEdge)
        for parent in parents:
            paths = networkx.all_simple_paths(vg, parent, target)
            #print('paths = ', [path for path in paths])
            for path in paths:
                #print('path = ', path)
                # Remove the last node of the path -- always the target
                intermediates = path[:-1]
                #print('int = ', intermediates)
                for i in intermediates:
                    if i not in pathNodes:
                        pathNodes[i] = 1
                    else:
                        pathNodes[i] += 1
        pathTups = []
        # First look for single node solutions
        for node in pathNodes.keys():
            cnt = pathNodes[node]
            outTup = (cnt, node)
            pathTups.append(outTup)

        # Sort the nodes in descending order of the number of paths containing it
        pathTups.sort()
        pathTups.reverse()
        combos = [(tup[1], ) for tup in pathTups]
        #print('pathNodes = ', pathNodes.keys())
        # Now add any multiple field combinations.  Order is not significant here.
        multiCombos = self.getCombinations(pathNodes.keys(),
                                           maxBlocking,
                                           minOrder=2)
        combos += multiCombos
        #print('combos = ', combos)
        for nodeSet in combos:
            testSet = set(nodeSet)
            #print('testSet = ', list(testSet))
            if networkx.d_separated(self.g, set(parents), {target}, testSet):
                bSet = list(testSet)
                break

        print('BDblocking = ', bSet)
        self.bdCache[cacheKey] = bSet
        return bSet
Exemplo n.º 16
0
def IDC(y, x, z, P, G, orderlist, hiddvar, **kwargs):
    '''
    Function implement the ID algorihtm as presented in Pearl 2006b 
    "Identification of Conditional Interventional Distributions" 
    (https://ftp.cs.ucla.edu/pub/stat_ser/r329-uai.pdf)
    
    
    Parameters
    ----------
    y         : array of viarables on which measures the effect of intervention
    x         : array of variables on which the intervention (do) is done
    z         :  array containing condition set
    P         : probability structure defined as the output of function 
                probabilitystr
    G         : netwrokx object
    orderlist : topological order
    hiddvar   : set of hidden (confonuders) nodes  
    **kwargs  : dictionary with following jey
                debug : if True message are print

    Raises
    ------
    ValueError: if the causal effect is not identifiable then the function 
                raise an error

    Returns
    -------
    probability structure that define the identification of causal effect

    '''

    debug = kwargs["debug"]

    #Transform array y, z and z in array
    y_set = set(list(y))

    if all(x) != "":
        x_set = set(list(x))

        #Transform z in set
    z_set = set(list(z))
    #Get graph without incoming edge on x and outgoing edge to z
    G_under_x_bar_z = incomingedgedel(x, outcomeedgedel(z, G))
    for zi in z:
        c_set = x_set.copy()
        c_set | (z_set - {zi})

        if nx.d_separated(G_under_x_bar_z, y_set, {zi}, c_set):
            if debug:
                print("> Iterate over IDC: Z variable is " + str(zi))

            P_numerator = IDC(y, np.union1d(x, np.array([zi])),
                              np.setdiff1d(z, np.array([zi])), P, G, orderlist,
                              hiddvar, **kwargs)

            return P_numerator

    if debug:
        print("> Iterate over ID")
    P_numerator = ID(np.union1d(y, z), x, P, G, orderlist, hiddvar, **kwargs)

    return P_numerator