From ea312c25111c084124ce740d1404f67e6e1a3c67 Mon Sep 17 00:00:00 2001 From: Axel Tillequin Date: Tue, 17 Mar 2020 10:03:11 +0100 Subject: [PATCH] blackify sources --- grandalf/__init__.py | 2 +- grandalf/graphs.py | 496 ++++++++++++++----------- grandalf/layouts.py | 736 ++++++++++++++++++++----------------- grandalf/routing.py | 55 +-- grandalf/utils/dot.py | 378 ++++++++++--------- grandalf/utils/geometry.py | 225 ++++++------ grandalf/utils/linalg.py | 337 +++++++++-------- grandalf/utils/nx.py | 20 +- grandalf/utils/poset.py | 85 ++--- 9 files changed, 1261 insertions(+), 1073 deletions(-) diff --git a/grandalf/__init__.py b/grandalf/__init__.py index 59b2dc4..65c98ba 100644 --- a/grandalf/__init__.py +++ b/grandalf/__init__.py @@ -1 +1 @@ -__all__ = ['graphs','layouts','routing','utils'] +__all__ = ["graphs", "layouts", "routing", "utils"] diff --git a/grandalf/graphs.py b/grandalf/graphs.py index 3869adc..893b374 100644 --- a/grandalf/graphs.py +++ b/grandalf/graphs.py @@ -16,9 +16,10 @@ from .utils import Poset -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ -class vertex_core(object): + +class vertex_core(object): """ The Vertex essentials attributes and methods. Attributes: @@ -43,50 +44,60 @@ def __init__(self): # will hold list of edges for this vertex (adjacency list) self.e = [] - def deg(self): return len(self.e) + def deg(self): + return len(self.e) def e_in(self): - return list(filter((lambda e:e.v[1]==self), self.e )) + return list(filter((lambda e: e.v[1] == self), self.e)) def e_out(self): - return list(filter( (lambda e:e.v[0]==self), self.e )) + return list(filter((lambda e: e.v[0] == self), self.e)) - def e_dir(self,dir): - if dir>0: return self.e_out() - if dir<0: return self.e_in() + def e_dir(self, dir): + if dir > 0: + return self.e_out() + if dir < 0: + return self.e_in() return self.e - def N(self,f_io=0): + def N(self, f_io=0): N = [] - if f_io<=0: N += [ e.v[0] for e in self.e_in() ] - if f_io>=0: N += [ e.v[1] for e in self.e_out() ] + if f_io <= 0: + N += [e.v[0] for e in self.e_in()] + if f_io >= 0: + N += [e.v[1] for e in self.e_out()] return N - def e_to(self,y): + def e_to(self, y): for e in self.e_out(): - if e.v[1]==y: return e + if e.v[1] == y: + return e return None - def e_from(self,x): + def e_from(self, x): for e in self.e_in(): - if e.v[0]==x: return e + if e.v[0] == x: + return e return None - def e_with(self,v): + def e_with(self, v): for e in self.e: - if v in e.v: return e + if v in e.v: + return e return None def detach(self): E = self.e[:] - for e in E: e.detach() - assert self.deg()==0 + for e in E: + e.detach() + assert self.deg() == 0 return E -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + -class edge_core(object): +class edge_core(object): """The Edge essentials attributes. Attributes: @@ -94,14 +105,15 @@ class edge_core(object): deg (int): degree of the edge (number of unique vertices). """ - def __init__(self,x,y): - self.deg = 0 if x==y else 1 - self.v = (x,y) + def __init__(self, x, y): + self.deg = 0 if x == y else 1 + self.v = (x, y) -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ -class Vertex(vertex_core): + +class Vertex(vertex_core): """Vertex class enhancing a vertex_core with graph-related features. Attributes: @@ -111,7 +123,7 @@ class Vertex(vertex_core): data (object) : an object associated with the vertex. """ - def __init__(self,data=None): + def __init__(self, data=None): super().__init__() # by default, a new vertex belongs to its own component # but when the vertex is added to a graph, c points to the @@ -122,36 +134,39 @@ def __init__(self,data=None): @property def index(self): - if self.__index: return self.__index - elif isinstance(self.c,graph_core): + if self.__index: + return self.__index + elif isinstance(self.c, graph_core): self.__index = self.c.sV.index(self) return self.__index else: return None - def __lt__(self,v): + def __lt__(self, v): return 0 - def __gt__(self,v): + def __gt__(self, v): return 0 - def __le__(self,v): + def __le__(self, v): return 0 - def __ge__(self,v): + def __ge__(self, v): return 0 def __getstate__(self): - return (self.index,self.data) + return (self.index, self.data) - def __setstate__(self,state): - self.__index,self.data = state + def __setstate__(self, state): + self.__index, self.data = state self.c = None self.e = [] -#------------------------------------------------------------------------------ -class Edge(edge_core): +# ------------------------------------------------------------------------------ + + +class Edge(edge_core): """Edge class enhancing edge_core with attributes and methods related to the graph. Attributes: @@ -167,8 +182,8 @@ class Edge(edge_core): detach(): remove this edge from its vertices edge lists. """ - def __init__(self,x,y,w=1,data=None,connect=False): - super().__init__(x,y) + def __init__(self, x, y, w=1, data=None, connect=False): + super().__init__(x, y) # w is an optional weight associated with the edge. self.w = w self.data = data @@ -178,44 +193,49 @@ def __init__(self,x,y,w=1,data=None,connect=False): c.add_edge(self) def attach(self): - if not self in self.v[0].e : self.v[0].e.append(self) - if not self in self.v[1].e : self.v[1].e.append(self) + if not self in self.v[0].e: + self.v[0].e.append(self) + if not self in self.v[1].e: + self.v[1].e.append(self) def detach(self): - if self.deg==1: + if self.deg == 1: assert self in self.v[0].e assert self in self.v[1].e self.v[0].e.remove(self) self.v[1].e.remove(self) else: - if self in self.v[0].e: self.v[0].e.remove(self) + if self in self.v[0].e: + self.v[0].e.remove(self) assert self not in self.v[0].e return [self] - def __lt__(self,v): + def __lt__(self, v): return 0 - def __gt__(self,v): + def __gt__(self, v): return 0 - def __le__(self,v): + def __le__(self, v): return 0 - def __ge__(self,v): + def __ge__(self, v): return 0 def __getstate__(self): - xi,yi = (self.v[0].index,self.v[1].index) - return (xi,yi,self.w,self.data,self.feedback) + xi, yi = (self.v[0].index, self.v[1].index) + return (xi, yi, self.w, self.data, self.feedback) - def __setstate__(self,state): - xi,yi,self.w,self.data,self.feedback = state - self._v = [xi,yi] - self.deg = 0 if xi==yi else 1 + def __setstate__(self, state): + xi, yi, self.w, self.data, self.feedback = state + self._v = [xi, yi] + self.deg = 0 if xi == yi else 1 -#------------------------------------------------------------------------------ -class graph_core(object): +# ------------------------------------------------------------------------------ + + +class graph_core(object): """A connected graph of Vertex/Edge objects. A graph_core is a *component* of a Graph that contains a connected set of Vertex and Edges. @@ -262,80 +282,88 @@ class graph_core(object): N(v): returns neighbours of a vertex v. """ - def __init__(self,V=None,E=None,directed=True): - if V is None: V=[] - if E is None: E=[] + 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=set() + self.degenerated_edges = set() - if len(self.sV)==1: + if len(self.sV) == 1: v = self.sV[0] v.c = self - for e in v.e: e.detach() + for e in v.e: + e.detach() return for e in E: x = self.sV.get(e.v[0]) y = self.sV.get(e.v[1]) - if (x is None or y is None): - raise ValueError('unknown Vertex (%s or %s)'%e.v) - e.v = (x,y) - if e.deg==0: + if x is None or y is None: + raise ValueError("unknown Vertex (%s or %s)" % e.v) + e.v = (x, y) + if e.deg == 0: self.degenerated_edges.add(e) e = self.sE.add(e) e.attach() - 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,y = (x,y) if len(x.c)>len(y.c) else (y,x) + 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, y = (x, y) if len(x.c) > len(y.c) else (y, x) x.c.update(y.c) - for v in y.c: v.c=x.c - s=x.c - #check if graph is connected: + for v in y.c: + v.c = x.c + s = x.c + # 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) + if v.c is None or (v.c != s): + raise ValueError("unconnected Vertex %s" % v.data) else: v.c = self def roots(self): - return list(filter(lambda v:len(v.e_in())==0, self.sV)) + return list(filter(lambda v: len(v.e_in()) == 0, self.sV)) def leaves(self): - return list(filter(lambda v:len(v.e_out())==0, self.sV)) + return list(filter(lambda v: len(v.e_out()) == 0, self.sV)) - def add_single_vertex(self,v): - if len(self.sE)==0 and len(self.sV)==0: + def add_single_vertex(self, v): + if len(self.sE) == 0 and len(self.sV) == 0: v = self.sV.add(v) v.c = self return v return None - def add_edge(self,e): + def add_edge(self, e): if e in self.sE: return self.sE.get(e) x = e.v[0] y = e.v[1] if not ((x in self.sV) or (y in self.sV)): - raise ValueError('unconnected edge') + raise ValueError("unconnected edge") x = self.sV.add(x) y = self.sV.add(y) - e.v = (x,y) + e.v = (x, y) e.attach() e = self.sE.add(e) x.c = self y.c = self - if e.deg==0: self.degenerated_edges.add(e) + if e.deg == 0: + self.degenerated_edges.add(e) return e - def remove_edge(self,e): - if (not e in self.sE): return + def remove_edge(self, e): + if not e in self.sE: + return e.detach() # check if still connected (path is not oriented here): - if e.deg==1 and not self.path(e.v[0],e.v[1]): + if e.deg == 1 and not self.path(e.v[0], e.v[1]): # return to inital state by reconnecting everything: e.attach() # exit with exception! @@ -346,48 +374,55 @@ def remove_edge(self,e): self.degenerated_edges.remove(e) return e - def remove_vertex(self,x): - if x not in self.sV: return - V = x.N() #get all neighbor vertices to check paths - E = x.detach() #remove the edges from x and neighbors list + def remove_vertex(self, x): + if x not in self.sV: + return + 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): + if not self.path(v0, v): # repair everything and raise exception if not connected: - for e in E: e.attach() + for e in E: + e.attach() raise ValueError(x) # remove edges and vertex from internal sets: - for e in E: self.sE.remove(e) + for e in E: + self.sE.remove(e) x = self.sV.remove(x) x.c = None return x - def V(self,cond=None): + def V(self, cond=None): V = self.sV - if cond is None: cond=(lambda x:True) + if cond is None: + cond = lambda x: True for v in V: if cond(v): yield v - def E(self,cond=None): + def E(self, cond=None): E = self.sE - if cond is None: cond=(lambda x:True) + if cond is None: + cond = lambda x: True for e in E: if cond(e): yield e - def M(self,cond=None): + def M(self, cond=None): from array import array + mat = [] for v in self.V(cond): - vec = array('b',[0]*self.order()) + vec = array("b", [0] * self.order()) mat.append(vec) for e in v.e_in(): v0 = e.v[0] - if v0.index==v.index: continue + if v0.index == v.index: + continue vec[v0.index] = -e.w for e in v.e_out(): v1 = e.v[1] @@ -407,79 +442,89 @@ def deg_max(self): return max([v.deg() for v in self.sV]) def deg_avg(self): - return sum([v.deg() for v in self.sV])/float(self.order()) + return sum([v.deg() for v in self.sV]) / float(self.order()) def eps(self): - return float(self.norm())/self.order() + return float(self.norm()) / self.order() - def path(self,x,y,f_io=0,hook=None): + def path(self, x, y, f_io=0, hook=None): assert x in self.sV assert y in self.sV x = self.sV.get(x) y = self.sV.get(y) - if x==y: return [] - if f_io!=0: assert self.directed==True + if x == y: + return [] + if f_io != 0: + assert self.directed == True # path: p = None - if hook is None: hook = lambda x:False + if hook is None: + hook = lambda x: False # apply hook: hook(x) # visisted: - v = {x:None} + v = {x: None} # queue: q = [x] - while (not p) and len(q)>0: + while (not p) and len(q) > 0: c = q.pop(0) for n in c.N(f_io): if not n in v: hook(n) v[n] = c - if n==y: p = [n] + 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]]) + 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 - def dijkstra(self,x,f_io=0,hook=None): + 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 + + if x not in self.sV: + return None + if f_io != 0: + assert self.directed == True # initiate with path to itself... v = self.sV.get(x) # D is the returned vector of distances: - D = defaultdict(lambda :None) + D = defaultdict(lambda: None) D[v] = 0.0 - L = [(D[v],v)] - while len(L)>0: - l,u = heappop(L) + L = [(D[v], v)] + while len(L) > 0: + l, u = heappop(L) for e in u.e_dir(f_io): v = e.v[0] if (u is e.v[1]) else e.v[1] - Dv = l+e.w - if D[v]!=None: + Dv = l + e.w + if D[v] != None: # check if heap/D needs updating: # ignore if a shorter path was found already... - if Dvlimit: + def get_scs_with_feedback(self, roots=None): + from sys import getrecursionlimit, setrecursionlimit + + limit = getrecursionlimit() + N = self.norm() + 10 + if N > limit: setrecursionlimit(N) - def _visit(v,L): + + def _visit(v, L): v.ind = v.ncur v.lowlink = v.ncur Vertex.ncur += 1 @@ -487,32 +532,37 @@ def _visit(v,L): 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) + 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])) + 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 - if roots is None: roots=self.roots() - self.tstack=[] + v.mark = False + + if roots is None: + roots = self.roots() + self.tstack = [] scs = [] - Vertex.ncur=1 - for v in self.sV: v.ind=0 + Vertex.ncur = 1 + for v in self.sV: + v.ind = 0 # start exploring tree from roots: for v in roots: v = self.sV.get(v) - if v.ind==0: _visit(v,scs) + if v.ind == 0: + _visit(v, scs) # now possibly unvisited vertices: for v in self.sV: - if v.ind==0: _visit(v,scs) + if v.ind == 0: + _visit(v, scs) # clean up Tarjan-specific data: for v in self.sV: del v.ind @@ -526,15 +576,17 @@ def _visit(v,L): def partition(self): V = self.sV.copy() R = self.roots() - for r in R: V.remove(r) + for r in R: + V.remove(r) parts = [] - while len(R)>0: + while len(R) > 0: v = R.pop(0) p = Poset([v]) l = v.N(+1) - while len(l)>0: + while len(l) > 0: x = l.pop(0) - if x in p: continue + if x in p: + continue if all([(y in p) for y in x.N(-1)]): p.add(x) if x in R: @@ -549,7 +601,7 @@ def partition(self): parts.append(list(p)) return parts - def N(self,v,f_io=0): + def N(self, v, f_io=0): return v.N(f_io) # general graph properties: @@ -559,15 +611,16 @@ def N(self,v,f_io=0): # - o is a subgraph of self, or # - o is a vertex in self, or # - o is an edge in self - def __contains__(self,o): + 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)) + 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 + def union_update(self, G): + for v in G.sV: + v.c = self self.sV.update(G.sV) self.sE.update(G.sE) @@ -575,36 +628,38 @@ def union_update(self,G): # ----------------- # returns subgraph spanned by vertices V - def spans(self,V): + def spans(self, V): raise NotImplementedError # returns join of G (if disjoint) - def __mul__(self,G): + def __mul__(self, G): raise NotImplementedError # returns complement of a graph G - def complement(self,G): + def complement(self, G): raise NotImplementedError # contraction G\e - def contract(self,e): + def contract(self, e): raise NotImplementedError def __getstate__(self): V = [v for v in self.sV] E = [e for e in self.sE] - return (V,E,self.directed) + return (V, E, self.directed) - def __setstate__(self,state): - V,E,directed=state + def __setstate__(self, state): + V, E, directed = state for e in E: e.v = [V[x] for x in e._v] del e._v - graph_core.__init__(self,V,E,directed) + graph_core.__init__(self, V, E, directed) + -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ -class Graph(object): + +class Graph(object): """Disjoint-set Graph. The graph is stored in disjoint-sets holding each connex component in self.C as a list of graph_core objects. @@ -631,15 +686,19 @@ class Graph(object): connected(): returns True if the graph is connected (i.e. it has only one component). components(): returns self.C """ + component_class = graph_core - def __init__(self,V=None,E=None,directed=True): - if V is None: V=[] - if E is None: E=[] + def __init__(self, V=None, E=None, directed=True): + if V is None: + V = [] + if E is None: + E = [] self.directed = directed # tag connex set of vertices: # at first, every vertex is its own component - for v in V: v.c = Poset([v]) + for v in V: + v.c = Poset([v]) CV = [v.c for v in V] # then pass through edges and union associated vertices such that # CV finally holds only connected sets: @@ -651,30 +710,33 @@ def __init__(self,V=None,E=None,directed=True): assert x.c in CV assert y.c in CV e.attach() - if x.c!=y.c: - #merge y.c into x.c : + if x.c != y.c: + # merge y.c into x.c : x.c.update(y.c) - #update set list (MUST BE DONE BEFORE UPDATING REFS!) + # update set list (MUST BE DONE BEFORE UPDATING REFS!) CV.remove(y.c) - #update reference: - for z in y.c: z.c = x.c + # update reference: + for z in y.c: + z.c = x.c # now create edge sets from connected vertex sets and # make the graph_core connected graphs for this component : self.C = [] for c in CV: s = set() - for v in c: s.update(v.e) - self.C.append(self.component_class(c,s,directed)) + for v in c: + s.update(v.e) + self.C.append(self.component_class(c, s, directed)) - def add_vertex(self,v): + def add_vertex(self, v): for c in self.C: - if (v in c.sV): return c.sV.get(v) + if v in c.sV: + return c.sV.get(v) g = self.component_class(directed=self.directed) v = g.add_single_vertex(v) self.C.append(g) return v - def add_edge(self,e): + def add_edge(self, e): # take vertices: x = e.v[0] y = e.v[1] @@ -686,7 +748,7 @@ def add_edge(self,e): # add edge: e = cy.add_edge(e) # connect (union) the graphs: - if cx!=cy: + if cx != cy: cx.union_update(cy) self.C.remove(cy) return e @@ -697,18 +759,21 @@ def get_vertices_count(self): def V(self): for c in self.C: V = c.sV - for v in V: yield v + for v in V: + yield v def E(self): for c in self.C: E = c.sE - for e in E: yield e + for e in E: + yield e - def remove_edge(self,e): + def remove_edge(self, e): # get the graph_core: c = e.v[0].c - assert c==e.v[1].c - if not c in self.C: return None + assert c == e.v[1].c + if not c in self.C: + return None # remove edge in graph_core and replace it with two new cores # if removing edge disconnects the graph_core: try: @@ -717,24 +782,27 @@ def remove_edge(self,e): e = c.sE.remove(e) e.detach() self.C.remove(c) - tmpg = type(self)(c.sV,c.sE,self.directed) - assert len(tmpg.C)==2 + tmpg = type(self)(c.sV, c.sE, self.directed) + assert len(tmpg.C) == 2 self.C.extend(tmpg.C) return e - def remove_vertex(self,x): + def remove_vertex(self, x): # get the graph_core: c = x.c - if not c in self.C: return None + if not c in self.C: + return None try: x = c.remove_vertex(x) - if c.order()==0: self.C.remove(c) + if c.order() == 0: + self.C.remove(c) except ValueError: - for e in x.detach(): c.sE.remove(e) + for e in x.detach(): + c.sE.remove(e) x = c.sV.remove(x) self.C.remove(c) - tmpg = type(self)(c.sV,c.sE,self.directed) - assert len(tmpg.C)==2 + tmpg = type(self)(c.sV, c.sE, self.directed) + assert len(tmpg.C) == 2 self.C.extend(tmpg.C) return x @@ -752,28 +820,32 @@ def deg_max(self): def deg_avg(self): t = 0.0 - for c in self.C: t += sum([v.deg() for v in c.sV]) - return t/float(self.order()) + for c in self.C: + t += sum([v.deg() for v in c.sV]) + return t / float(self.order()) def eps(self): - return float(self.norm())/self.order() + return float(self.norm()) / self.order() - def path(self,x,y,f_io=0,hook=None): - if x==y: return [] - if x.c!=y.c: return None + def path(self, x, y, f_io=0, hook=None): + if x == y: + return [] + if x.c != y.c: + return None # path: - return x.c.path(x,y,f_io,hook) + return x.c.path(x, y, f_io, hook) - def N(self,v,f_io=0): + def N(self, v, f_io=0): return v.N(f_io) - def __contains__(self,G): + def __contains__(self, G): r = False - for c in self.C: r |= (G in c) + for c in self.C: + r |= G in c return r def connected(self): - return len(self.C)==1 + return len(self.C) == 1 # returns connectivity (kappa) def connectivity(self): @@ -791,17 +863,17 @@ def components(self): # ----------------- # returns subgraph spanned by vertices V - def spans(self,V): + def spans(self, V): raise NotImplementedError # returns join of G (if disjoint) - def __mul__(self,G): + def __mul__(self, G): raise NotImplementedError # returns complement of a graph G - def complement(self,G): + def complement(self, G): raise NotImplementedError # contraction G\e - def contract(self,e): + def contract(self, e): raise NotImplementedError diff --git a/grandalf/layouts.py b/grandalf/layouts.py index e2461c8..12d417c 100644 --- a/grandalf/layouts.py +++ b/grandalf/layouts.py @@ -19,13 +19,14 @@ import importlib from bisect import bisect -from sys import getrecursionlimit,setrecursionlimit +from sys import getrecursionlimit, setrecursionlimit from grandalf.utils import * -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ -class VertexViewer(object): + +class VertexViewer(object): """ The VertexViewer class is used as the default provider of Vertex dimensions (w,h) and position (xy). @@ -33,18 +34,21 @@ class VertexViewer(object): with a ui widgets library, allowing to get dimensions and set position directly on the widget. """ - def __init__(self,w=2,h=2,data=None): + + def __init__(self, w=2, h=2, data=None): self.w = w self.h = h self.data = data self.xy = None def __str__(self, *args, **kwargs): - return 'VertexViewer (xy: %s) w: %s h: %s' % (self.xy, self.w, self.h) + return "VertexViewer (xy: %s) w: %s h: %s" % (self.xy, self.w, self.h) + -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ -class _sugiyama_vertex_attr(object): + +class _sugiyama_vertex_attr(object): """ The sugiyama layout adds new attributes to vertices. These attributes are stored in an internal _sugimyama_vertex_attr object. @@ -57,34 +61,38 @@ class _sugiyama_vertex_attr(object): x (list(float)): the list of computed horizontal coordinates of the vertex bar (float): the current *barycenter* of the vertex """ - def __init__(self,r=None,d=0): - self.rank=r - self.dummy=d - self.pos=None - self.x=0 - self.bar=None + + def __init__(self, r=None, d=0): + self.rank = r + self.dummy = d + self.pos = None + self.x = 0 + self.bar = None def __str__(self): - s="(%3d,%3d) x=%s"%(self.rank,self.pos,str(self.x)) - if self.dummy: s="[d] %s"%s + s = "(%3d,%3d) x=%s" % (self.rank, self.pos, str(self.x)) + if self.dummy: + s = "[d] %s" % s return s - #def __eq__(self,x): + # def __eq__(self,x): # return self.bar == x.bar - #def __ne__(self,x): + # def __ne__(self,x): # return self.bar != x.bar - #def __lt__(self,x): + # def __lt__(self,x): # return self.bar < x.bar - #def __le__(self,x): + # def __le__(self,x): # return self.bar <= x.bar - #def __gt__(self,x): + # def __gt__(self,x): # return self.bar > x.bar - #def __ge__(self,x): + # def __ge__(self,x): # return self.bar >= x.bar -#------------------------------------------------------------------------------ -class DummyVertex(_sugiyama_vertex_attr): +# ------------------------------------------------------------------------------ + + +class DummyVertex(_sugiyama_vertex_attr): """ The DummyVertex class is used by the sugiyama layout to represent *long* edges, i.e. edges that span over several ranks. @@ -100,31 +108,35 @@ class DummyVertex(_sugiyama_vertex_attr): vertices (possibly dummy) in the given direction. inner(dir): return True if a neighbor in the given direction is *dummy*. """ - def __init__(self,r=None,viewclass=VertexViewer): + + def __init__(self, r=None, viewclass=VertexViewer): self.view = viewclass() self.ctrl = None - super().__init__(r,d=1) + super().__init__(r, d=1) - def N(self,dir): - assert dir==+1 or dir==-1 - v = self.ctrl.get(self.rank+dir,None) + def N(self, dir): + assert dir == +1 or dir == -1 + v = self.ctrl.get(self.rank + dir, None) return [v] if v is not None else [] - def inner(self,dir): - assert dir==+1 or dir==-1 + def inner(self, dir): + assert dir == +1 or dir == -1 try: - return any([x.dummy==1 for x in self.N(dir)]) + return any([x.dummy == 1 for x in self.N(dir)]) except KeyError: return False except AttributeError: return False def __str__(self): - s="(%3d,%3d) x=%s"%(self.rank,self.pos,str(self.x)) - if self.dummy: s="[d] %s"%s + s = "(%3d,%3d) x=%s" % (self.rank, self.pos, str(self.x)) + if self.dummy: + s = "[d] %s" % s return s -#------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ + class Layer(list): """ @@ -146,90 +158,96 @@ class Layer(list): prevlayer(): returns *previous* layer in the current layout's direction parameter. order(): compute *optimal* ordering of vertices within the layer. """ - __r = None + + __r = None layout = None - upper = None - lower = None - __x = 1. + upper = None + lower = None + __x = 1.0 ccount = None - def __eq__(self,other): + def __eq__(self, other): return super().__eq__(other) def __str__(self): - s = '1: self.__x = 1./(len(self)-1) - for i,v in enumerate(self): - assert layout.grx[v].rank==r + self.__r = r + if len(self) > 1: + self.__x = 1.0 / (len(self) - 1) + for i, v in enumerate(self): + assert layout.grx[v].rank == r layout.grx[v].pos = i - layout.grx[v].bar = i*self.__x - if r>0: - self.upper = layout.layers[r-1] - if r 0: + self.upper = layout.layers[r - 1] + if r < len(layout.layers) - 1: + self.lower = layout.layers[r + 1] def nextlayer(self): - return self.lower if self.layout.dirv==-1 else self.upper + return self.lower if self.layout.dirv == -1 else self.upper + def prevlayer(self): - return self.lower if self.layout.dirv==+1 else self.upper + return self.lower if self.layout.dirv == +1 else self.upper def order(self): sug = self.layout sug._edge_inverter() c = self._cc() - if c>0: - for v in self: sug.grx[v].bar = self._meanvalueattr(v) + if c > 0: + for v in self: + sug.grx[v].bar = self._meanvalueattr(v) # now resort layers l according to bar value: self.sort(key=lambda x: sug.grx[x].bar) # reduce & count crossings: c = self._ordering_reduce_crossings() # assign new position in layer l: - for i,v in enumerate(self): + for i, v in enumerate(self): sug.grx[v].pos = i - sug.grx[v].bar = i*self.__x + sug.grx[v].bar = i * self.__x sug._edge_inverter() self.ccount = c return c - def _meanvalueattr(self,v): + def _meanvalueattr(self, v): """ find new position of vertex v according to adjacency in prevlayer. position is given by the mean value of adjacent positions. experiments show that meanvalue heuristic performs better than median. """ sug = self.layout - if not self.prevlayer(): return sug.grx[v].bar + if not self.prevlayer(): + return sug.grx[v].bar bars = [sug.grx[x].bar for x in self._neighbors(v)] - return sug.grx[v].bar if len(bars)==0 else float(sum(bars))/len(bars) + return sug.grx[v].bar if len(bars) == 0 else float(sum(bars)) / len(bars) - def _medianindex(self,v): + def _medianindex(self, v): """ find new position of vertex v according to adjacency in layer l+dir. position is given by the median value of adjacent positions. median heuristic is proven to achieve at most 3 times the minimum of crossings (while barycenter achieve in theory the order of |V|) """ - assert self.prevlayer()!=None + assert self.prevlayer() != None N = self._neighbors(v) - g=self.layout.grx + g = self.layout.grx pos = [g[x].pos for x in N] lp = len(pos) - if lp==0: return [] + if lp == 0: + return [] pos.sort() - pos = pos[::self.layout.dirh] - i,j = divmod(lp-1,2) - return [pos[i]] if j==0 else [pos[i],pos[i+j]] + pos = pos[:: self.layout.dirh] + i, j = divmod(lp - 1, 2) + return [pos[i]] if j == 0 else [pos[i], pos[i + j]] - def _neighbors(self,v): + def _neighbors(self, v): """ neighbors refer to upper/lower adjacent nodes. Note that v.N() provides neighbors of v in the graph, while @@ -239,19 +257,21 @@ def _neighbors(self,v): assert self.layout.dag dirv = self.layout.dirv grxv = self.layout.grx[v] - try: #(cache) + try: # (cache) return grxv.nvs[dirv] except AttributeError: - grxv.nvs={-1:v.N(-1),+1:v.N(+1)} - if grxv.dummy: return grxv.nvs[dirv] + grxv.nvs = {-1: v.N(-1), +1: v.N(+1)} + if grxv.dummy: + return grxv.nvs[dirv] # v is real, v.N are graph neigbors but we need layers neighbors - for d in (-1,+1): - tr=grxv.rank+d - for i,x in enumerate(v.N(d)): - if self.layout.grx[x].rank==tr:continue - e=v.e_with(x) + for d in (-1, +1): + tr = grxv.rank + d + for i, x in enumerate(v.N(d)): + if self.layout.grx[x].rank == tr: + continue + e = v.e_with(x) dum = self.layout.ctrls[e][tr] - grxv.nvs[d][i]=dum + grxv.nvs[d][i] = dum return grxv.nvs[dirv] def _crossings(self): @@ -262,14 +282,14 @@ def _crossings(self): The total count of crossings is the sum of flattened P: x = sum(sum(P,[])) """ - g=self.layout.grx - P=[] + g = self.layout.grx + P = [] for v in self: P.append([g[x].pos for x in self._neighbors(v)]) - for i,p in enumerate(P): - candidates = sum(P[i+1:],[]) - for j,e in enumerate(p): - p[j] = len(filter((lambda nx:nxnj]) + x = len([nx for nx in ni if nx > nj]) Xij += x - Xji += len(ni)-x - if Xji0.5: - for (l,mvmt) in self.ordering_step(): + while N > 0.5: + for (l, mvmt) in self.ordering_step(): pass - N = N-1 - if N>0: - for (l,mvmt) in self.ordering_step(oneway=True): + N = N - 1 + if N > 0: + for (l, mvmt) in self.ordering_step(oneway=True): pass self.setxy() self.draw_edges() def _edge_inverter(self): for e in self.alt_e: - x,y = e.v - e.v = (y,x) + x, y = e.v + e.v = (y, x) self.dag = not self.dag if self.dag: for e in self.g.degenerated_edges: @@ -428,28 +455,38 @@ def _edge_inverter(self): self.g.add_edge(e) @property - def dirvh(self): return self.__dirvh + def dirvh(self): + return self.__dirvh + @property - def dirv(self): return self.__dirv + def dirv(self): + return self.__dirv + @property - def dirh(self): return self.__dirh + def dirh(self): + return self.__dirh + @dirvh.setter - def dirvh(self,dirvh): + def dirvh(self, dirvh): assert dirvh in range(4) - self.__dirvh=dirvh - self.__dirh,self.__dirv={0:(1,-1), 1:(-1,-1), 2:(1,1), 3:(-1,1)}[dirvh] + self.__dirvh = dirvh + self.__dirh, self.__dirv = {0: (1, -1), 1: (-1, -1), 2: (1, 1), 3: (-1, 1)}[ + dirvh + ] + @dirv.setter - def dirv(self,dirv): - assert dirv in (-1,+1) - dirvh = (dirv+1)+(1-self.__dirh)//2 + def dirv(self, dirv): + assert dirv in (-1, +1) + dirvh = (dirv + 1) + (1 - self.__dirh) // 2 self.dirvh = dirvh + @dirh.setter - def dirh(self,dirh): - assert dirh in (-1,+1) - dirvh = (self.__dirv+1)+(1-dirh)//2 + def dirh(self, dirh): + assert dirh in (-1, +1) + dirvh = (self.__dirv + 1) + (1 - dirh) // 2 self.dirvh = dirvh - def rank_all(self,roots,optimize=False): + def rank_all(self, roots, optimize=False): """Computes rank of all vertices. add provided roots to rank 0 vertices, otherwise update ranking from provided roots. @@ -457,12 +494,13 @@ def rank_all(self,roots,optimize=False): optimal ranking may be derived from network flow (simplex). """ self._edge_inverter() - r = [x for x in self.g.sV if (len(x.e_in())==0 and x not in roots)] - self._rank_init(roots+r) - if optimize: self._rank_optimize() + r = [x for x in self.g.sV if (len(x.e_in()) == 0 and x not in roots)] + self._rank_init(roots + r) + if optimize: + self._rank_optimize() self._edge_inverter() - def _rank_init(self,unranked): + def _rank_init(self, unranked): """Computes rank of provided unranked list of vertices and all their children. A vertex will be asign a rank when all its inward edges have been *scanned*. When a vertex is asigned @@ -471,17 +509,19 @@ def _rank_init(self,unranked): assert self.dag scan = {} # set rank of unranked based on its in-edges vertices ranks: - while len(unranked)>0: + while len(unranked) > 0: l = [] for v in unranked: self.setrank(v) # mark out-edges has scan-able: - for e in v.e_out(): scan[e]=True + for e in v.e_out(): + scan[e] = True # check if out-vertices are rank-able: for x in v.N(+1): - if not (False in [scan.get(e,False) for e in x.e_in()]): - if x not in l: l.append(x) - unranked=l + if not (False in [scan.get(e, False) for e in x.e_in()]): + if x not in l: + l.append(x) + unranked = l def _rank_optimize(self): """optimize ranking by pushing long edges toward lower layers as much as possible. @@ -493,28 +533,27 @@ def _rank_optimize(self): for v in l: gv = self.grx[v] for x in v.N(-1): - if all((self.grx[y].rank>=gv.rank for y in x.N(+1))): + if all((self.grx[y].rank >= gv.rank for y in x.N(+1))): gx = self.grx[x] self.layers[gx.rank].remove(x) - gx.rank = gv.rank-1 - self.layers[gv.rank-1].append(x) - + gx.rank = gv.rank - 1 + self.layers[gv.rank - 1].append(x) - def setrank(self,v): + def setrank(self, v): """set rank value for vertex v and add it to the corresponding layer. The Layer is created if it is the first vertex with this rank. """ assert self.dag - r=max([self.grx[x].rank for x in v.N(-1)]+[-1])+1 - self.grx[v].rank=r + r = max([self.grx[x].rank for x in v.N(-1)] + [-1]) + 1 + self.grx[v].rank = r # add it to its layer: try: self.layers[r].append(v) except IndexError: - assert r==len(self.layers) + assert r == len(self.layers) self.layers.append(Layer([v])) - def dummyctrl(self,r,ctrl): + def dummyctrl(self, r, ctrl): """creates a DummyVertex at rank r inserted in the ctrl dict of the associated edge and layer. @@ -526,30 +565,30 @@ def dummyctrl(self,r,ctrl): DummyVertex : the created DummyVertex. """ dv = DummyVertex(r) - dv.view.w,dv.view.h=self.dw,self.dh + dv.view.w, dv.view.h = self.dw, self.dh self.grx[dv] = dv dv.ctrl = ctrl ctrl[r] = dv self.layers[r].append(dv) return dv - def setdummies(self,e): + def setdummies(self, e): """creates and defines all needed dummy vertices for edge e. """ - v0,v1 = e.v - r0,r1 = self.grx[v0].rank,self.grx[v1].rank - if r0>r1: + v0, v1 = e.v + r0, r1 = self.grx[v0].rank, self.grx[v1].rank + if r0 > r1: assert e in self.alt_e - v0,v1 = v1,v0 - r0,r1 = r1,r0 - if (r1-r0)>1: + v0, v1 = v1, v0 + r0, r1 = r1, r0 + if (r1 - r0) > 1: # "dummy vertices" are stored in the edge ctrl dict, # keyed by their rank in layers. - ctrl=self.ctrls[e]={} - ctrl[r0]=v0 - ctrl[r1]=v1 - for r in range(r0+1,r1): - self.dummyctrl(r,ctrl) + ctrl = self.ctrls[e] = {} + ctrl[r0] = v0 + ctrl[r1] = v1 + for r in range(r0 + 1, r1): + self.dummyctrl(r, ctrl) def draw_step(self): """iterator that computes all vertices coordinates and edge routing after @@ -562,23 +601,23 @@ def draw_step(self): self.draw_edges() yield s - def ordering_step(self,oneway=False): + def ordering_step(self, oneway=False): """iterator that computes all vertices ordering in their layers (one layer after the other from top to bottom, to top again unless oneway is True). """ - self.dirv=-1 + self.dirv = -1 crossings = 0 for l in self.layers: mvmt = l.order() crossings += mvmt - yield (l,mvmt) + yield (l, mvmt) if oneway or (crossings == 0): return - self.dirv=+1 + self.dirv = +1 while l: mvmt = l.order() - yield (l,mvmt) + yield (l, mvmt) l = l.nextlayer() def setxy(self): @@ -587,33 +626,33 @@ def setxy(self): """ self._edge_inverter() self._detect_alignment_conflicts() - inf = float('infinity') + inf = float("infinity") # initialize vertex coordinates attributes: for l in self.layers: for v in l: - self.grx[v].root = v + self.grx[v].root = v self.grx[v].align = v - self.grx[v].sink = v + self.grx[v].sink = v self.grx[v].shift = inf - self.grx[v].X = None - self.grx[v].x = [0.0]*4 - curvh = self.dirvh # save current dirvh value + self.grx[v].X = None + self.grx[v].x = [0.0] * 4 + curvh = self.dirvh # save current dirvh value for dirvh in range(4): self.dirvh = dirvh self._coord_vertical_alignment() self._coord_horizontal_compact() - self.dirvh = curvh # restore it + self.dirvh = curvh # restore it # vertical coordinate assigment of all nodes: Y = 0 for l in self.layers: - dY = max([v.view.h/2. for v in l]) + dY = max([v.view.h / 2.0 for v in l]) for v in l: vx = sorted(self.grx[v].x) # mean of the 2 medians out of the 4 x-coord computed above: - avgm = (vx[1]+vx[2])/2. + avgm = (vx[1] + vx[2]) / 2.0 # final xy-coordinates : - v.view.xy = (avgm,Y+dY) - Y += 2*dY+self.yspace + v.view.xy = (avgm, Y + dY) + Y += 2 * dY + self.yspace self._edge_inverter() def _detect_alignment_conflicts(self): @@ -623,62 +662,67 @@ def _detect_alignment_conflicts(self): type 1 is inner crossing regular (targeted crossings) type 2 is inner crossing inner (avoided by reduce_crossings phase) """ - curvh = self.dirvh # save current dirvh value - self.dirvh=0 + curvh = self.dirvh # save current dirvh value + self.dirvh = 0 self.conflicts = [] for L in self.layers: - last = len(L)-1 + last = len(L) - 1 prev = L.prevlayer() - if not prev: continue - k0=0 - k1_init=len(prev)-1 - l=0 - for l1,v in enumerate(L): - if not self.grx[v].dummy: continue - if l1==last or v.inner(-1): - k1=k1_init + if not prev: + continue + k0 = 0 + k1_init = len(prev) - 1 + l = 0 + for l1, v in enumerate(L): + if not self.grx[v].dummy: + continue + if l1 == last or v.inner(-1): + k1 = k1_init if v.inner(-1): - k1=self.grx[v.N(-1)[-1]].pos - for vl in L[l:l1+1]: + k1 = self.grx[v.N(-1)[-1]].pos + for vl in L[l : l1 + 1]: for vk in L._neighbors(vl): k = self.grx[vk].pos - if (kk1): - self.conflicts.append((vk,vl)) - l=l1+1 - k0=k1 - self.dirvh = curvh # restore it + if k < k0 or k > k1: + self.conflicts.append((vk, vl)) + l = l1 + 1 + k0 = k1 + self.dirvh = curvh # restore it def _coord_vertical_alignment(self): """performs vertical alignment according to current dirvh internal state. """ - dirh,dirv = self.dirh,self.dirv + dirh, dirv = self.dirh, self.dirv g = self.grx for l in self.layers[::-dirv]: - if not l.prevlayer(): continue - r=None + if not l.prevlayer(): + continue + r = None for vk in l[::dirh]: for m in l._medianindex(vk): # take the median node in dirv layer: um = l.prevlayer()[m] # if vk is "free" align it with um's root if g[vk].align is vk: - if dirv==1: vpair = (vk,um) - else: vpair = (um,vk) + if dirv == 1: + vpair = (vk, um) + else: + vpair = (um, vk) # if vk<->um link is used for alignment - if (vpair not in self.conflicts) and \ - ((r is None) or (dirh*rlimit: + limit = getrecursionlimit() + N = len(self.layers) + 10 + if N > limit: setrecursionlimit(N) - dirh,dirv = self.dirh,self.dirv + dirh, dirv = self.dirh, self.dirv g = self.grx L = self.layers[::-dirv] # recursive placement of blocks: @@ -688,32 +732,33 @@ def _coord_horizontal_compact(self): self.__place_block(v) setrecursionlimit(limit) # mirror all nodes if right-aligned: - if dirh==-1: + if dirh == -1: for l in L: for v in l: x = g[v].X - if x: g[v].X = -x + if x: + g[v].X = -x # then assign x-coord of its root: - inf=float('infinity') - rb=inf + inf = float("infinity") + rb = inf for l in L: for v in l[::dirh]: g[v].x[self.dirvh] = g[g[v].root].X rs = g[g[v].root].sink s = g[rs].shift - if seps: - l=[] + if (y - ordering[i + 1][0]) > eps: + l = [] self.levels.append(l) - y,v = ordering[-1] + y, v = ordering[-1] l.append(v) v.level = self.levels.index(l) return opty @@ -850,16 +900,16 @@ def part_to_levels(self,alpha,beta): def optimal_arrangement(self): b = self.balance() y = DigcoLayout.linalg.rand_ortho1(self.g.order()) - return self._conjugate_gradient_L(y,b) + return self._conjugate_gradient_L(y, b) # balance vector is assembled in finite-element way... # this is faster than computing b[i] for each i. def balance(self): - b = DigcoLayout.linalg.array([0.0]*self.g.order(),dtype=float) + b = DigcoLayout.linalg.array([0.0] * self.g.order(), dtype=float) for e in self.g.E(): s = e.v[0] d = e.v[1] - q = e.w*(self.yspace+(s.view.h+d.view.h)/2.) + q = e.w * (self.yspace + (s.view.h + d.view.h) / 2.0) b[s.i] += q b[d.i] -= q return b @@ -867,37 +917,38 @@ def balance(self): # We compute the solution Y of L.Y = b by conjugate gradient method # (L is semi-definite positive so Y is unique and convergence is O(n)) # note that only arrays are involved here... - def _conjugate_gradient_L(self,y,b): + def _conjugate_gradient_L(self, y, b): Lii = self.__Lii_() - r = b - self.__L_pk(Lii,y) - p = DigcoLayout.linalg.array(r,copy=True) - rr = sum(r*r) + r = b - self.__L_pk(Lii, y) + p = DigcoLayout.linalg.array(r, copy=True) + rr = sum(r * r) for k in range(self._cg_max_iter): try: - Lp = self.__L_pk(Lii,p) - alpha = rr/sum(p*Lp) - y += alpha/p - r -= alpha*Lp - newrr = sum(r*r) - beta = newrr/rr + Lp = self.__L_pk(Lii, p) + alpha = rr / sum(p * Lp) + y += alpha / p + r -= alpha * Lp + newrr = sum(r * r) + beta = newrr / rr rr = newrr - if rr ") - print(Z,xerr,yerr) + print(Z, xerr, yerr) # compute new stress: - FZ = K-float(x.transpose()*b[1:,0] + y.transpose()*b[1:,1]) + FZ = K - float(x.transpose() * b[1:, 0] + y.transpose() * b[1:, 1]) # precompute new b: - b = self.__Lij_Z_Z(Z) + b = self.__Lij_Z_Z(Z) # update new stress: - FZ += 2*float(x.transpose()*b[1:,0] + y.transpose()*b[1:,1]) + FZ += 2 * float(x.transpose() * b[1:, 0] + y.transpose() * b[1:, 1]) # test convergence: - print('stress=%.10f'%FZ) - if stress==0.0 : break - elif abs((stress-FZ)/stress) 2: calc_with_distance = round_at_distance @@ -85,25 +91,29 @@ def _round_corners(pts, round_at_distance): new_lst = [pts[0]] for i, curr in enumerate(pts[1:-1]): i += 1 - p1 = pts[i-1] + p1 = pts[i - 1] p2 = curr - p3 = pts[i+1] + p3 = pts[i + 1] if len(pts) > 3: # i.e.: at least 4 points - if sqrt((p3[0] - p2[0]) ** 2 + (p3[1] - p2[1]) ** 2) < (2 * calc_with_distance): + if sqrt((p3[0] - p2[0]) ** 2 + (p3[1] - p2[1]) ** 2) < ( + 2 * calc_with_distance + ): # prevent from crossing over. new_lst.append(p2) continue generated = _gen_smoother_middle_points_from_3_points( - [p1, p2, p3], calc_with_distance) + [p1, p2, p3], calc_with_distance + ) for j in generated: new_lst.append(j) new_lst.append(pts[-1]) pts = new_lst - calc_with_distance /= 2. + calc_with_distance /= 2.0 return pts -#------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ # Routing with a custom algorithm to round corners # It works by generating new points up to a distance from where an edge is # found (and then iteratively refining based on that). @@ -115,6 +125,7 @@ def _round_corners(pts, round_at_distance): # rounding from an edge). ROUND_AT_DISTANCE = 40 + def route_with_rounded_corners(e, pts): route_with_lines(e, pts) new_pts = _round_corners(pts, round_at_distance=ROUND_AT_DISTANCE) diff --git a/grandalf/utils/dot.py b/grandalf/utils/dot.py index 5b7b40b..ca89132 100644 --- a/grandalf/utils/dot.py +++ b/grandalf/utils/dot.py @@ -6,104 +6,102 @@ try: import ply.lex as lex import ply.yacc as yacc + _has_ply = True except ImportError: _has_ply = False -__all__ = ['_has_ply','Dot'] +__all__ = ["_has_ply", "Dot"] -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ # LALR(1) parser for Graphviz dot file format. class Dot: _reserved = ( - 'strict', - 'graph', - 'digraph', - 'subgraph', - 'node', - 'edge', + "strict", + "graph", + "digraph", + "subgraph", + "node", + "edge", ) - _tokens = ( - 'regulars', - 'string', - 'html', - 'comment', - )+_reserved + _tokens = ("regulars", "string", "html", "comment",) + _reserved - _literals = [',',';','-','>','=',':','[',']','{','}'] + _literals = [",", ";", "-", ">", "=", ":", "[", "]", "{", "}"] class Lexer(object): def __init__(self): - self.whitespace = '\0\t\n\f\r ' + self.whitespace = "\0\t\n\f\r " self.reserved = Dot._reserved self.tokens = Dot._tokens self.literals = Dot._literals self.t_ignore = self.whitespace - def t_regulars(self,t): - r'[-]?[\w.]+' + def t_regulars(self, t): + r"[-]?[\w.]+" v = t.value.lower() if v in self.reserved: t.type = v return t # check numeric string - if v[0].isdigit() or v[0] in ['-','.']: + if v[0].isdigit() or v[0] in ["-", "."]: try: float(v) except ValueError: - print('invalid numeral token: %s'%v) + print("invalid numeral token: %s" % v) raise SyntaxError - elif '.' in v: # forbidden in non-numeric + elif "." in v: # forbidden in non-numeric raise SyntaxError return t - def t_comment_online(self,t): - r'(//(.*)\n)|\\\n' + def t_comment_online(self, t): + r"(//(.*)\n)|\\\n" pass - def t_comment_macro(self,t): - r'(\#(.*)\n)' + def t_comment_macro(self, t): + r"(\#(.*)\n)" pass - def t_comment_multline(self,t): - r'(/\*)' - start=t.lexer.lexpos - t.lexer.lexpos = t.lexer.lexdata.index('*/',start)+2 + def t_comment_multline(self, t): + r"(/\*)" + start = t.lexer.lexpos + t.lexer.lexpos = t.lexer.lexdata.index("*/", start) + 2 - def t_string(self,t): + def t_string(self, t): r'"' - start=t.lexer.lexpos-1 - i = t.lexer.lexdata.index('"',start+1) - while t.lexer.lexdata[i-1] =='\\' : - i = t.lexer.lexdata.index('"',i+1) - t.value = t.lexer.lexdata[start:i+1] - t.lexer.lexpos = i+1 + start = t.lexer.lexpos - 1 + i = t.lexer.lexdata.index('"', start + 1) + while t.lexer.lexdata[i - 1] == "\\": + i = t.lexer.lexdata.index('"', i + 1) + t.value = t.lexer.lexdata[start : i + 1] + t.lexer.lexpos = i + 1 return t - def t_html(self,t): - r'<' - start=t.lexer.lexpos-1 - level=1 - i=start+1 - while level>0: + def t_html(self, t): + r"<" + start = t.lexer.lexpos - 1 + level = 1 + i = start + 1 + while level > 0: c = t.lexer.lexdata[i] - if c=='<': level += 1 - if c=='>': level -= 1 + if c == "<": + level += 1 + if c == ">": + level -= 1 i += 1 t.value = t.lexer.lexdata[start:i] t.lexer.lexpos = i return t - def t_ANY_error(self,t): + def t_ANY_error(self, t): print("Illegal character '%s'" % t.value[0]) t.lexer.skip(1) - def build(self,**kargs): + def build(self, **kargs): if _has_ply: self._lexer = lex.lex(module=self, **kargs) - def test(self,data): + def test(self, data): self._lexer.input(data) while 1: tok = self._lexer.token() @@ -113,7 +111,7 @@ def test(self,data): # Classes for the AST returned by Parser: class graph(object): - def __init__(self,name,data,strict=None,direct=None): + def __init__(self, name, data, strict=None, direct=None): self.name = name self.strict = strict self.direct = direct @@ -123,60 +121,62 @@ def __init__(self,name,data,strict=None,direct=None): self.attr = {} eattr = {} nattr = {} - for x in data: # data is a statements (list of stmt) + for x in data: # data is a statements (list of stmt) # x is a stmt, ie one of: # a graph object (subgraph) # a attr object (graph/node/edge attributes) # a dict object (ID=ID) # a node object # a list of edges - if isinstance(x,Dot.graph): + if isinstance(x, Dot.graph): self.subgraphs.append(x) - elif isinstance(x,Dot.attr): - if x.type=='graph': + elif isinstance(x, Dot.attr): + if x.type == "graph": self.attr.update(x.D) - elif x.type=='node' : + elif x.type == "node": nattr.update(x.D) - elif x.type=='edge' : + elif x.type == "edge": eattr.update(x.D) - else : - raise TypeError('invalid attribute type') - elif isinstance(x,dict): + else: + raise TypeError("invalid attribute type") + elif isinstance(x, dict): self.attr.update(x) - elif isinstance(x,Dot.node): + elif isinstance(x, Dot.node): x.attr.update(nattr) self.nodes[x.name] = x else: for e in x: e.attr.update(eattr) self.edges.append(e) - for n in [e.n1,e.n2]: - if isinstance(n,Dot.graph): continue + for n in [e.n1, e.n2]: + if isinstance(n, Dot.graph): + continue if n.name not in self.nodes: n.attr.update(nattr) self.nodes[n.name] = n def __repr__(self): - u = u'<%s instance at %x, name: %s, %d nodes>'%( - self.__class__, - id(self), - self.name, - len(self.nodes)) + u = "<%s instance at %x, name: %s, %d nodes>" % ( + self.__class__, + id(self), + self.name, + len(self.nodes), + ) return u class attr(object): - def __init__(self,type,D): - self.type=type + def __init__(self, type, D): + self.type = type self.D = D class edge(object): - def __init__(self,n1,n2): + def __init__(self, n1, n2): self.n1 = n1 self.n2 = n2 self.attr = {} class node(object): - def __init__(self,name,port=None): + def __init__(self, name, port=None): self.name = name self.port = port self.attr = {} @@ -185,219 +185,217 @@ class Parser(object): def __init__(self): self.tokens = Dot._tokens - def __makelist(self,p): - N=len(p) - if N>2: + def __makelist(self, p): + N = len(p) + if N > 2: L = p[1] - L.append(p[N-1]) + L.append(p[N - 1]) else: L = [] - if N>1: - L.append(p[N-1]) + if N > 1: + L.append(p[N - 1]) p[0] = L - def p_Data(self,p): - '''Data : Data Graph - | Graph''' + def p_Data(self, p): + """Data : Data Graph + | Graph""" self.__makelist(p) - def p_Graph_strict(self,p): - '''Graph : strict graph name Block''' - p[0] = Dot.graph(name=p[3],data=p[4],strict=1,direct=0) - #print 'Dot.Parser: graph object %s created'%p[0].name + def p_Graph_strict(self, p): + """Graph : strict graph name Block""" + p[0] = Dot.graph(name=p[3], data=p[4], strict=1, direct=0) + # print 'Dot.Parser: graph object %s created'%p[0].name - def p_Graph_graph(self,p): - '''Graph : graph name Block''' - p[0] = Dot.graph(name=p[2],data=p[3],strict=0,direct=0) + def p_Graph_graph(self, p): + """Graph : graph name Block""" + p[0] = Dot.graph(name=p[2], data=p[3], strict=0, direct=0) - def p_Graph_strict_digraph(self,p): - '''Graph : strict digraph name Block''' - p[0] = Dot.graph(name=p[3],data=p[4],strict=1,direct=1) + def p_Graph_strict_digraph(self, p): + """Graph : strict digraph name Block""" + p[0] = Dot.graph(name=p[3], data=p[4], strict=1, direct=1) - def p_Graph_digraph(self,p): - '''Graph : digraph name Block''' - p[0] = Dot.graph(name=p[2],data=p[3],strict=0,direct=1) + def p_Graph_digraph(self, p): + """Graph : digraph name Block""" + p[0] = Dot.graph(name=p[2], data=p[3], strict=0, direct=1) - def p_ID(self,p): - '''ID : regulars + def p_ID(self, p): + """ID : regulars | string - | html ''' + | html """ p[0] = p[1] - def p_name(self,p): - '''name : ID - | ''' - if len(p)==1: - p[0]='' + def p_name(self, p): + """name : ID + | """ + if len(p) == 1: + p[0] = "" else: - p[0]=p[1] + p[0] = p[1] - def p_Block(self,p): - '''Block : '{' statements '}' ''' + def p_Block(self, p): + """Block : '{' statements '}' """ p[0] = p[2] - def p_statements(self,p): - '''statements : statements stmt + def p_statements(self, p): + """statements : statements stmt | stmt - | ''' + | """ self.__makelist(p) - def p_stmt(self,p): - '''stmt : stmt ';' ''' + def p_stmt(self, p): + """stmt : stmt ';' """ p[0] = p[1] - def p_comment(self,p): - '''stmt : comment''' + def p_comment(self, p): + """stmt : comment""" pass # comment tokens are not outputed by lexer anyway - def p_stmt_sub(self,p): - '''stmt : sub''' + def p_stmt_sub(self, p): + """stmt : sub""" p[0] = p[1] - def p_subgraph(self,p): - '''sub : subgraph name Block - | Block ''' + def p_subgraph(self, p): + """sub : subgraph name Block + | Block """ N = len(p) - if N>2: + if N > 2: ID = p[2] else: - ID = '' - p[0] = Dot.graph(name=ID,data=p[N-1],strict=0,direct=0) + ID = "" + p[0] = Dot.graph(name=ID, data=p[N - 1], strict=0, direct=0) - def p_stmt_assign(self,p): - '''stmt : affect ''' + def p_stmt_assign(self, p): + """stmt : affect """ p[0] = p[1] - def p_affect(self,p): - '''affect : ID '=' ID ''' - p[0] = dict([(p[1],p[3])]) + def p_affect(self, p): + """affect : ID '=' ID """ + p[0] = dict([(p[1], p[3])]) - def p_stmt_lists(self,p): - '''stmt : graph attrs + def p_stmt_lists(self, p): + """stmt : graph attrs | node attrs - | edge attrs ''' - p[0] = Dot.attr(p[1],p[2]) + | edge attrs """ + p[0] = Dot.attr(p[1], p[2]) - def p_attrs(self,p): - '''attrs : attrs attrl - | attrl ''' - if len(p)==3: + def p_attrs(self, p): + """attrs : attrs attrl + | attrl """ + if len(p) == 3: p[1].update(p[2]) p[0] = p[1] - def p_attrl(self,p): - '''attrl : '[' alist ']' ''' - L={} + def p_attrl(self, p): + """attrl : '[' alist ']' """ + L = {} for a in p[2]: - if isinstance(a,dict): + if isinstance(a, dict): L.update(a) else: - L[a] = 'true' + L[a] = "true" p[0] = L - def p_alist_comma(self,p): - '''alist : alist ',' alist ''' + def p_alist_comma(self, p): + """alist : alist ',' alist """ p[1].extend(p[3]) p[0] = p[1] - def p_alist_affect(self,p): - '''alist : alist affect + def p_alist_affect(self, p): + """alist : alist affect | alist ID | affect | ID - | ''' + | """ self.__makelist(p) - def p_stmt_E_attrs(self,p): - '''stmt : E attrs ''' - for e in p[1]: e.attr = p[2] + def p_stmt_E_attrs(self, p): + """stmt : E attrs """ + for e in p[1]: + e.attr = p[2] p[0] = p[1] - def p_stmt_N_attrs(self,p): - '''stmt : N attrs ''' + def p_stmt_N_attrs(self, p): + """stmt : N attrs """ p[1].attr = p[2] p[0] = p[1] - def p_stmt_EN(self,p): - '''stmt : E - | N ''' + def p_stmt_EN(self, p): + """stmt : E + | N """ p[0] = p[1] - def p_E(self,p): - '''E : E link - | elt link ''' + def p_E(self, p): + """E : E link + | elt link """ try: L = p[1] - L.append(Dot.edge(L[-1].n2,p[2])) + L.append(Dot.edge(L[-1].n2, p[2])) except Exception: L = [] - L.append(Dot.edge(p[1],p[2])) + L.append(Dot.edge(p[1], p[2])) p[0] = L - def p_elt(self,p): - '''elt : N - | sub ''' + def p_elt(self, p): + """elt : N + | sub """ p[0] = p[1] - def p_link(self,p): - '''link : '-' '>' elt - | '-' '-' elt ''' + def p_link(self, p): + """link : '-' '>' elt + | '-' '-' elt """ p[0] = p[3] - def p_N_port(self,p): - '''N : ID port ''' - p[0] = Dot.node(p[1],port=p[2]) + def p_N_port(self, p): + """N : ID port """ + p[0] = Dot.node(p[1], port=p[2]) - def p_N(self,p): - '''N : ID ''' + def p_N(self, p): + """N : ID """ p[0] = Dot.node(p[1]) - def p_port(self,p): - '''port : ':' ID ''' + def p_port(self, p): + """port : ':' ID """ p[0] = p[2] - def p_port2(self,p): - '''port : port port''' - assert p[2] in ['n','ne', - 'e','se', - 's','sw', - 'w','nw', - 'c','_'] - p[0] = "%s:%s"%(p[1],p[2]) - - def p_error(self,p): - print('Syntax Error: %s' % (p,)) + def p_port2(self, p): + """port : port port""" + assert p[2] in ["n", "ne", "e", "se", "s", "sw", "w", "nw", "c", "_"] + p[0] = "%s:%s" % (p[1], p[2]) + + def p_error(self, p): + print("Syntax Error: %s" % (p,)) self._parser.restart() - def build(self,**kargs): - opt=dict(debug=0,write_tables=0) + def build(self, **kargs): + opt = dict(debug=0, write_tables=0) opt.update(**kargs) if _has_ply: - self._parser = yacc.yacc(module=self,**opt) + self._parser = yacc.yacc(module=self, **opt) - def __init__(self,**kargs): - self.lexer = Dot.Lexer() + def __init__(self, **kargs): + self.lexer = Dot.Lexer() self.parser = Dot.Parser() if not _has_ply: - print('warning: Dot parser not supported (install python-ply)') + print("warning: Dot parser not supported (install python-ply)") - def parse(self,data): + def parse(self, data): try: self.parser._parser.restart() except AttributeError: self.lexer.build(reflags=lex.re.UNICODE) self.parser.build() except Exception: - print('unexpected error') + print("unexpected error") return None try: - s = data.decode('utf-8') + s = data.decode("utf-8") except UnicodeDecodeError: s = data - L=self.parser._parser.parse(s, - lexer=self.lexer._lexer) + L = self.parser._parser.parse(s, lexer=self.lexer._lexer) return L - def read(self,filename): - f = open(filename,'rb') # As it'll try to decode later on with utf-8, read it binary at this point. + def read(self, filename): + f = open( + filename, "rb" + ) # As it'll try to decode later on with utf-8, read it binary at this point. return self.parse(f.read()) diff --git a/grandalf/utils/geometry.py b/grandalf/utils/geometry.py index a866a08..a9b39ce 100644 --- a/grandalf/utils/geometry.py +++ b/grandalf/utils/geometry.py @@ -7,186 +7,204 @@ from .poset import * from .dot import * -from math import atan2,sqrt +from math import atan2, sqrt from random import SystemRandom -#------------------------------------------------------------------------------ -def intersect2lines(xy1, xy2, xy3, xy4): - (x1,y1) = xy1 - (x2,y2) = xy2 - (x3,y3) = xy3 - (x4,y4) = xy4 - b = (x2-x1,y2-y1) - d = (x4-x3,y4-y3) - det = b[0]*d[1] - b[1]*d[0] - if det==0: return None - c = (x3-x1,y3-y1) - t = float(c[0]*b[1] - c[1]*b[0])/(det*1.) - if (t<0. or t>1.): return None - t = float(c[0]*d[1] - c[1]*d[0])/(det*1.) - if (t<0. or t>1.): return None - x = x1 + t*b[0] - y = y1 + t*b[1] - return (x,y) - -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +def intersect2lines(xy1, xy2, xy3, xy4): + (x1, y1) = xy1 + (x2, y2) = xy2 + (x3, y3) = xy3 + (x4, y4) = xy4 + b = (x2 - x1, y2 - y1) + d = (x4 - x3, y4 - y3) + det = b[0] * d[1] - b[1] * d[0] + if det == 0: + return None + c = (x3 - x1, y3 - y1) + t = float(c[0] * b[1] - c[1] * b[0]) / (det * 1.0) + if t < 0.0 or t > 1.0: + return None + t = float(c[0] * d[1] - c[1] * d[0]) / (det * 1.0) + if t < 0.0 or t > 1.0: + return None + x = x1 + t * b[0] + y = y1 + t * b[1] + return (x, y) + + +# ------------------------------------------------------------------------------ # intersectR returns the intersection point between the Rectangle # (w,h) that characterize the view object and the line that goes # from the views' object center to the 'topt' point. -def intersectR(view,topt): +def intersectR(view, topt): # we compute intersection in local views' coord: # center of view is obviously : - x1,y1 = 0,0 + x1, y1 = 0, 0 # endpoint in view's coord: - x2,y2 = topt[0]-view.xy[0],topt[1]-view.xy[1] + x2, y2 = topt[0] - view.xy[0], topt[1] - view.xy[1] # bounding box: - bbx2 = view.w//2 + bbx2 = view.w // 2 bbx1 = -bbx2 - bby2 = view.h//2 + bby2 = view.h // 2 bby1 = -bby2 # all 4 segments of the bb: - S = [((x1,y1),(x2,y2),(bbx1,bby1),(bbx2,bby1)), - ((x1,y1),(x2,y2),(bbx2,bby1),(bbx2,bby2)), - ((x1,y1),(x2,y2),(bbx1,bby2),(bbx2,bby2)), - ((x1,y1),(x2,y2),(bbx1,bby2),(bbx1,bby1))] + S = [ + ((x1, y1), (x2, y2), (bbx1, bby1), (bbx2, bby1)), + ((x1, y1), (x2, y2), (bbx2, bby1), (bbx2, bby2)), + ((x1, y1), (x2, y2), (bbx1, bby2), (bbx2, bby2)), + ((x1, y1), (x2, y2), (bbx1, bby2), (bbx1, bby1)), + ] # check intersection with each seg: for segs in S: xy = intersect2lines(*segs) - if xy!=None: - x,y = xy + if xy != None: + x, y = xy # return global coord: x += view.xy[0] y += view.xy[1] - return (x,y) + return (x, y) # there can't be no intersection unless the endpoint was # inside the bb ! - raise ValueError('no intersection found (point inside ?!). view: %s topt: %s' % (view, topt)) + raise ValueError( + "no intersection found (point inside ?!). view: %s topt: %s" % (view, topt) + ) + -#------------------------------------------------------------------------------ -def getangle(p1,p2): - x1,y1 = p1 - x2,y2 = p2 - theta = atan2(y2-y1,x2-x1) +# ------------------------------------------------------------------------------ +def getangle(p1, p2): + x1, y1 = p1 + x2, y2 = p2 + theta = atan2(y2 - y1, x2 - x1) return theta -#------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ def median_wh(views): mw = [v.w for v in views] mh = [v.h for v in views] mw.sort() mh.sort() - return (mw[len(mw)//2],mh[len(mh)//2]) + return (mw[len(mw) // 2], mh[len(mh) // 2]) + -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ try: - from numpy import array,matrix,cos,sin + from numpy import array, matrix, cos, sin + has_numpy = True except ImportError: has_numpy = False - from math import cos,sin,pi - from .linalg import array,matrix + from math import cos, sin, pi + from .linalg import array, matrix # rand_ortho1 returns a numpy.array representing # a random normalized n-dimension vector orthogonal to (1,1,1,...,1). -def rand_ortho1(n): +def rand_ortho1(n): r = SystemRandom() pos = [r.random() for x in range(n)] s = sum(pos) - v = array(pos,dtype=float)-(s/float(n)) - norm = sqrt(sum(v*v)) - return v/norm + v = array(pos, dtype=float) - (s / float(n)) + norm = sqrt(sum(v * v)) + return v / norm -#------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ # intersectC returns the intersection point between the Circle # of radius r and centered on views' position with the line # to the 'topt' point. -def intersectC(view, r, topt): - theta = getangle(view.xy,topt) - x = int(cos(theta)*r) - y = int(sin(theta)*r) - return (x,y) +def intersectC(view, r, topt): + theta = getangle(view.xy, topt) + x = int(cos(theta) * r) + y = int(sin(theta) * r) + return (x, y) -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ # setcurve returns the spline curve that path through the list of points P. # The spline curve is a list of cubic bezier curves (nurbs) that have # matching tangents at their extreme points. # The method considered here is taken from "The NURBS book" (Les A. Piegl, # Wayne Tiller, Springer, 1997) and implements a local interpolation rather # than a global interpolation. -def setcurve(e,pts,tgs=None): - P = list(map(array,pts)) +def setcurve(e, pts, tgs=None): + P = list(map(array, pts)) n = len(P) # tangent estimation if tgs: - assert len(tgs)==n - T = list(map(array,tgs)) - Q = [ P[k+1]-P[k] for k in range(0,n-1)] + assert len(tgs) == n + T = list(map(array, tgs)) + Q = [P[k + 1] - P[k] for k in range(0, n - 1)] else: - Q,T = tangents(P,n) - splines=[] - for k in range(n-1): - t = T[k]+T[k+1] - a = 16. - (t.dot(t)) - b = 12.*(Q[k].dot(t)) - c = -36. * Q[k].dot(Q[k]) - D = (b*b) - 4.*a*c - assert D>=0 + Q, T = tangents(P, n) + splines = [] + for k in range(n - 1): + t = T[k] + T[k + 1] + a = 16.0 - (t.dot(t)) + b = 12.0 * (Q[k].dot(t)) + c = -36.0 * Q[k].dot(Q[k]) + D = (b * b) - 4.0 * a * c + assert D >= 0 sd = sqrt(D) - s1,s2 = (-b-sd)/(2.*a),(-b+sd)/(2.*a) + s1, s2 = (-b - sd) / (2.0 * a), (-b + sd) / (2.0 * a) s = s2 - if s1>=0: s=s1 + if s1 >= 0: + s = s1 C0 = tuple(P[k]) - C1 = tuple(P[k] + (s/3.)*T[k]) - C2 = tuple(P[k+1] -(s/3.)*T[k+1]) - C3 = tuple(P[k+1]) - splines.append([C0,C1,C2,C3]) + C1 = tuple(P[k] + (s / 3.0) * T[k]) + C2 = tuple(P[k + 1] - (s / 3.0) * T[k + 1]) + C3 = tuple(P[k + 1]) + splines.append([C0, C1, C2, C3]) return splines -#------------------------------------------------------------------------------ -def tangents(P,n): - assert n>=2 + +# ------------------------------------------------------------------------------ +def tangents(P, n): + assert n >= 2 Q = [] T = [] - for k in range(0,n-1): - q = P[k+1]-P[k] - t = q/sqrt(q.dot(q)) + for k in range(0, n - 1): + q = P[k + 1] - P[k] + t = q / sqrt(q.dot(q)) Q.append(q) T.append(t) T.append(t) - return (Q,T) + return (Q, T) -#------------------------------------------------------------------------------ -def setroundcorner(e,pts): - P = list(map(array,pts)) + +# ------------------------------------------------------------------------------ +def setroundcorner(e, pts): + P = list(map(array, pts)) n = len(P) - Q,T = tangents(P,n) + Q, T = tangents(P, n) c0 = P[0] t0 = T[0] k0 = 0 splines = [] - k = 1 - while kk0: splines.append([c0,P[k-1]]) - if (k+1) k0: + splines.append([c0, P[k - 1]]) + if (k + 1) < n: + splines.extend(setcurve(e, [P[k - 1], P[k + 1]], tgs=[T[k - 1], T[k + 1]])) else: - splines.extend(setcurve(e,[P[k-1],P[k]],tgs=[T[k-1],T[k]])) + splines.extend(setcurve(e, [P[k - 1], P[k]], tgs=[T[k - 1], T[k]])) break - if (k+2)0: + if self.dim > 0: types = set([type(x) for x in data]) if dtype is not None: types = (dtype,) - tc,self.dtype = coerce_(types) + tc, self.dtype = coerce_(types) data = [self.dtype(x) for x in data] if copy is True: - self.data = _array(tc,data) + self.data = _array(tc, data) else: raise NotImplementedError - def coerce(self,dtype): + def coerce(self, dtype): data = [dtype(x) for x in self.data] - tc,dtype = coerce_((dtype,)) - self.data = _array(tc,data) + tc, dtype = coerce_((dtype,)) + self.data = _array(tc, data) self.dtype = dtype @property def typecode(self): return self.data.typecode - def __len__(self): return self.dim + def __len__(self): + return self.dim def __str__(self): - s = ' '.join(("%.12s"%x).ljust(12) for x in self) - return '[%s]'%s.strip() + s = " ".join(("%.12s" % x).ljust(12) for x in self) + return "[%s]" % s.strip() - def copy(self): return array(self.data,self.dtype) + def copy(self): + return array(self.data, self.dtype) - def __add__(self,v): - if isinstance(v,constants): - v = array([v]*self.dim) - assert v.dim==self.dim - return array([x+y for (x,y) in zip(self.data,v.data)]) + def __add__(self, v): + if isinstance(v, constants): + v = array([v] * self.dim) + assert v.dim == self.dim + return array([x + y for (x, y) in zip(self.data, v.data)]) - def __sub__(self,v): - if isinstance(v,constants): - v = array([v]*self.dim) - assert v.dim==self.dim - return array([x-y for (x,y) in zip(self.data,v.data)]) + def __sub__(self, v): + if isinstance(v, constants): + v = array([v] * self.dim) + assert v.dim == self.dim + return array([x - y for (x, y) in zip(self.data, v.data)]) def __neg__(self): - return array([-x for x in self.data],dtype=self.dtype) - - def __radd__(self,v): - return self+v - def __rsub__(self,v): - return (-self)+v - - def dot(self,v): - assert v.dim==self.dim - return sum([x*y for (x,y) in zip(self.data,v.data)]) - - def __rmul__(self,k): - return array([k*x for x in self.data]) - - def __mul__(self,v): - if isinstance(v,constants): - v = array([v]*self.dim) - assert v.dim==self.dim - return array([x*y for (x,y) in zip(self.data,v.data)]) - - def __truediv__(self,v): - if isinstance(v,constants): - v = array([v]*self.dim) - assert v.dim==self.dim - return array([x/y for (x,y) in zip(self.data,v.data)]) + return array([-x for x in self.data], dtype=self.dtype) + + def __radd__(self, v): + return self + v + + def __rsub__(self, v): + return (-self) + v + + def dot(self, v): + assert v.dim == self.dim + return sum([x * y for (x, y) in zip(self.data, v.data)]) + + def __rmul__(self, k): + return array([k * x for x in self.data]) + + def __mul__(self, v): + if isinstance(v, constants): + v = array([v] * self.dim) + assert v.dim == self.dim + return array([x * y for (x, y) in zip(self.data, v.data)]) + + def __truediv__(self, v): + if isinstance(v, constants): + v = array([v] * self.dim) + assert v.dim == self.dim + return array([x / y for (x, y) in zip(self.data, v.data)]) + __div__ = __truediv__ - def __rtruediv__(self,v): - if isinstance(v,constants): - v = array([v]*self.dim) - assert v.dim==self.dim - return array([x/y for (x,y) in zip(v.data,self.data)]) + def __rtruediv__(self, v): + if isinstance(v, constants): + v = array([v] * self.dim) + assert v.dim == self.dim + return array([x / y for (x, y) in zip(v.data, self.data)]) + __rdiv__ = __rtruediv__ - def __floordiv__(self,v): - if isinstance(v,constants): - v = array([v]*self.dim) - assert v.dim==self.dim - return array([x//y for (x,y) in zip(self.data,v.data)]) + def __floordiv__(self, v): + if isinstance(v, constants): + v = array([v] * self.dim) + assert v.dim == self.dim + return array([x // y for (x, y) in zip(self.data, v.data)]) - def __rfloordiv__(self,v): - if isinstance(v,constants): - v = array([v]*self.dim) - assert v.dim==self.dim - return array([x//y for (x,y) in zip(v.data,self.data)]) + def __rfloordiv__(self, v): + if isinstance(v, constants): + v = array([v] * self.dim) + assert v.dim == self.dim + return array([x // y for (x, y) in zip(v.data, self.data)]) def norm(self): return sqrt(self.dot(self)) def max(self): return max(self.data) + def min(self): return min(self.data) def __iter__(self): - for x in self.data: yield x + for x in self.data: + yield x - def __setitem__(self,i,v): - assert isinstance(i,int) + def __setitem__(self, i, v): + assert isinstance(i, int) self.data[i] = self.dtype(v) - def __getitem__(self,i): - i = _mkslice(i,self.dim) + def __getitem__(self, i): + i = _mkslice(i, self.dim) res = self.data[i] - if len(res)==1: return res[0] + if len(res) == 1: + return res[0] return array(res) def transpose(self): - return matrix(self.data,self.dtype) + return matrix(self.data, self.dtype) def __float__(self): - assert self.dim==1 + assert self.dim == 1 return float(self.data[0]) -#------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ # minimalistic numpy.matrix replacement class used as fallback # when numpy is not found in geometry module class matrix(object): - - def __init__(self,data,dtype=None,copy=True,transpose=False): + def __init__(self, data, dtype=None, copy=True, transpose=False): # check input data types: types = set([type(v) for v in data]) - if len(types)>1: raise TypeError + if len(types) > 1: + raise TypeError t = types.pop() # import data: if t in constants: - self.data = [array(data,dtype,copy)] + self.data = [array(data, dtype, copy)] else: - if transpose: data = zip(*data) - self.data = [array(v,dtype,copy) for v in data] + if transpose: + data = zip(*data) + self.data = [array(v, dtype, copy) for v in data] # define matrix sizes: self.n = len(self.data) sizes = set([len(v) for v in self.data]) - if len(sizes)>1: raise ValueError + if len(sizes) > 1: + raise ValueError self.p = sizes.pop() if dtype is None: # coerce types of arrays of matrix: types = set([v.dtype for v in self.data]) - tc,dtype = coerce_(types) + tc, dtype = coerce_(types) for v in self.data: v.coerce(dtype) self.dtype = dtype - def __len__(self): return self.n*self.p + def __len__(self): + return self.n * self.p def __str__(self): - s = '\n '.join([str(v) for v in self.data]) - return "[%s]"%s.strip() + s = "\n ".join([str(v) for v in self.data]) + return "[%s]" % s.strip() @property - def shape(self): return (self.n,self.p) + def shape(self): + return (self.n, self.p) - def lvecs(self): return self.data + def lvecs(self): + return self.data - def cvecs(self): return [array(v,self.dtype) for v in zip(*self.data)] + def cvecs(self): + return [array(v, self.dtype) for v in zip(*self.data)] - def copy(self): return matrix(self.data,self.dtype) + def copy(self): + return matrix(self.data, self.dtype) def transpose(self): - return matrix(self.data,dtype=self.dtype,transpose=True) + return matrix(self.data, dtype=self.dtype, transpose=True) def sum(self): return sum([sum(v) for v in self.data]) @make_ij_slices - def __getitem__(self,ij): - I,J = ij + def __getitem__(self, ij): + I, J = ij l = self.lvecs()[I] m = matrix([v[J] for v in l]) - if m.n==1: + if m.n == 1: v = m.data[0] - if v.dim==1: return v[0] - if len(l)>1: return v + if v.dim == 1: + return v[0] + if len(l) > 1: + return v return m @make_ij_slices - def __setitem__(self,ij,v): - I,J = ij - Ri =range(I.start,I.stop,I.step) - Rj =range(J.start,J.stop,J.step) - if type(v) in constants: v = (v,) + def __setitem__(self, ij, v): + I, J = ij + Ri = range(I.start, I.stop, I.step) + Rj = range(J.start, J.stop, J.step) + if type(v) in constants: + v = (v,) value = (x for x in v) for i in Ri: for j in Rj: self.data[i][j] = next(value) - def __add__(self,m): - if isinstance(m,constants): - return matrix([u+m for u in self.data]) + def __add__(self, m): + if isinstance(m, constants): + return matrix([u + m for u in self.data]) else: assert self.shape == m.shape - return matrix([u+v for (u,v) in zip(self.data,m.data)]) + return matrix([u + v for (u, v) in zip(self.data, m.data)]) - def __sub__(self,m): - if isinstance(m,constants): - return matrix([u-m for u in self.data]) + def __sub__(self, m): + if isinstance(m, constants): + return matrix([u - m for u in self.data]) else: assert self.shape == m.shape - return matrix([u-v for (u,v) in zip(self.data,m.data)]) + return matrix([u - v for (u, v) in zip(self.data, m.data)]) def __neg__(self): - return matrix([-x for x in self.data],dtype=self.dtype) + return matrix([-x for x in self.data], dtype=self.dtype) def __float__(self): - assert self.n==1 and self.p==1 - return self[0,0] - - def __radd__(self,v): - return self+v - def __rsub__(self,v): - return (-self)+v - - def __rmul__(self,k): - if not isinstance(k,constants): raise TypeError - return matrix([k*v for v in self.data]) - - def __mul__(self,X): - if isinstance(X,constants): - return X*self - if isinstance(X,array): - assert X.dim==self.p + assert self.n == 1 and self.p == 1 + return self[0, 0] + + def __radd__(self, v): + return self + v + + def __rsub__(self, v): + return (-self) + v + + def __rmul__(self, k): + if not isinstance(k, constants): + raise TypeError + return matrix([k * v for v in self.data]) + + def __mul__(self, X): + if isinstance(X, constants): + return X * self + if isinstance(X, array): + assert X.dim == self.p return array([v.dot(X) for v in self.data]) - if isinstance(X,matrix): + if isinstance(X, matrix): assert X.n == self.p - return matrix([self*v for v in X.cvecs()]) + return matrix([self * v for v in X.cvecs()]) - def __pow__(self,v): - S = [self]*v - assert len(S)>0 - return reduce(lambda x,y: x*y,S) + def __pow__(self, v): + S = [self] * v + assert len(S) > 0 + return reduce(lambda x, y: x * y, S) def __iter__(self): for l in self.data: for v in l: yield v + class SimplexMin(object): - def __init__(self,A,b,c): - self.A=A - self.b=b - self.c=c + def __init__(self, A, b, c): + self.A = A + self.b = b + self.c = c self.tableau() def tableau(self): - self.T=[] + self.T = [] def setup(self): self.enter = [] delf.outer = [] - diff --git a/grandalf/utils/nx.py b/grandalf/utils/nx.py index bc0e62b..5ac4b34 100644 --- a/grandalf/utils/nx.py +++ b/grandalf/utils/nx.py @@ -3,16 +3,18 @@ # published under GPLv2 license or EPLv1 license # Contributor(s): Fabio Zadrozny -__all__=['convert_grandalf_graph_to_networkx_graph', - 'convert_nextworkx_graph_to_grandalf', - ] +__all__ = [ + "convert_grandalf_graph_to_networkx_graph", + "convert_nextworkx_graph_to_grandalf", +] -#Some utilities to interact with networkx. +# Some utilities to interact with networkx. -#Converts a grandalf graph to a networkx graph. -#Note that the edge concept is the same, but a vertex in grandalf is called a node in networkx. +# Converts a grandalf graph to a networkx graph. +# Note that the edge concept is the same, but a vertex in grandalf is called a node in networkx. def convert_grandalf_graph_to_networkx_graph(G): from networkx import MultiDiGraph + nxg = MultiDiGraph() for v in G.V(): nxg.add_node(v.data) @@ -20,10 +22,12 @@ def convert_grandalf_graph_to_networkx_graph(G): nxg.add_edge(e.v[0].data, e.v[1].data) return nxg -#Converts a networkx graph to a grandalf graph. -#Note that the edge concept is the same, but a vertex in grandalf is called a node in networkx. + +# Converts a networkx graph to a grandalf graph. +# Note that the edge concept is the same, but a vertex in grandalf is called a node in networkx. def convert_nextworkx_graph_to_grandalf(G): from grandalf.graphs import Graph, Vertex, Edge + V = [] data_to_V = {} for x in G.nodes(): diff --git a/grandalf/utils/poset.py b/grandalf/utils/poset.py index 911105b..31f69df 100644 --- a/grandalf/utils/poset.py +++ b/grandalf/utils/poset.py @@ -5,51 +5,50 @@ from collections import OrderedDict -__all__ = ['Poset'] +__all__ = ["Poset"] -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ # Poset class implements a set but allows to interate over the elements in a # deterministic way and to get specific objects in the set. # Membership operator defaults to comparing __hash__ of objects but Poset # allows to check for __cmp__/__eq__ membership by using contains__cmp__(obj) -class Poset(object): - - def __init__(self,L): +class Poset(object): + def __init__(self, L): self.o = OrderedDict() for obj in L: self.add(obj) def __repr__(self): - return 'Poset(%r)' % (self.o,) + return "Poset(%r)" % (self.o,) def __str__(self): - f='%%%dd'%len(str(len(self.o))) - s=[] - for i,x in enumerate(self.o.values()): - s.append(f%i+'.| %s'%repr(x)) - return '\n'.join(s) + f = "%%%dd" % len(str(len(self.o))) + s = [] + for i, x in enumerate(self.o.values()): + s.append(f % i + ".| %s" % repr(x)) + return "\n".join(s) - def add(self,obj): + def add(self, obj): if obj in self: return self.get(obj) else: self.o[obj] = obj return obj - def remove(self,obj): + def remove(self, obj): if obj in self: obj = self.get(obj) del self.o[obj] return obj return None - def index(self,obj): + def index(self, obj): return list(self.o.values()).index(obj) - def get(self,obj): - return self.o.get(obj,None) + def get(self, obj): + return self.o.get(obj, None) - def __getitem__(self,i): + def __getitem__(self, i): return list(self.o.values())[i] def __len__(self): @@ -59,87 +58,89 @@ def __iter__(self): for obj in iter(self.o.values()): yield obj - def __cmp__(self,other): + def __cmp__(self, other): s1 = set(other.o.values()) s2 = set(self.o.values()) - return cmp(s1,s2) + return cmp(s1, s2) - def __eq__(self,other): + def __eq__(self, other): s1 = set(other.o.values()) s2 = set(self.o.values()) - return s1==s2 + return s1 == s2 - def __ne__(self,other): + def __ne__(self, other): s1 = set(other.o.values()) s2 = set(self.o.values()) - return s1!=s2 + return s1 != s2 def copy(self): return Poset(self.o.values()) __copy__ = copy + def deepcopy(self): from copy import deepcopy + L = deepcopy(self.o.values()) return Poset(L) - def __or__(self,other): + def __or__(self, other): return self.union(other) - def union(self,other): + def union(self, other): p = Poset([]) p.o.update(self.o) p.o.update(other.o) return p - def update(self,other): + def update(self, other): self.o.update(other.o) - def __and__(self,other): + def __and__(self, other): s1 = set(self.o.values()) s2 = set(other.o.values()) return Poset(s1.intersection(s2)) - def intersection(self,*args): + def intersection(self, *args): p = self for other in args: p = p & other return p - def __xor__(self,other): + def __xor__(self, other): s1 = set(self.o.values()) s2 = set(other.o.values()) return Poset(s1.symmetric_difference(s2)) - def symmetric_difference(self,*args): + def symmetric_difference(self, *args): p = self for other in args: p = p ^ other return p - def __sub__(self,other): + def __sub__(self, other): s1 = set(self.o.values()) s2 = set(other.o.values()) return Poset(s1.difference(s2)) - def difference(self,*args): + def difference(self, *args): p = self for other in args: p = p - other return p - def __contains__(self,obj): - return (obj in self.o) + def __contains__(self, obj): + return obj in self.o - def contains__cmp__(self,obj): - return (obj in self.o.values()) + def contains__cmp__(self, obj): + return obj in self.o.values() - def issubset(self,other): + def issubset(self, other): s1 = set(self.o.values()) s2 = set(other.o.values()) return s1.issubset(s2) - def issuperset(self,other): + def issuperset(self, other): s1 = set(self.o.values()) s2 = set(other.o.values()) return s1.issuperset(s2) @@ -147,8 +148,8 @@ def issuperset(self,other): __le__ = issubset __ge__ = issuperset - def __lt__(self,other): - return (self<=other and len(self)!=len(other)) + def __lt__(self, other): + return self <= other and len(self) != len(other) - def __gt__(self,other): - return (self>=other and len(self)!=len(other)) + def __gt__(self, other): + return self >= other and len(self) != len(other)