先坤
add greedrl
db26c81
import json
from greedrl.feature import *
from greedrl.variable import *
from greedrl.function import *
from greedrl import Problem
features = [local_category('task_order'),
global_category('task_type', 2),
global_category('task_new_order', 2),
variable_feature('time_this_to_task'),
continuous_feature('x_time_matrix'),
continuous_feature('task_due_time_x'),
continuous_feature('worker_task_mask')]
variables = [task_demand_now('task_demand_now', feature='task_demand'),
task_demand_now('task_demand_this', feature='task_demand', only_this=True),
task_variable('task_pickup_this', feature='task_pickup'),
task_variable('task_due_time_this', feature='task_due_time'),
feature_variable('task_order', feature='task_order'),
feature_variable('task_type', feature='task_type'),
feature_variable('task_new_pickup', feature='task_new_pickup'),
feature_variable('worker_task_mask', feature='worker_task_mask'),
worker_count_now('worker_count_now', feature='worker_count'),
worker_variable('worker_min_old_task_this', feature='worker_min_old_task'),
worker_variable('worker_max_new_order_this', feature='worker_max_new_order'),
worker_variable('worker_task_mask_this', feature='worker_task_mask'),
worker_used_resource('worker_used_old_task', task_require='task_old'),
worker_used_resource('worker_used_new_order', task_require='task_new_pickup'),
worker_used_resource('worker_used_time', edge_require='time_matrix'),
edge_variable('time_this_to_task', feature='x_time_matrix', this_to_task=True)]
class Constraint:
def do_task(self):
return self.task_demand_this
def mask_worker_start(self):
mask = self.worker_count_now <= 0
finished = self.task_demand_now <= 0
worker_task_mask = self.worker_task_mask | finished[:, None, :]
mask |= torch.all(worker_task_mask, 2)
return mask
def mask_worker_end(self):
mask = self.worker_used_old_task < self.worker_min_old_task_this
mask |= task_group_split(self.task_order, self.task_demand_now <= 0)
return mask
def mask_task(self):
mask = self.task_demand_now <= 0
mask |= task_group_priority(self.task_order, self.task_type, mask)
worker_max_new_order = self.worker_max_new_order_this - self.worker_used_new_order
mask |= self.task_new_pickup > worker_max_new_order[:, None]
mask |= self.worker_task_mask_this
return mask
def finished(self):
worker_mask = self.worker_count_now <= 0
task_mask = self.task_demand_now <= 0
worker_task_mask = worker_mask[:, :, None] | task_mask[:, None, :]
worker_task_mask |= self.worker_task_mask
batch_size = worker_task_mask.size(0)
worker_task_mask = worker_task_mask.view(batch_size, -1)
return worker_task_mask.all(1)
class Objective:
def step_task(self):
over_time = (self.worker_used_time - self.task_due_time_this).clamp(min=0)
pickup_time = self.worker_used_time * self.task_pickup_this
return self.worker_used_time + over_time + pickup_time
def step_finish(self):
return self.task_demand_now.sum(1) * 1000
def preprocess(problem):
NW, NT = problem.worker_task_mask.size()
worker_task_old = torch.ones(NW, NT, dtype=torch.int32)
new_task_mask = problem.task_new_order[None, :].expand(NW, NT)
worker_task_old[new_task_mask] = 0
worker_task_old[problem.worker_task_mask] = 0
assert torch.all(worker_task_old.sum(0) <= 1)
problem.worker_min_old_task = worker_task_old.sum(1)
problem.worker_count = torch.ones(NW, dtype=torch.int32)
problem.task_demand = torch.ones(NT, dtype=torch.int32)
problem.task_pickup = (problem.task_type == 0).to(torch.int32)
task_old = torch.ones(NT, dtype=torch.int32)
task_old[problem.task_new_order] = 0
problem.task_old = task_old
task_new_pickup = torch.ones(NT, dtype=torch.int32)
task_new_pickup[problem.task_type >= 1] = 0
task_new_pickup[~problem.task_new_order] = 0
problem.task_new_pickup = task_new_pickup
problem.task_due_time_x = problem.task_due_time.float() / 900
problem.x_time_matrix = problem.time_matrix.float() / 900
problem.features = features
problem.variables = variables
problem.constraint = Constraint
problem.objective = Objective
return problem
def make_problem_from_json(data):
data = json.loads(data)
problem = Problem()
problem.id = data['id']
problem.task_order = torch.tensor(data['task_order'], dtype=torch.int32)
problem.task_type = torch.tensor(data['task_type'], dtype=torch.int32)
problem.task_new_order = torch.tensor(data['task_new_order'], dtype=torch.bool)
problem.task_due_time = torch.tensor(data['task_due_time'], dtype=torch.int32)
problem.worker_max_new_order = torch.tensor(data['worker_max_new_order'], dtype=torch.int32)
problem.worker_task_mask = torch.tensor(data['worker_task_mask'], dtype=torch.bool)
problem.time_matrix = torch.tensor(data['time_matrix'], dtype=torch.int32)
NW, NT = problem.worker_task_mask.size()
assert problem.task_order.size() == (NT,), "task_order size error"
assert problem.task_type.size() == (NT,), "task_type size error"
assert problem.task_new_order.size() == (NT,), "task_new_order size error"
assert problem.task_due_time.size() == (NT,), "task_due_time size error"
assert problem.worker_max_new_order.size() == (NW,), "worker_max_new_order size error"
assert problem.time_matrix.size() == (NW + NT, NW + NT), "time_matrix size error"
return preprocess(problem)
def make_problem(batch_count, batch_size=1, task_count=100):
assert batch_size == 1
assert task_count == 100
NW = 100
NT = task_count
NO = NT // 2 # 订单数, 一个订单有pickup, delivery两个任务
problem_list = []
for i in range(batch_count):
problem = Problem()
# user-provided data
problem.worker_max_new_order = torch.full((NW,), 2, dtype=torch.int32)
task_order = torch.arange(NO, dtype=torch.int32)
problem.task_order = torch.cat([task_order, task_order], 0)
task_type = torch.zeros(NO, dtype=torch.int32)
problem.task_type = torch.cat([task_type, task_type + 1], 0)
problem.task_new_order = torch.ones(NT, dtype=torch.bool)
task_due_time = torch.randint(1000, 1800, (NO,), dtype=torch.int32)
problem.task_due_time = torch.cat([task_due_time, task_due_time + 1800], 0)
worker_task_mask = torch.rand(NW, NO) < 0.9
problem.worker_task_mask = torch.cat([worker_task_mask, worker_task_mask], 1)
loc = torch.rand(NW + NT, 2, dtype=torch.float32)
time_matrix = torch.norm(loc[:, None, :] - loc[None, :, :], dim=2) * 1000
problem.time_matrix = time_matrix.to(torch.int32)
problem_list.append(preprocess(problem))
return problem_list
if __name__ == '__main__':
import sys
import os.path as osp
sys.path.append(osp.join(osp.dirname(__file__), '../'))
import runner
runner.run(make_problem)