예제 #1
0
파일: graphs.py 프로젝트: idkwim/grandalf
    def __init__(self,V=None,E=None,directed=True):

        if V is None: V=[]
        if E is None: E=[]

        self.directed = directed

        self.sV = Poset(V)
        self.sE = Poset([])

        self.degenerated_edges=[]

        if len(self.sV)==1:
            v = self.sV.o[0]
            v.c = self
            for e in v.e: e.detach()
            return

        for e in E:
            x = e.v[0]
            y = e.v[1]
            if (x in V) and (y in V):
                if e.deg==0:
                    e.detach()
                    self.degenerated_edges.append(e)
                    continue
                e.attach()
                self.sE.add(e)
                if x.c is None: x.c=Poset([x])
                if y.c is None: y.c=Poset([y])
                if id(x.c)!=id(y.c):
                    x.c.update(y.c)
                    y.c=x.c
                s=x.c
            else:
                raise ValueError,'unknown Vertex (%s or %s)'%(x.data,y.data)
        #check if graph is connected:
        for v in self.V():
            if v.c is None or (v.c!=s):
                raise ValueError,'unconnected Vertex %s'%v.data
            else:
                v.c = self
예제 #2
0
파일: graphs.py 프로젝트: idkwim/grandalf
class  graph_core(object):
    def __init__(self,V=None,E=None,directed=True):

        if V is None: V=[]
        if E is None: E=[]

        self.directed = directed

        self.sV = Poset(V)
        self.sE = Poset([])

        self.degenerated_edges=[]

        if len(self.sV)==1:
            v = self.sV.o[0]
            v.c = self
            for e in v.e: e.detach()
            return

        for e in E:
            x = e.v[0]
            y = e.v[1]
            if (x in V) and (y in V):
                if e.deg==0:
                    e.detach()
                    self.degenerated_edges.append(e)
                    continue
                e.attach()
                self.sE.add(e)
                if x.c is None: x.c=Poset([x])
                if y.c is None: y.c=Poset([y])
                if id(x.c)!=id(y.c):
                    x.c.update(y.c)
                    y.c=x.c
                s=x.c
            else:
                raise ValueError,'unknown Vertex (%s or %s)'%(x.data,y.data)
        #check if graph is connected:
        for v in self.V():
            if v.c is None or (v.c!=s):
                raise ValueError,'unconnected Vertex %s'%v.data
            else:
                v.c = self

    # allow a graph_core to hold a single vertex:
    def add_single_vertex(self,v):
        if len(self.sE)==0 and len(self.sV)==0:
            self.sV.add(v)
            v.c = self

    # add edge e. At least one of its vertex must belong to the graph,
    # the other being added automatically. 
    def add_edge(self,e):
        if e not in self.sE:
            x = e.v[0]
            y = e.v[1]
            if not ((x in self.sV) or (y in self.sV)):
                raise ValueError,'unconnected edge'
            self.sV.add(x)
            self.sV.add(y)
            e.attach()
            self.sE.add(e)
            x.c = self
            y.c = self

    # remove Edge :
    # this procedure checks that the resulting graph is connex.
    def remove_edge(self,e):
        if e.deg==0: return
        e.detach()
        # check if still connected (path is not oriented here):
        if not self.path(e.v[0],e.v[1]):
            # return to inital state by reconnecting everything:
            e.attach()
            # exit with exception!
            raise ValueError,e
        else:
            self.sE.remove(e)

    # remove Vertex:
    # this procedure checks that the resulting graph is connex.
    def remove_vertex(self,x):
        V = x.N() #get all neighbor vertices to check paths
        E = x.detach() #remove the edges from x and neighbors list
        # now we need to check if all neighbors are still connected,
        # and it is sufficient to check if one of them is connected to
        # all others:
        v0 = V.pop(0)
        for v in V:
            if not self.path(v0,v):
                # repair everything and raise exception if not connected:
                for e in E: e.attach()
                raise ValueError,x
        # remove edges and vertex from internal sets:
        for e in E: self.sE.remove(e)
        self.sV.remove(x)
        x.c = None

    # generates an iterator over vertices, with optional filter
    def V(self,cond=None):
        V = self.sV
        if cond is None: cond=(lambda x:True)
        for v in V:
            if cond(v):
                yield v

    # generates an iterator over edges, with optional filter
    def E(self,cond=None):
        E = self.sE
        if cond is None: cond=(lambda x:True)
        for e in E:
            if cond(e):
                yield e

    # vertex/edge properties :
    #-------------------------
    # returns number of vertices
    def order(self):
        return len(self.sV)

    # returns number of edges
    def norm(self):
        return len(self.sE)

    # returns the minimum degree
    def deg_min(self):
        return min(map(vertex_core.deg,self.sV))

    # returns the maximum degree
    def deg_max(self):
        return max(map(vertex_core.deg,self.sV))

    # returns the average degree d(G)
    def deg_avg(self):
        return sum(map(vertex_core.deg,self.sV))/float(self.order())

    # returns the epsilon value (number of edges of G per vertex)
    def eps(self):
        return float(self.norm())/self.order()

    # shortest path between vertices x and y by breadth-first descent
    def path(self,x,y,f_io=0,hook=None):
        assert x in self.sV
        assert y in self.sV
        if x==y: return []
        if f_io!=0: assert self.directed==True
        # path:
        p = None
        if hook is None: hook = lambda x:False
        # apply hook:
        hook(x)
        # visisted:
        v = {x:None}
        # queue:
        q = [x]
        while (not p) and len(q)>0:
            c = q.pop(0)
            for n in c.N(f_io):
                if not v.has_key(n):
                    hook(n)
                    v[n] = c
                    if n==y: p = [n]
                    q.append(n)
                if p: break
        #now we fill the path p backward from y to x:
        while p and p[0]!=x:
            p.insert(0,v[p[0]])
        return p

    # shortest weighted-edges paths between x and all other vertices 
    # by dijkstra's algorithm with heap used as priority queue.
    def dijkstra(self,x,f_io=0,hook=None):
        from collections import defaultdict
        from heapq import heappop, heappush
        if x not in self.sV: return None
        if f_io!=0: assert self.directed==True
        # take a shallow copy of the set of vertices containing those for which
        # the shortest path to x needs to be computed:
        S = self.sV.copy()
        # initiate with path to itself...
        v = x
        # D is the returned vector of distances:
        D = defaultdict(lambda :None)
        D[v] = 0.0
        L = [(D[v],v)]
        while len(S)>0:
            #pdb.set_trace()
            l,u = heappop(L)
            S.remove(u)
            for e in u.e:
                v = e.v[0] if (u is e.v[1]) else e.v[1]
                Dv = l+e.w
                if D[v]!=None:
                    # check if heap/D needs updating:
                    # ignore if a shorter path was found already...
                    if Dv<D[v]:
                        for i,t in enumerate(L):
                            if t[1] is v:
                                L.pop(i)
                                break
                        D[v]=Dv
                        heappush(L,(Dv,v))
                else:
                    D[v]=Dv
                    heappush(L,(Dv,v))
        return D

    # returns the set of strongly connected components 
    # ("scs") by using Tarjan algorithm.
    # These are maximal sets of vertices such that there is a path from each 
    # vertex to every other vertex. 
    # The algorithm performs a DFS from the provided list of root vertices.
    # A cycle is of course a strongly connected component, 
    # but a strongly connected component can include several cycles.
    # The Feedback Acyclic Set of edge to be removed/reversed is provided by 
    # marking the edges with a "feedback" flag.
    # Complexity is O(V+E).
    def get_scs_with_feedback(self,roots):
        from  sys import getrecursionlimit,setrecursionlimit
        limit=getrecursionlimit()
        N=self.norm()+10
        if N>limit:
            setrecursionlimit(N)
        def _visit(v,L):
            v.ind = v.ncur
            v.lowlink = v.ncur
            Vertex.ncur += 1
            self.tstack.append(v)
            v.mark = True
            for e in v.e_out():
                w = e.v[1]
                if w.ind==0:
                    _visit(w,L)
                    v.lowlink = min(v.lowlink,w.lowlink)
                elif w.mark:
                    e.feedback = True
                if w in self.tstack:
                    v.lowlink = min(v.lowlink,w.ind)
            if v.lowlink==v.ind:
                l=[self.tstack.pop()]
                while l[0]!=v:
                    l.insert(0,self.tstack.pop())
                #print "unstacked %s"%('-'.join([x.data[1:13] for x in l]))
                L.append(l)
            v.mark=False
        self.tstack=[]
        scs = []
        Vertex.ncur=1
        for v in self.sV: v.ind=0
        # start exploring tree from roots:
        for v in roots:
            if v.ind==0: _visit(v,scs)
        # now possibly unvisited vertices:
        for v in self.sV:
            if v.ind==0: _visit(v,scs)
        # clean up Tarjan-specific data:
        for v in self.sV:
            del v.ind
            del v.lowlink
            del v.mark
        del Vertex.ncur
        del self.tstack
        setrecursionlimit(limit)
        return scs

    # returns neighbours of a vertex v:
    # f_io=-1 : parent nodes
    # f_io=+1 : child nodes
    # f_io= 0 : all (default)
    def N(self,v,f_io=0):
        return v.N(f_io)

    # general graph properties:
    # -------------------------

    # returns True iff 
    #  - o is a subgraph of self, or
    #  - o is a vertex in self, or
    #  - o is an edge in self
    def __contains__(self,o):
        try:
            return o.sV.issubset(self.sV) and o.sE.issubset(self.sE)
        except AttributeError:
            return ((o in self.sV) or (o in self.sE))

    # merge graph_core G into self
    def union_update(self,G):
        for v in G.sV: v.c = self
        self.sV.update(G.sV)
        self.sE.update(G.sE)

    # derivated graphs:
    # -----------------

    # returns subgraph spanned by vertices V
    def spans(self,V):
        raise NotImplementedError

    # returns join of G (if disjoint)
    def __mul__(self,G):
        raise NotImplementedError

    # returns complement of a graph G
    def complement(self,G):
        raise NotImplementedError

    # contraction G\e
    def contract(self,e):
        raise NotImplementedError