From d2383f368cee3e7839cc012fd05725a6027c4a25 Mon Sep 17 00:00:00 2001 From: Mikko Vinni Date: Thu, 24 Dec 2015 13:38:04 +0200 Subject: [PATCH 1/2] use a greedy total path length optimizer to improve edge order Records edge dependencies and uses that information to reorder edges in the edge sequence as long as the path length is reduced. --- lib/Triangle.py | 11 ++++- lib/agentOrder.py | 123 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 102 insertions(+), 32 deletions(-) diff --git a/lib/Triangle.py b/lib/Triangle.py index 3b818ba..a466a57 100644 --- a/lib/Triangle.py +++ b/lib/Triangle.py @@ -53,7 +53,7 @@ def try_ordered_edge(a,p,q,reversible): p,q = q,p m = a.size() - a.add_edge(p,q,{'order':m,'reversible':reversible,'fields':[]}) + a.add_edge(p,q,{'order':m,'reversible':reversible,'fields':[],'depends':[]}) try: a.edgeStack.append( (p,q) ) @@ -234,10 +234,19 @@ def markEdgesWithFields(self): p,q = edges[lastInd] self.a.edge[p][q]['fields'].append(self.verts) + # the last edge depends on the other two + del edges[lastInd] + self.a.edge[p][q]['depends'].extend(edges) for child in self.children: child.markEdgesWithFields() + # all edges starting from inside this triangle have to be completed before it + for c in self.contents: + self.a.edge[p][q]['depends'].append(c) + + #print("edge %d-%d depends on: %s" % (p, q, self.a.edge[p][q]['depends'])) + def edgesByDepth(self,depth): # Return list of edges of triangles at given depth # 0 means edges of this very triangle diff --git a/lib/agentOrder.py b/lib/agentOrder.py index e604806..822e2a4 100644 --- a/lib/agentOrder.py +++ b/lib/agentOrder.py @@ -237,48 +237,109 @@ def getAgentOrder(a,nagents,orderedEdges): # def improveEdgeOrder(a): ''' - Edges that do not complete any fields can be made earlier - This method alters the graph a such that - The relative order of edges that complete fields is unchanged - Edges that do not complete fields may only be completed earlier - Where possible, non-completing edges are made immediately before another edge with same origin + A greedy algorithm to reduce the path length. + Moves edges earlier or later, if they can be moved (dependencies are + done in the proper order) and the move reduces the total length of the + path. + The algorithm tries to move 1 to 5 edges at the same time as a block + to improve upon certain types of local optima. ''' + m = a.size() # If link i is e then orderedEdges[i]=e orderedEdges = [-1]*m + geo = np.array([ a.node[i]['geo'] for i in xrange(a.order())]) + d = geometry.sphereDist(geo,geo) + + def pathLength(d, edges): + return sum([d[edges[i][0]][edges[i+1][0]] for i in xrange(len(edges)-1)]) + + def dependsOn(subjects, objects): + ''' + Returns True, if an edge inside 'objects' should be made before + one (or more) of the edges inside 'subjects' + ''' + for p,q in subjects: + depends = a.edge[p][q]['depends'] + for u,v in objects: + if depends.count((u,v,)) + depends.count(u) > 0: + return True + + return False + + + def possiblePlaces(j, block): + ''' + A generator returning the possible places of the given + block of edges within the complete edge sequence. + The current position (j) is not returned. + ''' + pos = j + # smaller index means made earlier + while pos > 0 and not dependsOn(block, [orderedEdges[pos-1]]): + pos -= 1 + yield pos + + pos = j + bsize = len(block) + n = len(orderedEdges) - bsize + 1 + # bigger index means made later + while pos < n-1 and not dependsOn([orderedEdges[pos+bsize]], block): + pos += 1 + yield pos + + for p,q in a.edges_iter(): orderedEdges[a.edge[p][q]['order']] = (p,q) - for j in xrange(1,m): - p,q = orderedEdges[j] - # Only move those that don't complete fields - if len(a.edge[p][q]['fields']) > 0: - continue - -# print j,p,q,a.edge[p][q]['fields'] - - origin = orderedEdges[j][0] - # The first time this portal is used as an origin - i = 0 - while orderedEdges[i][0]!=origin: - i+=1 - - if i %d): %s" % (j, best, bestPath)) + orderedEdges = bestPath + cont = True + + length = pathLength(d, orderedEdges) + print("Length reduction: original = %d, improved = %d, change = %d meters" % (origLength, length, length-origLength)) + for i in xrange(m): p,q = orderedEdges[i] -# print p,q,a.edge[p][q]['fields'] a.edge[p][q]['order'] = i -# print + if __name__=='__main__': order = [0,5,5,5,2,2,1,0] From 2f0cedac56cabe36939c0e95bd7cc16ed67c7891 Mon Sep 17 00:00:00 2001 From: Mikko Vinni Date: Fri, 25 Dec 2015 23:29:33 +0200 Subject: [PATCH 2/2] fix try_reduce_out_degree: don't reverse non-reversible edges --- lib/Triangle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Triangle.py b/lib/Triangle.py index a466a57..fff3d05 100644 --- a/lib/Triangle.py +++ b/lib/Triangle.py @@ -19,7 +19,7 @@ def try_reduce_out_degree(a,p): # Reverse as many edges out-edges of p as possible toremove = [] for q in a.edge[p]: - if a.out_degree(q) < 8: + if a.out_degree(q) < 8 and a.edge[p][q]['reversible']: a.add_edge(q,p) a.edge[q][p] = a.edge[p][q] toremove.append(q)