Ejemplo n.º 1
0
 def test_data_types(self):
     data = [
         True,
         False,
         10**20,
         -2e33,
         "'",
         '"&&&""',
         [{
             (b"\xfd", ): "\x7f",
             chr(0x4444): (1, 2)
         }, (2, "3")],
     ]
     data.append(chr(0x14444))
     data.append(literal_eval("{2.3j, 1 - 2.3j, ()}"))
     G = eg.Graph()
     G.name = data
     G.graph["data"] = data
     G.add_node(0, int=-1, data=dict(data=data))
     G.add_edge(0, 0, float=-2.5, data=data)
     gml = "\n".join(eg.generate_gml(G, stringizer=literal_stringizer))
     G = eg.parse_gml(gml, destringizer=literal_destringizer)
     assert data == G.name
     assert {"name": data, "data": data} == G.graph
     assert G.nodes == {0: dict(int=-1, data=dict(data=data))}
     assert list(G.edges) == [(0, 0, dict(float=-2.5, data=data))]
     G = eg.Graph()
     G.graph["data"] = "frozenset([1, 2, 3])"
     G = eg.parse_gml(eg.generate_gml(G), destringizer=literal_eval)
     assert G.graph["data"] == "frozenset([1, 2, 3])"
Ejemplo n.º 2
0
    def test_edge_id_construct(self):
        G = eg.Graph()
        G.add_edges_from([(0, 1, {"id": 0}), (1, 2, {"id": 2}), (2, 3)])

        expected = f"""<gexf xmlns="http://www.gexf.net/1.2draft" xmlns:xsi\
="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.\
gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd" version="1.2">
  <meta lastmodifieddate="{time.strftime('%Y-%m-%d')}">
    <creator>EasyGraph</creator>
  </meta>
  <graph defaultedgetype="undirected" mode="static" name="">
    <nodes>
      <node id="0" label="0" />
      <node id="1" label="1" />
      <node id="2" label="2" />
      <node id="3" label="3" />
    </nodes>
    <edges>
      <edge source="0" target="1" id="0" />
      <edge source="1" target="2" id="2" />
      <edge source="2" target="3" id="1" />
    </edges>
  </graph>
</gexf>"""

        obtained = "\n".join(eg.generate_gexf(G))
        assert expected == obtained
Ejemplo n.º 3
0
def get_graph_blogcatalog():
    """Returns the undirected graph of blogcatalog.

    Returns
    -------
    get_graph_blogcatalog : easygraph.Graph
        The undirected graph instance of blogcatalog from dataset:
        https://github.com/phanein/deepwalk/blob/master/example_graphs/blogcatalog.mat

    References
    ----------
    .. [1] https://github.com/phanein/deepwalk/blob/master/example_graphs/blogcatalog.mat

    """
    from scipy.io import loadmat

    def sparse2graph(x):
        from collections import defaultdict

        G = defaultdict(lambda: set())
        cx = x.tocoo()
        for i, j, v in zip(cx.row, cx.col, cx.data):
            G[i].add(j)
        return {str(k): [str(x) for x in v] for k, v in G.items()}

    mat = loadmat("./samples/blogcatalog.mat")
    A = mat["network"]
    data = sparse2graph(A)

    G = eg.Graph()
    for u in data:
        for v in data[u]:
            G.add_edge(u, v)

    return G
Ejemplo n.º 4
0
    def test_outofrange_integers(self):
        # GML restricts integers to 32 signed bits.
        # Check that we honor this restriction on export
        G = eg.Graph()
        # Test export for numbers that barely fit or don't fit into 32 bits,
        # and 3 numbers in the middle
        numbers = {
            "toosmall": (-(2**31)) - 1,
            "small": -(2**31),
            "med1": -4,
            "med2": 0,
            "med3": 17,
            "big": (2**31) - 1,
            "toobig": 2**31,
        }
        G.add_node("Node", **numbers)

        fd, fname = tempfile.mkstemp()
        try:
            eg.write_gml(G, fname)
            # Check that the export wrote the nonfitting numbers as strings
            G2 = eg.read_gml(fname)
            for attr, value in G2.nodes["Node"].items():
                if attr == "toosmall" or attr == "toobig":
                    assert type(value) == str
                else:
                    assert type(value) == int
        finally:
            os.close(fd)
            os.unlink(fname)
