Пример #1
0
    def test_runSimulation_maxEvents(self):
        """Tests execution of simulation events with max_event parameter."""
        # The FEL contains multiple events.
        testEventTimestamp = 10
        testEventData = {'processed': False}
        testEvent = engine.DiscreteEvent(
            MockEvent, testEventTimestamp, data=testEventData)
        self.simEngine.schedule(testEvent)

        earlierTestEventData = {'processed': False}
        earlierTestEvent = engine.DiscreteEvent(
            MockEvent, testEventTimestamp - 1, data=earlierTestEventData)
        self.simEngine.schedule(earlierTestEvent)

        # The events are unprocessed.
        self.assertEqual(2, len(self.simEngine.FEL))
        self.assertEqual(False, testEventData['processed'])
        self.assertEqual(False, earlierTestEventData['processed'])

        # The simulation runs with max_events parameter.
        self.simEngine.runSimulation(maxEvents=1)

        # Only max_event events were processed.
        self.assertEqual([testEvent], self.simEngine.FEL)
        self.assertEqual(False, testEventData['processed'])
        self.assertEqual(True, earlierTestEventData['processed'])
        # The simulation time has been updated.
        self.assertEqual(
            earlierTestEvent.timestamp, self.simEngine.currentTime())
Пример #2
0
    def test_runSimulation(self):
        """Tests execution of simulation events."""
        # The simulation time is at zero.
        self.assertEqual(0, self.simEngine.currentTime())

        # The FEL contains events.
        testEventTimestamp = 10
        testEventData = {'processed': False}
        testEvent = engine.DiscreteEvent(
            MockEvent, testEventTimestamp, data=testEventData)
        self.simEngine.schedule(testEvent)

        earlierTestEventData = {'processed': False}
        earlierTestEvent = engine.DiscreteEvent(
            MockEvent, testEventTimestamp - 1, data=earlierTestEventData)
        self.simEngine.schedule(earlierTestEvent)
        
        # The events are unprocessed.
        self.assertEqual(2, len(self.simEngine.FEL))
        self.assertEqual(False, testEventData['processed'])
        self.assertEqual(False, earlierTestEventData['processed'])

        # The simulation runs.
        self.simEngine.runSimulation()

        # All events in the FEL were processed.
        self.assertEqual(0, len(self.simEngine.FEL))
        self.assertEqual(True, testEventData['processed'])
        self.assertEqual(True, earlierTestEventData['processed'])
        # The simulation time has been updated.
        self.assertEqual(testEventTimestamp, self.simEngine.currentTime())
Пример #3
0
    def test_schedule(self):
        """Tests scheduling of events."""
        # FEL is empty before adding events.
        self.assertEqual(0, len(self.simEngine.FEL))

        # An event is scheduled in the FEL.
        testEventTimestamp = 10
        testEvent = engine.DiscreteEvent(
            MockEvent, testEventTimestamp, data={'processed': False})
        self.simEngine.schedule(testEvent)
        self.assertEqual(
            [testEvent], self.simEngine.FEL)

        # An earlier event is scheduled at the front of the FEL.
        earlierTestEvent = engine.DiscreteEvent(
            MockEvent, testEventTimestamp - 1, data={'processed': False})
        self.simEngine.schedule(earlierTestEvent)
        self.assertEqual(
            [earlierTestEvent, testEvent],
            self.simEngine.FEL)

        # An later event is scheduled at the end of the FEL.
        laterTestEvent = engine.DiscreteEvent(
            MockEvent, testEventTimestamp + 1, data={'processed': False})
        self.simEngine.schedule(laterTestEvent)
        self.assertEqual(
            [earlierTestEvent, testEvent, laterTestEvent],
            self.simEngine.FEL)
