Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Individual Project 22/23Z - RL-based model introduction #39

Open
wants to merge 19 commits into
base: RL
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 84 additions & 1 deletion MaaSSim/data/config.json
Original file line number Diff line number Diff line change
@@ -1 +1,84 @@
{"NAME": "default", "city": "Nootdorp, Netherlands", "nP": 20, "nV": 5, "times": {"request": 15, "transaction": 20, "pickup": 30, "dropoff": 10, "patience": 600, "pickup_patience": 90}, "speeds": {"walk": 1.2, "ride": 10}, "t0": "17:00", "paths": {"G": "data/Nootdorp.graphml", "skim": "data/Nootdorp.csv", "_ipython_display_": {}, "_repr_mimebundle_": {}, "_repr_html_": {}, "_repr_markdown_": {}, "_repr_svg_": {}, "_repr_png_": {}, "_repr_pdf_": {}, "_repr_jpeg_": {}, "_repr_latex_": {}, "_repr_json_": {}, "_repr_javascript_": {}}, "dist_threshold": 100000, "big_time": 10, "sleep": false, "simTime": 1, "demand_structure": {"origins_dispertion": -0.0003, "destinations_dispertion": -0.001, "temporal_dispertion": 0.3, "temporal_distribution": "uniform"}, "shareability": {"windows": {"pickup": [-2, 5], "dropoff": [-10, 5]}, "avg_speed": 10, "shared_discount": 0.8, "VoT": 0.005, "WtS": 1.2, "delay_value": 1.5, "price": 1.5, "matching_obj": "u_veh", "pax_delay": 45, "horizon": 600, "max_degree": 600, "share": 0}, "parallel": {"nThread": 3, "nReplications": 2}, "nD": 1, "_ipython_display_": {}, "_repr_mimebundle_": {}, "_repr_html_": {}, "_repr_markdown_": {}, "_repr_svg_": {}, "_repr_png_": {}, "_repr_pdf_": {}, "_repr_jpeg_": {}, "_repr_latex_": {}, "_repr_json_": {}, "_repr_javascript_": {}}
{
"NAME": "default",
"city": "Nootdorp, Netherlands",
"nP": 20,
"nV": 5,
"user_controlled_vehicles_count": 0,
"times": {
"request": 15,
"transaction": 20,
"pickup": 30,
"dropoff": 10,
"patience": 600,
"pickup_patience": 90
},
"speeds": {
"walk": 1.2,
"ride": 10
},
"t0": "17:00",
"paths": {
"G": "data/Nootdorp.graphml",
"skim": "data/Nootdorp.csv",
"_ipython_display_": {},
"_repr_mimebundle_": {},
"_repr_html_": {},
"_repr_markdown_": {},
"_repr_svg_": {},
"_repr_png_": {},
"_repr_pdf_": {},
"_repr_jpeg_": {},
"_repr_latex_": {},
"_repr_json_": {},
"_repr_javascript_": {}
},
"dist_threshold": 100000,
"big_time": 10,
"sleep": false,
"simTime": 1,
"demand_structure": {
"origins_dispertion": -0.0003,
"destinations_dispertion": -0.001,
"temporal_dispertion": 0.3,
"temporal_distribution": "uniform"
},
"shareability": {
"windows": {
"pickup": [
-2,
5
],
"dropoff": [
-10,
5
]
},
"avg_speed": 10,
"shared_discount": 0.8,
"VoT": 0.005,
"WtS": 1.2,
"delay_value": 1.5,
"price": 1.5,
"matching_obj": "u_veh",
"pax_delay": 45,
"horizon": 600,
"max_degree": 600,
"share": 0
},
"parallel": {
"nThread": 3,
"nReplications": 2
},
"nD": 1,
"_ipython_display_": {},
"_repr_mimebundle_": {},
"_repr_html_": {},
"_repr_markdown_": {},
"_repr_svg_": {},
"_repr_png_": {},
"_repr_pdf_": {},
"_repr_jpeg_": {},
"_repr_latex_": {},
"_repr_json_": {},
"_repr_javascript_": {}
}
3 changes: 2 additions & 1 deletion MaaSSim/data_structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
'shift_start',
'shift_end',
'platform',
'expected_income']).set_index('id')
'expected_income',
'user_controlled']).set_index('id')

