From 658f66ebac48cfab82eb3b0bbd9a4c58c5b3516f Mon Sep 17 00:00:00 2001 From: Zhe Chen Date: Mon, 18 Nov 2024 10:49:45 +1100 Subject: [PATCH 1/6] Bug fix on duplicated costs count #96 --- src/CompetitionSystem.cpp | 2 -- 1 file changed, 2 deletions(-) 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()) From 6530cfdd4a91f902753e5c4e97e09e7d77184176 Mon Sep 17 00:00:00 2001 From: nobodyczcz Date: Mon, 18 Nov 2024 12:25:50 +1100 Subject: [PATCH 2/6] fix bug on reassignment #98 --- Changelog.md | 6 ++++++ src/TaskManager.cpp | 11 ++++++++--- version.txt | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index b588b27..7617019 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,10 @@ # Changelog +Version 2.1.1 - 2024-11-18 +---------------------------- +Fixed: +- Bug: Solution costs counted multiple times. +- Bug: When reassign a task to agent with smaller id, `task->agent_assigned` been reset to -1 instead of the smaller id agent. + Version 2.1.0 - 2024-11-15 ---------------------------- diff --git a/src/TaskManager.cpp b/src/TaskManager.cpp index e8ff880..d384ec4 100644 --- a/src/TaskManager.cpp +++ b/src/TaskManager.cpp @@ -68,14 +68,19 @@ bool TaskManager::set_task_assignment(vector< int> assignment) return false; } + //reset all the agent_assigned to -1, then set the updated agent_assigned according to new assignments. + for (int a = 0; a < assignment.size(); a++){ + if (current_assignment[a] >= 0){ + ongoing_tasks[current_assignment[a]]->agent_assigned = -1; + } + } + for (int a = 0; a < assignment.size(); a++) { - if (assignment[a] < 0 && current_assignment[a] < 0) + 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; 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 From 29eb386d2f2961e43729f502e8c5fa2c1229a800 Mon Sep 17 00:00:00 2001 From: nobodyczcz Date: Mon, 18 Nov 2024 13:00:20 +1100 Subject: [PATCH 3/6] typo fix --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 7617019..f1db36b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,7 +3,7 @@ Version 2.1.1 - 2024-11-18 ---------------------------- Fixed: - Bug: Solution costs counted multiple times. -- Bug: When reassign a task to agent with smaller id, `task->agent_assigned` been reset to -1 instead of the smaller id agent. +- 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. Version 2.1.0 - 2024-11-15 From f68f74cf8c7b1747d326dc11d0bb07f713f5fef7 Mon Sep 17 00:00:00 2001 From: nobodyczcz Date: Tue, 19 Nov 2024 10:32:52 +1100 Subject: [PATCH 4/6] default planner behavior documentation --- Prepare_Your_Submission.md | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/Prepare_Your_Submission.md b/Prepare_Your_Submission.md index 7cd7655..68e7c5d 100644 --- a/Prepare_Your_Submission.md +++ b/Prepare_Your_Submission.md @@ -62,6 +62,24 @@ In `src/MAPFPlanner.cpp`, you can find the default planner implementation, which 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. +⚠️ Note that, the default planner is an anytime algorithm, meaning the time it has to compute a solution directly impacts the quality of that solution. If insufficient time is allocated, the planner tends to act as vanilla PIBT, which returns less efficient actions. If you are a scheduler track participant, it is suggested that your `schedule_plan()` returns as soon as possible or been paralleled the scheduler computation with planner computation. + +### 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. + +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. + +You still have some control over the timing behaviour of the default scheduler and default planner. +File `default_planner/const.h` specifies a few parameters that control the timing of the scheduler and planner: +- `PIBT_RUNTIME_PER_100_AGENTS` specifies how much time in ms is required for PIBT to compute collision-free actions per 100 robots. The default planner computes the end time for traffic flow assignment by subtracting PIBT action time from the time limit so that the remaining time is left for PIBT to return actions. +- `TRAFFIC_FLOW_ASSIGNMENT_END_TIME_TOLERANCE` specifies the traffic flow assignment process end time tolerance in ms. The default planner will end the traffic flow assignment phase this many milliseconds before the traffic flow assignment end time. +- `PLANNER_TIMELIMIT_TOLERANCE` The MAPFPlanner will deduct this value from the time limit for the default planner. +- `SCHEDULER_TIMELIMIT_TOLERANCE` The TaskScheduler will deduct this value from the time limit for the default scheduler. + +You are allowed to modify the values of these parameters to reduce/increase the time spent on related components. + ## What to implement for each track @@ -120,22 +138,6 @@ You need to implement your own `Entry::initialize()` and `Entry::compute()` func The `Entry::compute()` needs to compute the task schedule and the actions for robots. Although the default entry does this by calling the scheduler and the planner separately, this is not required. If you compete in the combined track, you are allowed to modify the `Entry::compute()` function. -### 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. - -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. - -You still have some control over the timing behaviour of the default scheduler and default planner. -File `default_planner/const.h` specifies a few parameters that control the timing of the scheduler and planner: -- `PIBT_RUNTIME_PER_100_AGENTS` specifies how much time in ms is required for PIBT to compute collision-free actions per 100 robots. The default planner computes the end time for traffic flow assignment by subtracting PIBT action time from the time limit so that the remaining time is left for PIBT to return actions. -- `TRAFFIC_FLOW_ASSIGNMENT_END_TIME_TOLERANCE` specifies the traffic flow assignment process end time tolerance in ms. The default planner will end the traffic flow assignment phase this many milliseconds before the traffic flow assignment end time. -- `PLANNER_TIMELIMIT_TOLERANCE` The MAPFPlanner will deduct this value from the time limit for the default planner. -- `SCHEDULER_TIMELIMIT_TOLERANCE` The TaskScheduler will deduct this value from the time limit for the default scheduler. - -You are allowed to modify the values of these parameters to reduce/increase the time spent on related components. ### Unmodifiable files From 8db5df928dc56b679e8872bea76fefac99913b29 Mon Sep 17 00:00:00 2001 From: nobodyczcz Date: Tue, 19 Nov 2024 10:34:17 +1100 Subject: [PATCH 5/6] changelog details --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index f1db36b..c2877db 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,9 @@ 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 ---------------------------- From 56a930ccdcb750f57ee97a583f4e3f4b27ee28c5 Mon Sep 17 00:00:00 2001 From: nobodyczcz Date: Tue, 19 Nov 2024 13:35:47 +1100 Subject: [PATCH 6/6] update docs, replace function input with reference instead of copy --- Prepare_Your_Submission.md | 41 +++++++++++----------- inc/TaskManager.h | 8 ++--- src/TaskManager.cpp | 71 ++++++++++++++++++++++++++++++++------ 3 files changed, 86 insertions(+), 34 deletions(-) diff --git a/Prepare_Your_Submission.md b/Prepare_Your_Submission.md index 68e7c5d..e636ba1 100644 --- a/Prepare_Your_Submission.md +++ b/Prepare_Your_Submission.md @@ -60,27 +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. - -⚠️ Note that, the default planner is an anytime algorithm, meaning the time it has to compute a solution directly impacts the quality of that solution. If insufficient time is allocated, the planner tends to act as vanilla PIBT, which returns less efficient actions. If you are a scheduler track participant, it is suggested that your `schedule_plan()` returns as soon as possible or been paralleled the scheduler computation with planner computation. - -### 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. - -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. - -You still have some control over the timing behaviour of the default scheduler and default planner. -File `default_planner/const.h` specifies a few parameters that control the timing of the scheduler and planner: -- `PIBT_RUNTIME_PER_100_AGENTS` specifies how much time in ms is required for PIBT to compute collision-free actions per 100 robots. The default planner computes the end time for traffic flow assignment by subtracting PIBT action time from the time limit so that the remaining time is left for PIBT to return actions. -- `TRAFFIC_FLOW_ASSIGNMENT_END_TIME_TOLERANCE` specifies the traffic flow assignment process end time tolerance in ms. The default planner will end the traffic flow assignment phase this many milliseconds before the traffic flow assignment end time. -- `PLANNER_TIMELIMIT_TOLERANCE` The MAPFPlanner will deduct this value from the time limit for the default planner. -- `SCHEDULER_TIMELIMIT_TOLERANCE` The TaskScheduler will deduct this value from the time limit for the default scheduler. - -You are allowed to modify the values of these parameters to reduce/increase the time spent on related components. - +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 @@ -138,6 +124,21 @@ You need to implement your own `Entry::initialize()` and `Entry::compute()` func The `Entry::compute()` needs to compute the task schedule and the actions for robots. Although the default entry does this by calling the scheduler and the planner separately, this is not required. If you compete in the combined track, you are allowed to modify the `Entry::compute()` function. +### 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. + +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. + +You still have some control over the timing behaviour of the default scheduler and default planner. +File `default_planner/const.h` specifies a few parameters that control the timing of the scheduler and planner: +- `PIBT_RUNTIME_PER_100_AGENTS` specifies how much time in ms is required for PIBT to compute collision-free actions per 100 robots. The default planner computes the end time for traffic flow assignment by subtracting PIBT action time from the time limit so that the remaining time is left for PIBT to return actions. +- `TRAFFIC_FLOW_ASSIGNMENT_END_TIME_TOLERANCE` specifies the traffic flow assignment process end time tolerance in ms. The default planner will end the traffic flow assignment phase this many milliseconds before the traffic flow assignment end time. +- `PLANNER_TIMELIMIT_TOLERANCE` The MAPFPlanner will deduct this value from the time limit for the default planner. +- `SCHEDULER_TIMELIMIT_TOLERANCE` The TaskScheduler will deduct this value from the time limit for the default scheduler. + +You are allowed to modify the values of these parameters to reduce/increase the time spent on related components. ### Unmodifiable files 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/TaskManager.cpp b/src/TaskManager.cpp index d384ec4..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,18 +79,20 @@ 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, then set the updated agent_assigned according to new assignments. - for (int a = 0; a < assignment.size(); a++){ + //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 (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) @@ -97,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 @@ -127,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(); @@ -140,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 @@ -156,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); @@ -164,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 {