Пример #4
0
    def test_initializeEvent(self):
        """Tests the Initialize event."""
        self.globalData = self._initGlobalData(self.TEST_NUM_STATIONS,
                                               initEntities=False)

        # The Initialize event is scheduled (but not yet processed).
        initEvent = engine.DiscreteEvent(
            nycbike.Initialize,
            -1,
            globalData=self.globalData,
            initialDistribution=self.TEST_INITIAL_BIKE_DISTRIBUTION,
            racksPerStation=30)
        self.simEngine.schedule(initEvent)

        # Stations are not yet initialized.
        self.assertEqual([], self.globalData['stations'])
        self.assertEqual([], self.globalData['pickupQueues'])
        self.assertEqual([], self.globalData['dropoffQueues'])
        # Arrival events are not yet scheduled.
        self.assertEqual([initEvent], self.simEngine.FEL)

        # The Initialize event is processed.
        self.simEngine.runSimulation(maxEvents=1)

        # Checkout lines/counters are initialized.
        self.assertEqual(self.TEST_NUM_STATIONS,
                         len(self.globalData['stations']))
        self.assertEqual(self.TEST_NUM_STATIONS,
                         len(self.globalData['pickupQueues']))
        self.assertEqual(self.TEST_NUM_STATIONS,
                         len(self.globalData['dropoffQueues']))

        # Arrival events are scheduled.
        self.assertTrue(initEvent not in self.simEngine.FEL)
        self.assertTrue(len(self.simEngine.FEL) > 0)
Пример #5
0
    def test_rideEndEvent_racksAvailable(self):
        """Tests the RideEnd event when racks are available."""
        currentTime = 100
        self.simEngine.simTime = currentTime
        self.globalData = self._initGlobalData(self.TEST_NUM_STATIONS,
                                               initEntities=True)

        # The station has racks available.
        testStationID = 0
        testStation = self.globalData['stations'][testStationID]
        numBikesBefore = testStation.numBikes
        numRacksBefore = testStation.numRacks
        self.assertTrue(numRacksBefore > 0)

        # Two customers are waiting for bike pickup.
        testPickupQueue = self.globalData['pickupQueues'][testStationID]
        # One customer has waited too long and will leave the station.
        longWaitCustomer = nycbike.Customer()
        longWaitCustomer.startID = testStationID
        longWaitCustomer.startPickupWait = currentTime - 100  # > REFUND_TIME
        testPickupQueue.put(longWaitCustomer)
        # The other customer has just arrived.
        shortWaitCustomer = nycbike.Customer()
        shortWaitCustomer.startID = testStationID
        shortWaitCustomer.startPickupWait = currentTime - 1  # < REFUND_TIME
        testPickupQueue.put(shortWaitCustomer)

        # The RideEnd event is scheduled and processed.
        customer = nycbike.Customer()
        customer.endID = testStationID
        rideEndEvent = engine.DiscreteEvent(nycbike.RideEnd,
                                            10,
                                            globalData=self.globalData,
                                            stationID=testStationID,
                                            customer=customer)
        self.simEngine.schedule(rideEndEvent)
        self.simEngine.runSimulation(maxEvents=1)

        # The station attributes were updated.
        self.assertEqual(numBikesBefore + 1, testStation.numBikes)
        self.assertEqual(numRacksBefore - 1, testStation.numRacks)

        # Revenue is unchanged.
        self.assertEqual(0, self.globalData['statistics']['Revenue'])

        # No customers are waiting for pickup.
        self.assertEqual(0, len(testPickupQueue))
        # An Arrival event was scheduled for the short-wait customer.
        self.assertEqual(1, len(self.simEngine.FEL))
        self.assertEqual(nycbike.Arrival, self.simEngine.FEL[0].handler)
        self.assertEqual(shortWaitCustomer,
                         self.simEngine.FEL[0].handlerKwargs['customer'])
Пример #6
0
    def test_arrivalEvent_noBikesAvailable(self):
        """Tests the Arrival event when no bikes are available."""
        self.globalData = self._initGlobalData(self.TEST_NUM_STATIONS,
                                               initEntities=True)

        # The station has zero bikes available.
        testStationID = 0
        testStation = self.globalData['stations'][testStationID]
        testStation.numBikes = 0
        numRacksBefore = testStation.numRacks
        # There are zero people waiting for a bike.
        testPickupQueue = self.globalData['pickupQueues'][testStationID]
        self.assertEqual(0, len(testPickupQueue))

        # The Arrival event is scheduled and processed.
        arrivalEvent = engine.DiscreteEvent(nycbike.Arrival,
                                            10,
                                            globalData=self.globalData,
                                            stationID=testStationID)
        self.simEngine.schedule(arrivalEvent)
        self.simEngine.runSimulation(maxEvents=1)

        # The customer was put in the pickup waiting queue.
        self.assertEqual(1, len(testPickupQueue))
        # The start of waiting time was recorded.
        customer = testPickupQueue.remove()
        self.assertEqual(arrivalEvent.timestamp, customer.startPickupWait)

        # The station attributes were not updated.
        self.assertEqual(0, testStation.numBikes)
        self.assertEqual(numRacksBefore, testStation.numRacks)

        # Revenue is unchanged.
        self.assertEqual(0, self.globalData['statistics']['Revenue'])

        # The next Arrival event was scheduled for the station.
        scheduledArrivals = [
            e for e in self.simEngine.FEL if e.handler == nycbike.Arrival
        ]
        self.assertEqual(1, len(scheduledArrivals))
        self.assertEqual(testStationID,
                         scheduledArrivals[0].handlerKwargs['stationID'])
        # No other events were scheduled.
        self.assertEqual(1, len(self.simEngine.FEL))