Ejemplo n.º 5
0
def get_graph_youtube():
    """Returns the undirected graph of Youtube dataset.

    Returns
    -------
    get_graph_youtube : easygraph.Graph
        The undirected graph instance of Youtube from dataset:
        http://socialnetworks.mpi-sws.mpg.de/data/youtube-links.txt.gz

    References
    ----------
    .. [1] http://socialnetworks.mpi-sws.mpg.de/data/youtube-links.txt.gz

    """
    import gzip

    from urllib import request

    url = "http://socialnetworks.mpi-sws.mpg.de/data/youtube-links.txt.gz"
    zipped_data_path = "./samples/youtube-links.txt.gz"
    unzipped_data_path = "./samples/youtube-links.txt"

    # Download .gz file
    print("Downloading Youtube dataset...")
    request.urlretrieve(url, zipped_data_path, _show_progress)

    # Unzip
    unzipped_data = gzip.GzipFile(zipped_data_path)
    open(unzipped_data_path, "wb+").write(unzipped_data.read())
    unzipped_data.close()

    # Returns graph
    G = eg.Graph()
    G.add_edges_from_file(file=unzipped_data_path)
    return G
Ejemplo n.º 6
0
    def test_specials(self):
        from math import isnan

        inf, nan = float("inf"), float("nan")
        G = eg.Graph()
        G.add_node(1, testattr=inf, strdata="inf", key="a")
        G.add_node(2, testattr=nan, strdata="nan", key="b")
        G.add_node(3, testattr=-inf, strdata="-inf", key="c")

        fh = io.BytesIO()
        eg.write_gexf(G, fh)
        fh.seek(0)
        filetext = fh.read()
        fh.seek(0)
        H = eg.read_gexf(fh, node_type=int)

        assert b"INF" in filetext
        assert b"NaN" in filetext
        assert b"-INF" in filetext

        assert H.nodes[1]["testattr"] == inf
        assert isnan(H.nodes[2]["testattr"])
        assert H.nodes[3]["testattr"] == -inf

        assert H.nodes[1]["strdata"] == "inf"
        assert H.nodes[2]["strdata"] == "nan"
        assert H.nodes[3]["strdata"] == "-inf"

        assert H.nodes[1]["easygraph_key"] == "a"
        assert H.nodes[2]["easygraph_key"] == "b"
        assert H.nodes[3]["easygraph_key"] == "c"
Ejemplo n.º 7
0
def get_graph_flickr():
    """Returns the undirected graph of Flickr dataset.

    Returns
    -------
    get_graph_flickr : easygraph.Graph
        The undirected graph instance of Flickr from dataset:
        http://socialnetworks.mpi-sws.mpg.de/data/flickr-links.txt.gz

    References
    ----------
    .. [1] http://socialnetworks.mpi-sws.mpg.de/data/flickr-links.txt.gz

    """
    from urllib import request
    import gzip
    url = 'http://socialnetworks.mpi-sws.mpg.de/data/flickr-links.txt.gz'
    zipped_data_path = './samples/flickr-links.txt.gz'
    unzipped_data_path = './samples/flickr-links.txt'

    # Download .gz file
    print("Downloading Flickr dataset...")
    request.urlretrieve(url, zipped_data_path, _show_progress)

    # Unzip
    unzipped_data = gzip.GzipFile(zipped_data_path)
    open(unzipped_data_path, 'wb+').write(unzipped_data.read())
    unzipped_data.close()

    # Returns graph
    G = eg.Graph()
    G.add_edges_from_file(file=unzipped_data_path)
    return G
Ejemplo n.º 8
0
 def test_bool(self):
     G = eg.Graph()
     G.add_node(1, testattr=True)
     fh = io.BytesIO()
     eg.write_gexf(G, fh)
     fh.seek(0)
     H = eg.read_gexf(fh, node_type=int)
     assert H.nodes[1]["testattr"]
Ejemplo n.º 9
0
 def test_serialize_ints_to_strings(self):
     G = eg.Graph()
     G.add_node(1, id=7, label=77)
     fh = io.BytesIO()
     eg.write_gexf(G, fh)
     fh.seek(0)
     H = eg.read_gexf(fh, node_type=int)
     assert list(H) == [7]
     assert H.nodes[7]["label"] == "77"
