def test_overlapping_K5():
    G = nx.Graph()
    G.add_edges_from(combinations(range(5), 2))  # Add a five clique
    G.add_edges_from(combinations(range(2, 7), 2))  # Add another five clique
    c = list(k_clique_communities(G, 4))
    assert_equal(c, [frozenset(range(7))])
    c = set(k_clique_communities(G, 5))
    assert_equal(c, set([frozenset(range(5)), frozenset(range(2, 7))]))
    def _dispersion(G_u, u, v):
        """dispersion for all nodes 'v' in a ego network G_u of node 'u'"""
        u_nbrs = set(G_u[u])
        ST = set(n for n in G_u[v] if n in u_nbrs)
        set_uv = set([u, v])
        # all possible ties of connections that u and b share
        possib = combinations(ST, 2)
        total = 0
        for (s, t) in possib:
            # neighbors of s that are in G_u, not including u and v
            nbrs_s = u_nbrs.intersection(G_u[s]) - set_uv
            # s and t are not directly connected
            if t not in nbrs_s:
                # s and t do not share a connection
                if nbrs_s.isdisjoint(G_u[t]):
                    # tick for disp(u, v)
                    total += 1
        # neighbors that u and v share
        embededness = len(ST)

        if normalized:
            if embededness + c != 0:
                norm_disp = ((total + b)**alpha) / (embededness + c)
            else:
                norm_disp = (total + b)**alpha
            dispersion = norm_disp

        else:
            dispersion = total

        return dispersion