Пример #7
0
    def test_rideEndEvent_noRacksAvailable(self):
        """Tests the RideEnd event when no racks are available."""
        currentTime = 100
        self.simEngine.simTime = currentTime
        self.globalData = self._initGlobalData(self.TEST_NUM_STATIONS,
                                               initEntities=True)

        # The station has zero racks available.
        testStationID = 0
        testStation = self.globalData['stations'][testStationID]
        numBikesBefore = testStation.numBikes
        testStation.numRacks = 0

        # One customer is waiting for bike pickup.
        testPickupQueue = self.globalData['pickupQueues'][testStationID]
        waitingCustomer = nycbike.Customer()
        waitingCustomer.startID = testStationID
        waitingCustomer.startPickupWait = currentTime - 1  # < REFUND_TIME
        testPickupQueue.put(waitingCustomer)

        # The RideEnd event is scheduled and processed.
        customer = nycbike.Customer()
        customer.endID = testStationID
        rideEndEvent = engine.DiscreteEvent(nycbike.RideEnd,
                                            10,
                                            globalData=self.globalData,
                                            stationID=testStationID,
                                            customer=customer)
        self.simEngine.schedule(rideEndEvent)
        self.simEngine.runSimulation(maxEvents=1)

        # The station attributes were not changed.
        self.assertEqual(numBikesBefore, testStation.numBikes)
        self.assertEqual(0, testStation.numRacks)

        # Customers are still waiting for pickup.
        self.assertTrue(waitingCustomer in testPickupQueue)

        # The current customer entered the dropoff queue.
        self.assertTrue(
            customer in self.globalData['dropoffQueues'][testStationID])
        # The start of dropoff waiting time was recorded.
        self.assertEqual(rideEndEvent.timestamp, customer.startDropoffWait)
Пример #8
0
def Initialize(simEngine, **kwargs):
    """Initializes bike stations and schedules arrivals."""
    initialDistribution = kwargs['initialDistribution']
    globalData = kwargs['globalData']
    arrivalTimes = globalData['arrivalTimes']
    numStations = len(arrivalTimes)

    # Initialize bike stations and queues.
    for stationID in range(numStations):
        globalData['stations'].append(
            Station(stationID, kwargs['racksPerStation'],
                    initialDistribution[stationID]))
        globalData['pickupQueues'].append(Queue())
        globalData['dropoffQueues'].append(Queue())

    # Schedule first arrival event for each station.
    for stationID in range(numStations):
        if len(arrivalTimes[stationID]) > 0:
            t = arrivalTimes[stationID].pop(0)
            simEngine.schedule(
                engine.DiscreteEvent(Arrival,
                                     t,
                                     stationID=stationID,
                                     globalData=globalData))