Ejemplo n.º 10
0
 def test_simple_list(self):
     G = eg.Graph()
     list_value = [(1, 2, 3), (9, 1, 2)]
     G.add_node(1, key=list_value)
     fh = io.BytesIO()
     eg.write_gexf(G, fh)
     fh.seek(0)
     H = eg.read_gexf(fh, node_type=int)
     assert H.nodes[1]["easygraph_key"] == list_value
Ejemplo n.º 11
0
 def test_add_parent(self):
     G = eg.Graph()
     G.add_node(0, label="1", color="green", parents=[1, 2])
     fh = io.BytesIO()
     eg.write_gexf(G, fh)
     fh.seek(0)
     H = eg.read_gexf(fh, node_type=int)
     assert sorted(G.nodes) == sorted(H.nodes)
     assert sorted(sorted(e)
                   for e in G.edges) == sorted(sorted(e) for e in H.edges)
Ejemplo n.º 12
0
 def test_dynamic_mode(self):
     G = eg.Graph()
     G.add_node(1, label="1", color="green")
     G.graph["mode"] = "dynamic"
     fh = io.BytesIO()
     eg.write_gexf(G, fh)
     fh.seek(0)
     H = eg.read_gexf(fh, node_type=int)
     assert sorted(G.nodes) == sorted(H.nodes)
     assert sorted(sorted(e)
                   for e in G.edges) == sorted(sorted(e) for e in H.edges)
Ejemplo n.º 13
0
    def test_labels_are_strings(self):
        # GML requires labels to be strings (i.e., in quotes)
        answer = """graph [
  node [
    id 0
    label "1203"
  ]
]"""
        G = eg.Graph()
        G.add_node(1203)
        data = "\n".join(eg.generate_gml(G, stringizer=literal_stringizer))
        assert data == answer
Ejemplo n.º 14
0
def CombineNodes(records, G, label_dict, score_dict, node_dict,
                 Next_label_dict, nodes, degrees, distance_dict):
    onerecord = dict()
    for node, label in label_dict.items():
        if label in onerecord:
            onerecord[label].append(node)
        else:
            onerecord[label] = [node]
    records.append(onerecord)
    Gx = eg.Graph()
    label_dictx = dict()
    score_dictx = dict()
    node_dictx = dict()
    nodesx = []
    cnt = 0
    for record_label in onerecord:
        nodesx.append(cnt)
        label_dictx[cnt] = record_label
        score_dictx[record_label] = score_dict[record_label]
        node_dictx[record_label] = cnt
        cnt += 1
    record_labels = list(onerecord.keys())
    i = 0
    edge = dict()
    adj = G.adj
    for i in range(0, len(record_labels)):
        edge[i] = dict()
        for j in range(0, len(record_labels)):
            if i == j: continue
            inodes = onerecord[record_labels[i]]
            jnodes = onerecord[record_labels[j]]
            for unode in inodes:
                for vnode in jnodes:
                    if unode in adj and vnode in adj[unode]:
                        if j not in edge[i]:
                            edge[i][j] = 0
                        edge[i][j] += adj[unode][vnode].get("weight", 1)
    for unode in edge:
        for vnode, w in edge[unode].items():
            if unode < vnode:
                Gx.add_edge(unode, vnode, weight=w)
    G = Gx
    label_dict = label_dictx
    score_dict = score_dictx
    node_dict = node_dictx
    Next_label_dict = label_dictx
    nodes = nodesx
    degrees = G.degree()
    distance_dict = eg.Floyd(G)
    return records, G, label_dict, score_dict, node_dict, Next_label_dict, nodes, degrees, distance_dict
Ejemplo n.º 15
0
    def test_slice_and_spell(self):
        # Test spell first, so version = 1.2
        G = eg.Graph()
        G.add_node(0, label="1", color="green")
        G.nodes[0]["spells"] = [(1, 2)]
        fh = io.BytesIO()
        eg.write_gexf(G, fh)
        fh.seek(0)
        H = eg.read_gexf(fh, node_type=int)
        assert sorted(G.nodes) == sorted(H.nodes)
        assert sorted(sorted(e)
                      for e in G.edges) == sorted(sorted(e) for e in H.edges)

        G = eg.Graph()
        G.add_node(0, label="1", color="green")
        G.nodes[0]["slices"] = [(1, 2)]
        fh = io.BytesIO()
        eg.write_gexf(G, fh, version="1.1draft")
        fh.seek(0)
        H = eg.read_gexf(fh, node_type=int)
        assert sorted(G.nodes) == sorted(H.nodes)
        assert sorted(sorted(e)
                      for e in G.edges) == sorted(sorted(e) for e in H.edges)
