Exemplo n.º 1
0
def complete_binary_tree(n: int = None,
                         h: int = None,
                         directed=False) -> Graph:
    """
    Construct a perfect binary tree with n nodes/height of h.
    :param n: number of nodes
    :param h: height
    :param directed: directed edges
    :return:
    """
    if n is None and h is None:
        raise ValueError('One of n and h must be not-null.')
    if n is None and h is not None:
        n = 2**(h + 1) - 1
    if n == 0:
        return Graph([], [])
    # check n+1 is a power of two - 1
    if not (n & (n + 1)) == 0:
        raise ValueError(
            'Number of nodes not enough for a complete binary tree.')
    vertices: List[Vertex] = [Vertex(label=f'{i}', index=i) for i in range(n)]

    edges = [Edge(vertices[i], vertices[2 * i + 1]) for i in range(n // 2)]
    edges += [
        Edge(vertices[i], vertices[2 * i + 2]) for i in range(n // 2 - 1)
    ]
    if directed:
        return Graph(vertices, edges, directed=True)
    edges += [Edge(vertices[2 * i + 1], vertices[i]) for i in range(n // 2)]
    edges += [
        Edge(vertices[2 * i + 2], vertices[i]) for i in range(n // 2 - 1)
    ]
    return Graph(vertices, edges)
Exemplo n.º 2
0
    def test_vertex_adding(self):
        n = 100
        g = Graph([], [])
        vertices = [Vertex(label=f'{i}', index=i) for i in range(n)]
        g.add_vertices(vertices)

        self.assertEqual(True, True)
Exemplo n.º 3
0
def complete_graph(n: int, directed=False) -> Graph:
    vertices: List[Vertex] = [Vertex(label=f'{i}', index=i) for i in range(n)]
    if directed:
        edges = [Edge(v, i) for v, i in itertools.combinations(vertices, 2)]
    else:
        edges = [Edge(v, i) for v, i in itertools.permutations(vertices, 2)]
    return Graph(vertices, edges, directed=directed)
Exemplo n.º 4
0
def path_graph(n: int, directed=False) -> Graph:
    vertices: List[Vertex] = [Vertex(label=f'{i}', index=i) for i in range(n)]
    edges = [Edge(vertices[i], vertices[i + 1]) for i in range(n - 1)]
    if not directed:
        edges += [Edge(vertices[i], vertices[i - 1]) for i in range(1, n)]

    return Graph(vertices, edges, directed=directed)
Exemplo n.º 5
0
def small_world_phenomena(G: Graph):
    """
    Test the small-world-phenomena for this graph graphically, i.e. plot the number of paths between nodes
    vs. the distance between nodes (as number of edges).

    Parameters
    ----------
    G: Graph
        Input graph

    Returns
    -------

    References
    -------
    See http://snap.stanford.edu/na09/02-small_world.pdf for reference.
    """
    v = len(G.vertices)
    adj = G.adjacency_matrix()
    x = floyd(adj).flatten()
    y = [number_of_paths(G, i, j) for i in range(v) for j in range(v)]

    plt.scatter(x=x, y=y)

    plt.xlabel('Distance between nodes')
    plt.ylabel('Number of paths')

    plt.show()
Exemplo n.º 6
0
def graph_cartesian(g1: Graph, g2: Graph):
    # node labels are (v1,v2)
    vertices = [
        Vertex(label=f'{v}', index=i)
        for i, v in enumerate(zip(g1.vertices, g2.vertices))
    ]
    edges = []
    return Graph(vertices=vertices, edges=edges)
Exemplo n.º 7
0
def empty_graph(n: int = 0) -> Graph:
    """
    Empty graph with or without vertices.
    :param n: (optional) number of vertices
    :return:
    """
    vertices: List[Vertex] = [Vertex(label=f'{i}', index=i) for i in range(n)]

    return Graph(vertices, [])
Exemplo n.º 8
0
def number_of_paths(G: Graph, i: int, j: int, n: int = None) -> int:
    # TODO: scipy.sparse.csr_matrix.power
    """
    Gives the number of paths in an acyclic graph between two nodes.
    :param G: the graph
    :param i: first node
    :param j: second node
    :param n: length of path. If None, the total number is computed.
    :return:
    """
    A = G.adjacency_matrix(np.int)
    if n is not None:
        print(matrix_power(A, n)[i, j])
        return matrix_power(A, n)[i, j]
    gg = inv(eye(N=A.shape[0]) - A)
    return gg[i, j]
Exemplo n.º 9
0
def read_edgelist(path: str,
                  sep: str = '\t',
                  comments: list = None,
                  mark_comm: str = '#',
                  parse_meta=False,
                  meta_types: list = None,
                  open_gzip=False):
    """
    Parse an edge list in tsv format.

    Parameters
    ----------
    path: str
        Path to read the graph from
    sep: str
        Separator
    comments: list
        A list of comments to be written at top to the file
    mark_comm: str
        Comment marker
    parse_meta: bool
        Whether to parse the provided meta data.
        Meta data is distinguished from comments by having the marker written twice.
        Example meta data line: ##source:kaggle
    meta_types: list
        The types of the input meta data.
    open_gzip: bool
        Whether to open a zip file.

    Returns
    -------
    """

    G = Graph()
    if open_gzip:
        f = gzip.open(path, 'r+')
    else:
        f = open(path, 'r+')
    meta = dict()
    meta_marker = mark_comm + mark_comm
    for line in f:
        if parse_meta and line.startswith(meta_marker):
            meta += _parse_meta(line.strip())
    f.close()
Exemplo n.º 10
0
def plot_degree(G: Graph, bins: int = None):
    """
    Plot a histogram of the distribution of the node degrees.

    Parameters
    ----------
    G: Graph
        Input graph
    bins: int
        Number of bins to plot

    Returns
    -------

    """
    adj_list = G.adjacency_list()
    plt.hist(adj_list.values(), bins=bins)
    plt.title("Degree distribution")
    plt.xlabel('Number of neighbours')
    plt.show()
Exemplo n.º 11
0
def bound_conductance(G: Graph = None, d: int = None, l2: float = None):
    """
    Give a lower and an upper bound on the conductance of a d-regular graph.
    The conductance is the smallest value of the isoparametric ratio of some set of vertices of a graph.
    It tells us how close the graph is to being bipartite.
    (This is Cheeger's inequality)
    :param G: undirected graph
    :param d: number of edges for each vertex
    :param l2: (optional) smallest non-zero eigenvalue. Will be computed if not given.
    :return: lower bound on the conductance
    """
    if G is None and d is None and l2 is None:
        raise ValueError("You need to specify either the graph G or the parameters d and l2.")
    # the only meaningful bound for an empty graph
    if G is not None and len(G.vertices) == 0:
        warn('An empty graph was given')
        return 0, 0
    if d is None:
        d = G.get_degree(G.vertices[0])

    if l2 is None and G is not None:
        l2 = compute_l2(G)
    return l2 / (2 * d), (2 * l2 / d) ** 0.5
Exemplo n.º 12
0
def signed_laplacian_matrix(G: Graph) -> np.ndarray:
    g = G.adjacency_matrix()
    return np.diag(g.sum(axis=1)) + G.adjacency_matrix()
Exemplo n.º 13
0
def parse_edges_from_list(l: list,
                          default_weight: Union[int, float] = None,
                          edge_type=None,
                          **meta):
    """
    Parse a list of edges into a graph.
    Parameters
    ----------
    l: list
        List of edges. Edges could be strings of the format 'node1 node2 (weight)' or tuples.
    sep: str
        Separator in case that edges are given by strings.
    default_weight: int or float
        If only some edges are weighted, use this value as default for the others.
    edge_type: str or type
        The type of the edges of the graph.
    meta: dict
        Meta data of the graph

    Returns
    -------
    g: Graph

    Examples
    -------
    With string input
    l1 = ['v1 v2 2', 'v1 v3 1']
    g = parse_edges_from_list(l1)

    With default weight:
    l1 = ['v1 v2 2', 'v1 v3 1']
    g = parse_edges_from_list(l1)

    With tuple input:
    l2 = [('v1','v2', 2), ('v1','v3', 1)]
    g = parse_edges_from_list(l2)

    """
    if l is None or len(l) == 0:
        return Graph()
    l_type = type(l[0])

    if l_type == str:
        edges = [
            _parse_edge(e, default_weight=default_weight, edge_type=edge_type)
            for e in l
        ]
    elif l_type == tuple or l_type == list:
        # weighted graph with default edges
        if default_weight is not None:
            edges = [
                Edge(e[0], e[1], e[2]) if len(e) == 3 else Edge(
                    e[0], e[1], default_weight) for e in l
            ]

        # weighted graph
        elif len(l[0]) == 3:
            try:
                edges = [Edge(e[0], e[1], e[2]) for e in l]
            except IndexError:
                raise IndexError(
                    "It seems that not every edge is weighted. If this is on purpose, "
                    "use the default_weight argument to specify default weight for the missing weights."
                )

        # unweighted graph
        else:
            edges = [Edge(e[0], e[1]) for e in l]
    else:
        raise ValueError(
            'An edge must be represented either by a string or by a tuple or list.'
        )
    g = Graph(edges=edges)
    g.meta = meta
    return g