Пример #9
0
    def run(self,
            initialDistribution=None,
            totalNumBikes=NUM_BIKES,
            racksPerStation=RACKS,
            scaleArrivalRate=1,
            rngSeed=None,
            tripDataDir=None):
        """Runs the store checkout simulation until it completes.

        Args:
            initialDistribution: Initial distribution of bikes to stations.
            totalNumBikes: Total number of bikes used in the simulation. This
                parameter is ignored if initialDistribution is specified.
            racksPerStation: Number of bike racks per station.
            scaleArrivalRate: Scale factor for number of arrivals that occur
                during the simulation.

        Returns:
            Dictionary of simulation results.
        """
        simStartTime = time.time()
        logging.info('Citi Bike Sharing Simulation')
        logging.info('\ttotalNumBikes: %d' % totalNumBikes)
        logging.info('\tracksPerStation: %d' % racksPerStation)
        logging.info('\tscaleArrivalRate: %.3f' % scaleArrivalRate)

        # Seed RNG if specified.
        if rngSeed is not None:
            logging.info('RNG seed: %d' % rngSeed)
            np.random.seed(rngSeed)

        # Load statistics derived from the Citi Bike trip dataset.
        tripDataDir = {'tripDataDir': tripDataDir} if tripDataDir else {}
        tripCountData, tripDurations, destinationP = (
            load_trip_stats.loadTripStatistics(**tripDataDir))
        numStations = tripCountData.shape[0]

        # Compute arrival times based on trip count data for each station and
        # the arrival rate scale factor.
        tripCountData = np.rint(tripCountData * scaleArrivalRate).astype(int)
        arrivalTimes = self.computeArrivalTimes(tripCountData)

        # Initial distribution of bikes to stations (set at time 00:00).
        if initialDistribution is None:
            initialDistribution = self.almostUniformWithTotalSum(
                numStations, totalNumBikes)
        assert len(initialDistribution) == len(tripCountData)

        # Initialize simulation statistics.
        statistics = {
            'Revenue': 0,
            'TimeWaitForDropoff': np.zeros(numStations),
            'TimeWaitForCycle': np.zeros(numStations),
            'CustomersLost': np.zeros(numStations),
            'BikesLost': 0,
            'IdleTime': np.zeros(numStations),
        }

        # Global simulation variables.
        globalData = {
            # Entities.
            'stations': [],
            'pickupQueues': [],
            'dropoffQueues': [],
            # Citi bike dataset statistics.
            'arrivalTimes': arrivalTimes,
            'tripDurations': tripDurations,
            'destinationP': destinationP,
            # Simulation statistics.
            'statistics': statistics,
            # Constants.
            'bikeLossProb': BIKE_LOSS_PROBABILITY,
        }

        # Initialize the simulation engine.
        simEngine = engine.DiscreteEventSimulationEngine()

        # Schedule initial event.
        initEvent = engine.DiscreteEvent(
            Initialize,
            -1,
            globalData=globalData,
            initialDistribution=initialDistribution,
            racksPerStation=racksPerStation)
        simEngine.schedule(initEvent)
        endEvent = engine.DiscreteEvent(endSim, 1440)
        # Run the simulation.
        simEngine.runSimulation()
        simDuration = time.time() - simStartTime
        logging.info('Simulation complete. Took %.3f seconds.\n' % simDuration)

        # Report simulation statistics.
        logging.info('Revenue: %.2f dollars' % statistics['Revenue'])
        logging.info('TimeWaitForCycle: %.3f minutes' %
                     statistics['TimeWaitForCycle'].sum())
        logging.info('TimeWaitForDropoff: %.3f minutes' %
                     statistics['TimeWaitForDropoff'].sum())
        logging.info('CustomersLost: %d' % statistics['CustomersLost'].sum())
        logging.info('BikesLost: %d' % statistics['BikesLost'])
        logging.info('TotalIdleTime: %d' % statistics['IdleTime'].sum())

        return statistics