Ejemplo n.º 16
0
def graph_to_d_atleast2(G):
    n = len(G)
    LG = eg.Graph()
    LG = G.copy()
    new_node = n
    degree = LG.degree()
    node = LG.nodes.copy()
    for i in node:
        if degree[i] == 1:
            for neighbors in LG.neighbors(node=i):
                LG.add_edge(i, new_node)
                LG.add_edge(new_node, neighbors)
                break
            new_node = new_node + 1
    return LG
Ejemplo n.º 17
0
    def test_float_label(self):
        node = 1.0
        G = eg.Graph()
        G.add_node(node)
        fobj = tempfile.NamedTemporaryFile()
        eg.write_gml(G, fobj)
        fobj.seek(0)
        # Should be bytes in 2.x and 3.x
        data = fobj.read().strip().decode("ascii")
        answer = """graph [
  node [
    id 0
    label "1.0"
  ]
]"""
        assert data == answer
Ejemplo n.º 18
0
    def test_unicode_node(self):
        node = "node" + chr(169)
        G = eg.Graph()
        G.add_node(node)
        fobj = tempfile.NamedTemporaryFile()
        eg.write_gml(G, fobj)
        fobj.seek(0)
        # Should be bytes in 2.x and 3.x
        data = fobj.read().strip().decode("ascii")
        answer = """graph [
  node [
    id 0
    label "node&#169;"
  ]
]"""
        assert data == answer
Ejemplo n.º 19
0
def _find_separation_nodes(G):
    G_s = condensation(G)
    SCC_mapping = {}
    incoming_info = G_s.graph["incoming_info"]
    G_s_undirected = eg.Graph()
    sep_nodes = set()
    for node in (G_s.nodes).keys():
        SCC_mapping[node] = G_s.nodes[node]["member"]
        if len(G_s.nodes[node]["member"]) == 1:
            sep_nodes.add(node)
        G_s_undirected.add_node(node, member=G_s.nodes[node]["member"])
    for edge in G_s.edges:
        G_s_undirected.add_edge(edge[0], edge[1])
    cut_nodes = eg.generator_articulation_points(G_s_undirected)
    out_degree = G_s.out_degree()
    in_degree = G_s.in_degree()
    separations = set()
    for cut_node in cut_nodes:
        if cut_node in sep_nodes:
            if out_degree[cut_node] >= 1 and in_degree[cut_node] >= 1:
                CC_u = eg.connected_component_of_node(G_s_undirected,
                                                      node=cut_node)
                G_CC = G_s_undirected.nodes_subgraph(list(CC_u))
                G_CC.remove_node(cut_node)
                successors = G_s.neighbors(node=cut_node)
                predecessors = G_s.predecessors(node=cut_node)
                CC_removal = eg.connected_components(G_CC)
                flag = True
                for group in CC_removal:
                    flag_succ = False
                    flag_pred = False
                    for node in group:
                        if node in successors:
                            flag_succ = True
                            if flag_pred:
                                flag = False
                                break
                        elif node in predecessors:
                            flag_pred = True
                            if flag_succ:
                                flag = False
                                break
                    if not flag:
                        break
                if flag:
                    separations.add(list(SCC_mapping[cut_node])[0])
    return separations, SCC_mapping, incoming_info
Ejemplo n.º 20
0
    def test_writing_graph_with_one_element_property_list(self):
        g = eg.Graph()
        g.add_node("n1", properties=["element"])
        with byte_file() as f:
            eg.write_gml(g, f)
        result = f.read().decode()

        assert result == dedent("""\
            graph [
              node [
                id 0
                label "n1"
                properties "_easygraph_list_start"
                properties "element"
              ]
            ]
        """)
