|
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 |
|
problem_list = [] |
|
for i in range(batch_count): |
|
problem = Problem() |
|
|
|
|
|
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) |
|
|