-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSimulation.py
More file actions
293 lines (257 loc) · 14.9 KB
/
Copy pathSimulation.py
File metadata and controls
293 lines (257 loc) · 14.9 KB
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
'''
This class will perform events on the constructed bus network: it will simulate
passenger and bus movement/queueing in the network
'''
import Parser
import Models
import warnings
from random import uniform
from math import log10
from copy import deepcopy
import itertools
class Simulation:
''' A class that controls the entire simulation and performs events using
the constructed bus network'''
def __init__(self):
self.Network = Models.Network()
self.params = {'control' : {},
'general' : {},
'roads' : {},
'routes' : {}
}
self.params['control']['ignoreWarnings'] = False
self.params['control']['optimiseParameters'] = False
self.params['control']['experimentation'] = False
self.params['general']['board'] = []
self.params['general']['disembarks'] = []
self.params['general']['departs'] = []
self.params['general']['new passengers'] = []
def __eq__(self, another):
return (self.Network == another.Network) and (self.params == another.params)
def addRoad(self, stop1, stop2, throughput):
''' This method adds a road with specified throughput between stop1 and stop2'''
if not ((stop1, stop2) in self.params['roads']):
self.params['roads'][(stop1, stop2)] = throughput
elif throughput != self.params['roads'][(stop1, stop2)]:
raise Exception('Two different throughputs are specified for the road {0} -> {1}').format(stop1, stop2)
def addRoute(self, routeID, stopIDs, busCount, capacity):
''' This method adds a new route to the network and stores the experimentation values'''
busCount.sort()
capacity.sort()
self.Network.addRoute(routeID, stopIDs, busCount[0], capacity[0])
self.params['routes'][routeID] = {'routeID' : [routeID],
'buses' : busCount,
'capacity' : capacity
}
def generateRouteSets(self):
''' This method generates all possible route experimental value combinations'''
route_product = []
for route in self.params['routes'].values():
product = [x for x in apply(itertools.product, route.values())]
route_product.append([dict(zip(route.keys(), p)) for p in product])
return [list(set) for set in apply(itertools.product, route_product)]
def generateRoadSets(self):
''' This method generates all possible route throughput rate combinations'''
product = [x for x in apply(itertools.product, self.params['roads'].values())]
return [dict(zip(self.params['roads'].keys(), p)) for p in product]
def generateGeneralParamSets(self):
''' This method generates all possible general simulation parameter combinations'''
values = [value if hasattr(value, '__iter__') else [value] for value in self.params['general'].values()]
product = [x for x in apply(itertools.product, values)]
return [dict(zip(self.params['general'].keys(), p)) for p in product]
def printExperimentationParameters(self, generalParamSet, roadSet, routeSet):
''' Method that prints all experimentation values of the given parameter dicts'''
for key in generalParamSet:
if len(self.params['general'][key]) > 1:
print key + ' ' + str(generalParamSet[key])
for (stop1, stop2) in roadSet:
if len(self.params['roads'][(stop1, stop2)]) > 1:
print 'road {0} {1} {2}'.format(stop1, stop2, roadSet[(stop1, stop2)])
for route in routeSet:
outStr = ''
for key in self.params['routes'].values()[0]:
if len(self.params['routes'][route['routeID']][key]) > 1:
outStr += ' ' + key + ' ' + str(route[key])
if outStr != '':
print 'route ' + str(route['routeID']) + outStr
def executeExperimentation(self, generalParamSets, roadSets, routeSets):
''' This method performs experimentation over all parameter values'''
initialNetwork = deepcopy(self.Network)
for generalParamSet in generalParamSets:
for roadSet in roadSets:
for routeSet in routeSets:
self.Network.changeGeneralParams(generalParamSet)
self.Network.changeRoadParams(roadSet)
self.Network.changeRouteParams(routeSet)
self.printExperimentationParameters(generalParamSet, roadSet, routeSet)
self.executeSimulationLoop(outputEvents=False)
self.printStatistics()
self.Network = deepcopy(initialNetwork)
def executeOptimisation(self, generalParamSets, roadSets, routeSets):
''' This method performs parameter optimisation'''
minCost = None
initialNetwork = deepcopy(self.Network)
for generalParamSet in generalParamSets:
for roadSet in roadSets:
for routeSet in routeSets:
if minCost != 0:
self.Network.changeGeneralParams(generalParamSet)
self.Network.changeRoadParams(roadSet)
self.Network.changeRouteParams(routeSet)
self.executeSimulationLoop(outputEvents=False)
# Getting the number of missed passengers:
totalPassengers = sum([stop.missedPassengers for stop in self.Network.stops.values()])
generalParamSum = sum(generalParamSet.values())
roadParamSum = sum(roadSet.values())
routeParamSum = sum(sum([route.values() for route in routeSet], []))
cost = totalPassengers * (generalParamSum + roadParamSum + routeParamSum)
if not (minCost) or (minCost > cost):
minCost = cost
maxGeneralParamSet = generalParamSet
maxRoadSet = roadSet
maxRouteSet = routeSet
self.Network = deepcopy(initialNetwork)
print 'Bus network is optimized with setting the parameters as:'
self.printExperimentationParameters(maxGeneralParamSet, maxRoadSet, maxRouteSet)
def printStatistics(self):
''' Method that prints the statistics of the most recent run of the simulation'''
# Missed passengers:
total = 0
for stop in self.Network.stops.values():
print 'number of missed passengers stop {0} {1}'.format(stop.stopID, stop.missedPassengers)
total += stop.missedPassengers
for route in self.Network.routes.values():
print 'number of missed passengers route {0} {1}'.format(route.routeID, route.missedPassengers)
print 'number of missed passengers {0}'.format(total)
# Average number of passengers:
total = 0.0
for route in self.Network.routes.values():
totalPerRoute = 0.0
for bus in route.buses:
print 'average passengers bus {0}.{1} {2}'.format(bus.routeID, bus.busNumber, bus.averagePassengersTravelling)
totalPerRoute += bus.averagePassengersTravelling
''' I find this statistic a bit ambiguous. There are 2 possible cases:
1. The "average passengers route" should say how many passengers on average are on one of the route's buses
2. The "average passengers route" should say how many passengers on average are on the entire route
I left the first case uncommented. The second one is commented out below the first one.
'''
print 'average passengers route {0} {1}'.format(route.routeID, totalPerRoute/len(route.buses))
#print 'average passengers route {0} {1}'.format(route.routeID, totalPerRoute)
total += totalPerRoute
''' I find this statistic a bit ambiguous. There are 2 possible cases:
1. The "average passengers" should say how many passengers on average are on one of the routes
2. The "average passengers" should say how many passengers on average are on the entire network
I left the first case uncommented. The second one is commented out below the first one.
'''
print 'average passengers {0}'.format(total/len(self.Network.routes))
#print 'average passengers {0}'.format(total)
# Average time spent queueing:
totalTime = 0.0
totalBuses = 0
for stop in self.Network.stops.values():
print 'average queueing at stop {0} {1}'.format(stop.stopID, stop.totalQueueingTime/stop.numberOfBusesQueued)
totalTime += stop.totalQueueingTime
totalBuses += stop.numberOfBusesQueued
print 'average queueing at all stops {0}'.format(totalTime/totalBuses)
# I am not sure if there should be an empty line printed after the statistics.
# It looks nicer, but if it messes up your output parser then just comment it out.
print ''
def executeSimulation(self):
''' This method chooses the right kind of simulation type to be run '''
generalParamSets = self.generateGeneralParamSets()
roadSets = self.generateRoadSets()
routeSets = self.generateRouteSets()
if self.params['control']['optimiseParameters']:
self.executeOptimisation(generalParamSets, roadSets, routeSets)
elif self.params['control']['experimentation']:
self.executeExperimentation(generalParamSets, roadSets, routeSets)
else:
self.Network.changeGeneralParams(generalParamSets[0])
self.Network.changeRoadParams(roadSets[0])
self.Network.changeRouteParams(routeSets[0])
self.executeSimulationLoop()
self.printStatistics()
def executeSimulationLoop(self, outputEvents=True):
''' This method implements the main simulation loop '''
currentTime = 0
while currentTime <= self.params['control']['stopTime']:
# Getting all of the events that could occur:
rates = self.getEventRates()
totalRate = (self.Network.params['new passengers'] + rates['paxRTBRate'] +
rates['paxRTDRate'] + rates['busesRTARate'] +
rates['busesRTDRate'])
delay = -(1.0/totalRate) * log10(uniform(0.0, 1.0))
self.executeNextEvent(totalRate, rates, currentTime, outputEvents)
currentTime += delay
self.Network.finishTakingStatistics(self.params['control']['stopTime'])
def getEventRates(self):
''' This method gets rates needed for choosing the event to execute'''
rates = {}
# Passengers ready to board rate:
rates['paxRTBRate'] = len(self.Network.getPaxRTB()) * self.Network.params['board']
# Passengers ready to disembark rate:
rates['paxRTDRate'] = len(self.Network.getPaxRTD()) * self.Network.params['disembarks']
# Buses ready to depart rate:
rates['busesRTDRate'] = len(self.Network.getBusesRTD()) * self.Network.params['departs']
# Buses ready to arrive rate:
rates['busesRTARate'] = sum([self.Network.getThroughput(bus) for (bus, route) in self.Network.getBusesRTA()])
#print rates
return rates
def executeNextEvent(self, totalRate, rates, time, outputEvents):
''' This method chooses and executes an event, based on event rates'''
choice = uniform(0, totalRate)
if choice < rates['paxRTBRate']:
self.Network.boardPassenger(time, outputEvents)
elif choice < (rates['paxRTBRate'] + rates['paxRTDRate']):
self.Network.disembarkPassenger(time, outputEvents)
elif choice < (rates['paxRTBRate'] + rates['paxRTDRate'] +
rates['busesRTDRate']):
self.Network.departBus(time, outputEvents)
elif choice < (rates['paxRTBRate'] + rates['paxRTDRate'] +
rates['busesRTDRate'] + rates['busesRTARate']):
self.Network.arriveBus(time, outputEvents)
else:
self.Network.addPassenger(time, outputEvents)
def validateSimulation(self):
''' This method checks if simulation's bus network and other parameters are valid or not '''
warnings.simplefilter('always' if self.params['control']['ignoreWarnings'] else 'error')
# Checking if all of the rates that must be specified are there:
try:
if self.params['general']['board'] == []:
raise Exception('No board rate has been specified')
if self.params['general']['disembarks'] == []:
raise Exception('No disembarks rate has been specified')
if self.params['general']['departs'] == []:
raise Exception('No departs rate has been specified')
if self.params['general']['new passengers'] == []:
raise Exception('No new passenger rate has been specified')
if not('stopTime' in self.params['control']):
raise Exception('No stop time has been specified')
except KeyError:
raise Exception('Some of the necessary rates of the network are not specified')
# Checking if all routes have roads defined:
for route in self.Network.routes.values():
for stop1 in route.stopSequence:
stop2 = route.getNextStop(stop1)
try:
self.params['roads'][(stop1, stop2)]
except KeyError:
raise Exception('The road between stops {0} and {1} is undefined'.format(stop1, stop2))
# Checking if all roads are in some route:
for (depStop, destStop) in self.params['roads']:
roadUsed = False
for route in self.Network.routes.values():
for stop1 in route.stopSequence:
if depStop == stop1 and destStop == route.getNextStop(stop1):
roadUsed = True
if not roadUsed:
warnings.warn('Road between stops {0} and {1} is specified but not used'.format(depStop, destStop))
# Checking if the simulation has experimentation parameters if we need to optimise it:
if self.params['control']['optimiseParameters'] and not (self.params['control']['experimentation']):
raise Exception('There are no experimentation values given although optimisation flag is set to True')
if __name__ == '__main__':
simulation = Simulation()
fileName = raw_input('Please enter the name of the input file: ')
Parser.Parser.parseFile(fileName, simulation)
simulation.validateSimulation()
simulation.executeSimulation()