Ejemplo n.º 21
0
 def test_default_attribute(self):
     G = eg.Graph()
     G.add_node(1, label="1", color="green")
     eg.add_path(G, [0, 1, 2, 3])
     G.add_edge(1, 2, foo=3)
     G.graph["node_default"] = {"color": "yellow"}
     G.graph["edge_default"] = {"foo": 7}
     fh = io.BytesIO()
     eg.write_gexf(G, fh)
     fh.seek(0)
     H = eg.read_gexf(fh, node_type=int)
     assert sorted(G.nodes) == sorted(H.nodes)
     # Reading a gexf graph always sets mode attribute to either
     # 'static' or 'dynamic'. Remove the mode attribute from the
     # read graph for the sake of comparing remaining attributes.
     del H.graph["mode"]
     assert G.graph == H.graph
Ejemplo n.º 22
0
def _get_spanning_tree_of_component(G):
    spanning_tree = eg.Graph()
    seen = set()

    def _plain_dfs(u):
        for v, edge_data in G.adj[u].items():
            if v not in seen:
                seen.add(v)
                spanning_tree.add_edge(u, v)
                _plain_dfs(v)

    random_node = list(G.nodes)[0]
    seen.add(random_node)
    spanning_tree.add_node(random_node)

    _plain_dfs(random_node)

    return spanning_tree
Ejemplo n.º 23
0
    def make_graph(self, graph_xml, graphml_keys, defaults, G=None):
        # set default graph type
        edgedefault = graph_xml.get("edgedefault", None)
        if G is None:
            if edgedefault == "directed":
                G = eg.MultiDiGraph()
            else:
                G = eg.MultiGraph()
        # set defaults for graph attributes
        G.graph["node_default"] = {}
        G.graph["edge_default"] = {}
        for key_id, value in defaults.items():
            key_for = graphml_keys[key_id]["for"]
            name = graphml_keys[key_id]["name"]
            python_type = graphml_keys[key_id]["type"]
            if key_for == "node":
                G.graph["node_default"].update({name: python_type(value)})
            if key_for == "edge":
                G.graph["edge_default"].update({name: python_type(value)})
        # hyperedges are not supported
        hyperedge = graph_xml.find(f"{{{self.NS_GRAPHML}}}hyperedge")
        if hyperedge is not None:
            raise eg.EasyGraphError("GraphML reader doesn't support hyperedges")
        # add nodes
        for node_xml in graph_xml.findall(f"{{{self.NS_GRAPHML}}}node"):
            self.add_node(G, node_xml, graphml_keys, defaults)
        # add edges
        for edge_xml in graph_xml.findall(f"{{{self.NS_GRAPHML}}}edge"):
            self.add_edge(G, edge_xml, graphml_keys)
        # add graph data
        data = self.decode_data_elements(graphml_keys, graph_xml)
        G.graph.update(data)

        # switch to Graph or DiGraph if no parallel edges were found
        if self.multigraph:
            return G

        G = eg.DiGraph(G) if G.is_directed() else eg.Graph(G)
        # add explicit edge "id" from file as attribute in eg graph.
        eg.set_edge_attributes(G, values=self.edge_ids, name="id")
        return G
Ejemplo n.º 24
0
    def test_writing_graph_with_multi_element_property_list(self):
        g = eg.Graph()
        g.add_node("n1", properties=["element", 0, 1, 2.5, True, False])
        with byte_file() as f:
            eg.write_gml(g, f)
        result = f.read().decode()

        assert result == dedent("""\
            graph [
              node [
                id 0
                label "n1"
                properties "element"
                properties 0
                properties 1
                properties 2.5
                properties 1
                properties 0
              ]
            ]
        """)