Esempio n. 3
0
def random_tournament(n, seed=None):
    r"""Returns a random tournament graph on `n` nodes.

    Parameters
    ----------
    n : int
        The number of nodes in the returned graph.
    seed : integer, random_state, or None (default)
        Indicator of random number generation state.
        See :ref:`Randomness<randomness>`.

    Returns
    -------
    bool
        Whether the given graph is a tournament graph.

    Notes
    -----
    This algorithm adds, for each pair of distinct nodes, an edge with
    uniformly random orientation. In other words, `\binom{n}{2}` flips
    of an unbiased coin decide the orientations of the edges in the
    graph.

    """
    # Flip an unbiased coin for each pair of distinct nodes.
    coins = (seed.random() for i in range((n * (n - 1)) // 2))
    pairs = combinations(range(n), 2)
    edges = ((u, v) if r < 0.5 else (v, u) for (u, v), r in zip(pairs, coins))
    return nx.DiGraph(edges)
Esempio n. 4
0
def is_matching(G, matching):
    """Decides whether the given set or dictionary represents a valid
    matching in ``G``.

    A *matching* in a graph is a set of edges in which no two distinct
    edges share a common endpoint.

    Parameters
    ----------
    G : NetworkX graph

    matching : dict or set
        A dictionary or set representing a matching. If a dictionary, it
        must have ``matching[u] == v`` and ``matching[v] == u`` for each
        edge ``(u, v)`` in the matching. If a set, it must have elements
        of the form ``(u, v)``, where ``(u, v)`` is an edge in the
        matching.

    Returns
    -------
    bool
        Whether the given set or dictionary represents a valid matching
        in the graph.

    """
    if isinstance(matching, dict):
        matching = matching_dict_to_set(matching)
    # TODO This is parallelizable.
    return all(len(set(e1) & set(e2)) == 0
               for e1, e2 in combinations(matching, 2))
Esempio n. 5
0
def is_tournament(G):
    """Returns True if and only if `G` is a tournament.

    A tournament is a directed graph, with neither self-loops nor
    multi-edges, in which there is exactly one directed edge joining
    each pair of distinct nodes.

    Parameters
    ----------
    G : NetworkX graph
        A directed graph representing a tournament.

    Returns
    -------
    bool
        Whether the given graph is a tournament graph.

    Notes
    -----
    Some definitions require a self-loop on each node, but that is not
    the convention used here.

    """
    # In a tournament, there is exactly one directed edge joining each pair.
    return (all((v in G[u]) ^ (u in G[v]) for u, v in combinations(G, 2))
            and nx.number_of_selfloops(G) == 0)
    def test_tree_all_pairs_lowest_common_ancestor3(self):
        """Specifying no pairs same as specifying all."""
        all_pairs = chain(combinations(self.DG, 2),
                          ((node, node) for node in self.DG))

        ans = dict(tree_all_pairs_lca(self.DG, 0, all_pairs))
        self.assert_has_same_pairs(ans, self.ans)
 def test_several_communities(self):
     # This graph is the disjoint union of five triangles.
     ground_truth = set(
         [frozenset(range(3 * i, 3 * (i + 1))) for i in range(5)])
     edges = chain.from_iterable(combinations(c, 2) for c in ground_truth)
     G = nx.Graph(edges)
     self._check_communities(G, ground_truth)
Esempio n. 8
0
 def test_default_flow_function_karate_club_graph(self):
     G = nx.karate_club_graph()
     nx.set_edge_attributes(G, 1, 'capacity')
     T = nx.gomory_hu_tree(G)
     assert_true(nx.is_tree(T))
     for u, v in combinations(G, 2):
         cut_value, edge = self.minimum_edge_weight(T, u, v)
         assert_equal(nx.minimum_cut_value(G, u, v), cut_value)
Esempio n. 9
0
def _find_partition(G, starting_cell):
    """ Find a partition of the vertices of G into cells of complete graphs

    Parameters
    ----------
    G : NetworkX Graph
    starting_cell : tuple of vertices in G which form a cell

    Returns
    -------
    List of tuples of vertices of G

    Raises
    ------
    NetworkXError
        If a cell is not a complete subgraph then G is not a line graph
    """
    G_partition = G.copy()
    P = [starting_cell]  # partition set
    G_partition.remove_edges_from(list(combinations(starting_cell, 2)))
    # keep list of partitioned nodes which might have an edge in G_partition
    partitioned_vertices = list(starting_cell)
    while G_partition.number_of_edges() > 0:
        # there are still edges left and so more cells to be made
        u = partitioned_vertices[-1]
        deg_u = len(G_partition[u])
        if deg_u == 0:
            # if u has no edges left in G_partition then we have found
            # all of its cells so we do not need to keep looking
            partitioned_vertices.pop()
        else:
            # if u still has edges then we need to find its other cell
            # this other cell must be a complete subgraph or else G is
            # not a line graph
            new_cell = [u] + list(G_partition.neighbors(u))
            for u in new_cell:
                for v in new_cell:
                    if (u != v) and (v not in G.neighbors(u)):
                        msg = "G is not a line graph" \
                              "(partition cell not a complete subgraph)"
                        raise nx.NetworkXError(msg)
            P.append(tuple(new_cell))
            G_partition.remove_edges_from(list(combinations(new_cell, 2)))
            partitioned_vertices += new_cell
    return P
Esempio n. 10
0
 def test_florentine_families_graph(self):
     G = nx.florentine_families_graph()
     nx.set_edge_attributes(G, 1, 'capacity')
     for flow_func in flow_funcs:
         T = nx.gomory_hu_tree(G, flow_func=flow_func)
         assert_true(nx.is_tree(T))
         for u, v in combinations(G, 2):
             cut_value, edge = self.minimum_edge_weight(T, u, v)
             assert_equal(nx.minimum_cut_value(G, u, v), cut_value)
Esempio n. 11
0
    def test_theta(self):
        """Tests that pairs of vertices adjacent if and only if their sum
        weights exceeds the threshold parameter theta.
        """
        G = nx.thresholded_random_geometric_graph(50, 0.25, 0.1)

        for u, v in combinations(G, 2):
            # Adjacent vertices must be within the given distance.
            if v in G[u]:
                assert_true(
                    (G.nodes[u]['weight'] + G.nodes[v]['weight']) >= 0.1)
def _slow_edges(G, radius, p):
    """Returns edge list of node pairs within `radius` of each other
       using Minkowski distance metric `p`

    Works without scipy, but in `O(n^2)` time.
    """
    # TODO This can be parallelized.
    edges = []
    for (u, pu), (v, pv) in combinations(G.nodes(data='pos'), 2):
        if sum(abs(a - b) ** p for a, b in zip(pu, pv)) <= radius ** p:
            edges.append((u, v))
    return edges
Esempio n. 13
0
    def test_metric(self):
        """Tests for providing an alternate distance metric to the
        generator.

        """
        # Use the L1 metric.
        dist = l1dist
        G = nx.geographical_threshold_graph(50, 10, metric=dist)
        for u, v in combinations(G, 2):
            # Adjacent vertices must exceed the threshold.
            if v in G[u]:
                assert_true(join(G, u, v, 10, -2, dist))
            # Nonadjacent vertices must not exceed the threshold.
            else:
                assert_false(join(G, u, v, 10, -2, dist))
Esempio n. 14
0
    def test_p(self):
        """Tests for providing an alternate distance metric to the
        generator.

        """

        # Use the L1 metric.
        def dist(x, y):
            return sum(abs(a - b) for a, b in zip(x, y))

        G = nx.thresholded_random_geometric_graph(50, 0.25, 0.1, p=1)
        for u, v in combinations(G, 2):
            # Adjacent vertices must be within the given distance.
            if v in G[u]:
                assert_true(dist(G.nodes[u]['pos'], G.nodes[v]['pos']) <= 0.25)
Esempio n. 15
0
    def test_distances(self):
        """Tests that pairs of vertices adjacent if and only if they are
        within the prescribed radius.

        """

        # Use the Euclidean metric, the default according to the
        # documentation.
        def dist(x, y):
            return sqrt(sum((a - b)**2 for a, b in zip(x, y)))

        G = nx.soft_random_geometric_graph(50, 0.25)
        for u, v in combinations(G, 2):
            # Adjacent vertices must be within the given distance.
            if v in G[u]:
                assert_true(dist(G.nodes[u]['pos'], G.nodes[v]['pos']) <= 0.25)
Esempio n. 16
0
    def test_distances(self):
        """Tests that pairs of vertices adjacent if and only if their
        distances meet the given threshold.

        """
        # Use the Euclidean metric and alpha = -2
        # the default according to the documentation.
        dist = euclidean
        G = nx.geographical_threshold_graph(50, 10)
        for u, v in combinations(G, 2):
            # Adjacent vertices must exceed the threshold.
            if v in G[u]:
                assert_true(join(G, u, v, 10, -2, dist))
            # Nonadjacent vertices must not exceed the threshold.
            else:
                assert_false(join(G, u, v, 10, -2, dist))
Esempio n. 17
0
    def test_p(self):
        """Tests for providing an alternate distance metric to the
        generator.

        """
        # Use the L1 metric.
        dist = l1dist
        G = nx.random_geometric_graph(50, 0.25, p=1)
        for u, v in combinations(G, 2):
            # Adjacent vertices must be within the given distance.
            if v in G[u]:
                assert_true(dist(G.nodes[u]['pos'], G.nodes[v]['pos']) <= 0.25)
            # Nonadjacent vertices must be at greater distance.
            else:
                assert_false(
                    dist(G.nodes[u]['pos'], G.nodes[v]['pos']) <= 0.25)
Esempio n. 18
0
    def test_node_names(self):
        """Tests using values other than sequential numbers as node IDs.

        """
        import string
        nodes = list(string.ascii_lowercase)
        G = nx.thresholded_random_geometric_graph(nodes, 0.25, 0.1)
        assert_equal(len(G), len(nodes))

        def dist(x, y):
            return sqrt(sum((a - b)**2 for a, b in zip(x, y)))

        for u, v in combinations(G, 2):
            # Adjacent vertices must be within the given distance.
            if v in G[u]:
                assert_true(dist(G.nodes[u]['pos'], G.nodes[v]['pos']) <= 0.25)
Esempio n. 19
0
def _node_redundancy(G, v):
    """Returns the redundancy of the node `v` in the bipartite graph `G`.

    If `G` is a graph with `n` nodes, the redundancy of a node is the ratio
    of the "overlap" of `v` to the maximum possible overlap of `v`
    according to its degree. The overlap of `v` is the number of pairs of
    neighbors that have mutual neighbors themselves, other than `v`.

    `v` must have at least two neighbors in `G`.

    """
    n = len(G[v])
    # TODO On Python 3, we could just use `G[u].keys() & G[w].keys()` instead
    # of instantiating the entire sets.
    overlap = sum(1 for (u, w) in combinations(G[v], 2)
                  if (set(G[u]) & set(G[w])) - set([v]))
    return (2 * overlap) / (n * (n - 1))
Esempio n. 20
0
    def test_distances(self):
        """Tests that pairs of vertices adjacent if and only if they are
        within the prescribed radius.

        """
        # Use the Euclidean metric, the default according to the
        # documentation.
        dist = euclidean
        G = nx.random_geometric_graph(50, 0.25)
        for u, v in combinations(G, 2):
            # Adjacent vertices must be within the given distance.
            if v in G[u]:
                assert_true(dist(G.nodes[u]['pos'], G.nodes[v]['pos']) <= 0.25)
            # Nonadjacent vertices must be at greater distance.
            else:
                assert_false(
                    dist(G.nodes[u]['pos'], G.nodes[v]['pos']) <= 0.25)
 def phase3(self):
     # build potential remaining edges and choose with rejection sampling
     potential_edges = combinations(self.remaining_degree, 2)
     # build auxiliary graph of potential edges not already in graph
     H = nx.Graph([(u, v) for (u, v) in potential_edges
                   if not self.graph.has_edge(u, v)])
     rng = self.rng
     while self.remaining_degree:
         if not self.suitable_edge():
             raise nx.NetworkXUnfeasible('no suitable edges left')
         while True:
             u, v = sorted(rng.choice(list(H.edges())))
             if rng.random() < self.q(u, v):
                 break
         if rng.random() < self.p(u, v):  # accept edge
             self.graph.add_edge(u, v)
             self.update_remaining(u, v, aux_graph=H)
Esempio n. 22
0
    def test_node_names(self):
        """Tests using values other than sequential numbers as node IDs.

        """
        import string
        nodes = list(string.ascii_lowercase)
        G = nx.random_geometric_graph(nodes, 0.25)
        assert_equal(len(G), len(nodes))

        dist = euclidean
        for u, v in combinations(G, 2):
            # Adjacent vertices must be within the given distance.
            if v in G[u]:
                assert_true(dist(G.nodes[u]['pos'], G.nodes[v]['pos']) <= 0.25)
            # Nonadjacent vertices must be at greater distance.
            else:
                assert_false(
                    dist(G.nodes[u]['pos'], G.nodes[v]['pos']) <= 0.25)
Esempio n. 23
0
def make_max_clique_graph(G, create_using=None):
    """Returns the maximal clique graph of the given graph.

    The nodes of the maximal clique graph of `G` are the cliques of
    `G` and an edge joins two cliques if the cliques are not disjoint.

    Parameters
    ----------
    G : NetworkX graph

    create_using : NetworkX graph constructor, optional (default=nx.Graph)
       Graph type to create. If graph instance, then cleared before populated.

    Returns
    -------
    NetworkX graph
        A graph whose nodes are the cliques of `G` and whose edges
        join two cliques if they are not disjoint.

    Notes
    -----
    This function behaves like the following code::

        import networkx as nx
        G = nx.make_clique_bipartite(G)
        cliques = [v for v in G.nodes() if G.nodes[v]['bipartite'] == 0]
        G = nx.bipartite.project(G, cliques)
        G = nx.relabel_nodes(G, {-v: v - 1 for v in G})

    It should be faster, though, since it skips all the intermediate
    steps.

    """
    if create_using is None:
        B = G.__class__()
    else:
        B = nx.empty_graph(0, create_using)
    cliques = list(enumerate(set(c) for c in find_cliques(G)))
    # Add a numbered node for each clique.
    B.add_nodes_from(i for i, c in cliques)
    # Join cliques by an edge if they share a node.
    clique_pairs = combinations(cliques, 2)
    B.add_edges_from((i, j) for (i, c1), (j, c2) in clique_pairs if c1 & c2)
    return B
def _consolidate(sets, k):
    """Merge sets that share k or more elements.

    See: http://rosettacode.org/wiki/Set_consolidation

    The iterative python implementation posted there is
    faster than this because of the overhead of building a
    Graph and calling nx.connected_components, but it's not
    clear for us if we can use it in NetworkX because there
    is no licence for the code.

    """
    G = nx.Graph()
    nodes = dict((i, s) for i, s in enumerate(sets))
    G.add_nodes_from(nodes)
    G.add_edges_from((u, v) for u, v in combinations(nodes, 2)
                     if len(nodes[u] & nodes[v]) >= k)
    for component in nx.connected_components(G):
        yield set.union(*[nodes[n] for n in component])
Esempio n. 25
0
def _odd_triangle(G, T):
    """ Test whether T is an odd triangle in G

    Parameters
    ----------
    G : NetworkX Graph
    T : 3-tuple of vertices forming triangle in G

    Returns
    -------
    True is T is an odd triangle
    False otherwise

    Raises
    ------
    NetworkXError
        T is not a triangle in G

    Notes
    -----
    An odd triangle is one in which there exists another vertex in G which is
    adjacent to either exactly one or exactly all three of the vertices in the
    triangle.

    """
    for u in T:
        if u not in G.nodes():
            raise nx.NetworkXError("Vertex %s not in graph" % u)
    for e in list(combinations(T, 2)):
        if e[0] not in G.neighbors(e[1]):
            raise nx.NetworkXError("Edge (%s, %s) not in graph" % (e[0], e[1]))

    T_neighbors = defaultdict(int)
    for t in T:
        for v in G.neighbors(t):
            if v not in T:
                T_neighbors[v] += 1
    for v in T_neighbors:
        if T_neighbors[v] in [1, 3]:
            return True
    return False
Esempio n. 26
0
 def test_wikipedia_example(self):
     # Example from https://en.wikipedia.org/wiki/Gomory%E2%80%93Hu_tree
     G = nx.Graph()
     G.add_weighted_edges_from((
         (0, 1, 1),
         (0, 2, 7),
         (1, 2, 1),
         (1, 3, 3),
         (1, 4, 2),
         (2, 4, 4),
         (3, 4, 1),
         (3, 5, 6),
         (4, 5, 2),
     ))
     for flow_func in flow_funcs:
         T = nx.gomory_hu_tree(G, capacity='weight', flow_func=flow_func)
         assert_true(nx.is_tree(T))
         for u, v in combinations(G, 2):
             cut_value, edge = self.minimum_edge_weight(T, u, v)
             assert_equal(nx.minimum_cut_value(G, u, v, capacity='weight'),
                          cut_value)
def thresholded_random_geometric_graph(n, radius, theta, dim=2,
                                       pos=None, weight=None, p=2, seed=None):
    """Returns a thresholded random geometric graph in the unit cube.

    The thresholded random geometric graph [1] model places `n` nodes
    uniformly at random in the unit cube of dimensions `dim`. Each node
    `u` is assigned a weight :math:`w_u`. Two nodes `u` and `v` are
    joined by an edge if they are within the maximum connection distance,
    `radius` computed by the `p`-Minkowski distance and the summation of
    weights :math:`w_u` + :math:`w_v` is greater than or equal
    to the threshold parameter `theta`.

    Edges within `radius` of each other are determined using a KDTree when
    SciPy is available. This reduces the time complexity from :math:`O(n^2)`
    to :math:`O(n)`.

    Parameters
    ----------
    n : int or iterable
        Number of nodes or iterable of nodes
    radius: float
        Distance threshold value
    theta: float
        Threshold value
    dim : int, optional
        Dimension of graph
    pos : dict, optional
        A dictionary keyed by node with node positions as values.
    weight : dict, optional
        Node weights as a dictionary of numbers keyed by node.
    p : float, optional
        Which Minkowski distance metric to use.  `p` has to meet the condition
        ``1 <= p <= infinity``.

        If this argument is not specified, the :math:`L^2` metric
        (the Euclidean distance metric), p = 2 is used.

        This should not be confused with the `p` of an Erdős-Rényi random
        graph, which represents probability.
    seed : integer, random_state, or None (default)
        Indicator of random number generation state.
        See :ref:`Randomness<randomness>`.

    Returns
    -------
    Graph
        A thresholded random geographic graph, undirected and without
        self-loops.

        Each node has a node attribute ``'pos'`` that stores the
        position of that node in Euclidean space as provided by the
        ``pos`` keyword argument or, if ``pos`` was not provided, as
        generated by this function. Similarly, each node has a nodethre
        attribute ``'weight'`` that stores the weight of that node as
        provided or as generated.

    Examples
    --------
    Default Graph:

    G = nx.thresholded_random_geometric_graph(50, 0.2, 0.1)

    Custom Graph:

    Create a thresholded random geometric graph on 50 uniformly distributed
    nodes where nodes are joined by an edge if their sum weights drawn from
    a exponential distribution with rate = 5 are >= theta = 0.1 and their
    Euclidean distance is at most 0.2.

    Notes
    -----
    This uses a *k*-d tree to build the graph.

    The `pos` keyword argument can be used to specify node positions so you
    can create an arbitrary distribution and domain for positions.

    For example, to use a 2D Gaussian distribution of node positions with mean
    (0, 0) and standard deviation 2

    If weights are not specified they are assigned to nodes by drawing randomly
    from the exponential distribution with rate parameter :math:`\lambda=1`.
    To specify weights from a different distribution, use the `weight` keyword
    argument::

    ::

    >>> import random
    >>> import math
    >>> n = 50
    >>> pos = {i: (random.gauss(0, 2), random.gauss(0, 2)) for i in range(n)}
    >>> w = {i: random.expovariate(5.0) for i in range(n)}
    >>> G = nx.thresholded_random_geometric_graph(n, 0.2, 0.1, 2, pos, w)

    References
    ----------
    .. [1] http://cole-maclean.github.io/blog/files/thesis.pdf

    """

    n_name, nodes = n
    G = nx.Graph()
    namestr = 'thresholded_random_geometric_graph({}, {}, {}, {})'
    G.name = namestr % (n, radius, theta, dim,)
    G.add_nodes_from(nodes)
    # If no weights are provided, choose them from an exponential
    # distribution.
    if weight is None:
        weight = dict((v, seed.expovariate(1)) for v in G)
    # If no positions are provided, choose uniformly random vectors in
    # Euclidean space of the specified dimension.
    if pos is None:
        pos = dict((v, [seed.random() for i in range(dim)]) for v in nodes)
    # If no distance metric is provided, use Euclidean distance.

    nx.set_node_attributes(G, weight, 'weight')
    nx.set_node_attributes(G, pos, 'pos')

    # Returns ``True`` if and only if the nodes whose attributes are
    # ``du`` and ``dv`` should be joined, according to the threshold
    # condition and node pairs are within the maximum connection
    # distance, ``radius``.
    def should_join(pair):
        u, v = pair
        u_weight, v_weight = weight[u], weight[v]
        u_pos, v_pos = pos[u], pos[v]
        dist = (sum(abs(a - b) ** p for a, b in zip(u_pos, v_pos)))**(1 / p)
        # Check if dist is <= radius parameter. This check is redundant if
        # scipy is available and _fast_edges routine is used, but provides
        # the check in case scipy is not available and all edge combinations
        # need to be checked
        if dist <= radius:
            return theta <= u_weight + v_weight
        else:
            return False

    if _is_scipy_available:
        edges = _fast_edges(G, radius, p)
        G.add_edges_from(filter(should_join, edges))
    else:
        G.add_edges_from(filter(should_join, combinations(G, 2)))

    return G
def waxman_graph(n, beta=0.4, alpha=0.1, L=None, domain=(0, 0, 1, 1),
                 metric=None, seed=None):
    r"""Return a Waxman random graph.

    The Waxman random graph model places `n` nodes uniformly at random
    in a rectangular domain. Each pair of nodes at distance `d` is
    joined by an edge with probability

    .. math::
            p = \beta \exp(-d / \alpha L).

    This function implements both Waxman models, using the `L` keyword
    argument.

    * Waxman-1: if `L` is not specified, it is set to be the maximum distance
      between any pair of nodes.
    * Waxman-2: if `L` is specified, the distance between a pair of nodes is
      chosen uniformly at random from the interval `[0, L]`.

    Parameters
    ----------
    n : int or iterable
        Number of nodes or iterable of nodes
    beta: float
        Model parameter
    alpha: float
        Model parameter
    L : float, optional
        Maximum distance between nodes.  If not specified, the actual distance
        is calculated.
    domain : four-tuple of numbers, optional
        Domain size, given as a tuple of the form `(x_min, y_min, x_max,
        y_max)`.
    metric : function
        A metric on vectors of numbers (represented as lists or
        tuples). This must be a function that accepts two lists (or
        tuples) as input and yields a number as output. The function
        must also satisfy the four requirements of a `metric`_.
        Specifically, if $d$ is the function and $x$, $y$,
        and $z$ are vectors in the graph, then $d$ must satisfy

        1. $d(x, y) \ge 0$,
        2. $d(x, y) = 0$ if and only if $x = y$,
        3. $d(x, y) = d(y, x)$,
        4. $d(x, z) \le d(x, y) + d(y, z)$.

        If this argument is not specified, the Euclidean distance metric is
        used.

        .. _metric: https://en.wikipedia.org/wiki/Metric_%28mathematics%29

    seed : integer, random_state, or None (default)
        Indicator of random number generation state.
        See :ref:`Randomness<randomness>`.

    Returns
    -------
    Graph
        A random Waxman graph, undirected and without self-loops. Each
        node has a node attribute ``'pos'`` that stores the position of
        that node in Euclidean space as generated by this function.

    Examples
    --------
    Specify an alternate distance metric using the ``metric`` keyword
    argument. For example, to use the "`taxicab metric`_" instead of the
    default `Euclidean metric`_::

        >>> dist = lambda x, y: sum(abs(a - b) for a, b in zip(x, y))
        >>> G = nx.waxman_graph(10, 0.5, 0.1, metric=dist)

    .. _taxicab metric: https://en.wikipedia.org/wiki/Taxicab_geometry
    .. _Euclidean metric: https://en.wikipedia.org/wiki/Euclidean_distance

    Notes
    -----
    Starting in NetworkX 2.0 the parameters alpha and beta align with their
    usual roles in the probability distribution. In earlier versions their
    positions in the expression were reversed. Their position in the calling
    sequence reversed as well to minimize backward incompatibility.

    References
    ----------
    .. [1]  B. M. Waxman, *Routing of multipoint connections*.
       IEEE J. Select. Areas Commun. 6(9),(1988) 1617--1622.
    """
    n_name, nodes = n
    G = nx.Graph()
    G.add_nodes_from(nodes)
    (xmin, ymin, xmax, ymax) = domain
    # Each node gets a uniformly random position in the given rectangle.
    pos = dict((v, (seed.uniform(xmin, xmax), seed.uniform(ymin, ymax))) for v in G)
    nx.set_node_attributes(G, pos, 'pos')
    # If no distance metric is provided, use Euclidean distance.
    if metric is None:
        metric = euclidean
    # If the maximum distance L is not specified (that is, we are in the
    # Waxman-1 model), then find the maximum distance between any pair
    # of nodes.
    #
    # In the Waxman-1 model, join nodes randomly based on distance. In
    # the Waxman-2 model, join randomly based on random l.
    if L is None:
        L = max(metric(x, y) for x, y in combinations(pos.values(), 2))

        def dist(u, v): return metric(pos[u], pos[v])
    else:
        def dist(u, v): return seed.random() * L

    # `pair` is the pair of nodes to decide whether to join.
    def should_join(pair):
        return seed.random() < beta * math.exp(-dist(*pair) / (alpha * L))

    G.add_edges_from(filter(should_join, combinations(G, 2)))
    return G
Esempio n. 29
0
def eulerize(G):
    """
    Transforms a graph into an Eulerian graph

    Parameters
    ----------
    G : NetworkX graph
       An undirected graph

    Returns
    -------
    G : NetworkX multigraph

    Raises
    ------
    NetworkXError
       If the graph is not connected.

    See Also
    --------
    is_eulerian, eulerian_circuit


    References
    ----------
    .. [1] J. Edmonds, E. L. Johnson.
       Matching, Euler tours and the Chinese postman.
       Mathematical programming, Volume 5, Issue 1 (1973), 111-114.
       [2] https://en.wikipedia.org/wiki/Eulerian_path
    .. [3] http://web.math.princeton.edu/math_alive/5/Notes1.pdf

    Examples
    --------
        >>> G = nx.complete_graph(10)
        >>> H = nx.eulerize(G)
        >>> nx.is_eulerian(H)
        True

    """
    if G.order() == 0:
        raise nx.NetworkXPointlessConcept("Cannot Eulerize null graph")
    if not nx.is_connected(G):
        raise nx.NetworkXError("G is not connected")
    odd_degree_nodes = [n for n, d in G.degree() if d % 2 == 1]
    G = nx.MultiGraph(G)
    if len(odd_degree_nodes) == 0:
        return G

    # get all shortest paths between vertices of odd degree
    odd_deg_pairs_paths = [(m, {
        n: nx.shortest_path(G, source=m, target=n)
    }) for m, n in combinations(odd_degree_nodes, 2)]

    # use inverse path lengths as edge-weights in a new graph
    # store the paths in the graph for easy indexing later
    Gp = nx.Graph()
    for n, Ps in odd_deg_pairs_paths:
        for m, P in Ps.items():
            if n != m:
                Gp.add_edge(m, n, weight=1 / len(P), path=P)

    # find the minimum weight matching of edges in the weighted graph
    best_matching = nx.Graph(list(nx.max_weight_matching(Gp)))

    # duplicate each edge along each path in the set of paths in Gp
    for m, n in best_matching.edges():
        path = Gp[m][n]["path"]
        G.add_edges_from(nx.utils.pairwise(path))
    return G
def geographical_threshold_graph(n, theta, dim=2, pos=None, weight=None,
                                 metric=None, p_dist=None, seed=None):
    r"""Returns a geographical threshold graph.

    The geographical threshold graph model places $n$ nodes uniformly at
    random in a rectangular domain.  Each node $u$ is assigned a weight
    $w_u$. Two nodes $u$ and $v$ are joined by an edge if

    .. math::

       (w_u + w_v)h(r) \ge \theta

    where `r` is the distance between `u` and `v`, h(r) is a probability of
    connection as a function of `r`, and :math:`\theta` as the threshold
    parameter. h(r) corresponds to the p_dist parameter.

    Parameters
    ----------
    n : int or iterable
        Number of nodes or iterable of nodes
    theta: float
        Threshold value
    dim : int, optional
        Dimension of graph
    pos : dict
        Node positions as a dictionary of tuples keyed by node.
    weight : dict
        Node weights as a dictionary of numbers keyed by node.
    metric : function
        A metric on vectors of numbers (represented as lists or
        tuples). This must be a function that accepts two lists (or
        tuples) as input and yields a number as output. The function
        must also satisfy the four requirements of a `metric`_.
        Specifically, if $d$ is the function and $x$, $y$,
        and $z$ are vectors in the graph, then $d$ must satisfy

        1. $d(x, y) \ge 0$,
        2. $d(x, y) = 0$ if and only if $x = y$,
        3. $d(x, y) = d(y, x)$,
        4. $d(x, z) \le d(x, y) + d(y, z)$.

        If this argument is not specified, the Euclidean distance metric is
        used.

        .. _metric: https://en.wikipedia.org/wiki/Metric_%28mathematics%29
    p_dist : function, optional
        A probability density function computing the probability of
        connecting two nodes that are of distance, r, computed by metric.
        The probability density function, `p_dist`, must
        be any function that takes the metric value as input
        and outputs a single probability value between 0-1.
        The scipy.stats package has many probability distribution functions
        implemented and tools for custom probability distribution
        definitions [2], and passing the .pdf method of scipy.stats
        distributions can be used here. If the probability
        function, `p_dist`, is not supplied, the default exponential function
        :math: `r^{-2}` is used.
    seed : integer, random_state, or None (default)
        Indicator of random number generation state.
        See :ref:`Randomness<randomness>`.

    Returns
    -------
    Graph
        A random geographic threshold graph, undirected and without
        self-loops.

        Each node has a node attribute ``pos`` that stores the
        position of that node in Euclidean space as provided by the
        ``pos`` keyword argument or, if ``pos`` was not provided, as
        generated by this function. Similarly, each node has a node
        attribute ``weight`` that stores the weight of that node as
        provided or as generated.

    Examples
    --------
    Specify an alternate distance metric using the ``metric`` keyword
    argument. For example, to use the `taxicab metric`_ instead of the
    default `Euclidean metric`_::

        >>> dist = lambda x, y: sum(abs(a - b) for a, b in zip(x, y))
        >>> G = nx.geographical_threshold_graph(10, 0.1, metric=dist)

    .. _taxicab metric: https://en.wikipedia.org/wiki/Taxicab_geometry
    .. _Euclidean metric: https://en.wikipedia.org/wiki/Euclidean_distance

    Notes
    -----
    If weights are not specified they are assigned to nodes by drawing randomly
    from the exponential distribution with rate parameter $\lambda=1$.
    To specify weights from a different distribution, use the `weight` keyword
    argument::

    >>> import random
    >>> n = 20
    >>> w = {i: random.expovariate(5.0) for i in range(n)}
    >>> G = nx.geographical_threshold_graph(20, 50, weight=w)

    If node positions are not specified they are randomly assigned from the
    uniform distribution.

    Starting in NetworkX 2.1 the parameter ``alpha`` is deprecated and replaced
    with the customizable ``p_dist`` function parameter, which defaults to r^-2
    if ``p_dist`` is not supplied. To reproduce networks of earlier NetworkX
    versions, a custom function needs to be defined and passed as the
    ``p_dist`` parameter. For example, if the parameter ``alpha`` = 2 was used
    in NetworkX 2.0, the custom function def custom_dist(r): r**-2 can be
    passed in versions >=2.1 as the parameter p_dist = custom_dist to
    produce an equivalent network. Note the change in sign from +2 to -2 in
    this parameter change.

    References
    ----------
    .. [1] Masuda, N., Miwa, H., Konno, N.:
       Geographical threshold graphs with small-world and scale-free
       properties.
       Physical Review E 71, 036108 (2005)
    .. [2]  Milan Bradonjić, Aric Hagberg and Allon G. Percus,
       Giant component and connectivity in geographical threshold graphs,
       in Algorithms and Models for the Web-Graph (WAW 2007),
       Antony Bonato and Fan Chung (Eds), pp. 209--216, 2007
    """
    n_name, nodes = n
    G = nx.Graph()
    G.add_nodes_from(nodes)
    # If no weights are provided, choose them from an exponential
    # distribution.
    if weight is None:
        weight = dict((v, seed.expovariate(1)) for v in G)
    # If no positions are provided, choose uniformly random vectors in
    # Euclidean space of the specified dimension.
    if pos is None:
        pos = dict((v, [seed.random() for i in range(dim)]) for v in nodes)
    # If no distance metric is provided, use Euclidean distance.
    if metric is None:
        metric = euclidean
    nx.set_node_attributes(G, weight, 'weight')
    nx.set_node_attributes(G, pos, 'pos')

    # if p_dist is not supplied, use default r^-2
    if p_dist is None:
        def p_dist(r):
            return r**-2

    # Returns ``True`` if and only if the nodes whose attributes are
    # ``du`` and ``dv`` should be joined, according to the threshold
    # condition.
    def should_join(pair):
        u, v = pair
        u_pos, v_pos = pos[u], pos[v]
        u_weight, v_weight = weight[u], weight[v]
        return (u_weight + v_weight) * p_dist(metric(u_pos, v_pos)) >= theta

    G.add_edges_from(filter(should_join, combinations(G, 2)))
    return G