├── log └── .gitignore ├── model └── .gitignore ├── out └── .gitignore ├── bash_result └── .gitignore ├── reports ├── README.md ├── 09 D Technical report of City Brain Challenge.pdf ├── 10 TLab Solution to KDD CUP 2021 City Brain Challenge.pdf ├── 05 SUMO The Traffic Light Lane Queue Agent of Team SUMO.pdf ├── 01 IntelligentLight: Vote-Based Traffic Coordination Algorithm.pdf ├── 04 BOE_IOT_AIBD Technical Report of KDD CUP 2021 City Brain Challenge.pdf ├── 08 bingo DQN Control Solution for KDD Cup 2021 City Brain Challenge.pdf ├── 03 4PQC_team Traffic Light Control Solution Based On Lane Score and Jam Rules.pdf ├── 07 alphabeta Decentralized_Greedy_Strategy_for_Large_Scale_Traffic_Signal_Control.pdf ├── 02 GoodGoodStudy Ensemble Reinforcement Learning for Large-Scale Traffic Signal_Control.pdf └── 06 IF_Bigdata Spatial-Temporal Graph Convolutional Network for Large Scale_Traffic Signal Control.pdf ├── agent ├── checkpoint-25('classic' as observation) ├── gym_cfg.py ├── agent.py ├── agent_MP.py ├── agent_rllib.py └── CBEngine_round3.py ├── data ├── flow_1x1.txt ├── roadnet_1x1.txt ├── flow_warm_up_1000.txt ├── roadnet_warm_up.txt └── traffic_generator.py ├── train.sh ├── evaluate.sh ├── cfg ├── simulator_round2.cfg ├── simulator_warm_up.cfg └── simulator_round3_flow0.cfg ├── rllib_evaluate.sh ├── demo.py ├── .gitignore ├── rllib_train.py ├── evaluate.py └── rllib_test.py /log/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /model/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /out/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bash_result/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /reports/README.md: -------------------------------------------------------------------------------- 1 | The technical reports of top 10 teams are updated. 2 | 3 | -------------------------------------------------------------------------------- /agent/checkpoint-25('classic' as observation): -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CityBrainChallenge/KDDCup2021-CityBrainChallenge-starter-kit/HEAD/agent/checkpoint-25('classic' as observation) -------------------------------------------------------------------------------- /reports/09 D Technical report of City Brain Challenge.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CityBrainChallenge/KDDCup2021-CityBrainChallenge-starter-kit/HEAD/reports/09 D Technical report of City Brain Challenge.pdf -------------------------------------------------------------------------------- /reports/10 TLab Solution to KDD CUP 2021 City Brain Challenge.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CityBrainChallenge/KDDCup2021-CityBrainChallenge-starter-kit/HEAD/reports/10 TLab Solution to KDD CUP 2021 City Brain Challenge.pdf -------------------------------------------------------------------------------- /reports/05 SUMO The Traffic Light Lane Queue Agent of Team SUMO.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CityBrainChallenge/KDDCup2021-CityBrainChallenge-starter-kit/HEAD/reports/05 SUMO The Traffic Light Lane Queue Agent of Team SUMO.pdf -------------------------------------------------------------------------------- /reports/01 IntelligentLight: Vote-Based Traffic Coordination Algorithm.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CityBrainChallenge/KDDCup2021-CityBrainChallenge-starter-kit/HEAD/reports/01 IntelligentLight: Vote-Based Traffic Coordination Algorithm.pdf -------------------------------------------------------------------------------- /reports/04 BOE_IOT_AIBD Technical Report of KDD CUP 2021 City Brain Challenge.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CityBrainChallenge/KDDCup2021-CityBrainChallenge-starter-kit/HEAD/reports/04 BOE_IOT_AIBD Technical Report of KDD CUP 2021 City Brain Challenge.pdf -------------------------------------------------------------------------------- /reports/08 bingo DQN Control Solution for KDD Cup 2021 City Brain Challenge.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CityBrainChallenge/KDDCup2021-CityBrainChallenge-starter-kit/HEAD/reports/08 bingo DQN Control Solution for KDD Cup 2021 City Brain Challenge.pdf -------------------------------------------------------------------------------- /reports/03 4PQC_team Traffic Light Control Solution Based On Lane Score and Jam Rules.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CityBrainChallenge/KDDCup2021-CityBrainChallenge-starter-kit/HEAD/reports/03 4PQC_team Traffic Light Control Solution Based On Lane Score and Jam Rules.pdf -------------------------------------------------------------------------------- /reports/07 alphabeta Decentralized_Greedy_Strategy_for_Large_Scale_Traffic_Signal_Control.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CityBrainChallenge/KDDCup2021-CityBrainChallenge-starter-kit/HEAD/reports/07 alphabeta Decentralized_Greedy_Strategy_for_Large_Scale_Traffic_Signal_Control.pdf -------------------------------------------------------------------------------- /reports/02 GoodGoodStudy Ensemble Reinforcement Learning for Large-Scale Traffic Signal_Control.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CityBrainChallenge/KDDCup2021-CityBrainChallenge-starter-kit/HEAD/reports/02 GoodGoodStudy Ensemble Reinforcement Learning for Large-Scale Traffic Signal_Control.pdf -------------------------------------------------------------------------------- /reports/06 IF_Bigdata Spatial-Temporal Graph Convolutional Network for Large Scale_Traffic Signal Control.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CityBrainChallenge/KDDCup2021-CityBrainChallenge-starter-kit/HEAD/reports/06 IF_Bigdata Spatial-Temporal Graph Convolutional Network for Large Scale_Traffic Signal Control.pdf -------------------------------------------------------------------------------- /data/flow_1x1.txt: -------------------------------------------------------------------------------- 1 | 12 2 | 0 1800 40 3 | 2 4 | 2 3 5 | 0 1800 60 6 | 2 7 | 2 5 8 | 0 1800 70 9 | 2 10 | 2 7 11 | 0 1800 60 12 | 2 13 | 4 5 14 | 0 1800 50 15 | 2 16 | 4 7 17 | 0 1800 40 18 | 2 19 | 4 1 20 | 0 1800 50 21 | 2 22 | 6 7 23 | 0 1800 40 24 | 2 25 | 6 1 26 | 0 1800 60 27 | 2 28 | 6 3 29 | 0 1800 40 30 | 2 31 | 8 1 32 | 0 1800 60 33 | 2 34 | 8 3 35 | 0 1800 50 36 | 2 37 | 8 5 -------------------------------------------------------------------------------- /data/roadnet_1x1.txt: -------------------------------------------------------------------------------- 1 | 5 2 | 30 120 0 1 3 | 31 120 1 0 4 | 30 121 2 0 5 | 29 120 3 0 6 | 30 119 4 0 7 | 4 8 | 0 1 30 20 3 3 1 2 9 | 1 0 0 0 1 0 0 1 1 10 | 1 0 0 0 1 0 0 1 1 11 | 0 2 30 20 3 3 3 4 12 | 1 0 0 0 1 0 0 1 1 13 | 1 0 0 0 1 0 0 1 1 14 | 0 3 30 20 3 3 5 6 15 | 1 0 0 0 1 0 0 1 1 16 | 1 0 0 0 1 0 0 1 1 17 | 0 4 30 20 3 3 7 8 18 | 1 0 0 0 1 0 0 1 1 19 | 1 0 0 0 1 0 0 1 1 20 | 1 21 | 0 1 3 5 7 -------------------------------------------------------------------------------- /train.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | algo=$1 3 | worker=$2 4 | cfg='/starter-kit/cfg/simulator_round3_flow0.cfg' 5 | stop_iters=10 6 | 7 | echo "======================================" 8 | echo "algorithm : ${algo} cfg : ${cfg} workers : ${worker}" 9 | nohup python3 rllib_train.py --sim_cfg $cfg --algorithm ${algo} --stop-iters ${stop_iters} --foldername train_result --num_workers ${worker} --thread_num 4 >> ./bash_result/${algo}_${worker}worker.log 2>&1 & 10 | 11 | -------------------------------------------------------------------------------- /evaluate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | agent=$1 3 | out=$2 4 | cfg_array=('/starter-kit/cfg/simulator_round3_flow0.cfg') 5 | vehicle_log=$3 6 | thread_num=$4 7 | 8 | for cfg in ${cfg_array[*]} 9 | do 10 | echo "==========================" 11 | echo "now test ${cfg}" 12 | nohup python3 evaluate.py --input_dir $agent --output_dir $out --vehicle_info_path $vehicle_log --sim_cfg ${cfg} --metric_period 120 --threshold 1.4 --thread_num $thread_num > ./bash_result/evaluate.log 2>&1 & 13 | done 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /cfg/simulator_round2.cfg: -------------------------------------------------------------------------------- 1 | #configuration for simulator 2 | 3 | # Time Parameters 4 | start_time_epoch = 0 5 | max_time_epoch = 3600 6 | 7 | road_file_addr : /starter-kit/data/roadnet_round2.txt 8 | vehicle_file_addr : /starter-kit/data/flow_round2.txt 9 | #road_file_addr : /starter-kit/data/roadnet_warm_up.txt 10 | #vehicle_file_addr : /starter-kit/data/flow_warm_up_1000.txt 11 | 12 | 13 | # Log Trace 14 | report_log_mode : normal 15 | report_log_addr : /starter-kit/log/round2/ 16 | report_log_rate = 10 17 | warning_stop_time_log = 100 18 | -------------------------------------------------------------------------------- /cfg/simulator_warm_up.cfg: -------------------------------------------------------------------------------- 1 | #configuration for simulator 2 | 3 | # Time Parameters 4 | start_time_epoch = 0 5 | max_time_epoch = 3600 6 | 7 | #road_file_addr : /starter-kit/data/roadnet_round2.txt 8 | #vehicle_file_addr : /starter-kit/data/flow_round2.txt 9 | road_file_addr : /starter-kit/data/roadnet_warm_up.txt 10 | vehicle_file_addr : /starter-kit/data/flow_warm_up_1000.txt 11 | 12 | 13 | # Log Trace 14 | report_log_mode : normal 15 | report_log_addr : /starter-kit/log/warm_up/ 16 | report_log_rate = 10 17 | warning_stop_time_log = 100 18 | -------------------------------------------------------------------------------- /cfg/simulator_round3_flow0.cfg: -------------------------------------------------------------------------------- 1 | #configuration for simulator 2 | 3 | # Time Parameters 4 | start_time_epoch = 0 5 | max_time_epoch = 1200 6 | 7 | road_file_addr : /starter-kit/data/roadnet_round3.txt 8 | # change flow number 9 | vehicle_file_addr : /starter-kit/data/flow_round3_flow0.txt 10 | #road_file_addr : /starter-kit/data/roadnet_warm_up.txt 11 | #vehicle_file_addr : /starter-kit/data/flow_warm_up_1000.txt 12 | 13 | 14 | # Log Trace 15 | report_log_mode : normal 16 | # change flow number 17 | report_log_addr : /starter-kit/log/0/ 18 | report_log_rate = 10 19 | warning_stop_time_log = 100 20 | -------------------------------------------------------------------------------- /agent/gym_cfg.py: -------------------------------------------------------------------------------- 1 | class gym_cfg(): 2 | def __init__(self): 3 | ''' 4 | 'custom_observation': If 'True', use costom observation feature in CBEngine_round3.py of agent.zip. If 'False', use 'observation_features' 5 | 6 | 'observation_features' : Same as round2. Add 'classic' observation feature, which has dimension of 16. 7 | 8 | 'observation_dimension' : The dimension of observation. Need to be correct both custom observation and default observation. 9 | 10 | ''' 11 | 12 | self.cfg = { 13 | 'observation_features':['lane_vehicle_num','classic'], 14 | 'observation_dimension':40, 15 | 'custom_observation' : False 16 | } -------------------------------------------------------------------------------- /rllib_evaluate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cfg_array=('/starter-kit/cfg/simulator_round3_flow0.cfg') 3 | algorithm="DQN" 4 | foldername="train_result" 5 | iteration_array=(5 10) 6 | # Don't open lots of evaluating processes in parallel. It would cause the cloud server shutdown!!!! 7 | for cfg in ${cfg_array[*]} 8 | do 9 | for iteration in ${iteration_array[*]} 10 | do 11 | echo "==========================" 12 | echo "now test ${algorithm} ${cfg} iteration${iteration}" 13 | nohup python3 rllib_test.py --sim_cfg ${cfg} --iteration ${iteration} --algorithm ${algorithm} --foldername ${foldername} --metric_period 200 --thread_num 4 > ./bash_result/${cfg:0-9}_iteration${iteration}_${foldername}.log 2>&1 & 14 | done 15 | wait 16 | done 17 | 18 | -------------------------------------------------------------------------------- /demo.py: -------------------------------------------------------------------------------- 1 | from agent.CBEngine_round3 import CBEngine_round3 as CBEngine_rllib_class 2 | import gym 3 | import agent.gym_cfg as gym_cfg 4 | 5 | # load config 6 | simulator_cfg_file = '/starter-kit/cfg/simulator_round3_flow0.cfg' 7 | mx_step = 360 8 | gym_cfg_instance = gym_cfg.gym_cfg() 9 | gym_configs = gym_cfg_instance.cfg 10 | # gym 11 | env_config = { 12 | "simulator_cfg_file": simulator_cfg_file, 13 | "thread_num": 8, 14 | "gym_dict": gym_configs, 15 | "metric_period": 200, 16 | "vehicle_info_path": "/starter-kit/log/" 17 | } 18 | env = CBEngine_rllib_class(env_config) 19 | env.set_info(1) 20 | for i in range(mx_step): 21 | print("{}/{}".format(i, mx_step)) 22 | 23 | # run one step simulation 24 | # you can use act() in agent.py to get the actions predicted by agent. 25 | actions = {0: 1} 26 | obs, rwd, dones, info = env.step(actions) 27 | 28 | # print observations and infos 29 | # for k, v in obs.items(): 30 | # print("{}:{}".format(k, v)) 31 | for k, v in info.items(): 32 | print("{}:{}".format(k, v)) 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | */__pycache__ 7 | .idea 8 | out/*.json 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /agent/agent.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | import gym 3 | 4 | from pathlib import Path 5 | import pickle 6 | import gym 7 | 8 | # how to import or load local files 9 | import os 10 | import sys 11 | path = os.path.split(os.path.realpath(__file__))[0] 12 | sys.path.append(path) 13 | import gym_cfg 14 | with open(path + "/gym_cfg.py", "r") as f: 15 | pass 16 | 17 | class TestAgent(): 18 | def __init__(self): 19 | self.now_phase = {} 20 | self.green_sec = 2 21 | self.max_phase = 8 22 | self.last_change_step = {} 23 | self.agent_list = [] 24 | self.phase_passablelane = {} 25 | self.intersections = {} 26 | self.roads = {} 27 | self.agents = {} 28 | ################################ 29 | # don't modify this function. 30 | # agent_list is a list of agent_id 31 | def load_agent_list(self,agent_list): 32 | self.agent_list = agent_list 33 | self.now_phase = dict.fromkeys(self.agent_list,1) 34 | self.last_change_step = dict.fromkeys(self.agent_list,0) 35 | 36 | # intersections[key_id] = { 37 | # 'have_signal': bool, 38 | # 'end_roads': list of road_id. Roads that end at this intersection. The order is random. 39 | # 'start_roads': list of road_id. Roads that start at this intersection. The order is random. 40 | # 'lanes': list, contains the lane_id in. The order is explained in Docs. 41 | # } 42 | # roads[road_id] = { 43 | # 'start_inter':int. Start intersection_id. 44 | # 'end_inter':int. End intersection_id. 45 | # 'length': float. Road length. 46 | # 'speed_limit': float. Road speed limit. 47 | # 'num_lanes': int. Number of lanes in this road. 48 | # 'inverse_road': Road_id of inverse_road. 49 | # 'lanes': dict. roads[road_id]['lanes'][lane_id] = list of 3 int value. Contains the Steerability of lanes. 50 | # lane_id is road_id*100 + 0/1/2... For example, if road 9 have 3 lanes, then their id are 900, 901, 902 51 | # } 52 | # agents[agent_id] = list of length 8. contains the inroad0_id, inroad1_id, inroad2_id,inroad3_id, outroad0_id, outroad1_id, outroad2_id, outroad3_id 53 | def load_roadnet(self,intersections, roads, agents): 54 | self.intersections = intersections 55 | self.roads = roads 56 | self.agents = agents 57 | ################################ 58 | 59 | 60 | def act(self, obs): 61 | """ !!! MUST BE OVERRIDED !!! 62 | """ 63 | # here obs contains all of the observations and infos 64 | 65 | # observations is returned 'observation' of env.step() 66 | # info is returned 'info' of env.step() 67 | observations = obs['observations'] 68 | info = obs['info'] 69 | actions = {} 70 | 71 | now_step = info['step'] 72 | # a simple fixtime agent 73 | 74 | # get actions 75 | for agent in self.agent_list: 76 | # select the now_step 77 | step_diff = now_step - self.last_change_step[agent] 78 | if(step_diff >= self.green_sec): 79 | self.now_phase[agent] = self.now_phase[agent] % self.max_phase + 1 80 | self.last_change_step[agent] = now_step 81 | actions[agent] = self.now_phase[agent] 82 | # print(self.intersections,self.roads,self.agents) 83 | return actions 84 | 85 | scenario_dirs = [ 86 | "test" 87 | ] 88 | 89 | agent_specs = dict.fromkeys(scenario_dirs, None) 90 | for i, k in enumerate(scenario_dirs): 91 | # initialize an AgentSpec instance with configuration 92 | agent_specs[k] = TestAgent() -------------------------------------------------------------------------------- /data/flow_warm_up_1000.txt: -------------------------------------------------------------------------------- 1 | 88 2 | 3535 3580 75 3 | 2 4 | 56 89 5 | 2173 3134 90 6 | 1 7 | 14 8 | 26 1158 60 9 | 3 10 | 105 71 78 11 | 2545 2674 60 12 | 5 13 | 22 87 78 50 58 14 | 2451 2485 45 15 | 2 16 | 40 104 17 | 810 1053 60 18 | 3 19 | 56 52 53 20 | 1410 1474 45 21 | 9 22 | 67 24 37 35 20 4 62 86 76 23 | 2488 3557 75 24 | 4 25 | 103 13 38 25 26 | 2679 3137 45 27 | 7 28 | 19 36 38 25 7 10 30 29 | 2889 3231 75 30 | 7 31 | 56 89 63 21 25 5 11 32 | 250 1980 60 33 | 6 34 | 61 91 98 45 44 41 35 | 2420 3415 75 36 | 5 37 | 42 57 49 77 88 38 | 3492 3507 60 39 | 7 40 | 12 6 26 22 64 90 55 41 | 1403 3229 90 42 | 9 43 | 75 85 61 96 89 63 21 23 68 44 | 3319 3534 75 45 | 4 46 | 9 8 26 37 47 | 1431 1513 90 48 | 1 49 | 54 50 | 622 3306 90 51 | 3 52 | 16 90 95 53 | 3494 3551 45 54 | 5 55 | 65 64 90 95 91 56 | 2823 2961 45 57 | 3 58 | 28 10 32 59 | 2810 3068 75 60 | 3 61 | 52 36 38 62 | 259 2225 90 63 | 4 64 | 19 36 38 25 65 | 2410 2697 60 66 | 3 67 | 77 88 21 68 | 833 1357 60 69 | 2 70 | 72 106 71 | 1722 2448 60 72 | 7 73 | 46 56 52 36 38 23 68 74 | 3150 3417 75 75 | 8 76 | 80 83 65 87 78 50 58 41 77 | 3580 3592 75 78 | 4 79 | 77 88 21 23 80 | 456 1758 45 81 | 4 82 | 38 25 7 10 83 | 2369 3258 75 84 | 1 85 | 91 86 | 1950 3141 75 87 | 2 88 | 29 32 89 | 1863 2298 90 90 | 7 91 | 57 49 77 88 21 23 73 92 | 3013 3387 45 93 | 5 94 | 103 13 35 20 2 95 | 390 1326 90 96 | 4 97 | 6 26 37 35 98 | 3555 3573 45 99 | 1 100 | 63 101 | 2215 3063 75 102 | 2 103 | 89 100 104 | 932 2208 45 105 | 3 106 | 38 22 66 107 | 330 1475 45 108 | 3 109 | 93 98 45 110 | 3313 3594 60 111 | 2 112 | 20 2 113 | 1744 2796 60 114 | 2 115 | 102 106 116 | 1781 3337 45 117 | 5 118 | 1 4 96 89 100 119 | 1983 2351 75 120 | 7 121 | 1 19 36 38 25 5 11 122 | 1810 2629 45 123 | 3 124 | 98 105 71 125 | 3371 3387 90 126 | 9 127 | 67 24 37 35 20 4 62 86 76 128 | 2679 3095 60 129 | 1 130 | 62 131 | 2742 3292 60 132 | 2 133 | 30 80 134 | 2818 3554 90 135 | 7 136 | 75 85 61 96 89 15 14 137 | 1819 3497 90 138 | 4 139 | 21 25 7 10 140 | 3029 3460 75 141 | 3 142 | 1 4 91 143 | 2079 2528 75 144 | 1 145 | 19 146 | 989 3487 90 147 | 4 148 | 13 16 63 66 149 | 3489 3496 75 150 | 3 151 | 90 95 62 152 | 1617 2544 90 153 | 4 154 | 101 99 15 14 155 | 3591 3593 75 156 | 9 157 | 42 57 49 77 88 21 25 5 11 158 | 3476 3500 90 159 | 4 160 | 65 64 15 35 161 | 431 1343 60 162 | 6 163 | 36 38 25 7 10 30 164 | 3268 3340 60 165 | 3 166 | 46 56 95 167 | 363 1992 90 168 | 2 169 | 69 24 170 | 151 2452 60 171 | 4 172 | 12 28 10 34 173 | 2893 2994 75 174 | 3 175 | 12 28 10 176 | 1420 2907 75 177 | 1 178 | 57 179 | 2546 2640 75 180 | 2 181 | 95 3 182 | 3571 3590 60 183 | 6 184 | 12 6 26 22 87 72 185 | 3280 3575 75 186 | 3 187 | 55 45 44 188 | 1827 2878 75 189 | 6 190 | 26 22 64 90 55 45 191 | 2743 3010 60 192 | 5 193 | 24 22 87 78 50 194 | 90 3365 75 195 | 5 196 | 77 88 21 25 5 197 | 727 2458 75 198 | 4 199 | 64 90 95 62 200 | 1918 2776 90 201 | 2 202 | 28 10 203 | 2000 2503 45 204 | 2 205 | 1 4 206 | 2869 3494 60 207 | 3 208 | 66 84 79 209 | 2094 2286 60 210 | 4 211 | 19 36 38 25 212 | 907 3342 60 213 | 3 214 | 37 16 100 215 | 3225 3276 75 216 | 3 217 | 45 44 57 218 | 2597 3550 90 219 | 2 220 | 42 57 221 | 2394 2490 90 222 | 2 223 | 64 100 224 | 1842 3580 60 225 | 5 226 | 67 24 37 35 20 227 | 3379 3598 60 228 | 8 229 | 85 61 96 89 63 66 84 79 230 | 91 2641 60 231 | 2 232 | 99 15 233 | 1126 3050 45 234 | 3 235 | 61 91 98 236 | 970 1431 60 237 | 1 238 | 54 239 | 2331 2978 75 240 | 4 241 | 99 63 66 84 242 | 1774 2213 90 243 | 1 244 | 38 245 | 3245 3426 75 246 | 5 247 | 91 98 105 71 78 248 | 2349 3185 90 249 | 2 250 | 42 57 251 | 1170 2193 90 252 | 8 253 | 42 57 49 77 88 21 23 68 254 | 3309 3400 75 255 | 1 256 | 91 257 | 1635 1949 90 258 | 2 259 | 36 16 260 | 2413 3174 45 261 | 5 262 | 57 49 77 88 21 263 | 1253 1648 90 264 | 5 265 | 46 56 52 36 14 266 | -------------------------------------------------------------------------------- /rllib_train.py: -------------------------------------------------------------------------------- 1 | from ray import tune 2 | import gym 3 | from agent.CBEngine_round3 import CBEngine_round3 as CBEngine_rllib_class 4 | import citypb 5 | import ray 6 | from ray import tune 7 | import os 8 | import numpy as np 9 | import argparse 10 | import sys 11 | import subprocess 12 | parser = argparse.ArgumentParser() 13 | 14 | 15 | 16 | if __name__ == "__main__": 17 | # some argument 18 | parser.add_argument( 19 | "--num_workers", 20 | type=int, 21 | default=30, 22 | help="rllib num workers" 23 | ) 24 | parser.add_argument( 25 | "--multiflow", 26 | '-m', 27 | action="store_true", 28 | default = False, 29 | help="use multiple flow file in training" 30 | ) 31 | parser.add_argument( 32 | "--stop-iters", 33 | type=int, 34 | default=10, 35 | help="Number of iterations to train.") 36 | parser.add_argument( 37 | "--algorithm", 38 | type=str, 39 | default="A3C", 40 | help="algorithm for rllib" 41 | ) 42 | parser.add_argument( 43 | "--sim_cfg", 44 | type=str, 45 | default="/starter-kit/cfg/simulator_round3_flow0.cfg", 46 | help = "simulator file for CBEngine" 47 | ) 48 | parser.add_argument( 49 | "--metric_period", 50 | type=int, 51 | default=3600, 52 | help = "simulator file for CBEngine" 53 | ) 54 | parser.add_argument( 55 | "--thread_num", 56 | type=int, 57 | default=8, 58 | help = "thread num for CBEngine" 59 | ) 60 | parser.add_argument( 61 | "--gym_cfg_dir", 62 | type = str, 63 | default="agent", 64 | help = "gym_cfg (observation, reward) for CBEngine" 65 | ) 66 | parser.add_argument( 67 | "--checkpoint_freq", 68 | type = int, 69 | default = 5, 70 | help = "frequency of saving checkpoint" 71 | ) 72 | 73 | parser.add_argument( 74 | "--foldername", 75 | type = str, 76 | default = 'train_result', 77 | help = 'The result of the training will be saved in ./model/$algorithm/$foldername/. Foldername can\'t have any space' 78 | ) 79 | 80 | # find the submission path to import gym_cfg 81 | args = parser.parse_args() 82 | for dirpath, dirnames, file_names in os.walk(args.gym_cfg_dir): 83 | for file_name in [f for f in file_names if f.endswith(".py")]: 84 | if file_name == "gym_cfg.py": 85 | cfg_path = dirpath 86 | sys.path.append(str(cfg_path)) 87 | import gym_cfg as gym_cfg_submission 88 | gym_cfg_instance = gym_cfg_submission.gym_cfg() 89 | gym_dict = gym_cfg_instance.cfg 90 | simulator_cfg_files=[] 91 | 92 | # if set '--multiflow', then the CBEngine will utilize flows in 'simulator_cfg_files' 93 | if(args.multiflow): 94 | simulator_cfg_files = [ 95 | '/starter-kit/cfg/simulator_round3_flow0.cfg' 96 | ] 97 | else: 98 | simulator_cfg_files = [args.sim_cfg] 99 | print('The cfg files of this training ',format(simulator_cfg_files)) 100 | class MultiFlowCBEngine(CBEngine_rllib_class): 101 | def __init__(self, env_config): 102 | env_config["simulator_cfg_file"] = simulator_cfg_files[(env_config.worker_index - 1) % len(simulator_cfg_files)] 103 | super(MultiFlowCBEngine, self).__init__(config=env_config) 104 | 105 | 106 | # some configuration 107 | env_config = { 108 | "simulator_cfg_file": args.sim_cfg, 109 | "thread_num": args.thread_num, 110 | "gym_dict": gym_dict, 111 | "metric_period":args.metric_period, 112 | "vehicle_info_path":"/starter-kit/log/" 113 | } 114 | obs_size = gym_dict['observation_dimension'] 115 | OBSERVATION_SPACE = gym.spaces.Dict({ 116 | "observation": gym.spaces.Box(low=-1e10, high=1e10, shape=(obs_size,)) 117 | }) 118 | ACTION_SPACE = gym.spaces.Discrete(9) 119 | stop = { 120 | "training_iteration": args.stop_iters 121 | } 122 | ################################ 123 | # modify this 124 | tune_config = { 125 | # env config 126 | "env":MultiFlowCBEngine, 127 | "env_config" : env_config, 128 | "multiagent": { 129 | "policies": { 130 | "default_policy": (None, OBSERVATION_SPACE, ACTION_SPACE, {},) 131 | } 132 | }, 133 | 134 | "num_cpus_per_worker":args.thread_num, 135 | "num_workers":args.num_workers, 136 | 137 | 138 | 139 | # add your training config 140 | 141 | } 142 | ######################################## 143 | ray.init(address = "auto") 144 | local_path = './model' 145 | 146 | 147 | 148 | def name_creator(self=None): 149 | return args.foldername 150 | 151 | 152 | # train model 153 | ray.tune.run(args.algorithm, config=tune_config, local_dir=local_path, stop=stop, 154 | checkpoint_freq=args.checkpoint_freq,trial_dirname_creator = name_creator) 155 | 156 | 157 | -------------------------------------------------------------------------------- /agent/agent_MP.py: -------------------------------------------------------------------------------- 1 | 2 | import pickle 3 | import gym 4 | 5 | from pathlib import Path 6 | import pickle 7 | import gym 8 | import numpy as np 9 | 10 | # contains all of the intersections 11 | # First 24 items of observation must be 'lane_vehicle_num' 12 | class TestAgent(): 13 | def __init__(self): 14 | self.now_phase = {} 15 | self.green_sec = 2 16 | self.red_sec = 5 17 | self.max_phase = 4 18 | self.last_change_step = {} 19 | self.agent_list = [] 20 | self.phase_passablelane = {} 21 | self.phase_lane_map_in = [[1, 7], [2, 8], [4, 10], [5, 11], [2, 1], [5, 4], [8, 7], [11, 10]] 22 | self.phase_lane_map_out = [[16, 17, 18, 22, 23, 24], [13, 14, 15, 19, 20, 21], 23 | [13, 14, 15, 19, 20, 21], [16, 17, 18, 22, 23, 24], 24 | [16, 17, 18, 19, 20, 21], [19, 20, 21, 22, 23, 24], 25 | [13, 14, 15, 22, 23, 24], [13, 14, 15, 16, 17, 18]] 26 | self.phase_lane_map_in = np.array(self.phase_lane_map_in) - 1 27 | self.phase_lane_map_out = np.array(self.phase_lane_map_out) - 1 28 | # # print("MAX PRESSURE") 29 | ################################ 30 | # don't modify this function. 31 | # agent_list is a list of agent_id 32 | def load_agent_list(self,agent_list): 33 | self.agent_list = agent_list 34 | self.now_phase = dict.fromkeys(self.agent_list,1) 35 | self.last_change_step = dict.fromkeys(self.agent_list,0) 36 | 37 | def load_roadnet(self,intersections,roads,agents): 38 | # in_roads = [] 39 | # for agent, agent_roads in agents: 40 | # in_roads = agent_roads[:4] 41 | # now_phase = dict.fromkeys(range(9)) 42 | # now_phase[0] = [] 43 | # in_roads[0] 44 | pass 45 | 46 | ################################ 47 | 48 | 49 | def get_phase_pressures(self, lane_vehicle_num): 50 | pressures = [] 51 | for i in range(8): 52 | in_lanes = self.phase_lane_map_in[i] 53 | out_lanes = self.phase_lane_map_out[i] 54 | pressure = 0 55 | for in_lane in in_lanes: 56 | pressure += lane_vehicle_num[in_lane] * 3 57 | for out_lane in out_lanes: 58 | pressure -= lane_vehicle_num[out_lane] 59 | pressures.append(pressure) 60 | # # print("pressures: ", pressures) 61 | return pressures 62 | 63 | def get_action(self, lane_vehicle_num): 64 | pressures = self.get_phase_pressures(lane_vehicle_num) 65 | unavailable_phases = self.get_unavailable_phases(lane_vehicle_num) 66 | # if len(unavailable_phases) > 0: 67 | # # print("unavailable_phases: ", unavailable_phases) 68 | 69 | max_pressure_id = np.argmax(pressures) + 1 70 | while (max_pressure_id in unavailable_phases): 71 | pressures[max_pressure_id - 1] -= 999999 72 | max_pressure_id = np.argmax(pressures) + 1 73 | # # print(max_pressure_id) 74 | return max_pressure_id 75 | 76 | 77 | 78 | def get_unavailable_phases(self, lane_vehicle_num): 79 | unavailable_phases = [] 80 | not_exist_lanes = [] 81 | for i in range(24): 82 | if lane_vehicle_num[i] < 0: 83 | not_exist_lanes.append(i) 84 | for lane in not_exist_lanes: 85 | for phase_id, in_lanes in enumerate(self.phase_lane_map_in): 86 | phase_id += 1 87 | if lane in in_lanes and phase_id not in unavailable_phases: 88 | unavailable_phases.append(phase_id) 89 | 90 | return unavailable_phases 91 | # return [5, 6, 7, 8] 92 | 93 | 94 | def act(self, obs): 95 | """ !!! MUST BE OVERRIDED !!! 96 | """ 97 | # here obs contains all of the observations and infos 98 | observations = obs['observations'] 99 | info = obs['info'] 100 | actions = {} 101 | 102 | now_step = info['step'] 103 | # preprocess observations 104 | # a simple fixtime agent 105 | 106 | for agent in self.agent_list: 107 | # select the now_step 108 | 109 | lane_vehicle_num = observations[agent]['observation'] 110 | # print("agent id: ", agent) 111 | # print("lane vehicle: ", lane_vehicle_num) 112 | action = self.get_action(lane_vehicle_num) 113 | # print("action: ", action) 114 | 115 | step_diff = now_step - self.last_change_step[agent] 116 | if (step_diff >= self.green_sec): 117 | self.now_phase[agent] = action 118 | self.last_change_step[agent] = now_step 119 | 120 | actions[agent] = self.now_phase[agent] 121 | # print("phase: ", actions[agent]) 122 | # print("phase available lane: ", self.phase_lane_map_in[actions[agent]-1]) 123 | # print("________") 124 | 125 | return actions 126 | 127 | scenario_dirs = [ 128 | "test" 129 | ] 130 | 131 | agent_specs = dict.fromkeys(scenario_dirs, None) 132 | for i, k in enumerate(scenario_dirs): 133 | # initialize an AgentSpec instance with configuration 134 | agent_specs[k] = TestAgent() 135 | # **important**: assign policy builder to your agent spec 136 | # NOTE: the policy builder must be a callable function which returns an instance of `AgentPolicy` 137 | 138 | -------------------------------------------------------------------------------- /agent/agent_rllib.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | import gym 3 | import tensorflow as tf 4 | from pathlib import Path 5 | import pickle 6 | import gym 7 | from ray.rllib.models import ModelCatalog 8 | # how to import or load local files 9 | import os 10 | import sys 11 | path = os.path.split(os.path.realpath(__file__))[0] 12 | sys.path.append(path) 13 | import gym_cfg 14 | with open(path + "/gym_cfg.py", "r") as f: 15 | pass 16 | 17 | class TestAgent(): 18 | def __init__( 19 | self 20 | ): 21 | ############## 22 | # implement the config of your model 23 | gym_cfg_instance = gym_cfg.gym_cfg() 24 | obs_size = gym_cfg_instance.cfg['observation_dimension'] 25 | observation_space = gym.spaces.Dict({ 26 | # "observation": gym.spaces.Box(low=-1e10, high=1e10, shape=(24 * len(gym_dict['observation_features']),)) 27 | "observation": gym.spaces.Box(low=-1e10, high=1e10, shape=(obs_size,)) 28 | }) 29 | action_space = gym.spaces.Discrete(9) 30 | path = os.path.split(os.path.realpath(__file__))[0] 31 | self._checkpoint_path = os.path.join(path,'checkpoint-25(\'classic\' as observation)') 32 | self._algorithm = 'A3C' 33 | self._observation_space = observation_space 34 | self._action_space = action_space 35 | 36 | 37 | ##################### 38 | self._policy_name = 'default_policy' 39 | self._sess = None 40 | 41 | if isinstance(action_space, gym.spaces.Box): 42 | self.is_continuous = True 43 | elif isinstance(action_space, gym.spaces.Discrete): 44 | self.is_continuous = False 45 | else: 46 | raise TypeError("Unsupport action space") 47 | 48 | if self._sess: 49 | return 50 | 51 | if self._algorithm == "PPO": 52 | from ray.rllib.agents.ppo.ppo_tf_policy import PPOTFPolicy as LoadPolicy 53 | elif self._algorithm in ["A2C", "A3C"]: 54 | from ray.rllib.agents.a3c.a3c_tf_policy import A3CTFPolicy as LoadPolicy 55 | elif self._algorithm == "PG": 56 | from ray.rllib.agents.pg.pg_tf_policy import PGTFPolicy as LoadPolicy 57 | elif self._algorithm in ["DQN","APEX"]: 58 | from ray.rllib.agents.dqn.dqn_tf_policy import DQNTFPolicy as LoadPolicy 59 | else: 60 | raise TypeError("Unsupport algorithm") 61 | 62 | self._prep = ModelCatalog.get_preprocessor_for_space(self._observation_space) 63 | self._sess = tf.Session(graph=tf.Graph()) 64 | self._sess.__enter__() 65 | 66 | with tf.name_scope(self._policy_name): 67 | # obs_space need to be flattened before passed to PPOTFPolicy 68 | flat_obs_space = self._prep.observation_space 69 | self.policy = LoadPolicy(flat_obs_space, self._action_space, {}) 70 | objs = pickle.load(open(self._checkpoint_path, "rb")) 71 | objs = pickle.loads(objs["worker"]) 72 | state = objs["state"] 73 | weights = state[self._policy_name] 74 | list_keys = list(weights.keys()) 75 | for k in list_keys: 76 | if(k not in self.policy.get_weights().keys()): 77 | weights.pop(k) 78 | self.policy.set_weights(weights) 79 | 80 | self.agent_list = [] 81 | self.intersections = {} 82 | self.roads = {} 83 | self.agents = {} 84 | ################################ 85 | # don't modify this function. 86 | # agent_list is a list of agent_id 87 | def load_agent_list(self,agent_list): 88 | self.agent_list = agent_list 89 | self.now_phase = dict.fromkeys(self.agent_list,1) 90 | self.last_change_step = dict.fromkeys(self.agent_list,0) 91 | 92 | # intersections[key_id] = { 93 | # 'have_signal': bool, 94 | # 'end_roads': list of road_id. Roads that end at this intersection. The order is random. 95 | # 'start_roads': list of road_id. Roads that start at this intersection. The order is random. 96 | # 'lanes': list, contains the lane_id in. The order is explained in Docs. 97 | # } 98 | # roads[road_id] = { 99 | # 'start_inter':int. Start intersection_id. 100 | # 'end_inter':int. End intersection_id. 101 | # 'length': float. Road length. 102 | # 'speed_limit': float. Road speed limit. 103 | # 'num_lanes': int. Number of lanes in this road. 104 | # 'inverse_road': Road_id of inverse_road. 105 | # 'lanes': dict. roads[road_id]['lanes'][lane_id] = list of 3 int value. Contains the Steerability of lanes. 106 | # lane_id is road_id*100 + 0/1/2... For example, if road 9 have 3 lanes, then their id are 900, 901, 902 107 | # } 108 | # agents[agent_id] = list of length 8. contains the inroad0_id, inroad1_id, inroad2_id,inroad3_id, outroad0_id, outroad1_id, outroad2_id, outroad3_id 109 | def load_roadnet(self,intersections, roads, agents): 110 | self.intersections = intersections 111 | self.roads = roads 112 | self.agents = agents 113 | ################################ 114 | 115 | 116 | def act(self, obs): 117 | """ !!! MUST BE OVERRIDED !!! 118 | """ 119 | # here obs contains all of the observations and infos 120 | 121 | # observations is returned 'observation' of env.step() 122 | # info is returned 'info' of env.step() 123 | observations = obs['observations'] 124 | info = obs['info'] 125 | 126 | 127 | action = {} 128 | for k, v in observations.items(): 129 | obs_cur = self._prep.transform(v) 130 | action[k] = self.policy.compute_actions([obs_cur], explore=False)[0][0] 131 | 132 | return action 133 | 134 | 135 | scenario_dirs = [ 136 | "test" 137 | ] 138 | 139 | agent_specs = dict.fromkeys(scenario_dirs, None) 140 | for i, k in enumerate(scenario_dirs): 141 | # initialize an AgentSpec instance with configuration 142 | agent_specs[k] = TestAgent() -------------------------------------------------------------------------------- /data/roadnet_warm_up.txt: -------------------------------------------------------------------------------- 1 | 36 2 | 28.674579650000002 115.847174925 42167350403 1 3 | 28.675813249999997 115.841737175 42167350405 1 4 | 28.67896945 115.84839492500001 42167350420 1 5 | 28.6825176 115.84938435000001 42167350438 0 6 | 28.682649750000003 115.83705922499999 42167350445 1 7 | 28.68463535 115.84250444999999 42167350456 1 8 | 28.6848265 115.83234605 42167350458 0 9 | 28.6865176 115.8377342 12167350464 0 10 | 28.687264075 115.848014625 42167350474 0 11 | 28.688112099999998 115.83444109999999 42167350476 1 12 | 28.688536425 115.844729325 42167350479 1 13 | 28.681282225 115.84057437499999 42265990106 1 14 | 28.672432425 115.83987712499999 42294463892 1 15 | 28.673870899999997 115.84069500000001 22294463895 0 16 | 28.6739045 115.84058200000001 22294465661 0 17 | 28.6768983 115.8338534 12294471859 0 18 | 28.6738211 115.8368346 12294471861 0 19 | 28.67991605 115.84410872499998 42294477575 1 20 | 28.6751169 115.8447739 22318148289 1 21 | 28.67947425 115.84600585000001 22318148293 1 22 | 28.686007425 115.839110525 42365406897 1 23 | 28.6871129 115.8369108 12365406899 1 24 | 28.689819149999998 115.84146887499999 42365409354 1 25 | 28.67737555 115.83821185 22372852612 1 26 | 28.679039 115.8391812 12372852614 0 27 | 28.678914575 115.8348903 42381408549 1 28 | 28.69073945 115.8391207 23137016667 0 29 | 28.6904409 115.8457605 13137029377 0 30 | 28.6886759 115.8406186 13987210066 0 31 | 28.6895268 115.8384026 13987210067 1 32 | 28.69049995 115.83595005000001 23987210068 0 33 | 28.683320225 115.84592275 44052836274 1 34 | 28.6877547 115.8351392 14454920703 1 35 | 28.6828852 115.8471809 14670355735 1 36 | 28.6866282 115.8377798 14790105773 1 37 | 28.672666550000002 115.84665025 28076984869 0 38 | 51 39 | 42167350438 14670355735 177.0 13.88888888888889 3 3 1 2 40 | 1 0 0 0 1 0 0 0 1 41 | 1 0 0 0 1 0 0 0 1 42 | 22318148293 14670355735 391.0 11.11111111111111 3 3 3 4 43 | 1 0 0 0 1 0 0 0 1 44 | 1 0 0 0 1 0 0 0 1 45 | 14790105773 14454920703 278.0 16.666666666666668 3 3 5 6 46 | 1 0 0 0 1 0 0 0 1 47 | 1 0 0 0 1 0 0 0 1 48 | 14790105773 12365406899 100.0 16.666666666666668 3 3 7 8 49 | 1 0 0 0 1 0 0 0 1 50 | 1 0 0 0 1 0 0 0 1 51 | 13987210067 12365406899 305.0 11.11111111111111 3 3 9 10 52 | 1 0 0 0 1 0 0 0 1 53 | 1 0 0 0 1 0 0 0 1 54 | 14454920703 12167350464 287.0 16.666666666666668 3 3 11 12 55 | 1 0 0 0 1 0 0 0 1 56 | 1 0 0 0 1 0 0 0 1 57 | 42167350479 42167350456 474.0 11.11111111111111 3 3 13 14 58 | 1 0 0 0 1 0 0 0 1 59 | 1 0 0 0 1 0 0 0 1 60 | 42265990106 42167350456 407.0 11.11111111111111 3 3 15 16 61 | 1 0 0 0 1 0 0 0 1 62 | 1 0 0 0 1 0 0 0 1 63 | 14454920703 42167350476 68.0 2.7777777777777777 3 3 17 18 64 | 1 0 0 0 1 0 0 0 1 65 | 1 0 0 0 1 0 0 0 1 66 | 14670355735 44052836274 111.0 2.7777777777777777 3 3 19 20 67 | 1 0 0 0 1 0 0 0 1 68 | 1 0 0 0 1 0 0 0 1 69 | 42167350445 42365406897 411.0 13.88888888888889 3 3 21 22 70 | 1 0 0 0 1 0 0 0 1 71 | 1 0 0 0 1 0 0 0 1 72 | 42365406897 42365409354 468.0 13.88888888888889 3 3 23 24 73 | 1 0 0 0 1 0 0 0 1 74 | 1 0 0 0 1 0 0 0 1 75 | 42365406897 14790105773 134.0 16.666666666666668 3 3 25 26 76 | 1 0 0 0 1 0 0 0 1 77 | 1 0 0 0 1 0 0 0 1 78 | 12365406899 14454920703 213.0 16.666666666666668 3 3 27 28 79 | 1 0 0 0 1 0 0 0 1 80 | 1 0 0 0 1 0 0 0 1 81 | 23987210068 13987210067 259.0 11.11111111111111 3 3 29 30 82 | 1 0 0 0 1 0 0 0 1 83 | 1 0 0 0 1 0 0 0 1 84 | 13987210066 13987210067 236.0 11.11111111111111 3 3 31 32 85 | 1 0 0 0 1 0 0 0 1 86 | 1 0 0 0 1 0 0 0 1 87 | 23137016667 13987210067 147.0 11.11111111111111 3 3 33 34 88 | 1 0 0 0 1 0 0 0 1 89 | 1 0 0 0 1 0 0 0 1 90 | 42167350456 44052836274 281.0 16.666666666666668 3 3 35 36 91 | 1 0 0 0 1 0 0 0 1 92 | 1 0 0 0 1 0 0 0 1 93 | 42365406897 42167350456 347.0 16.666666666666668 3 3 37 38 94 | 1 0 0 0 1 0 0 0 1 95 | 1 0 0 0 1 0 0 0 1 96 | 42167350479 42167350474 333.0 11.11111111111111 3 3 39 40 97 | 1 0 0 0 1 0 0 0 1 98 | 1 0 0 0 1 0 0 0 1 99 | 42294463892 22294463895 269.0 11.11111111111111 3 3 41 42 100 | 1 0 0 0 1 0 0 0 1 101 | 1 0 0 0 1 0 0 0 1 102 | 42294463892 22294465661 236.0 11.11111111111111 3 3 43 44 103 | 1 0 0 0 1 0 0 0 1 104 | 1 0 0 0 1 0 0 0 1 105 | 42167350405 22294465661 125.0 16.666666666666668 3 3 45 46 106 | 1 0 0 0 1 0 0 0 1 107 | 1 0 0 0 1 0 0 0 1 108 | 12294471861 12294471859 530.0 16.666666666666668 3 3 49 50 109 | 1 0 0 0 1 0 0 0 1 110 | 1 0 0 0 1 0 0 0 1 111 | 44052836274 42294477575 256.0 16.666666666666668 3 3 51 52 112 | 1 0 0 0 1 0 0 0 1 113 | 1 0 0 0 1 0 0 0 1 114 | 44052836274 42167350474 342.0 16.666666666666668 3 3 53 54 115 | 1 0 0 0 1 0 0 0 1 116 | 1 0 0 0 1 0 0 0 1 117 | 42294477575 42167350405 358.0 16.666666666666668 3 3 55 56 118 | 1 0 0 0 1 0 0 0 1 119 | 1 0 0 0 1 0 0 0 1 120 | 42294463892 12294471861 228.0 16.666666666666668 3 3 57 58 121 | 1 0 0 0 1 0 0 0 1 122 | 1 0 0 0 1 0 0 0 1 123 | 42167350420 22318148293 187.0 11.11111111111111 3 3 61 62 124 | 1 0 0 0 1 0 0 0 1 125 | 1 0 0 0 1 0 0 0 1 126 | 42265990106 42167350445 360.0 11.11111111111111 3 3 63 64 127 | 1 0 0 0 1 0 0 0 1 128 | 1 0 0 0 1 0 0 0 1 129 | 42167350458 42167350445 505.0 11.11111111111111 3 3 65 66 130 | 1 0 0 0 1 0 0 0 1 131 | 1 0 0 0 1 0 0 0 1 132 | 23137016667 42365409354 239.0 11.11111111111111 3 3 67 68 133 | 1 0 0 0 1 0 0 0 1 134 | 1 0 0 0 1 0 0 0 1 135 | 42167350479 42365409354 330.0 11.11111111111111 3 3 69 70 136 | 1 0 0 0 1 0 0 0 1 137 | 1 0 0 0 1 0 0 0 1 138 | 22372852612 42381408549 354.0 11.11111111111111 3 3 71 72 139 | 1 0 0 0 1 0 0 0 1 140 | 1 0 0 0 1 0 0 0 1 141 | 42365409354 13987210066 145.0 13.88888888888889 3 3 73 74 142 | 1 0 0 0 1 0 0 0 1 143 | 1 0 0 0 1 0 0 0 1 144 | 28076984869 42167350403 153.0 13.88888888888889 3 3 75 76 145 | 1 0 0 0 1 0 0 0 1 146 | 1 0 0 0 1 0 0 0 1 147 | 12294471859 42381408549 238.0 13.88888888888889 3 3 77 78 148 | 1 0 0 0 1 0 0 0 1 149 | 1 0 0 0 1 0 0 0 1 150 | 42167350476 23987210068 246.0 13.88888888888889 3 3 79 80 151 | 1 0 0 0 1 0 0 0 1 152 | 1 0 0 0 1 0 0 0 1 153 | 42167350420 42167350438 259.0 13.88888888888889 3 3 81 82 154 | 1 0 0 0 1 0 0 0 1 155 | 1 0 0 0 1 0 0 0 1 156 | 42167350476 42167350458 351.0 13.88888888888889 3 3 83 84 157 | 1 0 0 0 1 0 0 0 1 158 | 1 0 0 0 1 0 0 0 1 159 | 42167350403 42167350420 368.0 13.88888888888889 3 3 85 86 160 | 1 0 0 0 1 0 0 0 1 161 | 1 0 0 0 1 0 0 0 1 162 | 42167350445 42381408549 455.0 13.88888888888889 3 3 87 88 163 | 1 0 0 0 1 0 0 0 1 164 | 1 0 0 0 1 0 0 0 1 165 | 42294477575 42265990106 297.0 11.11111111111111 3 3 89 90 166 | 1 0 0 0 1 0 0 0 1 167 | 1 0 0 0 1 0 0 0 1 168 | 22318148293 22318148289 491.0 11.11111111111111 3 3 91 92 169 | 1 0 0 0 1 0 0 0 1 170 | 1 0 0 0 1 0 0 0 1 171 | 42167350403 22318148289 173.0 11.11111111111111 3 3 93 94 172 | 1 0 0 0 1 0 0 0 1 173 | 1 0 0 0 1 0 0 0 1 174 | 42294477575 22318148293 138.0 11.11111111111111 3 3 95 96 175 | 1 0 0 0 1 0 0 0 1 176 | 1 0 0 0 1 0 0 0 1 177 | 42167350405 22318148289 228.0 11.11111111111111 3 3 97 98 178 | 1 0 0 0 1 0 0 0 1 179 | 1 0 0 0 1 0 0 0 1 180 | 12372852614 42265990106 283.0 11.11111111111111 3 3 99 100 181 | 1 0 0 0 1 0 0 0 1 182 | 1 0 0 0 1 0 0 0 1 183 | 22372852612 12372852614 202.0 11.11111111111111 3 3 101 102 184 | 1 0 0 0 1 0 0 0 1 185 | 1 0 0 0 1 0 0 0 1 186 | 13137029377 42167350479 230.0 11.11111111111111 3 3 103 104 187 | 1 0 0 0 1 0 0 0 1 188 | 1 0 0 0 1 0 0 0 1 189 | 42167350405 22372852612 322.0 11.11111111111111 3 3 105 106 190 | 1 0 0 0 1 0 0 0 1 191 | 1 0 0 0 1 0 0 0 1 192 | 22 193 | 14670355735 2 4 19 -1 194 | 22318148293 3 62 91 96 195 | 14790105773 26 -1 5 7 196 | 14454920703 28 6 11 17 197 | 12365406899 10 8 -1 27 198 | 13987210067 34 32 9 30 199 | 42167350479 104 39 13 69 200 | 42167350456 14 35 16 38 201 | 42265990106 15 90 100 63 202 | 42167350476 79 18 83 -1 203 | 44052836274 53 20 51 36 204 | 42167350445 21 64 87 66 205 | 42365406897 23 37 22 25 206 | 42365409354 70 24 73 68 207 | 42294463892 43 41 -1 57 208 | 42167350405 56 97 45 105 209 | 42294477575 52 95 55 89 210 | 42167350420 81 -1 86 61 211 | 22372852612 101 106 -1 71 212 | 42381408549 88 72 78 -1 213 | 42167350403 85 -1 76 93 214 | 22318148289 92 94 -1 98 215 | -------------------------------------------------------------------------------- /agent/CBEngine_round3.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import numpy as np 3 | import citypb 4 | from ray import tune 5 | import os 6 | from CBEngine_rllib.CBEngine_rllib import CBEngine_rllib as CBEngine_rllib_class 7 | import argparse 8 | 9 | 10 | class CBEngine_round3(CBEngine_rllib_class): 11 | """See CBEngine_rllib_class in /CBEngine_env/env/CBEngine_rllib/CBEngine_rllib.py 12 | 13 | Need to implement reward. 14 | 15 | implementation of observation is optional 16 | 17 | """ 18 | 19 | def __init__(self, config): 20 | super(CBEngine_round3, self).__init__(config) 21 | self.observation_features = self.gym_dict['observation_features'] 22 | self.custom_observation = self.gym_dict['custom_observation'] 23 | self.observation_dimension = self.gym_dict['observation_dimension'] 24 | 25 | def _get_observations(self): 26 | 27 | if (self.custom_observation == False): 28 | obs = super(CBEngine_round3, self)._get_observations() 29 | return obs 30 | else: 31 | ############ 32 | # implement your own observation 33 | # 34 | 35 | ############################# 36 | # observation 1 : 120 dimension 37 | # obs = {} 38 | # lane_vehicle = self.eng.get_lane_vehicles() 39 | # for agent_id, roads in self.agent_signals.items(): 40 | # result_obs = [] 41 | # # first 12 lanes 42 | # for id, lane in enumerate(self.intersections[agent_id]['lanes']): 43 | # if (id > 11): 44 | # break 45 | # if (lane == -1): 46 | # if (self.intersections[agent_id]['lanes'][id:id + 3] == [-1, -1, -1]): 47 | # result_obs.append(0) 48 | # result_obs.append(0) 49 | # result_obs.append(0) 50 | # else: 51 | # if (lane not in lane_vehicle.keys()): 52 | # result_obs.append(0) 53 | # else: 54 | # # the vehicle number of this lane 55 | # result_obs.append(len(lane_vehicle[lane])) 56 | # # onehot phase 57 | # cur_phase = self.agent_curphase[agent_id] 58 | # phase_map = [ 59 | # [-1, -1], 60 | # [0, 6], 61 | # [1, 7], 62 | # [3, 9], 63 | # [4, 10], 64 | # [0, 1], 65 | # [3, 4], 66 | # [6, 7], 67 | # [9, 10] 68 | # ] 69 | # one_hot_phase = [0] * 12 70 | # one_hot_phase[phase_map[cur_phase][0]] = 1 71 | # one_hot_phase[phase_map[cur_phase][1]] = 1 72 | # for i in range(4): 73 | # one_hot_phase[i * 3 + 2] = 1 74 | # result_obs += one_hot_phase 75 | 76 | # # calc rest 4 intersections 77 | # tar_roads = roads[:4] 78 | # tar_inters = [] 79 | # for road in tar_roads: 80 | # tar_inter = -1 81 | # if (road != -1): 82 | # tar_inter = self.roads[road]['start_inter'] 83 | # tar_inters.append(tar_inter) 84 | 85 | # for inter in tar_inters: 86 | # if (inter == -1): 87 | # for kk in range(12): 88 | # result_obs.append(0) 89 | # for kk in range(12): 90 | # result_obs.append(1) 91 | # else: 92 | # if ('lanes' in self.intersections[inter].keys()): 93 | # # first 12 lanes 94 | # for id, lane in enumerate(self.intersections[inter]['lanes']): 95 | # if (id > 11): 96 | # break 97 | # if (lane == -1): 98 | # if (self.intersections[inter]['lanes'][id:id + 3] == [-1, -1, -1]): 99 | # result_obs.append(0) 100 | # result_obs.append(0) 101 | # result_obs.append(0) 102 | # else: 103 | # if (lane not in lane_vehicle.keys()): 104 | # result_obs.append(0) 105 | # else: 106 | # # the vehicle number of this lane 107 | # result_obs.append(len(lane_vehicle[lane])) 108 | # # onehot phase 109 | # cur_phase = self.agent_curphase[inter] 110 | # phase_map = [ 111 | # [-1, -1], 112 | # [0, 6], 113 | # [1, 7], 114 | # [3, 9], 115 | # [4, 10], 116 | # [0, 1], 117 | # [3, 4], 118 | # [6, 7], 119 | # [9, 10] 120 | # ] 121 | # one_hot_phase = [0] * 12 122 | # one_hot_phase[phase_map[cur_phase][0]] = 1 123 | # one_hot_phase[phase_map[cur_phase][1]] = 1 124 | # for i in range(4): 125 | # one_hot_phase[i * 3 + 2] = 1 126 | # result_obs += one_hot_phase 127 | # else: 128 | # for in_road in self.intersections[inter]['end_roads']: 129 | # for xx in range(3): 130 | # lane = in_road * 100 + xx 131 | # if (lane not in lane_vehicle.keys()): 132 | # result_obs.append(0) 133 | # else: 134 | # result_obs.append(len(lane_vehicle[lane])) 135 | # remain_roads = 4 - len(self.intersections[inter]['end_roads']) 136 | # for kk in range(3 * remain_roads): 137 | # result_obs.append(0) 138 | 139 | # for kk in range(12): 140 | # result_obs.append(1) 141 | # obs[agent_id] = {"observation": result_obs} 142 | ############################## 143 | 144 | ############################## 145 | # observation 2, dim = 36 146 | obs = {} 147 | lane_vehicle = self.eng.get_lane_vehicles() 148 | # red is closer 149 | lane_vehicle_red = {} 150 | lane_vehicle_blue = {} 151 | 152 | def get_remain_time(self,vehicle_id): 153 | ''' 154 | For a vehicle, estimate the lower bound of travel time from current location to the intersection ahead. 155 | ''' 156 | remain_time = 0 157 | info = self.eng.get_vehicle_info(vehicle_id) 158 | speed = info['speed'][0] 159 | distance = info['distance'][0] 160 | lane_id = info['drivable'][0] 161 | road = info['road'][0] 162 | length = self.roads[road]['length'] 163 | speed_limit = self.roads[road]['speed_limit'] 164 | 165 | acc_time = (speed_limit - speed) / 2.0 166 | acc_dis = (speed_limit + speed)/ 2.0 * acc_time 167 | 168 | if(acc_dis + distance > length): 169 | remain_dis = length - distance 170 | acc_time_finish = (-speed + np.sqrt(speed * speed + 4 * remain_dis)) / 2.0 171 | remain_time = acc_time_finish 172 | else: 173 | remain_time += acc_time 174 | distance += acc_dis 175 | 176 | remain_dis = length - distance 177 | remain_time += remain_dis / speed_limit 178 | 179 | return remain_time, lane_id 180 | 181 | v_list = self.eng.get_vehicles() 182 | threshold = 10.0 183 | for vehicle in v_list: 184 | remain_time, lane_id = get_remain_time(self,vehicle) 185 | if(lane_id not in lane_vehicle_red.keys() or lane_id not in lane_vehicle_blue.keys()): 186 | lane_vehicle_red[lane_id] = [] 187 | lane_vehicle_blue[lane_id] = [] 188 | 189 | if(remain_time < threshold): 190 | lane_vehicle_red[lane_id].append(vehicle) 191 | else: 192 | lane_vehicle_blue[lane_id].append(vehicle) 193 | for agent_id, roads in self.agent_signals.items(): 194 | ############################## 195 | result_obs = [] 196 | for id, lane in enumerate(self.intersections[agent_id]['lanes']): 197 | if(id>11): 198 | break 199 | if(lane == -1): 200 | result_obs.append(0) 201 | else: 202 | if(lane not in lane_vehicle_red.keys()): 203 | result_obs.append(0) 204 | else: 205 | result_obs.append(len(lane_vehicle_red[lane])) 206 | for id, lane in enumerate(self.intersections[agent_id]['lanes']): 207 | if(id>11): 208 | break 209 | if(lane == -1): 210 | result_obs.append(0) 211 | else: 212 | if(lane not in lane_vehicle_blue.keys()): 213 | result_obs.append(0) 214 | else: 215 | result_obs.append(len(lane_vehicle_blue[lane])) 216 | # if(len(lane_vehicle_blue[lane])>500): 217 | # print("{} : {}".format(lane,lane_vehicle_blue[lane])) 218 | # onehot phase 219 | cur_phase = self.agent_curphase[agent_id] 220 | phase_map = [ 221 | [-1, -1], 222 | [0, 6], 223 | [1, 7], 224 | [3, 9], 225 | [4, 10], 226 | [0, 1], 227 | [3, 4], 228 | [6, 7], 229 | [9, 10] 230 | ] 231 | one_hot_phase = [0] * 12 232 | one_hot_phase[phase_map[cur_phase][0]] = 1 233 | one_hot_phase[phase_map[cur_phase][1]] = 1 234 | for i in range(4): 235 | one_hot_phase[i * 3 + 2] = 1 236 | result_obs += one_hot_phase 237 | 238 | 239 | 240 | obs[agent_id] = {"observation": result_obs} 241 | ######################### 242 | # Here agent_id must be str. So here change int to str 243 | int_agents = list(obs.keys()) 244 | for k in int_agents: 245 | obs[str(k)] = obs[k] 246 | obs.pop(k) 247 | 248 | return obs 249 | ############ 250 | 251 | def _get_reward(self): 252 | 253 | rwds = {} 254 | 255 | ################## 256 | ## Example : pressure as reward. 257 | lane_vehicle = self.eng.get_lane_vehicles() 258 | for agent_id, roads in self.agent_signals.items(): 259 | result_obs = [] 260 | for lane in self.intersections[agent_id]['lanes']: 261 | # -1 indicates empty roads in 'signal' of roadnet file 262 | if (lane == -1): 263 | result_obs.append(-1) 264 | else: 265 | # -2 indicates there's no vehicle on this lane 266 | if (lane not in lane_vehicle.keys()): 267 | result_obs.append(0) 268 | else: 269 | # the vehicle number of this lane 270 | result_obs.append(len(lane_vehicle[lane])) 271 | pressure = (np.sum(result_obs[12: 24]) - np.sum(result_obs[0: 12])) 272 | rwds[agent_id] = pressure 273 | ################## 274 | 275 | ################## 276 | ## Example : queue length as reward. 277 | # v_list = self.eng.get_vehicles() 278 | # for agent_id in self.agent_signals.keys(): 279 | # rwds[agent_id] = 0 280 | # for vehicle in v_list: 281 | # vdict = self.eng.get_vehicle_info(vehicle) 282 | # if(float(vdict['speed'][0])<0.5 and float(vdict['distance'][0]) > 1.0): 283 | # if(int(vdict['road'][0]) in self.road2signal.keys()): 284 | # agent_id = self.road2signal[int(vdict['road'][0])] 285 | # rwds[agent_id]-=1 286 | # normalization for qlength reward 287 | # for agent_id in self.agent_signals.keys(): 288 | # rwds[agent_id] /= 10 289 | 290 | ################## 291 | 292 | ################## 293 | ## Default reward, which can't be used in rllib 294 | ## self.lane_vehicle_state is dict. keys are agent_id(int), values are sets which maintain the vehicles of each lanes. 295 | 296 | # def get_diff(pre,sub): 297 | # in_num = 0 298 | # out_num = 0 299 | # for vehicle in pre: 300 | # if(vehicle not in sub): 301 | # out_num +=1 302 | # for vehicle in sub: 303 | # if(vehicle not in pre): 304 | # in_num += 1 305 | # return in_num,out_num 306 | # 307 | # lane_vehicle = self.eng.get_lane_vehicles() 308 | # bound of travel time from current location to the intersection ahead 309 | # for agent_id, roads in self.agents.items(): 310 | # rwds[agent_id] = [] 311 | # for lane in self.intersections[agent_id]['lanes']: 312 | # # -1 indicates empty roads in 'signal' of roadnet file 313 | # if (lane == -1): 314 | # rwds[agent_id].append(-1) 315 | # else: 316 | # if(lane not in lane_vehicle.keys()): 317 | # lane_vehicle[lane] = set() 318 | # rwds[agent_id].append(get_diff(self.lane_vehicle_state[lane],lane_vehicle[lane])) 319 | # self.lane_vehicle_state[lane] = lane_vehicle[lane] 320 | ################## 321 | # Change int keys to str keys because agent_id in actions must be str 322 | int_agents = list(rwds.keys()) 323 | for k in int_agents: 324 | rwds[str(k)] = rwds[k] 325 | rwds.pop(k) 326 | return rwds 327 | -------------------------------------------------------------------------------- /data/traffic_generator.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | import random 3 | from tqdm import tqdm 4 | class Flow: 5 | def __init__(self, roadnet_path='roadnet_round3.txt', graph=True, roadgraph_path=None): 6 | ''' 7 | :param roadmap_path: road map file 8 | :param graph: whether to build road graph again 9 | :param roadgraph_path: path to load road graph 10 | ''' 11 | self.roadgraph = None # a networkX Graph instance 12 | self.read_roadnet(roadnet_path=roadnet_path) 13 | if graph: 14 | self.generate_roadGraph() 15 | else: 16 | if roadgraph_path is None: 17 | exit("The road graph path is not provided!") 18 | self.roadgraph = nx.read_gpickle() 19 | 20 | self.left_lon = 115.7501 # left border longitude of the traffic zone, please do not change this default value 21 | self.right_lon = 115.9878 # right border longitude of the traffic zone, please do not change this default value 22 | self.bottom_lat = 28.5951 # bottom border latitude of the traffic zone, please do not change this default value 23 | self.top_lat = 28.7442 # top border latitude of the traffic zone, please do not change this default value 24 | self.flow = [] 25 | self.Veh_num_cur = 0 # total number of generated vehicle trips until now 26 | self.zone_info = None # zone information data 27 | self.Oprob = None # probabilities of a vehicle departs from Origin traffic zones 28 | self.ODprob = None # probabilities of a vehicle depart from an Origin zone and arrived into a Destination zone 29 | self.traffic_duration = 1200 # By default, 1200-second traffic sample data is generated 30 | 31 | def clear(self): 32 | self.flow = [] 33 | self.Veh_num_cur = 0 34 | self.zone_info = None 35 | 36 | def divide_roadNet(self, numRows=6, numColumns=8, Oprob_mode=3, prob_corner=0.2, prob_mid=0.15): 37 | 38 | ''' 39 | Divide road network into numRows * numColumns rectangle traffic zones 40 | return: IDs (rowIndex, columnIndex) of created traffic sub-zones 41 | ''' 42 | if type(numRows) != int or type(numColumns) != int: 43 | exit("Please enter integer numRows and numColumns.") 44 | 45 | self.numRows = numRows 46 | self.numColumns = numColumns 47 | self.unit_lon = (self.right_lon - self.left_lon) / self.numColumns 48 | self.unit_lat = (self.top_lat - self.bottom_lat) / self.numRows 49 | self.zone_info = {} 50 | for row in range(self.numRows): 51 | for col in range(self.numColumns): 52 | self.zone_info[(row, col)] = { 53 | 'inters_id': [], # a list of IDs of intersections 54 | 'left_lon': self.left_lon + col*self.unit_lon, # leftmost longitude of the zone 55 | 'right_lon': self.left_lon + (col+1)*self.unit_lon, # rightmost logitude of the zone 56 | 'top_lat': self.top_lat - row*self.unit_lat, # top latitude of the zone 57 | 'bottom_lat':self.top_lat - (row+1)*self.unit_lat, # bottom latitude of the zone 58 | 'num_inters': 0, # number of intersections within the zone 59 | 'num_signals': 0, # number of signalized intersections within the zone 60 | 'roadlen': 0.0, # road length within the zone 61 | 'roadseg':0 # number of road segments within the zone 62 | } 63 | for key in self.inter_info.keys(): 64 | zone_id = self.get_inter_zoneID(key) 65 | self.zone_info[zone_id]['inters_id'].append(key) 66 | self.zone_info[zone_id]['num_inters'] += 1 67 | self.zone_info[zone_id]['num_signals'] += self.inter_info[key]['sign'] 68 | self.zone_info[zone_id]['roadlen'] += self.inter_info[key]['roadlen'] 69 | self.zone_info[zone_id]['roadseg'] += self.inter_info[key]['roadseg'] 70 | print('Divide road network into {}*{} traffic zones successfully!'.format(self.numRows, self.numColumns)) 71 | 72 | # Estimate the OD matrix (in form of probabilities) based on road network information 73 | self.get_Oprob(Oprob_mode) # Get probabilities of a vehicle departs from zones (Origin zone) of the road network 74 | self.get_ODprob(prob_corner, prob_mid) #Get probabilities of a vehicle depart from an Origin zone and arrived into a Destination zone 75 | 76 | return self.zone_info.keys() 77 | 78 | def generate_traffic(self, numVeh=50000, percentVeh=[.3,.1,.2,.2,.2], weight=0.2): 79 | ''' 80 | Generate 1 sample traffic flow data given the road network data 81 | :param numVeh: total number of vehicles that will enter the network in 1-hour 82 | :param percentVeh: percentages of vehicles that will enter the network in each period (e.g., in 4-minute) 83 | :param weight: the larger the weight, the more diverse of route choice given Origin-Destination of a trip 84 | ''' 85 | if self.zone_info is None: 86 | exit('You need to divide road network into traffic sub-zones!') 87 | 88 | if abs(sum(percentVeh) - 1) > 0.001: 89 | exit('sum of percentages of vehicle entering in network over time should be 1!') 90 | 91 | num_intervals = len(percentVeh) # total number of periods with different traffic demands 92 | interval_length = self.traffic_duration / len(percentVeh) # compute the duration of one period (e.g., 600 seconds) 93 | numVeh_perInterval = [int(percent * numVeh) for percent in percentVeh] # number of vehicles entering the network over periods 94 | numVeh_generated = 0 # record number of generated vehicle trips 95 | 96 | for interval in tqdm(range(num_intervals)): 97 | for o_zone in self.zone_info.keys(): 98 | if len(self.zone_info[o_zone]['inters_id']) == 0: 99 | continue 100 | num_per_inter = numVeh_perInterval[interval] * self.Oprob[o_zone] / self.zone_info[o_zone]['num_inters'] # number of vehicles per intersection 101 | for o_interid in self.zone_info[o_zone]['inters_id']: 102 | d_zone = self.random_weight_choose(self.ODprob[o_zone]) # choose destination zone given ODprobs 103 | while len(self.zone_info[d_zone]['inters_id']) == 0: 104 | d_zone = self.random_weight_choose(self.ODprob[o_zone]) 105 | d_interid = random.choice(self.zone_info[d_zone]['inters_id']) 106 | 107 | start_time = random.randint(interval * interval_length, interval * interval_length + 10) # start time is in range of interval begining and +10 seconds 108 | end_time = (interval + 1) * interval_length 109 | 110 | try: 111 | tmp_flow, tmp_num = self.get_route(o_interid, d_interid, num_per_inter, start_time, end_time, weight) 112 | except TypeError: 113 | continue 114 | self.flow.append(tmp_flow) 115 | numVeh_generated += tmp_num 116 | print("Generating {} Vehicles. Current {} Vehicles in total.".format(int(numVeh_generated), int(self.Veh_num_cur))) 117 | 118 | def add_tripOD(self, o_zone, d_zone, start_time, end_time, num_Veh, weight=0.2): 119 | ''' 120 | Add extra trips from o_zone to d_zone in addition to background traffic 121 | This will help you simulate traffic during special events, for example, football games 122 | ''' 123 | if end_time > self.traffic_duration or start_time < 0: 124 | exit("end_time should be in time range of [0, traffic_duration]!") 125 | 126 | if o_zone[0] >= self.numRows or d_zone[0] > self.numRows: 127 | exit("The zone row index should be smaller than numRows!") 128 | 129 | if o_zone[1] >= self.numColumns or d_zone[1] > self.numColumns: 130 | exit("The zone column index should be smaller than numColumns!") 131 | 132 | num = 0 133 | num_per_inter = num_Veh / self.zone_info[o_zone]['num_inters'] 134 | for o_interid in self.zone_info[o_zone]['inters_id']: 135 | d_interid = random.choice(self.zone_info[d_zone]['inters_id']) 136 | try: 137 | tmp_flow, tmp_num = self.get_route(o_interid, d_interid, num_per_inter, start_time, end_time, weight) 138 | except TypeError: 139 | continue 140 | self.flow.append(tmp_flow) 141 | num += tmp_num 142 | print('Adding {} vehicles. Current {} Vehicles'.format(int(num), int(self.Veh_num_cur))) 143 | 144 | def get_route(self, O_interid, D_interid, numVeh, start_time, end_time, weight=0.2): 145 | ''' 146 | get route from o_inter to d_inter 147 | :param O_interid: 148 | :param D_interid: 149 | :param numVeh: 150 | :param start_time: 151 | :param end_time: 152 | :param weight: 153 | :return: 154 | ''' 155 | start_time = int(start_time) 156 | end_time = int(end_time) 157 | path_nodes = nx.shortest_path(self.roadgraph, source=O_interid, target=D_interid, weight='length') # return a list of intersection IDs 158 | 159 | if len(path_nodes) <= 3: # we omit the vehicle trips with number of edges <= 3 160 | return None 161 | 162 | interval = max(1,int((end_time - start_time) / numVeh)) 163 | tmp_data = [] 164 | tmp_data.append(start_time) 165 | tmp_data.append(end_time) 166 | tmp_data.append(interval) 167 | tmp_data.append(len(path_nodes) - 1) # append number of edges (road segments) 168 | 169 | for idx in range(len(path_nodes) - 1): 170 | self.roadgraph[path_nodes[idx]][path_nodes[idx + 1]]['length'] += numVeh * weight # update the 'length' of roadgraph considering the number of vehicles passed the edge 171 | 172 | edge_id = self.roadgraph[path_nodes[idx]][path_nodes[idx + 1]]['id'] # generate edge (road segment) ID given two adjacent node (intersection) IDs 173 | tmp_data.append(edge_id) 174 | self.Veh_num_cur += (end_time-start_time) / interval 175 | return tmp_data, (end_time-start_time) / interval 176 | 177 | 178 | def output(self, output_path): 179 | ''' 180 | Write the traffic flow data into a .txt file 181 | :param output_path: 182 | :return: self.zone_info.keys() 183 | ''' 184 | file = open(output_path, 'w') 185 | file.write("{}\n".format(len(self.flow))) 186 | for i in self.flow: 187 | for j in range(len(i)): 188 | if (j == 2) or (j == 3) or (j == len(i) - 1): 189 | file.write("{}\n".format(i[j])) 190 | else: 191 | file.write("{} ".format(i[j])) 192 | file.close() 193 | 194 | 195 | def get_Oprob(self, mode=None): 196 | ''' 197 | Get probabilities of a vehicle departs from zones (Origin zone) of the road network, 198 | Default method: use road length within zones as default reference for probabilities estimation 199 | 200 | :param mode: 201 | 1->use road length as reference for origin zone probabilities estimation 202 | 2->use number of road segments as reference for origin zone probabilities estimation 203 | 3->use number of intersections as reference for origin zone probabilities estimation 204 | 4->use number of signalized intersection as reference for origin zone probabilities estimation 205 | :return: 206 | ''' 207 | if mode == 1: 208 | self.Oprob = {(row, col): self.zone_info[(row,col)]['roadlen'] 209 | for row in range(self.numRows) for col in range(self.numColumns)} 210 | elif mode == 2: 211 | self.Oprob = {(row, col): self.zone_info[(row, col)]['roadseg'] 212 | for row in range(self.numRows) for col in range(self.numColumns)} 213 | elif mode == 3: 214 | self.Oprob = {(row, col): self.zone_info[(row, col)]['num_inters'] 215 | for row in range(self.numRows) for col in range(self.numColumns)} 216 | elif mode == 4: 217 | self.Oprob = {(row, col): self.zone_info[(row, col)]['num_signals'] 218 | for row in range(self.numRows) for col in range(self.numColumns)} 219 | else: 220 | exit("Mode Error") 221 | total = sum(self.Oprob.values()) 222 | for key in self.Oprob.keys(): 223 | self.Oprob[key] /= total 224 | return self.Oprob 225 | 226 | def get_ODprob(self, prob_corner=0.3, prob_mid=0.8): 227 | ''' 228 | Get probabilities of a vehicle depart from an Origin zone and arrived into a Destination zone 229 | Default method: use zone's row and column indices difference to estimate the probabilities 230 | 231 | :param prob_corner: probability for a vehicle's Origin and Destination within the same zone if it is a corner traffic zone, e.g. zone-(0,0) 232 | :param prob_mid: probability for a vehicle's Origin and Destination within the same zone if it is a middle zone, e.g. zone-(3,4) 233 | :return: 234 | ''' 235 | self.ODprob = {} # (row, col):{(row2, col2): probability} 236 | for o_row in range(self.numRows): 237 | for o_col in range(self.numColumns): 238 | self.ODprob[(o_row, o_col)] = {} 239 | for d_row in range(self.numRows): 240 | for d_col in range(self.numColumns): 241 | dis = abs(o_row - d_row) + abs(o_col - d_col) 242 | if dis == 0: 243 | if (o_row == 0 or o_row == 5 or o_col == 0 or o_col == 7): 244 | self.ODprob[(o_row, o_col)][(d_row, d_col)] = prob_corner 245 | else: 246 | self.ODprob[(o_row, o_col)][(d_row, d_col)] = prob_mid 247 | else: 248 | self.ODprob[(o_row, o_col)][(d_row, d_col)] = 1 / dis 249 | return 250 | 251 | def get_zoneInfo(self, zoneid=None): 252 | '''Return information of a traffic zone given zoneID''' 253 | if zoneid is None: 254 | return self.zone_info 255 | if zoneid[0] not in range(self.numRows) or zoneid[1] not in range(self.numColumns): 256 | print('Error: zone id') 257 | return 258 | print(self.zone_info[zoneid]) 259 | return self.zone_info[zoneid] 260 | 261 | def get_inter_zoneID(self, inter_id): 262 | '''Given an intersection ID, return the traffic zone ID - (rowIndex, columnIndex)''' 263 | lat = self.inter_info[inter_id]['lat'] 264 | lon = self.inter_info[inter_id]['lon'] 265 | 266 | row = int((lat - self.bottom_lat) / self.unit_lat) 267 | col = int((lon - self.left_lon) / self.unit_lon) 268 | return (row, col) 269 | 270 | def generate_roadGraph(self): 271 | '''Translate roadnetwork data into networkX format, return a networkX graph''' 272 | DG = nx.DiGraph() 273 | for key in self.inter_info.keys(): 274 | DG.add_node(key, **self.inter_info[key]) # node_id 275 | for key in self.road_info.keys(): 276 | pair = (self.road_info[key]['ininter_id'], self.road_info[key]['outinter_id']) 277 | DG.add_edge(*pair, **{"id": key, "length": self.road_info[key]['roadlen'], "speed": self.road_info[key]['speed']}) 278 | 279 | nx.write_gpickle(DG, "roadGraph.gpickle") 280 | print('Building road networkX graph successfully!') 281 | self.roadgraph = DG 282 | 283 | def read_roadnet(self, roadnet_path): 284 | '''Read road network data''' 285 | roadnet = open(roadnet_path, 'r') 286 | 287 | # read inters 288 | self.inter_num = int(roadnet.readline()) 289 | self.inter_info = {} 290 | print("Total number of intersections:{}".format(self.inter_num)) 291 | for _ in range(self.inter_num): 292 | line = roadnet.readline() 293 | lat, lon, id, sign = self.read_inter_line(line) 294 | self.inter_info[id] = {'lat': lat, 'lon': lon, 'sign': sign, 'roadlen': 0.0, 'roadseg':0} 295 | 296 | # read roads 297 | self.road_info = {} 298 | self.road_num = int(roadnet.readline()) 299 | print("Total number of road segments:{}".format(self.road_num)) 300 | for _ in range(self.road_num): 301 | line = roadnet.readline() 302 | inter_id1, inter_id2, roadlen, speed, road_id1, road_id2 = self.read_road_line(line) 303 | #print(road_id1, road_id2) 304 | self.road_info[road_id1] = {'ininter_id': inter_id1, 'outinter_id': inter_id2, 305 | 'roadlen': roadlen, 'speed': speed} 306 | self.road_info[road_id2] = {'ininter_id': inter_id2, 'outinter_id': inter_id1, 307 | 'roadlen': roadlen, 'speed': speed} 308 | self.inter_info[inter_id1]['roadlen'] += roadlen 309 | self.inter_info[inter_id2]['roadlen'] += roadlen 310 | self.inter_info[inter_id1]['roadseg'] += 1 311 | self.inter_info[inter_id2]['roadseg'] += 1 312 | roadnet.readline() 313 | roadnet.readline() 314 | roadnet.close() 315 | 316 | def read_inter_line(self, line): 317 | '''Read intersection data line-by-line''' 318 | line = line.split() 319 | lat = float(line[0]) 320 | lon = float(line[1]) 321 | id = int(line[2]) 322 | sign = bool(line[3]) 323 | return lat, lon, id, sign 324 | 325 | def read_road_line(self, line): 326 | '''Read road segment data line-by-line''' 327 | line = line.split() 328 | inter_id1 = int(line[0]) 329 | inter_id2 = int(line[1]) 330 | roadlen = float(line[2]) 331 | speed = float(line[3]) 332 | 333 | road_id1 = int(line[6]) 334 | road_id2 = int(line[7]) 335 | return inter_id1, inter_id2, roadlen, speed, road_id1, road_id2 336 | 337 | def random_weight_choose(self, weight_data): 338 | ''' 339 | Helper function for choosing the Destination zone of a vehicle trip, given weight_data = self.ODprob[o_zone] 340 | return the destination traffic zone, i.e., d_zone - (rowIndex, columnIndex) 341 | ''' 342 | total = sum(weight_data.values()) 343 | ran = random.uniform(0, total) 344 | curr_sum = 0 345 | d_zone = None 346 | 347 | for key in weight_data.keys(): 348 | curr_sum += weight_data[key] 349 | if ran <= curr_sum: 350 | d_zone = key 351 | break 352 | return d_zone 353 | 354 | 355 | 356 | fl = Flow(roadnet_path='roadnet_round3.txt') 357 | fl.divide_roadNet(numRows=6, numColumns=8, Oprob_mode=3) 358 | fl.generate_traffic(numVeh=50000, percentVeh=[.3,.1,.2,.2,.2], weight=0.2) 359 | 360 | fl.add_tripOD(o_zone=(0,0), d_zone=(2,0), start_time=360, end_time=700, num_Veh=2000) 361 | fl.add_tripOD(o_zone=(1,2), d_zone=(0,0), start_time=500, end_time=700, num_Veh=1500) 362 | fl.add_tripOD(o_zone=(1,3), d_zone=(2,5), start_time=400, end_time=680, num_Veh=3000) 363 | fl.add_tripOD(o_zone=(2,2), d_zone=(2,4), start_time=400, end_time=700, num_Veh=2000) 364 | fl.output(output_path='flow_round3.txt') 365 | -------------------------------------------------------------------------------- /evaluate.py: -------------------------------------------------------------------------------- 1 | import json 2 | import traceback 3 | import argparse 4 | import logging 5 | import os 6 | import sys 7 | import time 8 | from pathlib import Path 9 | import re 10 | import gym 11 | import numpy as np 12 | 13 | logging.basicConfig(level=logging.INFO) 14 | logger = logging.getLogger(__file__) 15 | logger.setLevel(logging.INFO) 16 | 17 | gym.logger.setLevel(gym.logger.ERROR) 18 | 19 | 20 | def pretty_files(path): 21 | contents = os.listdir(path) 22 | return "[{}]".format(", ".join(contents)) 23 | 24 | 25 | def resolve_dirs(root_path: str, input_dir: str = None, output_dir: str = None, log_dir:str=None): 26 | root_path = Path(root_path) 27 | 28 | logger.info(f"root_path={pretty_files(root_path)}") 29 | 30 | if input_dir is not None: 31 | input_dir = Path(input_dir) 32 | output_dir = Path(output_dir) 33 | log_dir = Path(log_dir) 34 | submission_dir = input_dir 35 | scores_dir = output_dir 36 | 37 | logger.info(f"input_dir={pretty_files(input_dir)}") 38 | logger.info(f"output_dir={pretty_files(output_dir)}") 39 | logger.info(f"log_dir={pretty_files(log_dir)}") 40 | else: 41 | raise ValueError('need input dir') 42 | 43 | if not scores_dir.exists(): 44 | os.makedirs(scores_dir) 45 | 46 | logger.info(f"submission_dir={pretty_files(submission_dir)}") 47 | logger.info(f"scores_dir={pretty_files(scores_dir)}") 48 | 49 | if not submission_dir.is_dir(): 50 | logger.warning(f"submission_dir={submission_dir} does not exist") 51 | 52 | return submission_dir, scores_dir, log_dir 53 | 54 | 55 | def load_agent_submission(submission_dir: Path): 56 | logger.info(f"files under submission dir:{pretty_files(submission_dir)}") 57 | 58 | # find agent.py 59 | module_path = None 60 | cfg_path = None 61 | class_path = None 62 | for dirpath, dirnames, file_names in os.walk(submission_dir): 63 | for file_name in [f for f in file_names if f.endswith(".py")]: 64 | if file_name == "agent.py": 65 | module_path = dirpath 66 | 67 | if file_name == "gym_cfg.py": 68 | cfg_path = dirpath 69 | 70 | if file_name == 'CBEngine_round3.py': 71 | class_path = dirpath 72 | # error 73 | assert ( 74 | module_path is not None 75 | ), "Cannot find file named agent.py, please check your submission zip" 76 | assert( 77 | cfg_path is not None 78 | ), "Cannot find file named gym_cfg.py, please check your submission zip" 79 | assert ( 80 | class_path is not None 81 | ), "Cannot find file named CBEngine_round3.py, please check your submission zip" 82 | sys.path.append(str(module_path)) 83 | 84 | 85 | # This will fail w/ an import error of the submissions directory does not exist 86 | import gym_cfg as gym_cfg_submission 87 | import agent as agent_submission 88 | from CBEngine_round3 import CBEngine_round3 as CBEngine_rllib_class 89 | 90 | gym_cfg_instance = gym_cfg_submission.gym_cfg() 91 | 92 | return agent_submission.agent_specs,gym_cfg_instance,CBEngine_rllib_class 93 | 94 | 95 | def read_config(cfg_file): 96 | configs = {} 97 | with open(cfg_file, 'r') as f: 98 | lines = f.readlines() 99 | for line in lines: 100 | line = line.rstrip('\n').split(' ') 101 | if(len(line) == 3 and line[0][0] != '#'): 102 | configs[line[0]] = line[-1] 103 | return configs 104 | 105 | 106 | def process_roadnet(roadnet_file): 107 | # intersections[key_id] = { 108 | # 'have_signal': bool, 109 | # 'end_roads': list of road_id. Roads that end at this intersection. The order is random. 110 | # 'start_roads': list of road_id. Roads that start at this intersection. The order is random. 111 | # 'lanes': list, contains the lane_id in. The order is explained in Docs. 112 | # } 113 | # roads[road_id] = { 114 | # 'start_inter':int. Start intersection_id. 115 | # 'end_inter':int. End intersection_id. 116 | # 'length': float. Road length. 117 | # 'speed_limit': float. Road speed limit. 118 | # 'num_lanes': int. Number of lanes in this road. 119 | # 'inverse_road': Road_id of inverse_road. 120 | # 'lanes': dict. roads[road_id]['lanes'][lane_id] = list of 3 int value. Contains the Steerability of lanes. 121 | # lane_id is road_id*100 + 0/1/2... For example, if road 9 have 3 lanes, then their id are 900, 901, 902 122 | # } 123 | # agents[agent_id] = list of length 8. contains the inroad0_id, inroad1_id, inroad2_id,inroad3_id, outroad0_id, outroad1_id, outroad2_id, outroad3_id 124 | 125 | intersections = {} 126 | roads = {} 127 | agents = {} 128 | 129 | agent_num = 0 130 | road_num = 0 131 | signal_num = 0 132 | with open(roadnet_file, 'r') as f: 133 | lines = f.readlines() 134 | cnt = 0 135 | pre_road = 0 136 | is_obverse = 0 137 | for line in lines: 138 | line = line.rstrip('\n').split(' ') 139 | if ('' in line): 140 | line.remove('') 141 | if (len(line) == 1): 142 | if (cnt == 0): 143 | agent_num = int(line[0]) 144 | cnt += 1 145 | elif (cnt == 1): 146 | road_num = int(line[0]) * 2 147 | cnt += 1 148 | elif (cnt == 2): 149 | signal_num = int(line[0]) 150 | cnt += 1 151 | else: 152 | if (cnt == 1): 153 | intersections[int(line[2])] = { 154 | 'have_signal': int(line[3]), 155 | 'end_roads': [], 156 | 'start_roads': [], 157 | 'lanes':[] 158 | } 159 | elif (cnt == 2): 160 | if (len(line) != 8): 161 | road_id = pre_road[is_obverse] 162 | roads[road_id]['lanes'] = {} 163 | for i in range(roads[road_id]['num_lanes']): 164 | roads[road_id]['lanes'][road_id * 100 + i] = list(map(int, line[i * 3:i * 3 + 3])) 165 | is_obverse ^= 1 166 | else: 167 | roads[int(line[-2])] = { 168 | 'start_inter': int(line[0]), 169 | 'end_inter': int(line[1]), 170 | 'length': float(line[2]), 171 | 'speed_limit': float(line[3]), 172 | 'num_lanes': int(line[4]), 173 | 'inverse_road': int(line[-1]) 174 | } 175 | roads[int(line[-1])] = { 176 | 'start_inter': int(line[1]), 177 | 'end_inter': int(line[0]), 178 | 'length': float(line[2]), 179 | 'speed_limit': float(line[3]), 180 | 'num_lanes': int(line[5]), 181 | 'inverse_road': int(line[-2]) 182 | } 183 | intersections[int(line[0])]['end_roads'].append(int(line[-1])) 184 | intersections[int(line[1])]['end_roads'].append(int(line[-2])) 185 | intersections[int(line[0])]['start_roads'].append(int(line[-2])) 186 | intersections[int(line[1])]['start_roads'].append(int(line[-1])) 187 | pre_road = (int(line[-2]), int(line[-1])) 188 | else: 189 | # 4 out-roads 190 | signal_road_order = list(map(int, line[1:])) 191 | now_agent = int(line[0]) 192 | in_roads = [] 193 | for road in signal_road_order: 194 | if (road != -1): 195 | in_roads.append(roads[road]['inverse_road']) 196 | else: 197 | in_roads.append(-1) 198 | in_roads += signal_road_order 199 | agents[now_agent] = in_roads 200 | for agent, agent_roads in agents.items(): 201 | intersections[agent]['lanes'] = [] 202 | for road in agent_roads: 203 | ## here we treat road -1 have 3 lanes 204 | if (road == -1): 205 | for i in range(3): 206 | intersections[agent]['lanes'].append(-1) 207 | else: 208 | for lane in roads[road]['lanes'].keys(): 209 | intersections[agent]['lanes'].append(lane) 210 | 211 | return intersections, roads, agents 212 | 213 | 214 | def process_delay_index(lines, roads, step): 215 | vehicles = {} 216 | 217 | for i in range(len(lines)): 218 | line = lines[i] 219 | if(line[0] == 'for'): 220 | vehicle_id = int(line[2]) 221 | now_dict = { 222 | 'distance': float(lines[i + 1][2]), 223 | 'drivable': int(float(lines[i + 2][2])), 224 | 'road': int(float(lines[i + 3][2])), 225 | 'route': list(map(int, list(map(float, lines[i + 4][2:])))), 226 | 'speed': float(lines[i + 5][2]), 227 | 'start_time': float(lines[i + 6][2]), 228 | 't_ff': float(lines[i+7][2]), 229 | ############## 230 | 'step': int(lines[i+8][2]) 231 | } 232 | step = now_dict['step'] 233 | ################## 234 | vehicles[vehicle_id] = now_dict 235 | tt = step - now_dict['start_time'] 236 | tt_ff = now_dict['t_ff'] 237 | tt_f_r = 0.0 238 | current_road_pos = 0 239 | for pos in range(len(now_dict['route'])): 240 | if(now_dict['road'] == now_dict['route'][pos]): 241 | current_road_pos = pos 242 | for pos in range(len(now_dict['route'])): 243 | road_id = now_dict['route'][pos] 244 | if(pos == current_road_pos): 245 | tt_f_r += (roads[road_id]['length'] - 246 | now_dict['distance']) / roads[road_id]['speed_limit'] 247 | elif(pos > current_road_pos): 248 | tt_f_r += roads[road_id]['length'] / roads[road_id]['speed_limit'] 249 | vehicles[vehicle_id]['tt_f_r'] = tt_f_r 250 | vehicles[vehicle_id]['delay_index'] = (tt + tt_f_r) / tt_ff 251 | 252 | vehicle_list = list(vehicles.keys()) 253 | delay_index_list = [] 254 | for vehicle_id, dict in vehicles.items(): 255 | # res = max(res, dict['delay_index']) 256 | if('delay_index' in dict.keys()): 257 | delay_index_list.append(dict['delay_index']) 258 | 259 | # 'delay_index_list' contains all vehicles' delayindex at this snapshot. 260 | # 'vehicle_list' contains the vehicle_id at this snapshot. 261 | # 'vehicles' is a dict contains vehicle infomation at this snapshot 262 | return delay_index_list, vehicle_list, vehicles 263 | 264 | def process_score(log_path,roads,step,scores_dir): 265 | result_write = { 266 | "data": { 267 | "total_served_vehicles": -1, 268 | "delay_index": -1 269 | } 270 | } 271 | 272 | with open(log_path / "info_step {}.log".format(step)) as log_file: 273 | lines = log_file.readlines() 274 | lines = list(map(lambda x: x.rstrip('\n').split(' '), lines)) 275 | # process delay index 276 | delay_index_list, vehicle_list, vehicles = process_delay_index(lines, roads, step) 277 | v_len = len(vehicle_list) 278 | delay_index = np.mean(delay_index_list) 279 | 280 | result_write['data']['total_served_vehicles'] = v_len 281 | result_write['data']['delay_index'] = delay_index 282 | with open(scores_dir / 'scores {}.json'.format(step), 'w' ) as f_out: 283 | json.dump(result_write,f_out,indent= 2) 284 | 285 | return result_write['data']['total_served_vehicles'],result_write['data']['delay_index'] 286 | def run_simulation(agent_spec, simulator_cfg_file, gym_cfg,metric_period,scores_dir,threshold,CBEngine_rllib_class,log_path): 287 | logger.info("\n") 288 | logger.info("*" * 40) 289 | 290 | # get gym instance 291 | gym_configs = gym_cfg.cfg 292 | simulator_configs = read_config(simulator_cfg_file) 293 | env_config = { 294 | "simulator_cfg_file": simulator_cfg_file, 295 | "thread_num": args.thread_num, 296 | "gym_dict": gym_configs, 297 | "metric_period": args.metric_period, 298 | "vehicle_info_path":log_path 299 | } 300 | env = CBEngine_rllib_class(env_config) 301 | scenario = [ 302 | 'test' 303 | ] 304 | 305 | # read roadnet file, get data 306 | roadnet_path = Path(simulator_configs['road_file_addr']) 307 | intersections, roads, agents = process_roadnet(roadnet_path) 308 | # env.set_warning(0) 309 | env.set_log(1) 310 | env.set_info(1) 311 | # env.set_ui(0) 312 | # get agent instance 313 | infos = {'step':0} 314 | 315 | observations = env.reset() 316 | agent_id_list = [] 317 | for k in observations.keys(): 318 | agent_id_list.append(k) 319 | agent_id_list = list(set(agent_id_list)) 320 | agent = agent_spec[scenario[0]] 321 | agent.load_agent_list(agent_id_list) 322 | agent.load_roadnet(intersections, roads, agents) 323 | dones = {} 324 | dones['__all__']=False 325 | # simulation 326 | step = 0 327 | sim_start = time.time() 328 | 329 | tot_v = -1 330 | d_i = -1 331 | while not dones['__all__']: 332 | actions = {} 333 | step+=1 334 | all_info = { 335 | 'observations':observations, 336 | 'info':infos 337 | } 338 | actions = agent.act(all_info) 339 | observations, rewards, dones, infos = env.step(actions) 340 | infos['step'] = step 341 | if(step * 10 % metric_period == 0): 342 | try: 343 | tot_v , d_i = process_score(log_path,roads,step*10-1,scores_dir) 344 | except Exception as e: 345 | print(e) 346 | print('Error in process_score. Maybe no log') 347 | continue 348 | if(d_i > threshold): 349 | break 350 | sim_end = time.time() 351 | logger.info("simulation cost : {}s".format(sim_end-sim_start)) 352 | # read log file 353 | 354 | # result = {} 355 | # vehicle_last_occur = {} 356 | 357 | # eval_start = time.time() 358 | # for dirpath, dirnames, file_names in os.walk(log_path): 359 | # for file_name in [f for f in file_names if f.endswith(".log") and f.startswith('info_step')]: 360 | # with open(log_path / file_name, 'r') as log_file: 361 | # pattern = '[0-9]+' 362 | # step = list(map(int, re.findall(pattern, file_name)))[0] 363 | # if(step >= int(simulator_configs['max_time_epoch'])): 364 | # continue 365 | # lines = log_file.readlines() 366 | # lines = list(map(lambda x: x.rstrip('\n').split(' '), lines)) 367 | # result[step] = {} 368 | # # result[step]['vehicle_num'] = int(lines[0][0]) 369 | # 370 | # # process delay index 371 | # delay_index_list, vehicle_list, vehicles = process_delay_index(lines, roads, step) 372 | # result[step]['vehicle_list'] = vehicle_list 373 | # result[step]['delay_index'] = delay_index_list 374 | # result[step]['vehicles'] = vehicles 375 | # 376 | # 377 | # steps = list(result.keys()) 378 | # steps.sort() 379 | # for step in steps: 380 | # for vehicle in result[step]['vehicles'].keys(): 381 | # vehicle_last_occur[vehicle] = result[step]['vehicles'][vehicle] 382 | # 383 | # delay_index_temp = {} 384 | # for vehicle in vehicle_last_occur.keys(): 385 | # if('delay_index' in vehicle_last_occur[vehicle]): 386 | # res = vehicle_last_occur[vehicle]['delay_index'] 387 | # delay_index_temp[vehicle] = res 388 | # 389 | # # calc 390 | # vehicle_total_set = set() 391 | # delay_index = [] 392 | # for k, v in result.items(): 393 | # vehicle_total_set = vehicle_total_set | set(v['vehicle_list']) 394 | # delay_index += delay_index_list 395 | # 396 | # if(len(delay_index)>0): 397 | # d_i = np.mean(delay_index) 398 | # else: 399 | # d_i = -1 400 | # 401 | # last_d_i = np.mean(list(delay_index_temp.values())) 402 | # eval_end = time.time() 403 | # logger.info("scoring cost {}s".format(eval_end-eval_start)) 404 | return tot_v, d_i 405 | 406 | 407 | def format_exception(grep_word): 408 | exception_list = traceback.format_stack() 409 | exception_list = exception_list[:-2] 410 | exception_list.extend(traceback.format_tb(sys.exc_info()[2])) 411 | exception_list.extend(traceback.format_exception_only( 412 | sys.exc_info()[0], sys.exc_info()[1])) 413 | filtered = [] 414 | for m in exception_list: 415 | if str(grep_word) in m: 416 | filtered.append(m) 417 | 418 | exception_str = "Traceback (most recent call last):\n" 419 | exception_str += "".join(filtered) 420 | # Removing the last \n 421 | exception_str = exception_str[:-1] 422 | 423 | return exception_str 424 | 425 | if __name__ == "__main__": 426 | 427 | 428 | # arg parse 429 | parser = argparse.ArgumentParser( 430 | prog="evaluation", 431 | description="1" 432 | ) 433 | parser.add_argument( 434 | "--thread_num", 435 | type=int, 436 | default=8 437 | ) 438 | parser.add_argument( 439 | "--input_dir", 440 | help="The path to the directory containing the reference " 441 | "data and user submission data.", 442 | default=None, 443 | type=str, 444 | ) 445 | 446 | parser.add_argument( 447 | "--output_dir", 448 | help="The path to the directory where the submission's " 449 | "scores.txt file will be written to.", 450 | default=None, 451 | type=str, 452 | ) 453 | 454 | parser.add_argument( 455 | "--sim_cfg", 456 | help='The path to the simulator cfg', 457 | default=None, 458 | type=str 459 | ) 460 | 461 | parser.add_argument( 462 | "--metric_period", 463 | help="period of scoring", 464 | default=3600, 465 | type=int 466 | ) 467 | parser.add_argument( 468 | "--threshold", 469 | help="period of scoring", 470 | default=1.6, 471 | type=float 472 | ) 473 | parser.add_argument( 474 | "--vehicle_info_path", 475 | help="path to log vehicle info to scoring", 476 | default='./log', 477 | type=str 478 | ) 479 | # result to be written in out/result.json 480 | result = { 481 | "success": False, 482 | "error_msg": "", 483 | "data": { 484 | "total_served_vehicles": -1, 485 | "delay_index": -1 486 | } 487 | } 488 | 489 | 490 | #################### 491 | # modify sim cfg 492 | args = parser.parse_args() 493 | msg = None 494 | metric_period = args.metric_period 495 | threshold = args.threshold 496 | # get input and output directory 497 | simulator_cfg_file = args.sim_cfg 498 | vehicle_info_path = args.vehicle_info_path 499 | try: 500 | submission_dir, scores_dir, log_dir = resolve_dirs( 501 | os.path.dirname(__file__), args.input_dir, args.output_dir, args.vehicle_info_path 502 | ) 503 | except Exception as e: 504 | msg = format_exception(e) 505 | result['error_msg'] = msg 506 | json.dump(result,open(scores_dir / "scores.json",'w'),indent=2) 507 | raise AssertionError() 508 | 509 | # get agent and configuration of gym 510 | try: 511 | agent_spec,gym_cfg,CBEngine_rllib_class = load_agent_submission(submission_dir) 512 | except Exception as e: 513 | msg = format_exception(e) 514 | result['error_msg'] = msg 515 | json.dump(result,open(scores_dir / "scores.json",'w'),indent=2) 516 | raise AssertionError() 517 | 518 | #################### 519 | # modify sim cfg 520 | simulator_configs = read_config(simulator_cfg_file) 521 | flow_index = re.findall('[0-9]+',simulator_configs['vehicle_file_addr'])[1] 522 | flow_index = int(flow_index) 523 | # log_path = Path(simulator_configs['report_log_addr']) 524 | 525 | log_path = Path(args.vehicle_info_path) / str(flow_index) 526 | scores_dir = Path(args.output_dir) / str(flow_index) 527 | if (not os.path.exists(log_path)): 528 | os.makedirs(log_path) 529 | if (not os.path.exists(scores_dir)): 530 | os.makedirs(scores_dir) 531 | logger.info("log_path : {}".format(log_path)) 532 | logger.info('scores_path : {}'.format(scores_dir)) 533 | # change cfg 534 | 535 | 536 | ################## 537 | 538 | logger.info(f"Loaded user agent instance={agent_spec}") 539 | 540 | # simulation 541 | start_time = time.time() 542 | try: 543 | scores = run_simulation(agent_spec, simulator_cfg_file, gym_cfg,metric_period,scores_dir,threshold,CBEngine_rllib_class,log_path) 544 | except Exception as e: 545 | msg = format_exception(e) 546 | result['error_msg'] = msg 547 | json.dump(result,open(scores_dir / "scores.json",'w'),indent=2) 548 | raise AssertionError() 549 | 550 | # write result 551 | result['data']['total_served_vehicles'] = scores[0] 552 | result['data']['delay_index'] = scores[1] 553 | # result['data']['last_d_i'] = scores[2] 554 | result['success'] = True 555 | 556 | # cal time 557 | end_time = time.time() 558 | 559 | logger.info(f"total evaluation cost {end_time-start_time} s") 560 | 561 | # write score 562 | logger.info("\n\n") 563 | logger.info("*" * 40) 564 | 565 | json.dump(result, open(scores_dir / "scores.json", 'w'), indent=2) 566 | 567 | logger.info("Evaluation complete") 568 | -------------------------------------------------------------------------------- /rllib_test.py: -------------------------------------------------------------------------------- 1 | import gym 2 | from agent.CBEngine_round3 import CBEngine_round3 as CBEngine_rllib_class 3 | from ray.rllib.models import ModelCatalog 4 | import tensorflow as tf 5 | import citypb 6 | import ray 7 | from pathlib import Path 8 | from ray import tune 9 | import os 10 | import re 11 | import numpy as np 12 | import argparse 13 | import sys 14 | import pickle 15 | import json 16 | import logging 17 | logging.basicConfig(level=logging.INFO) 18 | logger = logging.getLogger(__file__) 19 | logger.setLevel(logging.INFO) 20 | parser = argparse.ArgumentParser() 21 | 22 | class FT_agent(): 23 | def __init__(self, interval): 24 | self.interval = interval 25 | def act(self,obs): 26 | actions = {} 27 | for k,v in obs.items(): 28 | actions[k] = (v//self.interval) % 8 + 1 29 | return actions 30 | 31 | class MP_agent(): 32 | def __init__(self, interval): 33 | self.now_phase = {} 34 | self.green_sec = interval 35 | self.last_change_step = {} 36 | 37 | self.phase_lane_map_in = [[1, 7], [2, 8], [4, 10], [5, 11], [2, 1], [5, 4], [8, 7], [11, 10]] 38 | self.phase_lane_map_out = [[16, 17, 18, 22, 23, 24], [13, 14, 15, 19, 20, 21], 39 | [13, 14, 15, 19, 20, 21], [16, 17, 18, 22, 23, 24], 40 | [16, 17, 18, 19, 20, 21], [19, 20, 21, 22, 23, 24], 41 | [13, 14, 15, 22, 23, 24], [13, 14, 15, 16, 17, 18]] 42 | def get_phase_pressures(self, lane_vehicle_num): 43 | pressures = [] 44 | for i in range(8): 45 | in_lanes = self.phase_lane_map_in[i] 46 | out_lanes = self.phase_lane_map_out[i] 47 | pressure = 0 48 | for in_lane in in_lanes: 49 | pressure += lane_vehicle_num[in_lane] * 3 50 | for out_lane in out_lanes: 51 | pressure -= lane_vehicle_num[out_lane] 52 | pressures.append(pressure) 53 | # # print("pressures: ", pressures) 54 | return pressures 55 | 56 | def get_action(self, lane_vehicle_num): 57 | pressures = self.get_phase_pressures(lane_vehicle_num) 58 | unavailable_phases = self.get_unavailable_phases(lane_vehicle_num) 59 | # if len(unavailable_phases) > 0: 60 | # # print("unavailable_phases: ", unavailable_phases) 61 | 62 | max_pressure_id = np.argmax(pressures) + 1 63 | while (max_pressure_id in unavailable_phases): 64 | pressures[max_pressure_id - 1] -= 999999 65 | max_pressure_id = np.argmax(pressures) + 1 66 | # # print(max_pressure_id) 67 | return max_pressure_id 68 | 69 | 70 | 71 | def get_unavailable_phases(self, lane_vehicle_num): 72 | self.phase_lane_map_in = [[1, 7], [2, 8], [4, 10], [5, 11], [2, 1], [5, 4], [8, 7], [11, 10]] 73 | unavailable_phases = [] 74 | not_exist_lanes = [] 75 | for i in range(1, 25): 76 | if lane_vehicle_num[i] < 0: 77 | not_exist_lanes.append(i) 78 | for lane in not_exist_lanes: 79 | for phase_id, in_lanes in enumerate(self.phase_lane_map_in): 80 | phase_id += 1 81 | if lane in in_lanes and phase_id not in unavailable_phases: 82 | unavailable_phases.append(phase_id) 83 | 84 | return unavailable_phases 85 | # return [5, 6, 7, 8] 86 | 87 | 88 | def act(self, obs): 89 | """ !!! MUST BE OVERRIDED !!! 90 | """ 91 | # preprocess observations 92 | # a simple fixtime agent 93 | observations_for_agent = obs 94 | self.agent_list = list(obs.keys()) 95 | actions = {} 96 | for agent in self.agent_list: 97 | # select the now_step 98 | now_step = observations_for_agent[agent][0] 99 | lane_vehicle_num = observations_for_agent[agent] 100 | # print("agent id: ", agent) 101 | # print("lane vehicle: ", lane_vehicle_num) 102 | 103 | action = self.get_action(lane_vehicle_num) 104 | # print("action: ", action) 105 | if(agent not in self.last_change_step.keys()): 106 | self.last_change_step[agent] = 0 107 | self.now_phase[agent] = 1 108 | step_diff = now_step - self.last_change_step[agent] 109 | if (step_diff >= self.green_sec): 110 | self.now_phase[agent] = action 111 | self.last_change_step[agent] = now_step 112 | 113 | 114 | actions[agent] = self.now_phase[agent] 115 | # print("phase: ", actions[agent]) 116 | # print("phase available lane: ", self.phase_lane_map_in[actions[agent]-1]) 117 | # print("________") 118 | 119 | return actions 120 | 121 | class RLlibTFCheckpointPolicy(): 122 | def __init__( 123 | self, load_path, algorithm, policy_name, observation_space, action_space 124 | ): 125 | self._checkpoint_path = load_path 126 | self._algorithm = algorithm 127 | self._policy_name = policy_name 128 | self._observation_space = observation_space 129 | self._action_space = action_space 130 | self._sess = None 131 | 132 | if isinstance(action_space, gym.spaces.Box): 133 | self.is_continuous = True 134 | elif isinstance(action_space, gym.spaces.Discrete): 135 | self.is_continuous = False 136 | else: 137 | raise TypeError("Unsupport action space") 138 | 139 | if self._sess: 140 | return 141 | 142 | if self._algorithm == "PPO": 143 | from ray.rllib.agents.ppo.ppo_tf_policy import PPOTFPolicy as LoadPolicy 144 | elif self._algorithm in ["A2C", "A3C"]: 145 | from ray.rllib.agents.a3c.a3c_tf_policy import A3CTFPolicy as LoadPolicy 146 | elif self._algorithm == "PG": 147 | from ray.rllib.agents.pg.pg_tf_policy import PGTFPolicy as LoadPolicy 148 | elif self._algorithm in ["DQN","APEX"]: 149 | from ray.rllib.agents.dqn.dqn_tf_policy import DQNTFPolicy as LoadPolicy 150 | else: 151 | raise TypeError("Unsupport algorithm") 152 | 153 | self._prep = ModelCatalog.get_preprocessor_for_space(self._observation_space) 154 | self._sess = tf.Session(graph=tf.Graph()) 155 | self._sess.__enter__() 156 | 157 | with tf.name_scope(self._policy_name): 158 | # obs_space need to be flattened before passed to PPOTFPolicy 159 | flat_obs_space = self._prep.observation_space 160 | self.policy = LoadPolicy(flat_obs_space, self._action_space, {}) 161 | objs = pickle.load(open(self._checkpoint_path, "rb")) 162 | objs = pickle.loads(objs["worker"]) 163 | state = objs["state"] 164 | weights = state[self._policy_name] 165 | list_keys = list(weights.keys()) 166 | for k in list_keys: 167 | if(k not in self.policy.get_weights().keys()): 168 | weights.pop(k) 169 | self.policy.set_weights(weights) 170 | 171 | def act(self, obs): 172 | action = {} 173 | if isinstance(obs, list): 174 | # batch infer 175 | obs = [self._prep.transform(o) for o in obs] 176 | action = self.policy.compute_actions(obs, explore=False)[0] 177 | elif isinstance(obs, dict): 178 | for k,v in obs.items(): 179 | obs = self._prep.transform(v) 180 | action[k] = self.policy.compute_actions([obs], explore=False)[0][0] 181 | else: 182 | # single infer 183 | obs = self._prep.transform(obs) 184 | action = self.policy.compute_actions([obs], explore=False)[0][0] 185 | 186 | return action 187 | def process_delay_index(lines, roads,step): 188 | vehicles = {} 189 | 190 | for i in range(len(lines)): 191 | line = lines[i] 192 | if(line[0] == 'for'): 193 | vehicle_id = int(line[2]) 194 | now_dict = { 195 | 'distance': float(lines[i + 1][2]), 196 | 'drivable': int(float(lines[i + 2][2])), 197 | 'road': int(float(lines[i + 3][2])), 198 | 'route': list(map(int, list(map(float, lines[i + 4][2:])))), 199 | 'speed': float(lines[i + 5][2]), 200 | 'start_time': float(lines[i + 6][2]), 201 | 't_ff': float(lines[i+7][2]), 202 | ############## 203 | 'step': int(lines[i+8][2]) 204 | } 205 | step = now_dict['step'] 206 | ################## 207 | vehicles[vehicle_id] = now_dict 208 | tt = step - now_dict['start_time'] 209 | tt_ff = now_dict['t_ff'] 210 | tt_f_r = 0.0 211 | current_road_pos = 0 212 | for pos in range(len(now_dict['route'])): 213 | if(now_dict['road'] == now_dict['route'][pos]): 214 | current_road_pos = pos 215 | for pos in range(len(now_dict['route'])): 216 | road_id = now_dict['route'][pos] 217 | if(pos == current_road_pos): 218 | tt_f_r += (roads[road_id]['length'] - 219 | now_dict['distance']) / roads[road_id]['speed_limit'] 220 | elif(pos > current_road_pos): 221 | tt_f_r += roads[road_id]['length'] / roads[road_id]['speed_limit'] 222 | vehicles[vehicle_id]['tt_f_r'] = tt_f_r 223 | vehicles[vehicle_id]['delay_index'] = (tt + tt_f_r) / tt_ff 224 | 225 | vehicle_list = list(vehicles.keys()) 226 | delay_index_list = [] 227 | for vehicle_id, dict in vehicles.items(): 228 | # res = max(res, dict['delay_index']) 229 | if('delay_index' in dict.keys()): 230 | delay_index_list.append(dict['delay_index']) 231 | 232 | # 'delay_index_list' contains all vehicles' delayindex at this snapshot. 233 | # 'vehicle_list' contains the vehicle_id at this snapshot. 234 | # 'vehicles' is a dict contains vehicle infomation at this snapshot 235 | return delay_index_list, vehicle_list, vehicles 236 | 237 | def process_score(log_path,roads,step,scores_dir,travel_time): 238 | result_write = { 239 | "data": { 240 | "total_served_vehicles": -1, 241 | "delay_index": -1, 242 | 'average_travel_time':travel_time 243 | } 244 | } 245 | with open(log_path / "info_step {}.log".format(step)) as log_file: 246 | lines = log_file.readlines() 247 | lines = list(map(lambda x: x.rstrip('\n').split(' '), lines)) 248 | # process delay index 249 | delay_index_list, vehicle_list, vehicles = process_delay_index(lines, roads, step) 250 | v_len = len(vehicle_list) 251 | delay_index = np.mean(delay_index_list) 252 | 253 | result_write['data']['total_served_vehicles'] = v_len 254 | result_write['data']['delay_index'] = delay_index 255 | with open(scores_dir / 'scores {}.json'.format(step), 'w' ) as f_out: 256 | json.dump(result_write,f_out,indent= 2) 257 | 258 | return result_write['data']['total_served_vehicles'],result_write['data']['delay_index'] 259 | 260 | def read_config(cfg_file): 261 | configs = {} 262 | with open(cfg_file, 'r') as f: 263 | lines = f.readlines() 264 | for line in lines: 265 | line = line.rstrip('\n').split(' ') 266 | if(len(line) == 3 and line[0][0] != '#'): 267 | configs[line[0]] = line[-1] 268 | return configs 269 | 270 | def process_roadnet(roadnet_file): 271 | # intersections[key_id] = { 272 | # 'have_signal': bool, 273 | # 'end_roads': list of road_id. Roads that end at this intersection. The order is random. 274 | # 'start_roads': list of road_id. Roads that start at this intersection. The order is random. 275 | # 'lanes': list, contains the lane_id in. The order is explained in Docs. 276 | # } 277 | # roads[road_id] = { 278 | # 'start_inter':int. Start intersection_id. 279 | # 'end_inter':int. End intersection_id. 280 | # 'length': float. Road length. 281 | # 'speed_limit': float. Road speed limit. 282 | # 'num_lanes': int. Number of lanes in this road. 283 | # 'inverse_road': Road_id of inverse_road. 284 | # 'lanes': dict. roads[road_id]['lanes'][lane_id] = list of 3 int value. Contains the Steerability of lanes. 285 | # lane_id is road_id*100 + 0/1/2... For example, if road 9 have 3 lanes, then their id are 900, 901, 902 286 | # } 287 | # agents[agent_id] = list of length 8. contains the inroad0_id, inroad1_id, inroad2_id,inroad3_id, outroad0_id, outroad1_id, outroad2_id, outroad3_id 288 | 289 | intersections = {} 290 | roads = {} 291 | agents = {} 292 | 293 | agent_num = 0 294 | road_num = 0 295 | signal_num = 0 296 | with open(roadnet_file, 'r') as f: 297 | lines = f.readlines() 298 | cnt = 0 299 | pre_road = 0 300 | is_obverse = 0 301 | for line in lines: 302 | line = line.rstrip('\n').split(' ') 303 | if ('' in line): 304 | line.remove('') 305 | if (len(line) == 1): 306 | if (cnt == 0): 307 | agent_num = int(line[0]) 308 | cnt += 1 309 | elif (cnt == 1): 310 | road_num = int(line[0]) * 2 311 | cnt += 1 312 | elif (cnt == 2): 313 | signal_num = int(line[0]) 314 | cnt += 1 315 | else: 316 | if (cnt == 1): 317 | intersections[int(line[2])] = { 318 | 'have_signal': int(line[3]), 319 | 'end_roads': [], 320 | 'start_roads': [], 321 | 'lanes':[] 322 | } 323 | elif (cnt == 2): 324 | if (len(line) != 8): 325 | road_id = pre_road[is_obverse] 326 | roads[road_id]['lanes'] = {} 327 | for i in range(roads[road_id]['num_lanes']): 328 | roads[road_id]['lanes'][road_id * 100 + i] = list(map(int, line[i * 3:i * 3 + 3])) 329 | is_obverse ^= 1 330 | else: 331 | roads[int(line[-2])] = { 332 | 'start_inter': int(line[0]), 333 | 'end_inter': int(line[1]), 334 | 'length': float(line[2]), 335 | 'speed_limit': float(line[3]), 336 | 'num_lanes': int(line[4]), 337 | 'inverse_road': int(line[-1]) 338 | } 339 | roads[int(line[-1])] = { 340 | 'start_inter': int(line[1]), 341 | 'end_inter': int(line[0]), 342 | 'length': float(line[2]), 343 | 'speed_limit': float(line[3]), 344 | 'num_lanes': int(line[5]), 345 | 'inverse_road': int(line[-2]) 346 | } 347 | intersections[int(line[0])]['end_roads'].append(int(line[-1])) 348 | intersections[int(line[1])]['end_roads'].append(int(line[-2])) 349 | intersections[int(line[0])]['start_roads'].append(int(line[-2])) 350 | intersections[int(line[1])]['start_roads'].append(int(line[-1])) 351 | pre_road = (int(line[-2]), int(line[-1])) 352 | else: 353 | # 4 out-roads 354 | signal_road_order = list(map(int, line[1:])) 355 | now_agent = int(line[0]) 356 | in_roads = [] 357 | for road in signal_road_order: 358 | if (road != -1): 359 | in_roads.append(roads[road]['inverse_road']) 360 | else: 361 | in_roads.append(-1) 362 | in_roads += signal_road_order 363 | agents[now_agent] = in_roads 364 | for agent, agent_roads in agents.items(): 365 | intersections[agent]['lanes'] = [] 366 | for road in agent_roads: 367 | ## here we treat road -1 have 3 lanes 368 | if (road == -1): 369 | for i in range(3): 370 | intersections[agent]['lanes'].append(-1) 371 | else: 372 | for lane in roads[road]['lanes'].keys(): 373 | intersections[agent]['lanes'].append(lane) 374 | 375 | return intersections, roads, agents 376 | 377 | def get_agent(target_iteration,algorithm,foldername): 378 | ACTION_SPACE = gym.spaces.Discrete(9) 379 | for dirpath, dirnames, file_names in os.walk('agent'): 380 | for file_name in [f for f in file_names if f.endswith(".py")]: 381 | if file_name == "gym_cfg.py": 382 | cfg_path = dirpath 383 | sys.path.append(str(cfg_path)) 384 | import gym_cfg as gym_cfg_submission 385 | gym_cfg_instance = gym_cfg_submission.gym_cfg() 386 | gym_dict = gym_cfg_instance.cfg 387 | obs_size = gym_dict['observation_dimension'] 388 | OBSERVATION_SPACE = gym.spaces.Dict({ 389 | "observation": gym.spaces.Box(low=-1e10, high=1e10, shape=(obs_size,)) 390 | }) 391 | agents = [] 392 | 393 | result_path = Path('./model') 394 | for dirpath, dirnames, file_names in os.walk(result_path / algorithm /foldername,topdown=True): 395 | dir_list = dirpath.split('/') 396 | if(dir_list[-1].startswith("checkpoint")): 397 | iteration = int(dir_list[-1][-6:]) 398 | if(target_iteration!=-1 and iteration != target_iteration): 399 | continue 400 | model_path = os.path.join(dirpath,'checkpoint-{}'.format(iteration)) 401 | agents.append( 402 | ( 403 | RLlibTFCheckpointPolicy(load_path=model_path, algorithm=algorithm, policy_name="default_policy", 404 | observation_space=OBSERVATION_SPACE, action_space=ACTION_SPACE) 405 | , iteration 406 | ) 407 | ) 408 | 409 | return agents 410 | 411 | def write_cfg(cfgs,path): 412 | with open(path,'w') as f: 413 | for k,v in cfgs.items(): 414 | f.write('{} : {}\n'.format(k,v)) 415 | 416 | 417 | if __name__ == "__main__": 418 | # parser.add_argument( 419 | # "--sim_cfg", 420 | # type=str, 421 | # required=True, 422 | # help = "simulator file for CBEngine" 423 | # ) 424 | parser.add_argument( 425 | "--use_half", 426 | action="store_true", 427 | default = False, 428 | help="first half Model. second half MP" 429 | ) 430 | 431 | parser.add_argument( 432 | "--thread_num", 433 | type=int, 434 | default=8, 435 | help = "thread num for CBEngine" 436 | ) 437 | parser.add_argument( 438 | "--sim_cfg", 439 | type=str, 440 | required=True, 441 | help = "which simulator cfg to be evaluated" 442 | ) 443 | parser.add_argument( 444 | "--gym_cfg_dir", 445 | type = str, 446 | default="agent", 447 | help = "gym_cfg (observation, reward) for CBEngine" 448 | ) 449 | parser.add_argument( 450 | "--metric_period", 451 | type=int, 452 | default=3600, 453 | help = "simulator file for CBEngine" 454 | ) 455 | parser.add_argument( 456 | "--algorithm", 457 | type=str, 458 | required = True, 459 | help = "algorithm to be evaluate" 460 | ) 461 | parser.add_argument( 462 | '--iteration', 463 | type = int, 464 | required = True, 465 | help = "which iteration to be evaluated" 466 | ) 467 | parser.add_argument( 468 | '--foldername', 469 | type = str, 470 | required =True, 471 | help = "which folder in model/${algorithm}/" 472 | ) 473 | args = parser.parse_args() 474 | for dirpath, dirnames, file_names in os.walk(args.gym_cfg_dir): 475 | for file_name in [f for f in file_names if f.endswith(".py")]: 476 | if file_name == "gym_cfg.py": 477 | cfg_path = dirpath 478 | sys.path.append(str(cfg_path)) 479 | import gym_cfg as gym_cfg_submission 480 | gym_cfg_instance = gym_cfg_submission.gym_cfg() 481 | gym_dict = gym_cfg_instance.cfg 482 | metric_period = args.metric_period 483 | 484 | 485 | # ray.init() 486 | 487 | mp_agent_instance = MP_agent(2) 488 | # get agents 489 | if(args.algorithm not in ['FT','MP']): 490 | agents = get_agent(target_iteration=args.iteration, algorithm=args.algorithm, foldername = args.foldername) 491 | elif(args.algorithm == 'FT'): 492 | agents = [(FT_agent(2),None)] 493 | elif(args.algorithm == 'MP'): 494 | agents = [(MP_agent(2),None)] 495 | gym_dict['observation_features'] = ['lane_vehicle_num'] 496 | # get sim_cfg list 497 | sim_cfgs = [] 498 | if(args.sim_cfg==None): 499 | sim_cfgs = [ 500 | '/starter-kit/cfg/simulator_round3_flow0.cfg' 501 | ] 502 | else: 503 | sim_cfgs = [args.sim_cfg] 504 | 505 | ################ scoreing 506 | for agent,iteration in agents: 507 | for sim_cfg in sim_cfgs: 508 | simulator_configs = read_config(sim_cfg) 509 | 510 | log_path = Path(simulator_configs['report_log_addr']) 511 | model_log_path = log_path / args.foldername 512 | score_path = model_log_path / 'iteration_{}/'.format(iteration) 513 | if(not os.path.exists(log_path)): 514 | os.makedirs(log_path) 515 | if(not os.path.exists(model_log_path)): 516 | os.makedirs(model_log_path) 517 | if(not os.path.exists(score_path)): 518 | os.makedirs(score_path) 519 | 520 | logger.info("log_path :{}\nmodel_log_path :{}\nscore_path :{}".format(log_path,model_log_path,score_path)) 521 | # change cfg 522 | simulator_configs = read_config(sim_cfg) 523 | simulator_configs['report_log_addr'] = str(score_path) + '/' 524 | 525 | sim_cfg_modified = sim_cfg[:-4] + "_iter{}_{}.cfg".format(iteration,args.foldername) 526 | write_cfg(simulator_configs,sim_cfg_modified) 527 | 528 | 529 | env_config = { 530 | "simulator_cfg_file": sim_cfg_modified, 531 | "thread_num": args.thread_num, 532 | "gym_dict": gym_dict, 533 | "metric_period": args.metric_period, 534 | "vehicle_info_path":score_path 535 | } 536 | 537 | 538 | roadnet_path = Path(simulator_configs['road_file_addr']) 539 | intersections, roads, agents = process_roadnet(roadnet_path) 540 | env = CBEngine_rllib_class(env_config) 541 | env.set_log(1) 542 | obs = env.reset() 543 | dones = {"__all__":False} 544 | step = 0 545 | while not dones['__all__']: 546 | step+=1 547 | if(args.use_half==False): 548 | if(args.algorithm == 'FT'): 549 | for k, v in obs.items(): 550 | obs[k] = step 551 | elif(args.algorithm == 'MP'): 552 | for k,v in obs.items(): 553 | temp = obs[k]['observation'] 554 | obs[k] = [step] 555 | obs[k] += temp 556 | for k,v in obs.items(): 557 | logger.info("cur_phase: {}".format(env.agent_curphase[int(k)])) 558 | break 559 | action = agent.act(obs) 560 | else: 561 | if(step < 180): 562 | action = agent.act(obs) 563 | else: 564 | for k,v in obs.items(): 565 | temp = obs[k]['observation'] 566 | obs[k] = [step] 567 | obs[k] += temp 568 | action = mp_agent_instance.act(obs) 569 | obs, reward, dones, info = env.step(action) 570 | for k,v in obs.items(): 571 | logger.info("step : {}\nobs : {}\naction: {}\n=====================".format(step,obs[k],action[k])) 572 | break 573 | if (step * 10 % metric_period == 0): 574 | try: 575 | tot_v, d_i = process_score(score_path, roads, step * 10 - 1, score_path, travel_time = env.eng.get_average_travel_time()) 576 | except Exception as e: 577 | logger.error(e) 578 | logger.error('Error in process_score. Maybe no log') 579 | continue 580 | 581 | logger.info("cfgfile : {} , iteration {} evaluating finished".format(sim_cfg,iteration)) 582 | os.remove(sim_cfg_modified) 583 | 584 | 585 | --------------------------------------------------------------------------------