Ejemplo n.º 25
0
def _commonUpdate(G, node_u, node_v, threshold, score_dict):
    for node_w in G.neighbors(node=node_u):
        _computeTieStrength(G, node_u, node_w)
    for node_w in G.predecessors(node=node_u):
        _computeTieStrength(G, node_w, node_u)
    G_un = eg.Graph()
    for node in G.nodes:
        G_un.add_node(node)
    for edge in G.edges:
        if not G_un.has_edge(edge[0], edge[1]):
            G_un.add_edge(edge[0], edge[1])
    u_2hop = _get2hop(G_un, node_u)
    G_u = G.nodes_subgraph(from_nodes=u_2hop)
    v_2hop = _get2hop(G_un, node_v)
    G_v = G.nodes_subgraph(from_nodes=v_2hop)
    score_u = _updateScore(node_u, G_u, threshold)
    score_v = _updateScore(node_v, G_v, threshold)
    score_dict[node_u] = score_u
    score_dict[node_v] = score_v
    all_neigh_u = list(set(G.all_neighbors(node=node_u)))
    # print("all_neigh:",all_neigh_u)
    all_neigh_v = list(set(G.all_neighbors(node=node_v)))
    for node_w in all_neigh_u:
        if node_w in all_neigh_v:
            w_2hop = _get2hop(G_un, node_w)
            G_w = G.nodes_subgraph(from_nodes=w_2hop)
            score_w = _updateScore(node_w, G_w, threshold)
        else:
            score_w = 0
            w_2hop = _get2hop(G_un, node_w)
            G_w = G.nodes_subgraph(from_nodes=w_2hop)
            for c in _strongly_connected_components(G_w, threshold):
                if node_u in c:
                    length = len(c)
                    closeness_c_w = _computeCloseness(G, c, node_w, threshold, length)
                    if closeness_c_w < 0:
                        score_w -= closeness_c_w
        score_dict[node_w] = score_w
