将图形转换为outdegree 1(除了额外的零权重边缘)

时间:2013-12-05 17:37:54

标签: python networkx igraph

我正在python中从http://www.dis.uniroma1.it/challenge9/data/rome/rome99.gr读取http://www.dis.uniroma1.it/challenge9/download.shtml等图形。例如,使用此代码。

#!/usr/bin/python
from igraph import *
fname = "rome99.gr"
g = Graph.Read_DIMACS(fname, directed=True )

(我需要将“p sp 3353 8870”“更改为”p max 3353 8870“以使用igraph来实现此功能。)

我想将图形转换为所有节点都具有outdegree 1的图形(除了允许添加的额外零权重边缘)但仍保留所有最短路径。这是原始图中两个节点之间的路径应该是新图中的最短路径,当且仅当它是转换图中的最短路径时。我会在一个例子之后再详细解释一下。

这样做的一种方法我想的是用v.outdegree(mode = OUT)节点用一个小的线性子图替换每个节点v。在子图中,节点通过零权重边缘依次连接。然后,我们将子图中的节点连接到我们创建的其他小子图中的第一个节点。

我不介意使用igraph或networkx执行此任务,但我仍然坚持使用如何执行此操作的语法。

例如,如果我们从图G开始:

enter image description here

我想将其转换为图H:

enter image description here

由于第二个图比第一个图有更多的节点,我们需要通过它具有与第一个图相同的最短路径来定义我们的意思。我只考虑用标记为X1的节点的简单字母标记的节点之间的路径。换句话说,在此示例中,路径不能在A2或B2中开始或结束。我们还在考虑路径时合并节点的所有版本。因此,H中的路径A1-> A2-> D被认为与G中的A-> D相同。

这是我有多远。首先,我将零权重边添加到新图

h = Graph(g.ecount(), directed=True)
#Connect the nodes with zero weight edges
gtoh = [0]*g.vcount()
i=0
for v in g.vs:
    gtoh[v.index] = i 
    if (v.degree(mode=OUT) > 1):
        for j in xrange(v.degree(mode=OUT)-1):
            h.add_edge(i,i+1, weight = 0) 
            i = i+1
    i  = i + 1

然后我添加主边

#Now connect the nodes to the relevant "head" nodes.
for v in g.vs:
    h_v_index = gtoh[v.index]
    i = 0
    for neighbour in g.neighbors(v, mode=OUT):
        h.add_edge(gtoh[v.index]+i,gtoh[neighbour], weight = g.es[g.get_eid(v.index, neighbour)]["weight"])
        i = i +1

有更好/更好的方法吗?我觉得必须有。

1 个答案:

答案 0 :(得分:2)

以下代码应该适用于igraph和Python 2.x;基本上你做了你提出的建议:它为图中的每个单个节点创建一个“线性子图”,并将一个出口边正好连接到与旧节点对应的线性子图中的每个节点。

#!/usr/bin/env python

from igraph import Graph
from itertools import izip

def pairs(l):
    """Given a list l, returns an iterable that yields pairs of the form
    (l[i], l[i+1]) for all possible consecutive pairs of items in l"""
    return izip(l, l[1:])

def convert(g):
    # Get the old vertex names from g
    if "name" in g.vertex_attributes():
        old_names = map(str, g.vs["name"])
    else:
        old_names = map(str, xrange(g.vcount))

    # Get the outdegree vector of the old graph
    outdegs = g.outdegree()

    # Create a mapping from old node IDs to the ID of the first node in
    # the linear subgraph corresponding to the old node in the new graph
    new_node_id = 0
    old_to_new = []
    new_names = []
    for old_node_id in xrange(g.vcount()):
        old_to_new.append(new_node_id)
        new_node_id += outdegs[old_node_id]
        old_name = old_names[old_node_id]
        if outdegs[old_node_id] <= 1:
            new_names.append(old_name)
        else:
            for i in xrange(1, outdegs[old_node_id]+1):
                new_names.append(old_name + "." + str(i))

    # Add a sentinel element to old_to_new just to make our job easier
    old_to_new.append(new_node_id)

    # Create the edge list of the new graph and the weights of the new
    # edges
    new_edgelist = []
    new_weights = []

    # 1) Create the linear subgraphs
    for new_node_id, next_new_node_id in pairs(old_to_new):
        for source, target in pairs(range(new_node_id, next_new_node_id)):
            new_edgelist.append((source, target))
            new_weights.append(0)

    # 2) Create the new edges based on the old ones
    for old_node_id in xrange(g.vcount()):
        new_node_id = old_to_new[old_node_id]
        for edge_id in g.incident(old_node_id, mode="out"):
            neighbor = g.es[edge_id].target
            new_edgelist.append((new_node_id, old_to_new[neighbor]))
            new_node_id += 1
            print g.es[edge_id].source, g.es[edge_id].target, g.es[edge_id]["weight"]
            new_weights.append(g.es[edge_id]["weight"])

    # Return the graph
    vertex_attrs = {"name": new_names}
    edge_attrs = {"weight": new_weights}
    return Graph(new_edgelist, directed=True, vertex_attrs=vertex_attrs, \
            edge_attrs=edge_attrs)