diff --git a/include/fiction/algorithms/path_finding/k_shortest_paths.hpp b/include/fiction/algorithms/path_finding/k_shortest_paths.hpp index a7b62c49e..30c228317 100644 --- a/include/fiction/algorithms/path_finding/k_shortest_paths.hpp +++ b/include/fiction/algorithms/path_finding/k_shortest_paths.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include namespace fiction @@ -51,7 +52,7 @@ class yen_k_shortest_paths_impl unit_cost_functor, uint8_t>(), ps.astar_params)); } - path_collection run() + path_collection run() noexcept { assert(!objective.source.is_dead() && !objective.target.is_dead() && "Neither source nor target coordinate can be dead"); @@ -87,6 +88,8 @@ class yen_k_shortest_paths_impl { // block the connection that was already used in the previous shortest path layout.obstruct_connection(p[i], p[i + 1]); + // store connection for later clearing + temporarily_obstructed_connections.push_back({p[i], p[i + 1]}); } } @@ -98,6 +101,8 @@ class yen_k_shortest_paths_impl { // block them from further exploration layout.obstruct_coordinate(root); + // store coordinate for later clearing + temporarily_obstructed_coordinates.push_back(root); } } @@ -124,8 +129,7 @@ class yen_k_shortest_paths_impl } // clear obstructions again (prepare for the next potential path) - layout.clear_obstructed_coordinates(); - layout.clear_obstructed_connections(); + reset_temporary_obstructions(); } // if there were no spur paths or if all spur paths have been added to k_shortest_paths already @@ -173,6 +177,14 @@ class yen_k_shortest_paths_impl * A set of potential shortest paths. */ path_set shortest_path_candidates{}; + /** + * A temporary storage for coordinates that are obstructed during the algorithm. + */ + std::vector> temporarily_obstructed_coordinates{}; + /** + * A temporary storage for coordinates that are obstructed during the algorithm. + */ + std::vector, coordinate>> temporarily_obstructed_connections{}; /** * Computes the cost of a path. This function can be adjusted to fetch paths of differing costs. * @@ -185,6 +197,23 @@ class yen_k_shortest_paths_impl { return p.size(); } + /** + * Resets all temporary obstructions. + */ + void reset_temporary_obstructions() noexcept + { + for (const auto& c : temporarily_obstructed_coordinates) + { + layout.clear_obstructed_coordinate(c); + } + for (const auto& c : temporarily_obstructed_connections) + { + layout.clear_obstructed_connection(c.first, c.second); + } + + temporarily_obstructed_coordinates.clear(); + temporarily_obstructed_connections.clear(); + } }; } // namespace detail diff --git a/include/fiction/layouts/obstruction_layout.hpp b/include/fiction/layouts/obstruction_layout.hpp index d725926e5..c603685d6 100644 --- a/include/fiction/layouts/obstruction_layout.hpp +++ b/include/fiction/layouts/obstruction_layout.hpp @@ -90,6 +90,27 @@ class obstruction_layout : public Lyt { strg->obstructed_connections.insert({src, tgt}); } + /** + * Clears the obstruction status of the given coordinate `c` if the obstruction was manually marked via + * `obstruct_coordinate`. + * + * @param c Coordinate to clear. + */ + void clear_obstructed_coordinate(const typename Lyt::coordinate& c) noexcept + { + strg->obstructed_coordinates.erase(c); + } + /** + * Clears the obstruction status of the connection from coordinate `src` to coordinate `tgt` if the obstruction was + * manually marked via `obstruct_connection`. + * + * @param src Source coordinate. + * @param tgt Target coordinate. + */ + void clear_obstructed_connection(const typename Lyt::coordinate& src, const typename Lyt::coordinate& tgt) noexcept + { + strg->obstructed_connections.erase({src, tgt}); + } /** * Clears all obstructed coordinates that were manually marked via `obstruct_coordinate`. */ diff --git a/test/algorithms/path_finding/k_shortest_paths.cpp b/test/algorithms/path_finding/k_shortest_paths.cpp index 706b45ea9..eccb2316d 100644 --- a/test/algorithms/path_finding/k_shortest_paths.cpp +++ b/test/algorithms/path_finding/k_shortest_paths.cpp @@ -280,7 +280,7 @@ TEST_CASE("Yen's algorithm on 4x4 gate-level layouts with coordinate obstruction { const gate_lyt layout{{3, 3}, twoddwave_clocking()}; - SECTION("(0,0) to (3,3) with coordinate obstruction") // path of length 7 + SECTION("(0,0) to (3,3) with coordinate obstruction via PIs") // path of length 7 { obstruction_layout obstr_lyt{layout}; @@ -303,12 +303,41 @@ TEST_CASE("Yen's algorithm on 4x4 gate-level layouts with coordinate obstruction CHECK(path[4] == coordinate{1, 3}); CHECK(path[5] == coordinate{2, 3}); } + SECTION("(0,0) to (3,3) with coordinate obstruction via declaration") // path of length 7 + { + obstruction_layout obstr_lyt{layout}; + + // create some PIs as obstruction + obstr_lyt.obstruct_coordinate({3, 0}); + obstr_lyt.obstruct_coordinate({3, 1}); + obstr_lyt.obstruct_coordinate({1, 2}); + obstr_lyt.obstruct_coordinate({2, 2}); + // effectively blocking (3,2) as well + + const auto collection = + yen_k_shortest_paths(obstr_lyt, {{0, 0}, {3, 3}}, 1); // only one path possible + + REQUIRE(collection.size() == 1); + const auto& path = collection[0]; + REQUIRE(path.size() == 7); + CHECK(path[1] == coordinate{0, 1}); + CHECK(path[2] == coordinate{0, 2}); + CHECK(path[3] == coordinate{0, 3}); + CHECK(path[4] == coordinate{1, 3}); + CHECK(path[5] == coordinate{2, 3}); + + // coordinates should still be obstructed + CHECK(obstr_lyt.is_obstructed_coordinate({3, 0})); + CHECK(obstr_lyt.is_obstructed_coordinate({3, 1})); + CHECK(obstr_lyt.is_obstructed_coordinate({1, 2})); + CHECK(obstr_lyt.is_obstructed_coordinate({2, 2})); + } } SECTION("USE") { const gate_lyt layout{{3, 3}, use_clocking()}; - SECTION("(0,0) to (3,3) with coordinate obstruction") // path of length 7 + SECTION("(0,0) to (3,3) with coordinate obstruction via PIs") // path of length 7 { obstruction_layout obstr_lyt{layout}; @@ -327,6 +356,28 @@ TEST_CASE("Yen's algorithm on 4x4 gate-level layouts with coordinate obstruction CHECK(path[4] == coordinate{2, 2}); CHECK(path[5] == coordinate{3, 2}); } + SECTION("(0,0) to (3,3) with coordinate obstruction via declaration") // path of length 7 + { + obstruction_layout obstr_lyt{layout}; + + // create a PI as obstruction + obstr_lyt.obstruct_coordinate({3, 0}); // blocks 3 paths + + const auto collection = + yen_k_shortest_paths(obstr_lyt, {{0, 0}, {3, 3}}, 1); // only one path possible + + REQUIRE(collection.size() == 1); + const auto& path = collection[0]; + REQUIRE(path.size() == 7); + CHECK(path[1] == coordinate{1, 0}); + CHECK(path[2] == coordinate{1, 1}); + CHECK(path[3] == coordinate{1, 2}); + CHECK(path[4] == coordinate{2, 2}); + CHECK(path[5] == coordinate{3, 2}); + + // coordinates should still be obstructed + CHECK(obstr_lyt.is_obstructed_coordinate({3, 0})); + } } } @@ -481,6 +532,11 @@ TEST_CASE("Yen's algorithm on 4x4 gate-level layouts with connection obstruction CHECK(path[3] == coordinate{0, 3}); CHECK(path[4] == coordinate{1, 3}); CHECK(path[5] == coordinate{2, 3}); + + // connections should still be obstructed + CHECK(obstr_lyt.is_obstructed_connection({0, 0}, {1, 0})); + CHECK(obstr_lyt.is_obstructed_connection({0, 1}, {1, 1})); + CHECK(obstr_lyt.is_obstructed_connection({0, 2}, {1, 2})); } } SECTION("USE") @@ -504,6 +560,9 @@ TEST_CASE("Yen's algorithm on 4x4 gate-level layouts with connection obstruction CHECK(path[3] == coordinate{1, 2}); CHECK(path[4] == coordinate{2, 2}); CHECK(path[5] == coordinate{3, 2}); + + // connections should still be obstructed + CHECK(obstr_lyt.is_obstructed_connection({2, 0}, {3, 0})); } } } diff --git a/test/layouts/obstruction_layout.cpp b/test/layouts/obstruction_layout.cpp index c4d00023a..d49f7d31d 100644 --- a/test/layouts/obstruction_layout.cpp +++ b/test/layouts/obstruction_layout.cpp @@ -120,6 +120,18 @@ TEST_CASE("Coordinate obstruction", "[obstruction-layout]") CHECK(obstr_lyt.is_obstructed_coordinate({3, 0})); CHECK(obstr_lyt.is_obstructed_coordinate({4, 0})); + // remove some artificial obstructions + obstr_lyt.clear_obstructed_coordinate({0, 0}); + obstr_lyt.clear_obstructed_coordinate({1, 0}); + obstr_lyt.clear_obstructed_coordinate({2, 0}); + + CHECK(!obstr_lyt.is_obstructed_coordinate({0, 0})); + CHECK(!obstr_lyt.is_obstructed_coordinate({1, 0})); + CHECK(!obstr_lyt.is_obstructed_coordinate({2, 0})); + CHECK(obstr_lyt.is_obstructed_coordinate({3, 0})); + CHECK(obstr_lyt.is_obstructed_coordinate({4, 0})); + + // remove all obstructions obstr_lyt.clear_obstructed_coordinates(); CHECK(!obstr_lyt.is_obstructed_coordinate({0, 0})); @@ -161,6 +173,24 @@ TEST_CASE("Coordinate obstruction", "[obstruction-layout]") CHECK(!obstr_lyt.is_obstructed_coordinate({3, 0})); CHECK(obstr_lyt.is_obstructed_coordinate({3, 2})); + // remove some manually added obstructions + obstr_lyt.clear_obstructed_coordinate({0, 1}); + obstr_lyt.clear_obstructed_coordinate({1, 2}); + + CHECK(!obstr_lyt.is_obstructed_coordinate({0, 1})); + CHECK(!obstr_lyt.is_obstructed_coordinate({1, 2})); + CHECK(obstr_lyt.is_obstructed_coordinate({3, 2})); + + // removing an obstruction that was not added manually should not change anything + obstr_lyt.clear_obstructed_coordinate({1, 1}); + obstr_lyt.clear_obstructed_coordinate({2, 0}); + obstr_lyt.clear_obstructed_coordinate({3, 1}); + + CHECK(obstr_lyt.is_obstructed_coordinate({1, 1})); + CHECK(obstr_lyt.is_obstructed_coordinate({2, 0})); + CHECK(obstr_lyt.is_obstructed_coordinate({3, 1})); + + // remove all obstructions obstr_lyt.clear_obstructed_coordinates(); CHECK(!obstr_lyt.is_obstructed_coordinate({0, 1})); @@ -231,6 +261,35 @@ TEST_CASE("Coordinate obstruction", "[obstruction-layout]") CHECK(!obstr_lyt.is_obstructed_coordinate({4, 3})); CHECK(!obstr_lyt.is_obstructed_coordinate({4, 4})); + // remove some artificial obstructions + obstr_lyt.clear_obstructed_coordinate({0, 0}); + obstr_lyt.clear_obstructed_coordinate({0, 1}); + obstr_lyt.clear_obstructed_coordinate({0, 3}); + obstr_lyt.clear_obstructed_coordinate({0, 4}); + + CHECK(!obstr_lyt.is_obstructed_coordinate({0, 0})); + CHECK(!obstr_lyt.is_obstructed_coordinate({0, 1})); + CHECK(!obstr_lyt.is_obstructed_coordinate({0, 3})); + CHECK(!obstr_lyt.is_obstructed_coordinate({0, 4})); + CHECK(obstr_lyt.is_obstructed_coordinate({1, 0})); + CHECK(obstr_lyt.is_obstructed_coordinate({1, 1})); + CHECK(obstr_lyt.is_obstructed_coordinate({1, 3})); + CHECK(obstr_lyt.is_obstructed_coordinate({1, 4})); + + // removing an obstruction that was not added manually should not change anything + obstr_lyt.clear_obstructed_coordinate({0, 2}); + obstr_lyt.clear_obstructed_coordinate({2, 4}); + obstr_lyt.clear_obstructed_coordinate({2, 0}); + obstr_lyt.clear_obstructed_coordinate({2, 1}); + obstr_lyt.clear_obstructed_coordinate({2, 2}); + + CHECK(obstr_lyt.is_obstructed_coordinate({0, 2})); + CHECK(obstr_lyt.is_obstructed_coordinate({2, 4})); + CHECK(obstr_lyt.is_obstructed_coordinate({2, 0})); + CHECK(obstr_lyt.is_obstructed_coordinate({2, 1})); + CHECK(obstr_lyt.is_obstructed_coordinate({2, 2})); + + // remove all artificial obstructions obstr_lyt.clear_obstructed_coordinates(); CHECK(!obstr_lyt.is_obstructed_coordinate({0, 0})); @@ -286,6 +345,14 @@ TEST_CASE("Connection obstruction", "[obstruction-layout]") CHECK(!obstr_lyt.is_obstructed_connection({3, 3}, {2, 2})); CHECK(!obstr_lyt.is_obstructed_connection({3, 3}, {2, 3})); + // remove some artificial obstructions + obstr_lyt.clear_obstructed_connection({0, 0}, {0, 1}); + + CHECK(!obstr_lyt.is_obstructed_connection({0, 0}, {0, 1})); + CHECK(obstr_lyt.is_obstructed_connection({2, 2}, {2, 3})); + CHECK(obstr_lyt.is_obstructed_connection({2, 4}, {4, 0})); + + // remove all artificial obstructions obstr_lyt.clear_obstructed_connections(); CHECK(!obstr_lyt.is_obstructed_connection({0, 0}, {0, 1})); @@ -334,6 +401,23 @@ TEST_CASE("Connection obstruction", "[obstruction-layout]") CHECK(!obstr_lyt.is_obstructed_connection({3, 3}, {2, 2})); CHECK(!obstr_lyt.is_obstructed_connection({3, 3}, {2, 3})); + // remove some artificial obstructions + obstr_lyt.clear_obstructed_connection({0, 0}, {0, 1}); + + CHECK(!obstr_lyt.is_obstructed_connection({0, 0}, {0, 1})); + CHECK(obstr_lyt.is_obstructed_connection({2, 2}, {2, 3})); + CHECK(obstr_lyt.is_obstructed_connection({2, 4}, {4, 0})); + + // removing an obstruction that was not added manually should not change anything + obstr_lyt.clear_obstructed_connection({1, 1}, {2, 1}); + obstr_lyt.clear_obstructed_connection({2, 0}, {2, 1}); + obstr_lyt.clear_obstructed_connection({3, 1}, {2, 1}); + + CHECK(obstr_lyt.is_obstructed_connection({1, 1}, {2, 1})); + CHECK(obstr_lyt.is_obstructed_connection({2, 0}, {2, 1})); + CHECK(obstr_lyt.is_obstructed_connection({3, 1}, {2, 1})); + + // remove all artificial obstructions obstr_lyt.clear_obstructed_connections(); CHECK(!obstr_lyt.is_obstructed_connection({0, 0}, {0, 1})); @@ -379,6 +463,14 @@ TEST_CASE("Connection obstruction", "[obstruction-layout]") CHECK(!obstr_lyt.is_obstructed_connection({3, 3}, {2, 2})); CHECK(!obstr_lyt.is_obstructed_connection({3, 3}, {2, 3})); + // remove some artificial obstructions + obstr_lyt.clear_obstructed_connection({0, 0}, {0, 1}); + + CHECK(!obstr_lyt.is_obstructed_connection({0, 0}, {0, 1})); + CHECK(obstr_lyt.is_obstructed_connection({2, 2}, {2, 3})); + CHECK(obstr_lyt.is_obstructed_connection({2, 4}, {4, 0})); + + // remove all artificial obstructions obstr_lyt.clear_obstructed_connections(); CHECK(!obstr_lyt.is_obstructed_connection({0, 0}, {0, 1}));