Skip to content

Commit

Permalink
Remove all code
Browse files Browse the repository at this point in the history
  • Loading branch information
laxatives committed May 18, 2020
1 parent 436a424 commit 54f5e99
Show file tree
Hide file tree
Showing 11 changed files with 186 additions and 43 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
.idea/
*~
images/
data/
1 change: 1 addition & 0 deletions mobility_on_demand/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
data/
*~
submission.zip
*__pycache__*
41 changes: 22 additions & 19 deletions mobility_on_demand/model/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,36 @@
# @File: agent.py
# @Author: Benjamin Han (template provided by Xiaocheng Tang)
# @Date: 2020-05
import os
from typing import List, Dict

import utils
from dispatch import Dql
from utils import RepositionData


MODEL_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'TODO')
import grid
import parser
#from dispatch import Sarsa
#from parser import RepositionData
#from reposition import StateValueGreedy


class Agent:
""" Agent for dispatching and repositioning drivers for the 2020 ACM SIGKDD Cup Competition """
def __init__(self, alpha=2/(5*60), gamma=0.9, idle_reward=-2/(60*60)):
self.dispatcher = Dql(alpha, gamma, idle_reward)
pass
#self.dispatcher = Sarsa(alpha, gamma, idle_reward)
#self.repositioner = StateValueGreedy(self.dispatcher, gamma)

def dispatch(self, dispatch_input):

def dispatch(self, dispatch_input) -> List[Dict[str, str]]:
""" Compute the assignment between drivers and passengers at each time step """
drivers, requests, candidates = utils.parse_dispatch(dispatch_input)
dispatch = self.dispatcher.dispatch(drivers, requests, candidates)
return [dict(order_id=order_id, driver_id=d.driver_id) for order_id, d in dispatch.items()]
#drivers, requests, candidates = utils.parse_dispatch(dispatch_input)
#dispatch = self.dispatcher.dispatch(drivers, requests, candidates)
#return [dict(order_id=order_id, driver_id=d.driver_id) for order_id, d in dispatch.items()]
return []


def reposition(self, reposition_input):
def reposition(self, reposition_input) -> List[Dict[str, str]]:
""" Return target new positions for the given idle drivers """
reposition = []
data = RepositionData(reposition_input)
for driver_id, position_id in data.drivers:
reposition.append(dict(driver_id=driver_id, destination=position_id))
#data = RepositionData(reposition_input)
#if not data.drivers:
# return []

# TODO: Brute-force expected reward + update discounted by GAMMA^ETA
return reposition
#return self.repositioner.reposition(data)
return []
4 changes: 2 additions & 2 deletions mobility_on_demand/model/agent_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ def setUp(self):


def test_agent(self):
for _ in range(3):
for _ in range(10):
d = Agent().dispatch(self.dispatch_observ)
assert d
r = Agent().reposition(self.repo_observ)
assert r
assert r
12 changes: 11 additions & 1 deletion mobility_on_demand/model/dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from abc import abstractmethod
from typing import Dict, List, Set, Tuple

from utils import DispatchCandidate, Driver, Request
from parser import DispatchCandidate, Driver, Request

class Dispatcher:
def __init__(self, alpha, gamma, idle_reward):
Expand All @@ -16,6 +16,10 @@ def dispatch(self, drivers: Dict[str, Driver], requests: Dict[str, Request],
candidates: Dict[str, Set[DispatchCandidate]]) -> Dict[str, DispatchCandidate]:
...

@abstractmethod
def get_grid_ids(self) -> List[str]:
...