Пример #10
0
def RideEnd(simEngine, **kwargs):
    """Customer finishes the bike ride."""

    globalData = kwargs['globalData']
    customer = kwargs['customer']
    stationID = customer.endID
    currentTime = simEngine.currentTime()

    # Check if there are empty racks to keep the bike.
    if globalData['stations'][stationID].numRacks <= 0:
        # No empty racks. The customer begins waiting in queue.
        customer.startDropoffWait = currentTime
        globalData['dropoffQueues'][stationID].put(customer)
        logging.debug(
            '\t(customer %d) damn there are no empty racks at station %d at time %.3f'
            % (customer.customerID, stationID, currentTime))
        return
    # Update total Idle Time till current time
    if currentTime < 1440:
        globalData['statistics']['IdleTime'][
            stationID] += globalData['stations'][stationID].numBikes * (
                currentTime - globalData['stations'][stationID].lastEvent)
        globalData['stations'][stationID].lastEvent = currentTime

    # Customer returns the bike to the rack.
    globalData['stations'][stationID].numRacks -= 1
    globalData['stations'][stationID].numBikes += 1
    logging.debug(
        '\t(customer %d) perfecto! i reached my destination %d at time %.3f, my journey is complete'
        % (customer.customerID, stationID, currentTime))

    # If there is at least one customer waiting for a bike and waittime < 5
    # mins, schedule arrival event. Note: not every customer waiting for a
    # bike eventually takes a bike..customers leave after 5 mins.
    while (True):
        # Check if customers are waiting.
        if len(globalData['pickupQueues'][stationID]) == 0:
            break

        waitingCustomer = globalData['pickupQueues'][stationID].remove()
        waitTime = currentTime - waitingCustomer.startPickupWait
        globalData['statistics']['TimeWaitForCycle'][stationID] += waitTime
        if waitTime < REFUND_TIME:
            # Next waiting customer gets a bike
            simEngine.schedule(
                engine.DiscreteEvent(Arrival,
                                     currentTime,
                                     customer=waitingCustomer,
                                     stationID=stationID,
                                     globalData=globalData))
            logging.debug(
                '\t(customer %d) finally i get my ride at stn %d after waiting for %.3f having arrived at %.3f'
                %
                (waitingCustomer.customerID, stationID, waitTime, currentTime))
            break
        else:
            # We lose a customer
            globalData['statistics']['CustomersLost'][stationID] += 1
            logging.debug(
                '\t(customer %d) @#$%%! u wasted my time! i waited for %.3f minutes for a bike at stn %d, i dont want it anymore'
                % (waitingCustomer.customerID, waitTime, stationID))
Пример #11
0
def Arrival(simEngine, **kwargs):
    """Customer arrives at the station to pick up a bike."""
    globalData = kwargs['globalData']
    stationID = kwargs['stationID']
    numStations = globalData['destinationP'].shape[0]
    numTimeframes = globalData['destinationP'].shape[1]
    # Customer who will pick up a bike.
    if 'customer' in kwargs:
        customer = kwargs['customer']
    else:
        customer = Customer()
        customer.startID = stationID
    currentTime = simEngine.currentTime()

    # Checks the ArrivalData for the next arrival and schedules it.
    # Note: schedule immediately in case customer has to wait in line / leaves
    if len(globalData['arrivalTimes'][stationID]) > 0:
        t = globalData['arrivalTimes'][stationID].pop(0)
        simEngine.schedule(
            engine.DiscreteEvent(Arrival,
                                 t,
                                 stationID=stationID,
                                 globalData=globalData))

    # Check if there are bikes available.
    if globalData['stations'][stationID].numBikes <= 0:
        # Customer begins waiting for bike to become available.
        customer.startPickupWait = currentTime
        globalData['pickupQueues'][stationID].put(customer)
        logging.debug(
            '\t(customer %d) Damn! where are all the bikes at station %d, the time is %.3f'
            % (customer.customerID, stationID, currentTime))
        return

    # Customer pays to rent bike.
    globalData['statistics']['Revenue'] += TRIP_COST

    # Select destination based on the probabilities.
    currentTimeframe = int(
        np.floor((currentTime / float(DAY_DURATION)) * numTimeframes))
    currentTimeframe = min(currentTimeframe, numTimeframes - 1)
    customer.endID = np.random.choice(
        numStations, p=globalData['destinationP'][stationID][currentTimeframe])

    # Schedule end of ride using the average trip duration.
    t = (currentTime + globalData['tripDurations'][stationID][customer.endID])

    # Determine if bike will become lost or damaged.
    rideOutcome = RideEnd
    if np.random.random() <= globalData['bikeLossProb']:
        rideOutcome = RideCrash
    simEngine.schedule(
        engine.DiscreteEvent(rideOutcome,
                             t,
                             customer=customer,
                             globalData=globalData))

    # Update total Idle Time till current time
    if currentTime <= 1440:
        globalData['statistics']['IdleTime'][
            stationID] += globalData['stations'][stationID].numBikes * (
                currentTime - globalData['stations'][stationID].lastEvent)
        globalData['stations'][stationID].lastEvent = currentTime

    # Update number of bikes and racks for the station.
    globalData['stations'][stationID].numBikes -= 1
    globalData['stations'][stationID].numRacks += 1

    logging.debug(
        '\t(customer %d) yay! i got a bike from %d at time %.3f n im going to %d n will reach at %.3f'
        % (customer.customerID, stationID, currentTime, customer.endID, t))

    # Checks if there are people waiting to put the bikes back.
    if len(globalData['dropoffQueues'][stationID]) > 0:
        # Calculate time the customer waited to drop off the bike.
        waitingCustomer = globalData['dropoffQueues'][stationID].remove()
        waitTime = currentTime - waitingCustomer.startDropoffWait
        #  Update total wait time.
        globalData['statistics']['TimeWaitForDropoff'][stationID] += waitTime
        logging.debug(
            '\t(customer %d) finally i can return my bike at stn %d after waiting for %.3f having arrived at %.3f'
            % (waitingCustomer.customerID, stationID, waitTime, currentTime))
        # If customer has waited too long to return the bike, refund is given.
        if waitTime > REFUND_TIME:
            globalData['statistics']['Revenue'] -= TRIP_COST
            logging.debug(
                '\t(customer %d) at least i got my refund for waiting too long to return the bike'
                % (waitingCustomer.customerID))
        # Schedule RideEnd for the waiting customer.
        simEngine.schedule(
            engine.DiscreteEvent(RideEnd,
                                 currentTime,
                                 customer=waitingCustomer,
                                 globalData=globalData))
