diff --git a/Changelog.md b/Changelog.md index b588b27..c2877db 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,13 @@ # Changelog +Version 2.1.1 - 2024-11-18 +---------------------------- +Fixed: +- Bug: Solution costs counted multiple times. +- Bug: When reassigning a task to an agent with a smaller id, `task->agent_assigned` will be reset to -1 instead of the smaller id agent. + +Added: +- Docs explaining the anytime behaviour of the default planner. + Version 2.1.0 - 2024-11-15 ---------------------------- diff --git a/Prepare_Your_Submission.md b/Prepare_Your_Submission.md index 7cd7655..e636ba1 100644 --- a/Prepare_Your_Submission.md +++ b/Prepare_Your_Submission.md @@ -60,9 +60,13 @@ In `src/TaskScheduler.cpp`, you can find the default task scheduler, which calls #### The default planner In `src/MAPFPlanner.cpp`, you can find the default planner implementation, which calls the functions that are further defined in `default_planner/planner.cpp`. The default planner shares the same heuristic distance tables with the default scheduler. Its `initialize()` function prepares necessary data structures and a global heuristic table (if not initialized by the scheduler). Its `plan()` function computes collision-free actions for the current timestep. -The MAPF planner implemented in the default planner is a variant of Traffic Flow Optimised Guided PIBT, [Chen, Z., Harabor, D., Li, J., & Stuckey, P. J. (2024, March). Traffic flow optimisation for lifelong multi-agent path finding. In Proceedings of the AAAI Conference on Artificial Intelligence (Vol. 38, No. 18, pp. 20674-20682)](https://ojs.aaai.org/index.php/AAAI/article/view/30054/31856). The planner first optimises traffic flow assignments for each robot, then computes collision-free actions using [Priority Inheritance with Backtracking](https://www.sciencedirect.com/science/article/pii/S0004370222000923) following the optimised traffic flow. A more detailed technical report will be provided soon. - +The MAPF planner implemented in the default planner is a variant of Traffic Flow Optimised Guided PIBT, [Chen, Z., Harabor, D., Li, J., & Stuckey, P. J. (2024, March). Traffic flow optimisation for lifelong multi-agent path finding. In Proceedings of the AAAI Conference on Artificial Intelligence (Vol. 38, No. 18, pp. 20674-20682)](https://ojs.aaai.org/index.php/AAAI/article/view/30054/31856). The planner first optimises traffic flow assignments for each robot, then computes collision-free actions using [Priority Inheritance with Backtracking](https://www.sciencedirect.com/science/article/pii/S0004370222000923) (PIBT) following the optimised traffic flow. A more detailed technical report will be provided soon. +> ⚠️ **NOTE** ⚠️, the default planner is an anytime algorithm, meaning the time it has to compute a solution directly impacts the quality of that solution. +When a small amount of time is allocated, the default planner behaves like vanilla PIBT. When more time is allocated, the default planner updates traffic cost and incrementally recomputes guide paths for agents to improve their arrival time. +If you are a Scheduling Track participant, remember that the sooner your `schedule_plan()` function returns, the more time is avaliable to the default planner. +Better plans and better schedules can both substantially influence the performance of your submission on the leaderboard. +How to allocate time between these two components is an important part of a successful strategy. ## What to implement for each track @@ -122,8 +126,7 @@ If you compete in the combined track, you are allowed to modify the `Entry::comp ### Timing parameters for default planner and scheduler - -At every timestep, we will ask your planner to compute the next valid action for each robot subject to a given `time_limit` in ms. The `time_limit` is given as an input parameter to the `compute()` function of `entry.cpp`, which is then passed to `TaskScheduler::plan()` and `MAPFPlanner::plan()`. Note that, for `TaskScheduler::plan()` and `MAPFPlanner::plan()` the start time of the current timestep begins at `env->plan_start_time`, indicating the scheduler and the planner should return actions before `env->plan_start_time` plus `time_limit` ms. This is a soft limit, which means if you do not return actions before the `time_limit` elapses, the simulator will continue, and all robots will wait in place until the next planning episode. +At every timestep, we will ask your planner to compute the next valid action for each robot subject to a given `time_limit` in ms. The `time_limit` is given as an input parameter to the `compute()` function of `Entry.cpp`, which is then passed to `TaskScheduler::plan()` and `MAPFPlanner::plan()`. Note that, for `TaskScheduler::plan()` and `MAPFPlanner::plan()` the start time of the current timestep begins at `env->plan_start_time`, indicating the scheduler and the planner should return actions before `env->plan_start_time` plus `time_limit` ms. This is a soft limit, which means if you do not return actions before the `time_limit` elapses, the simulator will continue, and all robots will wait in place until the next planning episode. The default scheduler and default planner run in a sequential manner. The default scheduler uses `time_limit/2` as the timelimit to compute schedules. The default planner uses the remaining time, after the scheduler returns, to compute collision-free actions. diff --git a/inc/TaskManager.h b/inc/TaskManager.h index bd1689e..623ea44 100644 --- a/inc/TaskManager.h +++ b/inc/TaskManager.h @@ -17,22 +17,22 @@ class TaskManager{ vector new_freeagents; vector new_tasks; - list check_finished_tasks(vector states, int timestep); + list check_finished_tasks(vector& states, int timestep); int curr_timestep; // reveal new task void reveal_tasks(int timestep); - void update_tasks(vector states, vector assignment, int timestep); + void update_tasks(vector& states, vector& assignment, int timestep); void sync_shared_env(SharedEnvironment* env); void set_num_tasks_reveal(float num){num_tasks_reveal = num*num_of_agents;}; void set_logger(Logger* logger){this->logger = logger;} - bool validate_task_assgnment(vector assignment); // validate the task assignment - bool set_task_assignment( vector assignment); // set the task assignment; return true if task is valid + bool validate_task_assignment(vector& assignment); // validate the task assignment + bool set_task_assignment( vector& assignment); // set the task assignment; return true if task is valid int get_number_errors() const {return schedule_errors.size();} diff --git a/src/CompetitionSystem.cpp b/src/CompetitionSystem.cpp index d77acbc..38dcfc1 100644 --- a/src/CompetitionSystem.cpp +++ b/src/CompetitionSystem.cpp @@ -154,8 +154,6 @@ void BaseSystem::simulate(int simulation_time) for (int i = 0 ; i< timeout_timesteps; i ++){ simulator.move(all_wait_actions); - for (int k = 0; k < num_of_agents; k++) - for (int a = 0; a < num_of_agents; a++) { if (!env->goal_locations[a].empty()) diff --git a/src/TaskManager.cpp b/src/TaskManager.cpp index e8ff880..5d8122e 100644 --- a/src/TaskManager.cpp +++ b/src/TaskManager.cpp @@ -5,11 +5,18 @@ using json = nlohmann::ordered_json; - -bool TaskManager::validate_task_assgnment(vector assignment) +/** + * This function validates the proposed schedule (assignment) from participants + * + * @param assignment a vector of task_ids, one for each agent. The length of the vector should be equal to the number of agents. + * + */ +bool TaskManager::validate_task_assignment(vector& assignment) { if (assignment.size() != num_of_agents) { + schedule_errors.push_back(make_tuple("Invalid schedule size",-1,-1,-1,curr_timestep+1)); + logger->log_warning("Scheduler Error: assignment size does not match number of agents",curr_timestep+1); return false; } @@ -54,7 +61,16 @@ bool TaskManager::validate_task_assgnment(vector assignment) return true; } -bool TaskManager::set_task_assignment(vector< int> assignment) + +/** + * This function updates the current task assignments of agents. + * It first checks if the proposed assignment is valid, + * then updates the current assignment and updates the corresponding agent_assigned of each affected task. + * + * @param assignment a vector of task_ids, one for each agent. The length of the vector should be equal to the number of agents. + * + */ +bool TaskManager::set_task_assignment(vector< int>& assignment) { for (int a = 0; a < assignment.size(); a++) { @@ -63,19 +79,26 @@ bool TaskManager::set_task_assignment(vector< int> assignment) planner_schedule[a].push_back(make_pair(curr_timestep,assignment[a])); } } - if (! validate_task_assgnment(assignment)) + if (! validate_task_assignment(assignment)) { return false; } + //reset all the agent_assigned to -1, so that any droped task->agent_assignment will be -1 for (int a = 0; a < assignment.size(); a++) { - if (assignment[a] < 0 && current_assignment[a] < 0) + if (current_assignment[a] >= 0){ + ongoing_tasks[current_assignment[a]]->agent_assigned = -1; + } + } + + // then set the updated agent_assigned according to new assignments. + for (int a = 0; a < assignment.size(); a++) + { + if (assignment[a] < 0) { continue; } - if (current_assignment[a] >= 0) - ongoing_tasks[current_assignment[a]]->agent_assigned = -1; int t_id = assignment[a]; current_assignment[a] = t_id; ongoing_tasks[t_id]->agent_assigned = a; @@ -92,7 +115,14 @@ bool TaskManager::set_task_assignment(vector< int> assignment) return true; } -list TaskManager::check_finished_tasks(vector states, int timestep) +/** + * This function checks if any task is finished at the current timestep. + * If a task is finished, it updates the task's completion time and the agent's current assignment. + * + * @param states a vector of states of all agents, including the current location of each agent on the map. + * @param timestep the current timestep. + */ +list TaskManager::check_finished_tasks(vector& states, int timestep) { list finished_tasks_this_timestep; // new_freeagents.clear(); //prepare to push all new free agents to the shared environment @@ -122,7 +152,12 @@ list TaskManager::check_finished_tasks(vector states, int timestep) return finished_tasks_this_timestep; } - +/** + * This function synchronises the shared environment with the current task manager. + * It copies the current task pool, current task schedule, new free agents, and new tasks to the shared environment. + * + * @param env a pointer to the shared environment. + */ void TaskManager::sync_shared_env(SharedEnvironment* env) { env->task_pool.clear(); @@ -135,6 +170,13 @@ void TaskManager::sync_shared_env(SharedEnvironment* env) env->new_tasks = new_tasks; } +/** + * This function reveals new tasks at the current timestep. + * It reveals a fixed number of tasks at each timestep, + * and adds them to the ongoing tasks, new_tasks, and all_tasks. + * + * @param timestep the current timestep. + */ void TaskManager::reveal_tasks(int timestep) { new_tasks.clear(); //prepare to push all new revealed tasks to the shared environment @@ -151,7 +193,17 @@ void TaskManager::reveal_tasks(int timestep) } } -void TaskManager::update_tasks(vector states, vector assignment, int timestep) +/** + * This function is reponsible for the task management process: + * 1. It updates the current assignments of agents with proposed schedule from participants. + * 2. It checks if any task is finished at the current timestep. + * 3. It reveals new tasks at the current timestep. + * + * @param states a vector of states of all agents, including the current location of each agent on the map. + * @param assignment a vector of task_ids, one for each agent. The length of the vector should be equal to the number of agents. + * @param timestep the current timestep. + */ +void TaskManager::update_tasks(vector& states, vector& assignment, int timestep) { curr_timestep = timestep; set_task_assignment(assignment); @@ -159,7 +211,11 @@ void TaskManager::update_tasks(vector states, vector assignment, int reveal_tasks(timestep); } - +/** + * This function converts all tasks to a JSON object. + * + * @param map_cols the number of columns in the map. + */ json TaskManager::to_json(int map_cols) const { diff --git a/version.txt b/version.txt index 7ec1d6d..3e3c2f1 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.1.0 +2.1.1