structures.platforms = pd.DataFrame(columns=['id',
'fare',
Expand Down
42 changes: 32 additions & 10 deletions MaaSSim/decisions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
# Description: Agent decision function templates
# Rafal Kucharski @ TU Delft, The Netherlands
################################################################################
from enum import IntEnum
from math import exp
import random
from typing import Any, List, TypedDict

import pandas as pd
from dotmap import DotMap
from numpy.random.mtrand import choice
Expand Down Expand Up @@ -42,6 +45,23 @@ def f_dummy_repos(*args, **kwargs):
################
# DRIVER #
################
class OfferStatus(IntEnum):
MADE = 0
ACCEPTED = 1
REJECTED_BY_TRAVELLER = -1
REJECTED_BY_DRIVER = -2


class Offer(TypedDict):
pax_id: int
req_id: int
simpaxes: List[Any]
veh_id: int
status: OfferStatus
request: pd.Series
wait_time: int
travel_time: float
fare: float


def f_driver_out(*args, **kwargs):
Expand Down Expand Up @@ -218,18 +238,20 @@ def f_match(**kwargs):
ttrav = pax_request.ttrav
else:
ttrav = pax_request.ttrav.total_seconds()
offer = {'pax_id': i,
'req_id': pax_request.name,
'simpaxes': simpaxes,
'veh_id': veh_id,
'status': 0, # 0 - offer made, 1 - accepted, -1 rejected by traveller, -2 rejected by veh
'request': pax_request,
'wait_time': mintime,
'travel_time': ttrav,
'fare': platform.platform.fare * sim.pax[i].request.dist / 1000} # make an offer
offer: Offer = {
'pax_id': i,
'req_id': pax_request.name,
'simpaxes': simpaxes,
'veh_id': veh_id,
'status': 0, # 0 - offer made, 1 - accepted, -1 rejected by traveller, -2 rejected by veh
'request': pax_request,
'wait_time': mintime,
'travel_time': ttrav,
'fare': platform.platform.fare * sim.pax[i].request.dist / 1000,
} # make an offer
platform.offers[offer_id] = offer # bookkeeping of offers made by platform
sim.pax[i].offers[platform.platform.name] = offer # offer transferred to
if veh.f_driver_decline(veh=veh): # allow driver reject the request
if veh.decision_system.f_driver_decline(veh=veh, offer=offer): # allow driver reject the request
veh.update(event=driverEvent.REJECTS_REQUEST)
platform.offers[offer_id]['status'] = -2
for i in simpaxes:
Expand Down
33 changes: 25 additions & 8 deletions MaaSSim/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
# Rafal Kucharski @ TU Delft, The Netherlands
################################################################################

from enum import Enum
import time
from enum import Enum


class driverEvent(Enum):
Expand All @@ -30,6 +30,20 @@ class driverEvent(Enum):
ENDS_SHIFT = -2


class SimpleVehicleAgentDecisionSystem:
def __init__(self, simData) -> None:
self.f_driver_out = simData.functions.f_driver_out # exit from the system due to prev exp
self.f_driver_decline = simData.functions.f_driver_decline # reject the incoming request
self.f_driver_repos = simData.functions.f_driver_repos # reposition after you are free again


class UserControlledVehicleAgentDecisionSystem:
def __init__(self, simData) -> None:
self.f_driver_out = simData.functions.f_user_controlled_driver_out
self.f_driver_decline = simData.functions.f_user_controlled_driver_decline
self.f_driver_repos = simData.functions.f_user_controlled_driver_repos


class VehicleAgent(object):
"""
Driver Agent
Expand All @@ -50,15 +64,18 @@ def __init__(self, simData, veh_id):
self.schedule = None # schedule served by vehicle (single request for case of non-shared rides)
self.exit_flag = False # raised at the end of the shift
self.tveh_pickup = None # travel time from .pos to request first node
self.total_income = 0.
# output reports
self.myrides = list() # report of this vehicle process, populated while simulating
# functions
self.f_driver_out = self.sim.functions.f_driver_out # exit from the system due to prev exp
self.f_driver_decline = self.sim.functions.f_driver_decline # reject the incoming request
self.f_driver_repos = self.sim.functions.f_driver_repos # reposition after you are free again
if self.veh.user_controlled:
self.decision_system = UserControlledVehicleAgentDecisionSystem(self.sim)
else:
self.decision_system = SimpleVehicleAgentDecisionSystem(self.sim)

# events
self.requested = self.sim.env.event() # triggers when vehicle is requested
self.arrives_at_pick_up = dict() # list of events for each passengers in the schedule
self.arrives_at_pick_up = dict() # list of events for each passenger in the schedule
self.arrives = dict() # list of events for each arrival at passenger origin
# main action
self.action = self.sim.env.process(self.loop_day()) # main process in simu
Expand Down Expand Up @@ -110,7 +127,7 @@ def clear_me(self):
def loop_day(self):
# main routine of the vehicle process
self.update(event=driverEvent.STARTS_DAY)
if self.f_driver_out(veh=self): # first see if driver wants to work this day (by default he wants)
if self.decision_system.f_driver_out(veh=self): # first see if driver wants to work this day (by default he wants)
self.update(event=driverEvent.DECIDES_NOT_TO_DRIVE)
msg = "veh {:>4} {:40} {}".format(self.id, 'opted-out from the system', self.sim.print_now())
self.sim.logger.info(msg)
Expand All @@ -120,7 +137,7 @@ def loop_day(self):

while True:
# try: # depreciated since now traveller rejects instantly for simplicity
repos = self.f_driver_repos(veh=self) # reposition yourself
repos = self.decision_system.f_driver_repos(veh=self) # reposition yourself
if repos.flag: # if reposition
self.update(event=driverEvent.STARTS_REPOSITIONING)
yield self.sim.timeout(repos.time, variability=self.sim.vars.ride)
Expand Down Expand Up @@ -152,7 +169,7 @@ def loop_day(self):
self.sim.timeout(self.sim.params.times.pickup_patience,
variability=self.sim.vars.ride)
if not self.sim.pax[stage.req_id].arrived_at_pick_up: # if traveller did not arrive
no_shows.apppend(stage.req_id)
no_shows.append(stage.req_id)
break # we do not serve this gentleman
self.update(event=driverEvent.MEETS_TRAVELLER_AT_PICKUP)
yield self.sim.pax[stage.req_id].pickuped # wait until passenger has boarded
Expand Down
33 changes: 20 additions & 13 deletions MaaSSim/maassim.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,26 @@
################################################################################


from dotmap import DotMap
import pandas as pd
import logging
import os.path
import zipfile
from pathlib import Path

import math
import networkx as nx
import numpy as np
import pandas as pd
import simpy
import sys
import time
import numpy as np
import os.path
import zipfile
from pathlib import Path
from dotmap import DotMap

from MaaSSim.traveller import PassengerAgent, travellerEvent
from MaaSSim.driver import VehicleAgent
from MaaSSim.decisions import f_dummy_repos, f_match, dummy_False
from MaaSSim.platform import PlatformAgent
from MaaSSim.driver import VehicleAgent
from MaaSSim.performance import kpi_pax, kpi_veh
from MaaSSim.transport_platform import PlatformAgent
from MaaSSim.traveller import PassengerAgent, travellerEvent
from MaaSSim.utils import initialize_df
import sys
import logging

DEFAULTS = dict(f_match=f_match,
f_driver_learn=dummy_False, # deprecated
Expand All @@ -32,9 +33,13 @@
f_driver_decline=dummy_False,
f_driver_repos=f_dummy_repos,

f_user_controlled_driver_out=dummy_False,
f_user_controlled_driver_decline=dummy_False,
f_user_controlled_driver_repos=f_dummy_repos,

f_trav_out=dummy_False,
f_trav_mode=dummy_False,
f_platform_choice = dummy_False,
f_platform_choice=dummy_False,


f_stop_crit=dummy_False,
Expand Down Expand Up @@ -63,12 +68,14 @@ class Simulator:
'f_driver_decline',
'f_platform_choice',
'f_driver_repos',
'f_user_controlled_driver_out',
'f_user_controlled_driver_decline',
'f_user_controlled_driver_repos',
'f_timeout',
'f_stop_crit',
'kpi_pax',
'kpi_veh']


def __init__(self, _inData, **kwargs):
# input
self.inData = _inData.copy() # copy of data structure for simulations (copy needed for multi-threading)
Expand Down
12 changes: 5 additions & 7 deletions MaaSSim/simulators.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@
# Description: Wrappers to prepare and run simulations
# Rafal Kucharski @ TU Delft
################################################################################
import logging
import re

import pandas as pd
from scipy.optimize import brute

from MaaSSim.maassim import Simulator
from MaaSSim.shared import prep_shared_rides
from MaaSSim.utils import get_config, load_G, generate_demand, generate_vehicles, initialize_df, empty_series, \
slice_space, read_requests_csv, read_vehicle_positions
import pandas as pd
from scipy.optimize import brute
import logging
import re



def single_pararun(one_slice, *args):
Expand Down Expand Up @@ -108,7 +107,7 @@ def simulate(config="data/config.json", inData=None, params=None, **kwargs):
if len(inData.passengers) == 0: # only if no passengers in input
inData = generate_demand(inData, params, avg_speed=True)
if len(inData.vehicles) == 0: # only if no vehicles in input
inData.vehicles = generate_vehicles(inData, params.nV)
inData.vehicles = generate_vehicles(inData, params.nV, params.user_controlled_vehicles_count)
if len(inData.platforms) == 0: # only if no platforms in input
inData.platforms = initialize_df(inData.platforms)
inData.platforms.loc[0] = empty_series(inData.platforms)
Expand All @@ -133,4 +132,3 @@ def simulate(config="data/config.json", inData=None, params=None, **kwargs):
from MaaSSim.utils import test_space

simulate_parallel(search_space = test_space())

16 changes: 8 additions & 8 deletions MaaSSim/platform.py → MaaSSim/transport_platform.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
################################################################################
# Module: platform.py
# Module: transport_platform.py
# Platform agent
# Rafal Kucharski @ TU Delft
################################################################################


from .driver import driverEvent
from .decisions import Offer, OfferStatus
from .driver import driverEvent, VehicleAgent
from .traveller import travellerEvent
import simpy
import random
Expand Down Expand Up @@ -129,7 +128,7 @@ def handle_rejected(self, offer_id):
:return:
"""
offer = self.offers[offer_id]
offer['status'] = -1
offer['status'] = OfferStatus.REJECTED_BY_TRAVELLER
veh = self.sim.vehs[offer['veh_id']]
veh.update(event=driverEvent.IS_REJECTED_BY_TRAVELLER)
self.tabu.append((offer['veh_id'], offer_id)) # they are unmatchable
Expand All @@ -147,9 +146,9 @@ def handle_accepted(self, offer_id):
:param offer_id:
:return:
"""
offer = self.offers[offer_id]
offer['status'] = -1
veh = self.sim.vehs[offer['veh_id']]
offer: Offer = self.offers[offer_id]
offer['status'] = OfferStatus.ACCEPTED
veh: VehicleAgent = self.sim.vehs[offer['veh_id']]

for i in offer['simpaxes']:
self.sim.pax[i].update(event=travellerEvent.ACCEPTS_OFFER)
Expand All @@ -161,6 +160,7 @@ def handle_accepted(self, offer_id):

# veh.request = request # assign request to vehicles
veh.schedule = self.sim.pax[offer['simpaxes'][0]].schedule
veh.total_income += offer['fare']
# simpax.found_veh.succeed() # raise the event for passenger
# simpax.veh = vehicle # assigne the vehicle to passenger

Expand Down
Loading