Пример #12
0
    def test_arrivalEvent_bikesAvailable(self):
        """Tests the Arrival event when bikes are available."""
        currentTime = 100
        self.simEngine.simTime = currentTime
        self.globalData = self._initGlobalData(self.TEST_NUM_STATIONS,
                                               initEntities=True)

        # The simulation time is 00:00, so we expect station 2 to be chosen
        # as the destination from station 0 (other stations have zero
        # probability of being the destination).
        self.simEngine.simTime = 0
        expectedDestID = 2

        # There is initially non-zero revenue.
        revenueBefore = 500
        self.globalData['statistics']['Revenue'] = revenueBefore

        # The station has bikes available.
        testStationID = 0
        testStation = self.globalData['stations'][testStationID]
        numBikesBefore = testStation.numBikes
        numRacksBefore = testStation.numRacks
        self.assertTrue(numBikesBefore > 0)

        # A customer has waited too long and should receive a refund.
        testDropoffQueue = self.globalData['dropoffQueues'][testStationID]
        longWaitCustomer = nycbike.Customer()
        longWaitCustomer.startID = testStationID
        longWaitCustomer.startDropoffWait = currentTime - 100  # > REFUND_TIME
        testDropoffQueue.put(longWaitCustomer)

        # The Arrival event is scheduled and processed.
        arrivalEvent = engine.DiscreteEvent(nycbike.Arrival,
                                            10,
                                            globalData=self.globalData,
                                            stationID=testStationID)
        self.simEngine.schedule(arrivalEvent)
        self.simEngine.runSimulation(maxEvents=1)

        # The station attributes were updated.
        self.assertEqual(numBikesBefore - 1, testStation.numBikes)
        self.assertEqual(numRacksBefore + 1, testStation.numRacks)

        # The current customer paid for the bike.
        expectedRevenue = revenueBefore + nycbike.TRIP_COST
        # A RideEnd event was scheduled at the destination.
        rideEndEvents = [
            e for e in self.simEngine.FEL if (e.handler == nycbike.RideEnd)
        ]
        rideEndEvent = rideEndEvents[0]
        # The trip destination is correct.
        ridingCustomer = rideEndEvent.handlerKwargs['customer']
        self.assertEqual(expectedDestID, ridingCustomer.endID)
        # The trip duration is correct.
        expectedRideEndTime = (
            arrivalEvent.timestamp +
            self.TEST_TRIP_DURATIONS[testStationID][expectedDestID])
        self.assertEqual(expectedRideEndTime, rideEndEvent.timestamp)

        # The long-wait customer received a refund and left.
        self.assertFalse(longWaitCustomer in testDropoffQueue)
        expectedRevenue -= nycbike.TRIP_COST

        # The next Arrival event was scheduled for the station.
        scheduledArrivals = [
            e for e in self.simEngine.FEL if e.handler == nycbike.Arrival
        ]
        self.assertEqual(1, len(scheduledArrivals))
        self.assertEqual(testStationID,
                         scheduledArrivals[0].handlerKwargs['stationID'])