-
Notifications
You must be signed in to change notification settings - Fork 2
/
travellingPlane.py
executable file
·293 lines (227 loc) · 8.68 KB
/
travellingPlane.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
from shared import *
import numpy,itertools
def progressiveRoute(nodes,beta,nodesPerRoute=4):
"""function to progressively work through the given nodes and
return an approximation of the best cost route"""
#define total nodes in subset
totalRouteNodes = nodesPerRoute*2
#set all nodes as numpy arrays then set energy matrix
numberNode = len(nodes)
nodes = [numpy.array(node) for node in nodes]
setEnergyMatrix(nodes,beta)
#define list of nodes sorted by vertical location
sortedIndexs = sorted(list(range(len(nodes))), key = lambda x:nodes[x][2])
#assign start and end points for total route
routeA = [sortedIndexs[0]]
routeB = [sortedIndexs[1]]
#work through all nodes in steps of two
for i in range(0,numberNode,2):
#select last indexs of both routes as base indexs
indexA = routeA[-1]
indexB = routeB[-1]
#consider nodes above current
currentIndexs = sortedIndexs[:i+totalRouteNodes]
#remove nodes already in route
for index in routeA[:-1]:
currentIndexs.remove(index)
for index in routeB[:-1]:
currentIndexs.remove(index)
#if there are 3 or less nodes left calculate the exact route
if (len(currentIndexs) <= 3):
#find all possible routes between last node in route a and first in route b
routes = routePermutations(currentIndexs,indexA,indexB)
#compute costs for each route
costs = [routeCost(route) for route in routes]
#select cheapest cost
bestCost = min(costs)
#select corresponding route
bestRoute = routes[costs.index(bestCost)]
#for all items in route aside from start and end nodes add them to route a
for index in bestRoute[1:-1]:
routeA.append(index)
#break the for loop as route complete
break
#compute the index of the highest node
highestIndex = sorted(currentIndexs, key=lambda x:nodes[x][2])[-1]
#calculate all possible route combinations using even number of current indexs
possibleRoutes = doubleRoutePermutations(currentIndexs[:2*(len(currentIndexs)//2)],indexA,indexB)
#assign a default best route and cost to
bestRoute = possibleRoutes[0]
bestCost = numpy.infty
#for all possible routes check if least cost
for route in possibleRoutes:
#define up route A (plus route to highest node in subset)
currentRouteA = list(route[0])+[highestIndex]
#define down route B
currentRouteB = [i for i in reversed(route[1])]
#compute cost of both routes
costA = routeCost(currentRouteA)
costB = routeCost(currentRouteB)
cost = sum([costA,costB])
#if cost is improvement assign best route and best cost
if (cost<bestCost):
bestRoute = route
bestCost = cost
#decompose best route into A and B
bestRouteA,bestRouteB = bestRoute
#if the best routes are more than inity length add second nodes to final routes
if (max([len(bestRouteA),len(bestRouteB)])>1):
routeA.append(bestRouteA[1])
routeB.append(bestRouteB[1])
#combine routes A and B to form a single best route
routeB.reverse()
bestRoute = routeA+routeB
bestCost = routeCost(bestRoute,True)
return bestRoute,bestCost
def allRoutes(beta,nodes,startIndex=None,endIndex=None):
"function to compute and return all possible route combinations and the resulting costs"
#define indexs of all nodes given
numberNodes = len(nodes)
nodeIndexs = list(range(0,numberNodes))
#if start node defined remove from index list
if (startIndex != None):
nodeIndexs.pop(startIndex)
startIndex = [startIndex]
else:
startIndex = []
#if end node defined remove from index list
if (endIndex != None):
nodeIndexs.pop(endIndex)
endIndex = [endIndex]
else:
endIndex = []
#set energy matrix from all nodes
nodes = [numpy.array(node) for node in nodes]
setEnergyMatrix(nodes,beta)
#for all permutations append route and cost
routes,costs = [],[]
for route in itertools.permutations(nodeIndexs):
route = startIndex+list(route)+endIndex
cost = routeCost(route,True)
routes.append(route)
costs.append(cost)
return routes,costs
def orderNodes(nodes,order,loop=False):
"function to order the given nodes and return along with the final node to produce a route"
#order nodes by order given
orderedNodes = [nodes[index] for index in order]
#if loop required append first node at end
if loop: orderedNodes.append(nodes[order[0]])
return orderedNodes
def routeCost(route,loop=False):
"calculates the cost of a route given a route (sequence of nodes)"
cost = 0
#for all connections between nodes append the cost of the path
for i in range(len(route)-1):
nodeIndexFrom = route[i]
nodeIndexTo = route[i+1]
cost += energyMatrix[nodeIndexFrom][nodeIndexTo]
#if loop required add cost of path back to start node
if loop:
nodeIndexFrom = route[-1]
nodeIndexTo = route[0]
cost += energyMatrix[nodeIndexFrom][nodeIndexTo]
return cost
def setEnergyMatrix(nodes,beta):
"returns a matrix of energy costs to navigate the given node locations"
global energyMatrix
numberNodes = len(nodes)
energyMatrix = numpy.zeros([numberNodes,numberNodes])
for i in range(numberNodes):
for j in range(numberNodes):
vector = nodes[j]-nodes[i]
distance = numpy.linalg.norm(vector)
height = vector[2]
energyMatrix[i][j] = calculateEnergy(distance,height,beta)
def routePermutations(indexs,startIndex=None,endIndex=None):
"""function to return all possible route permutations given a start and end index"""
#if start node defined remove from indexs
if (startIndex != None):
indexs.remove(startIndex)
startIndex = [startIndex]
else:
startIndex = []
#if end node defined remove from indexs
if (endIndex != None):
indexs.remove(endIndex)
endIndex = [endIndex]
else:
endIndex = []
#compute all possible permutations
permutations = []
for route in itertools.permutations(indexs):
permutations.append(startIndex+list(route)+endIndex)
return permutations
def doubleRoutePermutations(indexs,nodeA=None,nodeB=None):
"""function to display the possible permutation sets for 2 routes
starting at node index nodeA and node index nodeB in set indexs"""
#define the number nodes and route length given the node indexs
numberNodes = len(indexs)
routeLength = numberNodes//2
#if the number of nodes is not even rasise an error
if (numberNodes%2 != 0):
raise ValueError("N needs to be an even number of indexs, {} is not even".format(numberNodes))
#for all potential permutations of length N/2 in N indexs
permutationA,permutationB = [],[]
for item in itertools.permutations(indexs,routeLength):
#if nodeA is the start node or nodeA is none append to permuatonA
if ((nodeA == item[0]) or (nodeA == None)):
permutationA.append(item)
#if nodeB is the start node or nodeB is none append to permuatonB
elif ((nodeB == item[0]) or (nodeB == None)):
permutationB.append(item)
def checkRoutes(routeA,routeB):
"""checks the given routes to see if any indexs are repeated
returns True if exclusive
returns False if any repetition
"""
for node in routeA:
if (node in routeB):
return False
return True
#for all combinations of sub routes
permutations = []
for routeA in permutationA:
for routeB in permutationB:
#if the combination is exclusive append to final permutations
if checkRoutes(routeA,routeB):
permutations.append([routeA,routeB])
return permutations
def routeData(nodes,beta,loop=False):
"function to return the route data of the given node ordering"
distances,heights,costs = [],[],[]
def distanceHeightCost(nodeFrom,nodeTo):
"returns the distance,height and cost for travel between nodes"
vector = nodes[i+1]-nodes[i]
distance = numpy.linalg.norm(vector)
height = vector[2]
cost = calculateEnergy(distance,height,beta)
return distance,height,cost
for i in range(len(nodes)-1):
nodeFrom = nodes[i]
nodeTo = nodes[i+1]
distance,height,cost = distanceHeightCost(nodeFrom,nodeTo)
distances.append(distance)
heights.append(height)
costs.append(cost)
if loop:
nodeFrom = nodes[-1]
nodeTo = nodes[0]
distance,height,cost = distanceHeightCost(nodeFrom,nodeTo)
distances.append(distance)
heights.append(height)
costs.append(cost)
heights = [abs(height) for height in heights]
glideHeight = sum([height for i,height in enumerate(heights) if (costs[i] == 0)])
glideDistance = sum([distance for i,distance in enumerate(distances) if (costs[i] == 0)])
totalHeight = sum(heights)
totalDistance = sum(distances)
totalCost = sum(costs)
data = {
"cost":totalCost,
"height":totalHeight,
"distance":totalDistance,
"glideHeight":glideHeight,
"glideDistance":glideDistance
}
return data