Ejemplo n.º 26
0
def parse_gml_lines(lines, label, destringizer):
    """Parse GML `lines` into a graph."""
    def tokenize():
        patterns = [
            r"[A-Za-z][0-9A-Za-z_]*\b",  # keys
            # reals
            r"[+-]?(?:[0-9]*\.[0-9]+|[0-9]+\.[0-9]*|INF)(?:[Ee][+-]?[0-9]+)?",
            r"[+-]?[0-9]+",  # ints
            r'".*?"',  # strings
            r"\[",  # dict start
            r"\]",  # dict end
            r"#.*$|\s+",  # comments and whitespaces
        ]
        tokens = re.compile("|".join(f"({pattern})" for pattern in patterns))
        lineno = 0
        for line in lines:
            length = len(line)
            pos = 0
            while pos < length:
                match = tokens.match(line, pos)
                if match is None:
                    m = f"cannot tokenize {line[pos:]} at ({lineno + 1}, {pos + 1})"
                    raise EasyGraphError(m)
                for i in range(len(patterns)):
                    group = match.group(i + 1)
                    if group is not None:
                        if i == 0:  # keys
                            value = group.rstrip()
                        elif i == 1:  # reals
                            value = float(group)
                        elif i == 2:  # ints
                            value = int(group)
                        else:
                            value = group
                        if i != 6:  # comments and whitespaces
                            yield Token(Pattern(i), value, lineno + 1, pos + 1)
                        pos += len(group)
                        break
            lineno += 1
        yield Token(None, None, lineno + 1, 1)  # EOF

    def unexpected(curr_token, expected):
        category, value, lineno, pos = curr_token
        value = repr(value) if value is not None else "EOF"
        raise EasyGraphError(
            f"expected {expected}, found {value} at ({lineno}, {pos})")

    def consume(curr_token, category, expected):
        if curr_token.category == category:
            return next(tokens)
        unexpected(curr_token, expected)

    def parse_dict(curr_token):
        # dict start
        curr_token = consume(curr_token, Pattern.DICT_START, "'['")
        # dict contents
        curr_token, dct = parse_kv(curr_token)
        # dict end
        curr_token = consume(curr_token, Pattern.DICT_END, "']'")
        return curr_token, dct

    def parse_kv(curr_token):
        dct = defaultdict(list)
        while curr_token.category == Pattern.KEYS:
            key = curr_token.value
            curr_token = next(tokens)
            category = curr_token.category
            if category == Pattern.REALS or category == Pattern.INTS:
                value = curr_token.value
                curr_token = next(tokens)
            elif category == Pattern.STRINGS:
                value = unescape(curr_token.value[1:-1])
                if destringizer:
                    try:
                        value = destringizer(value)
                    except ValueError:
                        pass
                curr_token = next(tokens)
            elif category == Pattern.DICT_START:
                curr_token, value = parse_dict(curr_token)
            else:
                if key in ("id", "label", "source", "target"):
                    try:
                        # String convert the token value
                        value = unescape(str(curr_token.value))
                        if destringizer:
                            try:
                                value = destringizer(value)
                            except ValueError:
                                pass
                        curr_token = next(tokens)
                    except Exception:
                        msg = ("an int, float, string, '[' or string" +
                               " convertable ASCII value for node id or label")
                        unexpected(curr_token, msg)
                elif curr_token.value in {"NAN", "INF"}:
                    value = float(curr_token.value)
                    curr_token = next(tokens)
                else:  # Otherwise error out
                    unexpected(curr_token, "an int, float, string or '['")
            dct[key].append(value)

        def clean_dict_value(value):
            if not isinstance(value, list):
                return value
            if len(value) == 1:
                return value[0]
            if value[0] == LIST_START_VALUE:
                return value[1:]
            return value

        dct = {key: clean_dict_value(value) for key, value in dct.items()}
        return curr_token, dct

    def parse_graph():
        curr_token, dct = parse_kv(next(tokens))
        if curr_token.category is not None:  # EOF
            unexpected(curr_token, "EOF")
        if "graph" not in dct:
            raise EasyGraphError("input contains no graph")
        graph = dct["graph"]
        if isinstance(graph, list):
            raise EasyGraphError("input contains more than one graph")
        return graph

    tokens = tokenize()
    graph = parse_graph()
    directed = graph.pop("directed", False)
    multigraph = graph.pop("multigraph", False)
    if not multigraph:
        G = eg.DiGraph() if directed else eg.Graph()
    else:
        G = eg.MultiDiGraph() if directed else eg.MultiGraph()
    graph_attr = {k: v for k, v in graph.items() if k not in ("node", "edge")}
    G.graph.update(graph_attr)

    def pop_attr(dct, category, attr, i):
        try:
            return dct.pop(attr)
        except KeyError as err:
            raise EasyGraphError(
                f"{category} #{i} has no {attr!r} attribute") from err

    nodes = graph.get("node", [])
    mapping = {}
    node_labels = set()
    for i, node in enumerate(nodes if isinstance(nodes, list) else [nodes]):
        id = pop_attr(node, "node", "id", i)
        if id in G:
            raise EasyGraphError(f"node id {id!r} is duplicated")
        if label is not None and label != "id":
            node_label = pop_attr(node, "node", label, i)
            if node_label in node_labels:
                raise EasyGraphError(
                    f"node label {node_label!r} is duplicated")
            node_labels.add(node_label)
            mapping[id] = node_label
        G.add_node(id, **node)

    edges = graph.get("edge", [])
    for i, edge in enumerate(edges if isinstance(edges, list) else [edges]):
        source = pop_attr(edge, "edge", "source", i)
        target = pop_attr(edge, "edge", "target", i)
        if source not in G:
            raise EasyGraphError(f"edge #{i} has undefined source {source!r}")
        if target not in G:
            raise EasyGraphError(f"edge #{i} has undefined target {target!r}")
        if not multigraph:
            if not G.has_edge(source, target):
                G.add_edge(source, target, **edge)
            else:
                arrow = "->" if directed else "--"
                msg = f"edge #{i} ({source!r}{arrow}{target!r}) is duplicated"
                raise EasyGraphError(msg)
        else:
            key = edge.pop("key", None)
            if key is not None and G.has_edge(source, target, key):
                arrow = "->" if directed else "--"
                msg = f"edge #{i} ({source!r}{arrow}{target!r}, {key!r})"
                msg2 = 'Hint: If multigraph add "multigraph 1" to file header.'
                raise EasyGraphError(msg + " is duplicated\n" + msg2)
            G.add_edge(source, target, key, **edge)

    if label is not None and label != "id":
        G = eg.relabel_nodes(G, mapping)
    return G
Ejemplo n.º 27
0
 def test_clustering(self):
     G = eg.Graph()
     assert list(eg.clustering(G).values()) == []
     assert eg.clustering(G) == {}
Ejemplo n.º 28
0
 def test_from_agraph_name(self):
     G = eg.Graph(name="test")
     A = eg.to_agraph(G)
     H = eg.from_agraph(A)
     assert G.name == "test"
Ejemplo n.º 29
0
 def test_undirected(self):
     self.agraph_checks(eg.Graph())