@abstractmethod
def state_value(self, grid_id: str) -> int:
...
Expand Down Expand Up @@ -76,6 +80,9 @@ def dispatch(self, drivers: Dict[str, Driver], requests: Dict[str, Request],

return dispatch

def get_grid_ids(self):
return set(self.state_values.keys())

def state_value(self, grid_id: str) -> int:
return self.state_values[grid_id]

Expand Down Expand Up @@ -141,5 +148,8 @@ def dispatch(self, drivers: Dict[str, Driver], requests: Dict[str, Request],
student[driver.location] += self.alpha * (self.idle_reward + self.gamma * v1 - v0)
return dispatch

def get_grid_ids(self):
return set(self.values_left.keys()).union(set(self.values_right.keys()))

def state_value(self, grid_id: str) -> int:
return self.values_left[grid_id] + self.values_right[grid_id]
6 changes: 3 additions & 3 deletions mobility_on_demand/model/dispatch_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import os
import unittest

import utils
import parser
from dispatch import Sarsa

SAMPLE_DIR = os.path.abspath('../samples')
Expand All @@ -18,14 +18,14 @@ def setUp(self):
self.dispatch_observ = json.load(f)

def test_sarsa(self):
drivers, requests, candidates = utils.parse_dispatch(self.dispatch_observ)
drivers, requests, candidates = parser.parse_dispatch(self.dispatch_observ)
dispatcher = Sarsa(self.alpha, self.gamma, self.idle_reward)
for _ in range(3):
d = dispatcher.dispatch(drivers, requests, candidates)
assert d

def test_dql(self):
drivers, requests, candidates = utils.parse_dispatch(self.dispatch_observ)
drivers, requests, candidates = parser.parse_dispatch(self.dispatch_observ)
dispatcher = Sarsa(self.alpha, self.gamma, self.idle_reward)
for _ in range(3):
d = dispatcher.dispatch(drivers, requests, candidates)
Expand Down
49 changes: 49 additions & 0 deletions mobility_on_demand/model/grid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import csv
import math
import os
from typing import Dict, List, Tuple


class Grid:
def __init__(self):
self.ids = [] # type: List[str]
self.coords = dict() # type: Dict[str, Tuple[float, float]]
coords_list = []

grid_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/hexagon_grid_table.csv')
with open(grid_path, 'r') as csvfile:
for row in csv.reader(csvfile):
if len(row) != 13:
continue
grid_id = row[0]
self.ids.append(grid_id)
lng = sum([float(row[i]) for i in range(1, 13, 2)]) / 6
lat = sum([float(row[i]) for i in range(2, 13, 2)]) / 6
coords_list.append((lng, lat))
self.coords[grid_id] = (lng, lat)

assert len(coords_list) == 8518, f'Failed to initialize hex grid: found {len(coords_list)} of 8518 expected ids'

def lookup(self, lng: float, lat: float) -> str:
best_id, best_distance = None, 1000
for grid_id, (grid_lng, grid_lat) in self.coords.items():
dist = abs(lng - grid_lng) + abs(lat - grid_lat)
if dist < best_distance:
best_id, best_distance = grid_id, dist

return best_id

def distance(self, x: str, y: str) -> float:
""" Return haversine distance in meters """
lng_x, lat_x = self.coords[x]
lng_y, lat_y = self.coords[y]

# Haversine
lng_x, lng_y, lat_x, lat_y = map(math.radians, [lng_x, lng_y, lat_x, lat_y])
lng_delta, lat_delta = abs(lng_x - lng_y), abs(lat_x - lat_y)
a = math.sin(lat_delta / 2) ** 2 + math.cos(lat_x) * math.cos(lat_y) * math.sin(lng_delta / 2) ** 2
return 6371000 * 2 * math.asin(a ** 0.5)

def cancel_prob(self, id: str) -> float:
# TODO
return 0
35 changes: 35 additions & 0 deletions mobility_on_demand/model/grid_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import unittest

from grid import Grid


class GridTest(unittest.TestCase):
def setUp(self):
self.grid = Grid()

def test_hex(self):
grid_id = self.grid.lookup(104.50, 30.71)
assert grid_id == '5973d1e3fdf1f878', grid_id
grid_id = self.grid.lookup(103.99, 30.40)
assert grid_id == '27471aff3df268a1', grid_id
grid_id = self.grid.lookup(104.52, 30.37)
assert grid_id == '15948343c6223064', grid_id

def test_distance(self):
# SE: (30.65924666666667, 104.12614), NW: (30.73054666666667, 104.04442)
distance = self.grid.distance('386c78bc3c226d88', '926d27c14e84f5d0')
expected = 11126
error = abs(distance - expected)
assert error < 100, distance

# NE: (30.725296666666665, 104.13031000000001), NW: (30.73054666666667, 104.04442)
distance = self.grid.distance('111297464a0c9cc8', '926d27c14e84f5d0')
expected = 8247
error = abs(distance - expected)
assert error < 100, distance

# NE: (30.725296666666665, 104.13031000000001), SE: (30.65924666666667, 104.12614)
distance = self.grid.distance('111297464a0c9cc8', '386c78bc3c226d88')
expected = 7333
error = abs(distance - expected)
assert error < 100, distance
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import collections
from typing import Any, Dict, Set, Tuple

from grid import Grid

# Apparently these aren't correct...
LAT_RANGE = (30.652828, 30.727818)
LNG_RANGE = (104.042102, 104.129591)
HEX_GRID = Grid()


class Driver:
def __init__(self, od):
self.driver_id = od['driver_id']
self.location = loc_to_grid(od['driver_location'])
self.location = HEX_GRID.lookup(*od['driver_location'])

def __repr__(self):
return f'Driver:{self.driver_id}@{self.location}'
Expand All @@ -19,8 +18,8 @@ def __repr__(self):
class Request:
def __init__(self, od):
self.request_id = od['order_id']
self.start_loc = loc_to_grid(od['order_start_location'])
self.end_loc = loc_to_grid(od['order_finish_location'])
self.start_loc = HEX_GRID.lookup(*od['order_start_location'])
self.end_loc = HEX_GRID.lookup(*od['order_finish_location'])
self.request_ts = od['timestamp']
self.finish_ts = od['order_finish_timestamp']
self.day_of_week = od['day_of_week']
Expand Down Expand Up @@ -49,6 +48,7 @@ def __init__(self, r):
self.drivers.append((d['driver_id'], d['grid_id']))
self.day_of_week = r['day_of_week']


def parse_dispatch(dispatch_input: Dict[str, Any]) -> (Dict[str, Driver], Dict[str, Request], Dict[str, Set[DispatchCandidate]]):
drivers = dict()
requests = dict()
Expand All @@ -63,9 +63,5 @@ def parse_dispatch(dispatch_input: Dict[str, Any]) -> (Dict[str, Driver], Dict[s


def loc_to_grid(location: Tuple[float, float]) -> str:
# TODO: Convert to Didi grid
# Apparently we can't use libraries...
#h3.geo_to_h3(location[1], location[0], h3_resolution)

# This is actually not bad
return f'{location[1]:0.2f},{location[0]:0.2f}'
56 changes: 56 additions & 0 deletions mobility_on_demand/model/reposition.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from abc import abstractmethod
from typing import Dict, List, Set

from dispatch import Dispatcher
from parser import HEX_GRID, RepositionData

SPEED = 6 # 3 m/s @ 2 second interval


class Repositioner:
def __init__(self, dispatcher: Dispatcher, gamma: float):
self.dispatcher = dispatcher
self.gamma = gamma

@abstractmethod
def reposition(self, data: RepositionData) -> List[Dict[str, str]]:
...


class ScoredCandidate:
def __init__(self, grid_id: str, score: float):
self.grid_id = grid_id
self.score = score


class StateValueGreedy(Repositioner):
def reposition(self, data: RepositionData) -> List[Dict[str, str]]:
reposition = [] # type: List[Dict[str, str]]
candidate_grid_ids = [] # type: List[ScoredCandidate]
for grid_id in self.dispatcher.get_grid_ids():
value = self.dispatcher.state_value(grid_id)
candidate_grid_ids.append(ScoredCandidate(grid_id, value))

max_candidates = (len(data.drivers) + 1) ** 2
candidate_grid_ids = sorted(candidate_grid_ids, key=lambda x: x.score, reverse=True)[:max_candidates]

assigned_grid_ids = set() # type: Set[str]
for driver_id, current_grid_id in data.drivers:
current_value = self.dispatcher.state_value(current_grid_id)
best_grid_id, best_value = None, 0 # don't move if negative value
for grid_candidate in candidate_grid_ids:
if grid_candidate.grid_id in assigned_grid_ids:
continue

eta = HEX_GRID.distance(current_grid_id, grid_id) / SPEED
discount = self.gamma ** eta
incremental_value = discount * self.dispatcher.state_value(grid_id) - current_value
if incremental_value > best_value:
best_grid_id, best_value = grid_id, incremental_value

new_grid_id = best_grid_id if best_grid_id else current_grid_id
assigned_grid_ids.add(new_grid_id)
reposition.append(dict(driver_id=driver_id, destination=new_grid_id))

# TODO: Brute-force expected reward + update discounted by GAMMA^ETA
return reposition
8 changes: 0 additions & 8 deletions mobility_on_demand/model/utils_test.py

This file was deleted.

0 comments on commit 54f5e99

Please sign in to comment.