Ejemplo n.º 30
0
    def test_exceptions(self):
        pytest.raises(ValueError, literal_destringizer, "(")
        pytest.raises(ValueError, literal_destringizer, "frozenset([1, 2, 3])")
        pytest.raises(ValueError, literal_destringizer, literal_destringizer)
        pytest.raises(ValueError, literal_stringizer, frozenset([1, 2, 3]))
        pytest.raises(ValueError, literal_stringizer, literal_stringizer)
        with tempfile.TemporaryFile() as f:
            f.write(codecs.BOM_UTF8 + b"graph[]")
            f.seek(0)
            pytest.raises(eg.EasyGraphError, eg.read_gml, f)

        def assert_parse_error(gml):
            pytest.raises(eg.EasyGraphError, eg.parse_gml, gml)

        assert_parse_error(["graph [\n\n", "]"])
        assert_parse_error("")
        assert_parse_error('Creator ""')
        assert_parse_error("0")
        assert_parse_error("graph ]")
        assert_parse_error("graph [ 1 ]")
        assert_parse_error("graph [ 1.E+2 ]")
        assert_parse_error('graph [ "A" ]')
        assert_parse_error("graph [ ] graph ]")
        assert_parse_error("graph [ ] graph [ ]")
        assert_parse_error("graph [ data [1, 2, 3] ]")
        assert_parse_error("graph [ node [ ] ]")
        assert_parse_error("graph [ node [ id 0 ] ]")
        eg.parse_gml('graph [ node [ id "a" ] ]', label="id")
        assert_parse_error(
            "graph [ node [ id 0 label 0 ] node [ id 0 label 1 ] ]")
        assert_parse_error(
            "graph [ node [ id 0 label 0 ] node [ id 1 label 0 ] ]")
        assert_parse_error("graph [ node [ id 0 label 0 ] edge [ ] ]")
        assert_parse_error("graph [ node [ id 0 label 0 ] edge [ source 0 ] ]")
        eg.parse_gml(
            "graph [edge [ source 0 target 0 ] node [ id 0 label 0 ] ]")
        assert_parse_error(
            "graph [ node [ id 0 label 0 ] edge [ source 1 target 0 ] ]")
        assert_parse_error(
            "graph [ node [ id 0 label 0 ] edge [ source 0 target 1 ] ]")
        assert_parse_error(
            "graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
            "edge [ source 0 target 1 ] edge [ source 1 target 0 ] ]")
        eg.parse_gml("graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
                     "edge [ source 0 target 1 ] edge [ source 1 target 0 ] "
                     "directed 1 ]")
        eg.parse_gml("graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
                     "edge [ source 0 target 1 ] edge [ source 0 target 1 ]"
                     "multigraph 1 ]")
        eg.parse_gml(
            "graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
            "edge [ source 0 target 1 key 0 ] edge [ source 0 target 1 ]"
            "multigraph 1 ]")
        assert_parse_error(
            "graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
            "edge [ source 0 target 1 key 0 ] edge [ source 0 target 1 key 0 ]"
            "multigraph 1 ]")
        eg.parse_gml(
            "graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] "
            "edge [ source 0 target 1 key 0 ] edge [ source 1 target 0 key 0 ]"
            "directed 1 multigraph 1 ]")

        # Tests for string convertable alphanumeric id and label values
        eg.parse_gml(
            "graph [edge [ source a target a ] node [ id a label b ] ]")
        eg.parse_gml("graph [ node [ id n42 label 0 ] node [ id x43 label 1 ]"
                     "edge [ source n42 target x43 key 0 ]"
                     "edge [ source x43 target n42 key 0 ]"
                     "directed 1 multigraph 1 ]")
        assert_parse_error(
            "graph [edge [ source u'u\4200' target u'u\4200' ] " +
            "node [ id u'u\4200' label b ] ]")

        def assert_generate_error(*args, **kwargs):
            pytest.raises(eg.EasyGraphError,
                          lambda: list(eg.generate_gml(*args, **kwargs)))

        G = eg.Graph()
        G.graph[3] = 3
        assert_generate_error(G)
        G = eg.Graph()
        G.graph["3"] = 3
        assert_generate_error(G)
        G = eg.Graph()
        G.graph["data"] = frozenset([1, 2, 3])
        assert_generate_error(G, stringizer=literal_stringizer)
        G = eg.Graph()
        G.graph["data"] = []
        assert_generate_error(G)
        assert_generate_error(G, stringizer=len)