From 6f9f63c554fc9020b8fe6961f5512c3d9b8d04db Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Sun, 18 Jun 2023 22:37:39 +0200 Subject: [PATCH 01/17] :sparkles: Added a 2DDWave distance function --- .../algorithms/path_finding/distance.hpp | 37 ++++++++ test/algorithms/path_finding/distance.cpp | 94 +++++++++++++++++++ 2 files changed, 131 insertions(+) diff --git a/include/fiction/algorithms/path_finding/distance.hpp b/include/fiction/algorithms/path_finding/distance.hpp index 364aed553..90b2cfc97 100644 --- a/include/fiction/algorithms/path_finding/distance.hpp +++ b/include/fiction/algorithms/path_finding/distance.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace fiction @@ -62,6 +63,30 @@ template return static_cast(std::hypot(x, y)); } +/** + * The 2DDWave distance \f$ D \f$ between two layout coordinates \f$ s = (x_1, y_1) \f$ and \f$ t = (x_2, y_2) \f$ given + * by + * + * \f$ D = |x_1 - x_2| + |y_1 - y_2| \f$ iff \f$ s \leq t \f$ and \f$ \infty \f$, otherwise. + * + * @tparam Lyt Coordinate layout type. + * @tparam Dist Integral type for the distance. + * @param lyt Layout. + * @param source Source coordinate. + * @param target Target coordinate. + * @return 2DDWave distance between `source` and `target`. + */ +template +[[nodiscard]] constexpr Dist twoddwave_distance([[maybe_unused]] const Lyt& lyt, const coordinate& source, + const coordinate& target) noexcept +{ + static_assert(is_coordinate_layout_v, "Lyt is not a coordinate layout"); + static_assert(std::is_integral_v, "Dist is not an integral type"); + + return coordinate{source.x, source.y} <= coordinate{target.x, target.y} ? + manhattan_distance(lyt, source, target) : + std::numeric_limits::max(); +} /** * Computes the distance between two SiDB cells in nanometers. * @@ -166,6 +191,18 @@ class euclidean_distance_functor : public distance_functor public: euclidean_distance_functor() : distance_functor(&euclidean_distance) {} }; +/** + * A pre-defined distance functor that uses the 2DDWave distance. + * + * @tparam Lyt Coordinate layout type. + * @tparam Dist Integral distance type. + */ +template +class twoddwave_distance_functor : public distance_functor +{ + public: + twoddwave_distance_functor() : distance_functor(&twoddwave_distance) {} +}; } // namespace fiction diff --git a/test/algorithms/path_finding/distance.cpp b/test/algorithms/path_finding/distance.cpp index eb6a902e0..190c6a9a7 100644 --- a/test/algorithms/path_finding/distance.cpp +++ b/test/algorithms/path_finding/distance.cpp @@ -213,6 +213,100 @@ TEST_CASE("Euclidean distance functor", "[distance]") } } +TEST_CASE("2DDWave distance", "[distance]") +{ + SECTION("Unsigned Cartesian layout") + { + using cart_lyt = cartesian_layout; + + const cart_lyt layout{}; + + CHECK(twoddwave_distance(layout, {0, 0}, {0, 0}) == 0); + CHECK(twoddwave_distance(layout, {1, 1}, {1, 1}) == 0); + CHECK(twoddwave_distance(layout, {0, 0}, {0, 1}) == 1); + CHECK(twoddwave_distance(layout, {0, 0}, {1, 1}) == 2); + CHECK(twoddwave_distance(layout, {1, 2}, {3, 3}) == 3); + CHECK(twoddwave_distance(layout, {0, 0}, {4, 4}) == 8); + CHECK(twoddwave_distance(layout, {4, 4}, {0, 0}) == std::numeric_limits::max()); + + // ignore z-axis + CHECK(twoddwave_distance(layout, {0, 0, 1}, {8, 9, 0}) == 17); + CHECK(twoddwave_distance(layout, {0, 0, 1}, {8, 9, 1}) == 17); + } + SECTION("Signed Cartesian layout") + { + using cart_lyt = cartesian_layout; + + const cart_lyt layout{}; + + CHECK(twoddwave_distance(layout, {0, 0}, {0, 0}) == 0); + CHECK(twoddwave_distance(layout, {1, 1}, {1, 1}) == 0); + CHECK(twoddwave_distance(layout, {0, 0}, {0, 1}) == 1); + CHECK(twoddwave_distance(layout, {0, 0}, {1, 1}) == 2); + CHECK(twoddwave_distance(layout, {1, 2}, {3, 3}) == 3); + CHECK(twoddwave_distance(layout, {0, 0}, {4, 4}) == 8); + CHECK(twoddwave_distance(layout, {4, 4}, {0, 0}) == std::numeric_limits::max()); + + // ignore z-axis + CHECK(twoddwave_distance(layout, {0, 0, 1}, {8, 9, 0}) == 17); + CHECK(twoddwave_distance(layout, {0, 0, 1}, {8, 9, 1}) == 17); + + // negative coordinates + CHECK(twoddwave_distance(layout, {0, 0}, {-1, -1}) == std::numeric_limits::max()); + CHECK(twoddwave_distance(layout, {-4, -3}, {1, -1}) == 7); + CHECK(twoddwave_distance(layout, {-2, -8}, {-6, -4}) == 8); + } +} + +TEST_CASE("2DDWave distance functor", "[distance]") +{ + SECTION("Unsigned Cartesian layout") + { + using cart_lyt = cartesian_layout; + + const cart_lyt layout{}; + + const twoddwave_distance_functor distance{}; + + CHECK(distance(layout, {0, 0}, {0, 0}) == 0); + CHECK(distance(layout, {1, 1}, {1, 1}) == 0); + CHECK(distance(layout, {0, 0}, {0, 1}) == 1); + CHECK(distance(layout, {0, 0}, {1, 1}) == 2); + CHECK(distance(layout, {1, 2}, {3, 3}) == 3); + CHECK(distance(layout, {0, 0}, {4, 4}) == 8); + CHECK(distance(layout, {4, 4}, {0, 0}) == std::numeric_limits::max()); + + // ignore z-axis + CHECK(distance(layout, {0, 0, 1}, {8, 9, 0}) == 17); + CHECK(distance(layout, {0, 0, 1}, {8, 9, 1}) == 17); + } + SECTION("Signed Cartesian layout") + { + using cart_lyt = cartesian_layout; + + const cart_lyt layout{}; + + const twoddwave_distance_functor distance{}; + + CHECK(distance(layout, {0, 0}, {0, 0}) == 0); + CHECK(distance(layout, {1, 1}, {1, 1}) == 0); + CHECK(distance(layout, {0, 0}, {0, 1}) == 1); + CHECK(distance(layout, {0, 0}, {1, 1}) == 2); + CHECK(distance(layout, {1, 2}, {3, 3}) == 3); + CHECK(distance(layout, {0, 0}, {4, 4}) == 8); + CHECK(distance(layout, {4, 4}, {0, 0}) == std::numeric_limits::max()); + + // ignore z-axis + CHECK(distance(layout, {0, 0, 1}, {8, 9, 0}) == 17); + CHECK(distance(layout, {0, 0, 1}, {8, 9, 1}) == 17); + + // negative coordinates + CHECK(distance(layout, {0, 0}, {-1, -1}) == std::numeric_limits::max()); + CHECK(distance(layout, {-4, -3}, {1, -1}) == 7); + CHECK(distance(layout, {-2, -8}, {-6, -4}) == 8); + } +} + TEST_CASE("A* distance", "[distance]") { SECTION("Unsigned Cartesian layout") From a6c83093223999fb779d2c6ff11471b3d3f08d65 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Sun, 18 Jun 2023 22:41:25 +0200 Subject: [PATCH 02/17] :memo: Added RST documentation for the 2DDWave distance function --- docs/algorithms/path_finding.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/algorithms/path_finding.rst b/docs/algorithms/path_finding.rst index f4e493d9f..5b0d79ebe 100644 --- a/docs/algorithms/path_finding.rst +++ b/docs/algorithms/path_finding.rst @@ -7,11 +7,13 @@ Distance functions compute (an approximation for) the distance between two coord .. doxygenfunction:: fiction::manhattan_distance .. doxygenfunction:: fiction::euclidean_distance +.. doxygenfunction:: fiction::twoddwave_distance .. doxygenclass:: fiction::distance_functor :members: .. doxygenclass:: fiction::manhattan_distance_functor .. doxygenclass:: fiction::euclidean_distance_functor +.. doxygenclass:: fiction::twoddwave_distance_functor Cost Functions -------------- From cf5d448a95f6675f81a218d72304825facf819d5 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 28 Jun 2023 13:31:41 +0200 Subject: [PATCH 03/17] :construction: Work in progress save commit --- .../algorithms/path_finding/distance_map.hpp | 59 ++++++++++++++++++ test/algorithms/path_finding/distance_map.cpp | 62 +++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 include/fiction/algorithms/path_finding/distance_map.hpp create mode 100644 test/algorithms/path_finding/distance_map.cpp diff --git a/include/fiction/algorithms/path_finding/distance_map.hpp b/include/fiction/algorithms/path_finding/distance_map.hpp new file mode 100644 index 000000000..10c5c6d5a --- /dev/null +++ b/include/fiction/algorithms/path_finding/distance_map.hpp @@ -0,0 +1,59 @@ +// +// Created by marcel on 18.06.23. +// + +#ifndef FICTION_DISTANCE_MAP_HPP +#define FICTION_DISTANCE_MAP_HPP + +#include "fiction/algorithms/path_finding/distance.hpp" +#include "fiction/traits.hpp" + +#include + +#include +#include +#include + +namespace fiction +{ + +template +using distance_map = std::vector>; + +template +using sparse_distance_map = phmap::parallel_flat_hash_map, Dist>; + +template +[[nodiscard]] distance_map initialize_distance_map(const Lyt& lyt, + const distance_functor& dist_fn) noexcept +{ + distance_map dist_map(std::vector(lyt.area(), Dist{}), lyt.area()); + + lyt.foreach_coordinate( + [&lyt, &dist_fn, &dist_map](const auto& c1, const unsigned y) + { + lyt.foreach_coordinate([&lyt, &dist_fn, &dist_map, &c1, y](const auto& c2, const unsigned x) + { dist_map[x][y] = dist_fn(lyt, c1, c2); }); + }); + + return dist_map; +} + +template +[[nodiscard]] sparse_distance_map +initialize_sparse_distance_map(const Lyt& lyt, const distance_functor& dist_fn) noexcept +{ + sparse_distance_map dist_map(lyt.area() * 2); + + lyt.foreach_coordinate( + [&lyt, &dist_fn, &dist_map](const auto& c1) { + lyt.foreach_coordinate([&lyt, &dist_fn, &dist_map, &c1](const auto& c2) + { dist_map[c1][c2] = dist_fn(lyt, c1, c2); }); + }); + + return dist_map; +} + +} // namespace fiction + +#endif // FICTION_DISTANCE_MAP_HPP diff --git a/test/algorithms/path_finding/distance_map.cpp b/test/algorithms/path_finding/distance_map.cpp new file mode 100644 index 000000000..8b9f916bc --- /dev/null +++ b/test/algorithms/path_finding/distance_map.cpp @@ -0,0 +1,62 @@ +// +// Created by marcel on 18.06.23. +// + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace fiction; + +TEST_CASE("Distance map", "[distance-map]") +{ + using clk_lyt = clocked_layout>; + + SECTION("2DDWave clocking") + { + const clk_lyt layout{aspect_ratio{2, 2}, twoddwave_clocking()}; + + const auto dist_map = initialize_distance_map(layout, twoddwave_distance_functor{}); + + CHECK(manhattan_distance(layout, {0, 0}, {0, 0}) == 0); + CHECK(manhattan_distance(layout, {1, 1}, {1, 1}) == 0); + CHECK(manhattan_distance(layout, {0, 0}, {0, 1}) == 1); + CHECK(manhattan_distance(layout, {0, 0}, {1, 1}) == 2); + CHECK(manhattan_distance(layout, {1, 2}, {3, 3}) == 3); + CHECK(manhattan_distance(layout, {0, 0}, {4, 4}) == 8); + CHECK(manhattan_distance(layout, {4, 4}, {0, 0}) == 8); + + // ignore z-axis + CHECK(manhattan_distance(layout, {0, 0, 1}, {8, 9, 0}) == 17); + CHECK(manhattan_distance(layout, {0, 0, 1}, {8, 9, 1}) == 17); + } + SECTION("Signed Cartesian layout") + { + using cart_lyt = cartesian_layout; + + const cart_lyt layout{}; + + CHECK(manhattan_distance(layout, {0, 0}, {0, 0}) == 0); + CHECK(manhattan_distance(layout, {1, 1}, {1, 1}) == 0); + CHECK(manhattan_distance(layout, {0, 0}, {0, 1}) == 1); + CHECK(manhattan_distance(layout, {0, 0}, {1, 1}) == 2); + CHECK(manhattan_distance(layout, {1, 2}, {3, 3}) == 3); + CHECK(manhattan_distance(layout, {0, 0}, {4, 4}) == 8); + CHECK(manhattan_distance(layout, {4, 4}, {0, 0}) == 8); + + // ignore z-axis + CHECK(manhattan_distance(layout, {0, 0, 1}, {8, 9, 0}) == 17); + CHECK(manhattan_distance(layout, {0, 0, 1}, {8, 9, 1}) == 17); + + // negative coordinates + CHECK(manhattan_distance(layout, {0, 0}, {-1, -1}) == 2); + CHECK(manhattan_distance(layout, {-4, -3}, {1, -1}) == 7); + CHECK(manhattan_distance(layout, {-2, -8}, {-6, -4}) == 8); + } +} \ No newline at end of file From 7698565f3ed19fbe9df00ab0a43c78d34542fe40 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Tue, 11 Jul 2023 15:56:00 +0200 Subject: [PATCH 04/17] :memo: Adjusted docstrings --- include/fiction/algorithms/path_finding/distance.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/fiction/algorithms/path_finding/distance.hpp b/include/fiction/algorithms/path_finding/distance.hpp index 90b2cfc97..6cce05c78 100644 --- a/include/fiction/algorithms/path_finding/distance.hpp +++ b/include/fiction/algorithms/path_finding/distance.hpp @@ -69,6 +69,10 @@ template * * \f$ D = |x_1 - x_2| + |y_1 - y_2| \f$ iff \f$ s \leq t \f$ and \f$ \infty \f$, otherwise. * + * Thereby, \f$ s \leq t \f$ iff \f$ x_1 \leq x_2 \f$ and \f$ y_1 \leq y_2 \f$. + * + * @note To represent \f$ \infty \f$, `std::numeric_limits::max()` is returned for distances of infinite length. + * * @tparam Lyt Coordinate layout type. * @tparam Dist Integral type for the distance. * @param lyt Layout. From d0fe319aef1c1a9ef61792b2a94485a4f04ed141 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Tue, 11 Jul 2023 16:11:05 +0200 Subject: [PATCH 05/17] :bug: Fixed coordinate comparison for 2DDWave distance --- include/fiction/algorithms/path_finding/distance.hpp | 5 ++--- test/algorithms/path_finding/distance.cpp | 7 ++++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/include/fiction/algorithms/path_finding/distance.hpp b/include/fiction/algorithms/path_finding/distance.hpp index 6cce05c78..aa8716f70 100644 --- a/include/fiction/algorithms/path_finding/distance.hpp +++ b/include/fiction/algorithms/path_finding/distance.hpp @@ -87,9 +87,8 @@ template static_assert(is_coordinate_layout_v, "Lyt is not a coordinate layout"); static_assert(std::is_integral_v, "Dist is not an integral type"); - return coordinate{source.x, source.y} <= coordinate{target.x, target.y} ? - manhattan_distance(lyt, source, target) : - std::numeric_limits::max(); + return source.x <= target.x && source.y <= target.y ? manhattan_distance(lyt, source, target) : + std::numeric_limits::max(); } /** * Computes the distance between two SiDB cells in nanometers. diff --git a/test/algorithms/path_finding/distance.cpp b/test/algorithms/path_finding/distance.cpp index 190c6a9a7..2fe6884a2 100644 --- a/test/algorithms/path_finding/distance.cpp +++ b/test/algorithms/path_finding/distance.cpp @@ -228,6 +228,8 @@ TEST_CASE("2DDWave distance", "[distance]") CHECK(twoddwave_distance(layout, {1, 2}, {3, 3}) == 3); CHECK(twoddwave_distance(layout, {0, 0}, {4, 4}) == 8); CHECK(twoddwave_distance(layout, {4, 4}, {0, 0}) == std::numeric_limits::max()); + CHECK(twoddwave_distance(layout, {2, 1}, {0, 2}) == std::numeric_limits::max()); + CHECK(twoddwave_distance(layout, {1, 0}, {0, 1}) == std::numeric_limits::max()); // ignore z-axis CHECK(twoddwave_distance(layout, {0, 0, 1}, {8, 9, 0}) == 17); @@ -246,6 +248,8 @@ TEST_CASE("2DDWave distance", "[distance]") CHECK(twoddwave_distance(layout, {1, 2}, {3, 3}) == 3); CHECK(twoddwave_distance(layout, {0, 0}, {4, 4}) == 8); CHECK(twoddwave_distance(layout, {4, 4}, {0, 0}) == std::numeric_limits::max()); + CHECK(twoddwave_distance(layout, {2, 1}, {0, 2}) == std::numeric_limits::max()); + CHECK(twoddwave_distance(layout, {1, 0}, {0, 1}) == std::numeric_limits::max()); // ignore z-axis CHECK(twoddwave_distance(layout, {0, 0, 1}, {8, 9, 0}) == 17); @@ -253,8 +257,9 @@ TEST_CASE("2DDWave distance", "[distance]") // negative coordinates CHECK(twoddwave_distance(layout, {0, 0}, {-1, -1}) == std::numeric_limits::max()); + CHECK(twoddwave_distance(layout, {-2, -8}, {-6, -4}) == std::numeric_limits::max()); + CHECK(twoddwave_distance(layout, {-6, -4}, {-2, -8}) == std::numeric_limits::max()); CHECK(twoddwave_distance(layout, {-4, -3}, {1, -1}) == 7); - CHECK(twoddwave_distance(layout, {-2, -8}, {-6, -4}) == 8); } } From d0a6fc9a3265a1a3d9373390859fc58bc22c400a Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Tue, 11 Jul 2023 17:05:32 +0200 Subject: [PATCH 06/17] :sparkles: Added `distance_map` and `sparse_distance_map` data types that can cache distance values for faster access. This is particularly useful for complex distance functions that must be queried many times. Additionally, `distance_map_functor` and `sparse_distance_map_functor` are introduced, which are `distance_functors` that query a distance map type instead of a distance function. They can be used in path-finding algorithms that require `distance_functor` arguments --- .../algorithms/path_finding/distance.hpp | 4 +- .../algorithms/path_finding/distance_map.hpp | 165 +++++++++++++++-- test/algorithms/path_finding/distance_map.cpp | 171 ++++++++++++++---- 3 files changed, 295 insertions(+), 45 deletions(-) diff --git a/include/fiction/algorithms/path_finding/distance.hpp b/include/fiction/algorithms/path_finding/distance.hpp index aa8716f70..0b0bef744 100644 --- a/include/fiction/algorithms/path_finding/distance.hpp +++ b/include/fiction/algorithms/path_finding/distance.hpp @@ -138,7 +138,7 @@ class distance_functor * @param dist_fn A function that maps from layout coordinates to a distance value. */ explicit distance_functor( - const std::function&, const coordinate&)>& dist_fn) : + const std::function&, const coordinate&)>& dist_fn) : distance_function{dist_fn} { static_assert(is_coordinate_layout_v, "Lyt is not a coordinate layout"); @@ -165,7 +165,7 @@ class distance_functor /** * Distance function. */ - const std::function&, const coordinate&)> distance_function; + const std::function&, const coordinate&)> distance_function; }; // NOLINTEND(*-special-member-functions) diff --git a/include/fiction/algorithms/path_finding/distance_map.hpp b/include/fiction/algorithms/path_finding/distance_map.hpp index 10c5c6d5a..979b212b4 100644 --- a/include/fiction/algorithms/path_finding/distance_map.hpp +++ b/include/fiction/algorithms/path_finding/distance_map.hpp @@ -12,48 +12,189 @@ #include #include +#include #include namespace fiction { +/** + * A distance map is a two-dimensional array of distances between coordinates. The distance map is accessed via the + * indices of the coordinates in the associated layout. A coordinate index \f$ i \f$ is calculated as follows: + * \f$ i = y \cdot W + x \f$, with \f$ W \f$ being the width of the layout. As an example, in a layout of size + * \f$ 3 \times 3 \f$, the coordinate index of the coordinate \f$ (2, 1) \f$ has index \f$ 1 \cdot 3 + 2 = 5 \f$. + */ template using distance_map = std::vector>; - +/** + * A sparse distance map is a flat hash map of distances between coordinates. The sparse distance map is accessed via + * coordinate pairs. + */ template -using sparse_distance_map = phmap::parallel_flat_hash_map, Dist>; - +using sparse_distance_map = phmap::parallel_flat_hash_map, coordinate>, Dist>; +/** + * This function initializes a `distance_map` for a given layout and distance functor. It computes the distances between + * all pairs of coordinates in the layout and stores them in the distance map for quick subsequent access. + * + * This function performs \f$ \mathcal{O}(|L|^2) \f$ distance computations, where \f$ |L| \f$ is the number of + * coordinates in the layout. + * + * @tparam Lyt Coordinate layout type. + * @tparam Dist Distance type. + * @param lyt Layout to compute distances for. + * @param dist_fn Distance functor to apply to all pairs of coordinates in `lyt`. + * @return Fully initialized `distance_map` for `lyt`. + */ template [[nodiscard]] distance_map initialize_distance_map(const Lyt& lyt, const distance_functor& dist_fn) noexcept { - distance_map dist_map(std::vector(lyt.area(), Dist{}), lyt.area()); + static_assert(is_coordinate_layout_v, "Lyt is not a coordinate layout"); + // initialize distance map + distance_map dist_map(lyt.area(), std::vector(lyt.area(), Dist{})); + + // compute distances and store them via coordinate index lyt.foreach_coordinate( - [&lyt, &dist_fn, &dist_map](const auto& c1, const unsigned y) + [&lyt, &dist_fn, &dist_map](const auto& c1, const unsigned src) { - lyt.foreach_coordinate([&lyt, &dist_fn, &dist_map, &c1, y](const auto& c2, const unsigned x) - { dist_map[x][y] = dist_fn(lyt, c1, c2); }); + lyt.foreach_coordinate([&lyt, &dist_fn, &dist_map, &c1, src](const auto& c2, const unsigned tgt) + { dist_map[src][tgt] = dist_fn(lyt, c1, c2); }); }); return dist_map; } - +/** + * This function initializes a `sparse_distance_map` for a given layout and distance functor. It computes the distances + * between all pairs of coordinates in the layout and stores them in the distance map for quick subsequent access. + * + * This function performs \f$ \mathcal{O}(|L|^2) \f$ distance computations, where \f$ |L| \f$ is the number of + * coordinates in the layout. + * + * @tparam Lyt Coordinate layout type. + * @tparam Dist Distance type. + * @param lyt Layout to compute distances for. + * @param dist_fn Distance functor to apply to all pairs of coordinates in `lyt`. + * @return Fully initialized `sparse_distance_map` for `lyt`. + */ template [[nodiscard]] sparse_distance_map initialize_sparse_distance_map(const Lyt& lyt, const distance_functor& dist_fn) noexcept { - sparse_distance_map dist_map(lyt.area() * 2); + static_assert(is_coordinate_layout_v, "Lyt is not a coordinate layout"); + // initialize distance map + sparse_distance_map dist_map{}; + dist_map.reserve(lyt.area() * lyt.area()); + + // compute distances and store them via coordinate lookup lyt.foreach_coordinate( - [&lyt, &dist_fn, &dist_map](const auto& c1) { - lyt.foreach_coordinate([&lyt, &dist_fn, &dist_map, &c1](const auto& c2) - { dist_map[c1][c2] = dist_fn(lyt, c1, c2); }); + [&lyt, &dist_fn, &dist_map](const auto& c1) + { + lyt.foreach_coordinate( + [&lyt, &dist_fn, &dist_map, &c1](const auto& c2) { + dist_map[{c1, c2}] = dist_fn(lyt, c1, c2); + }); }); return dist_map; } +/** + * A distance functor that uses a precomputed `distance_map` to determine distances between coordinates. + * + * @tparam Lyt Coordinate layout type. + * @tparam Dist Distance type. + */ +template +class distance_map_functor : public distance_functor +{ + public: + /** + * Construct a distance functor from a `distance_map`. + * + * @param dm Distance map. + */ + explicit distance_map_functor(const distance_map& dm) : + distance_functor([](const Lyt&, const coordinate&, const coordinate&) + { return Dist{}; }), // dummy distance function + dist_map{dm} + {} + /** + * Override the call operator to query the distance map instead of the distance function. + * + * @param lyt Layout. + * @param source Source coordinate. + * @param target Target coordinate. + * @return Distance between source and target according to the stored distance map. + */ + [[nodiscard]] Dist operator()(const Lyt& lyt, const coordinate& source, + const coordinate& target) const override + { + return static_cast(dist_map[coordinate_index(lyt, source)][coordinate_index(lyt, target)]); + } + + protected: + /** + * Distance map. + */ + const distance_map dist_map; + + private: + /** + * This function calculates the coordinate index of a given coordinate in a given layout. + * + * @param lyt Layout. + * @param c Coordinate. + * @return Coordinate index. + */ + [[nodiscard]] static constexpr std::size_t coordinate_index(const Lyt& lyt, const coordinate& c) noexcept + { + return c.y * (lyt.x() + 1) + c.x; + } +}; + +/** + * A distance functor that uses a precomputed `sparse_distance_map` to determine distances between coordinates. + * + * @tparam Lyt Coordinate layout type. + * @tparam Dist Distance type. + */ +template +class sparse_distance_map_functor : public distance_functor +{ + public: + /** + * Construct a distance functor from a distance map. + * + * @param sdm Sparse distance map. + */ + explicit sparse_distance_map_functor(const sparse_distance_map& sdm) : + distance_functor([](const Lyt&, const coordinate&, const coordinate&) + { return Dist{}; }), // dummy distance function + sparse_dist_map{sdm} + {} + /** + * Override the call operator to query the sparse distance map instead of the distance function. + * + * @param lyt Layout. + * @param source Source coordinate. + * @param target Target coordinate. + * @return Distance between source and target according to the stored sparse distance map. + */ + [[nodiscard]] Dist operator()(const Lyt& lyt, const coordinate& source, + const coordinate& target) const override + { + return static_cast(sparse_dist_map.at({source, target})); + } + + protected: + /** + * Sparse distance map. + */ + const sparse_distance_map sparse_dist_map; +}; + } // namespace fiction #endif // FICTION_DISTANCE_MAP_HPP diff --git a/test/algorithms/path_finding/distance_map.cpp b/test/algorithms/path_finding/distance_map.cpp index 8b9f916bc..9f791ad46 100644 --- a/test/algorithms/path_finding/distance_map.cpp +++ b/test/algorithms/path_finding/distance_map.cpp @@ -3,8 +3,8 @@ // #include -#include +#include #include #include #include @@ -20,43 +20,152 @@ TEST_CASE("Distance map", "[distance-map]") SECTION("2DDWave clocking") { - const clk_lyt layout{aspect_ratio{2, 2}, twoddwave_clocking()}; + const clk_lyt layout{aspect_ratio{4, 4}, twoddwave_clocking()}; - const auto dist_map = initialize_distance_map(layout, twoddwave_distance_functor{}); + const auto dist_map = initialize_distance_map(layout, a_star_distance_functor{}); + const auto dist_map_func = distance_map_functor{dist_map}; - CHECK(manhattan_distance(layout, {0, 0}, {0, 0}) == 0); - CHECK(manhattan_distance(layout, {1, 1}, {1, 1}) == 0); - CHECK(manhattan_distance(layout, {0, 0}, {0, 1}) == 1); - CHECK(manhattan_distance(layout, {0, 0}, {1, 1}) == 2); - CHECK(manhattan_distance(layout, {1, 2}, {3, 3}) == 3); - CHECK(manhattan_distance(layout, {0, 0}, {4, 4}) == 8); - CHECK(manhattan_distance(layout, {4, 4}, {0, 0}) == 8); + layout.foreach_coordinate( + [&layout, &dist_map, &dist_map_func](const auto& c1, const unsigned src) + { + layout.foreach_coordinate( + [&layout, &dist_map, &dist_map_func, &c1, src](const auto& c2, const unsigned tgt) + { + CHECK(dist_map[src][tgt] == twoddwave_distance(layout, c1, c2)); + CHECK(dist_map[src][tgt] == dist_map_func(layout, c1, c2)); + }); + }); + } + SECTION("USE clocking") + { + const clk_lyt layout{aspect_ratio{4, 4}, use_clocking()}; + + const auto dist_map = initialize_distance_map(layout, a_star_distance_functor{}); + const auto dist_map_func = distance_map_functor{dist_map}; + + layout.foreach_coordinate( + [&layout, &dist_map, &dist_map_func](const auto& c1, const unsigned src) + { + layout.foreach_coordinate( + [&layout, &dist_map, &dist_map_func, &c1, src](const auto& c2, const unsigned tgt) + { + CHECK(dist_map[src][tgt] == a_star_distance(layout, c1, c2)); + CHECK(dist_map[src][tgt] == dist_map_func(layout, c1, c2)); + }); + }); + } + SECTION("RES clocking") + { + const clk_lyt layout{aspect_ratio{4, 4}, res_clocking()}; + + const auto dist_map = initialize_distance_map(layout, a_star_distance_functor{}); + const auto dist_map_func = distance_map_functor{dist_map}; + + layout.foreach_coordinate( + [&layout, &dist_map, &dist_map_func](const auto& c1, const unsigned src) + { + layout.foreach_coordinate( + [&layout, &dist_map, &dist_map_func, &c1, src](const auto& c2, const unsigned tgt) + { + CHECK(dist_map[src][tgt] == a_star_distance(layout, c1, c2)); + CHECK(dist_map[src][tgt] == dist_map_func(layout, c1, c2)); + }); + }); + } + SECTION("CFE clocking") + { + const clk_lyt layout{aspect_ratio{4, 4}, cfe_clocking()}; + + const auto dist_map = initialize_distance_map(layout, a_star_distance_functor{}); + const auto dist_map_func = distance_map_functor{dist_map}; + + layout.foreach_coordinate( + [&layout, &dist_map, &dist_map_func](const auto& c1, const unsigned src) + { + layout.foreach_coordinate( + [&layout, &dist_map, &dist_map_func, &c1, src](const auto& c2, const unsigned tgt) + { + CHECK(dist_map[src][tgt] == a_star_distance(layout, c1, c2)); + CHECK(dist_map[src][tgt] == dist_map_func(layout, c1, c2)); + }); + }); + } +} - // ignore z-axis - CHECK(manhattan_distance(layout, {0, 0, 1}, {8, 9, 0}) == 17); - CHECK(manhattan_distance(layout, {0, 0, 1}, {8, 9, 1}) == 17); +TEST_CASE("Sparse distance map", "[distance-map]") +{ + using clk_lyt = clocked_layout>; + + SECTION("2DDWave clocking") + { + const clk_lyt layout{aspect_ratio{4, 4}, twoddwave_clocking()}; + + const auto dist_map = initialize_sparse_distance_map(layout, a_star_distance_functor{}); + const auto dist_map_func = sparse_distance_map_functor{dist_map}; + + layout.foreach_coordinate( + [&layout, &dist_map, &dist_map_func](const auto& c1) + { + layout.foreach_coordinate( + [&layout, &dist_map, &dist_map_func, &c1](const auto& c2) + { + CHECK(dist_map.at({c1, c2}) == twoddwave_distance(layout, c1, c2)); + CHECK(dist_map.at({c1, c2}) == dist_map_func(layout, c1, c2)); + }); + }); } - SECTION("Signed Cartesian layout") + SECTION("USE clocking") { - using cart_lyt = cartesian_layout; + const clk_lyt layout{aspect_ratio{4, 4}, use_clocking()}; - const cart_lyt layout{}; + const auto dist_map = initialize_sparse_distance_map(layout, a_star_distance_functor{}); + const auto dist_map_func = sparse_distance_map_functor{dist_map}; - CHECK(manhattan_distance(layout, {0, 0}, {0, 0}) == 0); - CHECK(manhattan_distance(layout, {1, 1}, {1, 1}) == 0); - CHECK(manhattan_distance(layout, {0, 0}, {0, 1}) == 1); - CHECK(manhattan_distance(layout, {0, 0}, {1, 1}) == 2); - CHECK(manhattan_distance(layout, {1, 2}, {3, 3}) == 3); - CHECK(manhattan_distance(layout, {0, 0}, {4, 4}) == 8); - CHECK(manhattan_distance(layout, {4, 4}, {0, 0}) == 8); + layout.foreach_coordinate( + [&layout, &dist_map, &dist_map_func](const auto& c1) + { + layout.foreach_coordinate( + [&layout, &dist_map, &dist_map_func, &c1](const auto& c2) + { + CHECK(dist_map.at({c1, c2}) == a_star_distance(layout, c1, c2)); + CHECK(dist_map.at({c1, c2}) == dist_map_func(layout, c1, c2)); + }); + }); + } + SECTION("RES clocking") + { + const clk_lyt layout{aspect_ratio{4, 4}, res_clocking()}; + + const auto dist_map = initialize_sparse_distance_map(layout, a_star_distance_functor{}); + const auto dist_map_func = sparse_distance_map_functor{dist_map}; + + layout.foreach_coordinate( + [&layout, &dist_map, &dist_map_func](const auto& c1) + { + layout.foreach_coordinate( + [&layout, &dist_map, &dist_map_func, &c1](const auto& c2) + { + CHECK(dist_map.at({c1, c2}) == a_star_distance(layout, c1, c2)); + CHECK(dist_map.at({c1, c2}) == dist_map_func(layout, c1, c2)); + }); + }); + } + SECTION("CFE clocking") + { + const clk_lyt layout{aspect_ratio{4, 4}, cfe_clocking()}; - // ignore z-axis - CHECK(manhattan_distance(layout, {0, 0, 1}, {8, 9, 0}) == 17); - CHECK(manhattan_distance(layout, {0, 0, 1}, {8, 9, 1}) == 17); + const auto dist_map = initialize_sparse_distance_map(layout, a_star_distance_functor{}); + const auto dist_map_func = sparse_distance_map_functor{dist_map}; - // negative coordinates - CHECK(manhattan_distance(layout, {0, 0}, {-1, -1}) == 2); - CHECK(manhattan_distance(layout, {-4, -3}, {1, -1}) == 7); - CHECK(manhattan_distance(layout, {-2, -8}, {-6, -4}) == 8); + layout.foreach_coordinate( + [&layout, &dist_map, &dist_map_func](const auto& c1) + { + layout.foreach_coordinate( + [&layout, &dist_map, &dist_map_func, &c1](const auto& c2) + { + CHECK(dist_map.at({c1, c2}) == a_star_distance(layout, c1, c2)); + CHECK(dist_map.at({c1, c2}) == dist_map_func(layout, c1, c2)); + }); + }); } -} \ No newline at end of file +} From b2c518543588e2e7ef8a4585797cba81fd4322c0 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Tue, 11 Jul 2023 17:17:53 +0200 Subject: [PATCH 07/17] :memo: Added documentation on distance maps --- docs/algorithms/path_finding.rst | 20 ++++++++++++++++ .../algorithms/path_finding/distance_map.hpp | 24 ++++++++++++------- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/docs/algorithms/path_finding.rst b/docs/algorithms/path_finding.rst index 5b0d79ebe..a9f17d934 100644 --- a/docs/algorithms/path_finding.rst +++ b/docs/algorithms/path_finding.rst @@ -15,6 +15,26 @@ Distance functions compute (an approximation for) the distance between two coord .. doxygenclass:: fiction::euclidean_distance_functor .. doxygenclass:: fiction::twoddwave_distance_functor +Distance Maps +------------- + +Distance maps can store the distance from a coordinate to all other coordinates. They are particularly useful when +repeatedly calling complex distance functions that are expensive to evaluate. The distance maps can serve as a cache for +these cases. + +**Header:** ``fiction/algorithms/path_finding/distance_map.hpp`` + +.. doxygentypedef:: fiction::distance_map +.. doxygentypedef:: fiction::sparse_distance_map + +.. doxygenfunction:: fiction::initialize_distance_map +.. doxygenfunction:: fiction::initialize_sparse_distance_map + +.. doxygenclass:: fiction::distance_map_functor + :members: +.. doxygenclass:: fiction::sparse_distance_map_functor + :members: + Cost Functions -------------- diff --git a/include/fiction/algorithms/path_finding/distance_map.hpp b/include/fiction/algorithms/path_finding/distance_map.hpp index 979b212b4..ea60ce77e 100644 --- a/include/fiction/algorithms/path_finding/distance_map.hpp +++ b/include/fiction/algorithms/path_finding/distance_map.hpp @@ -28,13 +28,14 @@ template using distance_map = std::vector>; /** * A sparse distance map is a flat hash map of distances between coordinates. The sparse distance map is accessed via - * coordinate pairs. + * coordinate pairs. The sparse distance map is to be preferred over the distance map if only a small subset of the + * distances between coordinates is to be cached. */ template using sparse_distance_map = phmap::parallel_flat_hash_map, coordinate>, Dist>; /** - * This function initializes a `distance_map` for a given layout and distance functor. It computes the distances between - * all pairs of coordinates in the layout and stores them in the distance map for quick subsequent access. + * This function fully initializes a `distance_map` for a given layout and distance functor. It computes the distances + * between all pairs of coordinates in the layout and stores them in the distance map for quick subsequent access. * * This function performs \f$ \mathcal{O}(|L|^2) \f$ distance computations, where \f$ |L| \f$ is the number of * coordinates in the layout. @@ -65,8 +66,9 @@ template return dist_map; } /** - * This function initializes a `sparse_distance_map` for a given layout and distance functor. It computes the distances - * between all pairs of coordinates in the layout and stores them in the distance map for quick subsequent access. + * This function fully initializes a `sparse_distance_map` for a given layout and distance functor. It computes the + * distances between all pairs of coordinates in the layout and stores them in the distance map for quick subsequent + * access. * * This function performs \f$ \mathcal{O}(|L|^2) \f$ distance computations, where \f$ |L| \f$ is the number of * coordinates in the layout. @@ -101,7 +103,8 @@ initialize_sparse_distance_map(const Lyt& lyt, const distance_functor } /** - * A distance functor that uses a precomputed `distance_map` to determine distances between coordinates. + * A distance functor that uses a fully precomputed `distance_map` to determine distances between coordinates. It can be + * used as a drop-in replacement for any other distance functor in path-finding algorithms. * * @tparam Lyt Coordinate layout type. * @tparam Dist Distance type. @@ -123,6 +126,8 @@ class distance_map_functor : public distance_functor /** * Override the call operator to query the distance map instead of the distance function. * + * @note This function will cause a SEGFAULT if the queried distance is not stored in the distance map. + * * @param lyt Layout. * @param source Source coordinate. * @param target Target coordinate. @@ -155,7 +160,8 @@ class distance_map_functor : public distance_functor }; /** - * A distance functor that uses a precomputed `sparse_distance_map` to determine distances between coordinates. + * A distance functor that uses a fully precomputed `sparse_distance_map` to determine distances between coordinates. It + * can be used as a drop-in replacement for any other distance functor in path-finding algorithms. * * @tparam Lyt Coordinate layout type. * @tparam Dist Distance type. @@ -177,6 +183,8 @@ class sparse_distance_map_functor : public distance_functor /** * Override the call operator to query the sparse distance map instead of the distance function. * + * @note This function will cause a SEGFAULT if the queried distance is not stored in the sparse distance map. + * * @param lyt Layout. * @param source Source coordinate. * @param target Target coordinate. @@ -185,7 +193,7 @@ class sparse_distance_map_functor : public distance_functor [[nodiscard]] Dist operator()(const Lyt& lyt, const coordinate& source, const coordinate& target) const override { - return static_cast(sparse_dist_map.at({source, target})); + return static_cast(sparse_dist_map[{source, target}]); } protected: From 666e0fbad2c1a26d19c93308e368938b74b1f40c Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Tue, 11 Jul 2023 17:49:29 +0200 Subject: [PATCH 08/17] :sparkles: Added `smart_distance_cache_functor` that builds up a distance cache on-the-fly. Tests are still missing. --- .../algorithms/path_finding/distance_map.hpp | 82 ++++++++++++++++--- test/algorithms/path_finding/distance_map.cpp | 34 ++++---- 2 files changed, 90 insertions(+), 26 deletions(-) diff --git a/include/fiction/algorithms/path_finding/distance_map.hpp b/include/fiction/algorithms/path_finding/distance_map.hpp index ea60ce77e..c8c7d7620 100644 --- a/include/fiction/algorithms/path_finding/distance_map.hpp +++ b/include/fiction/algorithms/path_finding/distance_map.hpp @@ -109,12 +109,12 @@ initialize_sparse_distance_map(const Lyt& lyt, const distance_functor * @tparam Lyt Coordinate layout type. * @tparam Dist Distance type. */ -template +template class distance_map_functor : public distance_functor { public: /** - * Construct a distance functor from a `distance_map`. + * Construct the distance functor from a `distance_map`. * * @param dm Distance map. */ @@ -126,7 +126,7 @@ class distance_map_functor : public distance_functor /** * Override the call operator to query the distance map instead of the distance function. * - * @note This function will cause a SEGFAULT if the queried distance is not stored in the distance map. + * @note This function will throw an exception if the queried distance is not stored in the distance map. * * @param lyt Layout. * @param source Source coordinate. @@ -136,7 +136,7 @@ class distance_map_functor : public distance_functor [[nodiscard]] Dist operator()(const Lyt& lyt, const coordinate& source, const coordinate& target) const override { - return static_cast(dist_map[coordinate_index(lyt, source)][coordinate_index(lyt, target)]); + return static_cast(dist_map.at(coordinate_index(lyt, source)).at(coordinate_index(lyt, target))); } protected: @@ -158,7 +158,6 @@ class distance_map_functor : public distance_functor return c.y * (lyt.x() + 1) + c.x; } }; - /** * A distance functor that uses a fully precomputed `sparse_distance_map` to determine distances between coordinates. It * can be used as a drop-in replacement for any other distance functor in path-finding algorithms. @@ -166,12 +165,12 @@ class distance_map_functor : public distance_functor * @tparam Lyt Coordinate layout type. * @tparam Dist Distance type. */ -template +template class sparse_distance_map_functor : public distance_functor { public: /** - * Construct a distance functor from a distance map. + * Construct the distance functor from a sparse distance map. * * @param sdm Sparse distance map. */ @@ -183,17 +182,17 @@ class sparse_distance_map_functor : public distance_functor /** * Override the call operator to query the sparse distance map instead of the distance function. * - * @note This function will cause a SEGFAULT if the queried distance is not stored in the sparse distance map. + * @note This function will throw an exception if the queried distance is not stored in the sparse distance map. * * @param lyt Layout. * @param source Source coordinate. * @param target Target coordinate. * @return Distance between source and target according to the stored sparse distance map. */ - [[nodiscard]] Dist operator()(const Lyt& lyt, const coordinate& source, + [[nodiscard]] Dist operator()(const Lyt&, const coordinate& source, const coordinate& target) const override { - return static_cast(sparse_dist_map[{source, target}]); + return static_cast(sparse_dist_map.at({source, target})); } protected: @@ -203,6 +202,69 @@ class sparse_distance_map_functor : public distance_functor const sparse_distance_map sparse_dist_map; }; +/** + * A distance functor that internally uses a `sparse_distance_map` as a cache to prevent re-computing distances that + * have already been evaluated. In contrast to `distance_map_functor` and `sparse_distance_map_functor`, this functor + * does not require a pre-computed distance map upon construction, but instead will gradually build up its own cache + * when queried multiple times. It can be used as a drop-in replacement for any other distance functor in path-finding + * algorithms. + * + * @tparam Lyt Coordinate layout type. + * @tparam Dist Distance type. + */ +template +class smart_distance_cache_functor : public distance_functor +{ + public: + /** + * Construct a distance functor from a layout and a distance function. + * + * The internal cache will be initialized empty. Distances will be computed on the fly and stored in the cache + * whenever they are queried. + * + * @param lyt Layout. + * @param dist_fn Distance function. + */ + explicit smart_distance_cache_functor( + const Lyt& lyt, + const std::function&, const coordinate&)>& dist_fn) : + distance_functor(dist_fn) + { + distance_cache.reserve(lyt.area() * lyt.area()); + } + /** + * Override the call operator to first query the cache instead of the distance function. Only on a cache miss, + * the distance function will be called and the result will be stored in the cache. + * + * @param lyt Layout. + * @param source Source coordinate. + * @param target Target coordinate. + * @return Distance between source and target according to the cache or the distance function. + */ + [[nodiscard]] Dist operator()(const Lyt& lyt, const coordinate& source, + const coordinate& target) const override + { + if (const auto it = distance_cache.find({source, target}); it != distance_cache.cend()) // cache hit + { + return it->second; + } + + // cache miss + + const auto d = distance_functor(lyt, source, target); + + distance_cache[{source, target}] = d; + + return d; + } + + protected: + /** + * Sparse distance map serving as a cache. + */ + sparse_distance_map distance_cache{}; +}; + } // namespace fiction #endif // FICTION_DISTANCE_MAP_HPP diff --git a/test/algorithms/path_finding/distance_map.cpp b/test/algorithms/path_finding/distance_map.cpp index 9f791ad46..467e0408f 100644 --- a/test/algorithms/path_finding/distance_map.cpp +++ b/test/algorithms/path_finding/distance_map.cpp @@ -17,13 +17,14 @@ using namespace fiction; TEST_CASE("Distance map", "[distance-map]") { using clk_lyt = clocked_layout>; + using dist = uint64_t; SECTION("2DDWave clocking") { const clk_lyt layout{aspect_ratio{4, 4}, twoddwave_clocking()}; - const auto dist_map = initialize_distance_map(layout, a_star_distance_functor{}); - const auto dist_map_func = distance_map_functor{dist_map}; + const auto dist_map = initialize_distance_map(layout, a_star_distance_functor{}); + const auto dist_map_func = distance_map_functor{dist_map}; layout.foreach_coordinate( [&layout, &dist_map, &dist_map_func](const auto& c1, const unsigned src) @@ -40,8 +41,8 @@ TEST_CASE("Distance map", "[distance-map]") { const clk_lyt layout{aspect_ratio{4, 4}, use_clocking()}; - const auto dist_map = initialize_distance_map(layout, a_star_distance_functor{}); - const auto dist_map_func = distance_map_functor{dist_map}; + const auto dist_map = initialize_distance_map(layout, a_star_distance_functor{}); + const auto dist_map_func = distance_map_functor{dist_map}; layout.foreach_coordinate( [&layout, &dist_map, &dist_map_func](const auto& c1, const unsigned src) @@ -58,8 +59,8 @@ TEST_CASE("Distance map", "[distance-map]") { const clk_lyt layout{aspect_ratio{4, 4}, res_clocking()}; - const auto dist_map = initialize_distance_map(layout, a_star_distance_functor{}); - const auto dist_map_func = distance_map_functor{dist_map}; + const auto dist_map = initialize_distance_map(layout, a_star_distance_functor{}); + const auto dist_map_func = distance_map_functor{dist_map}; layout.foreach_coordinate( [&layout, &dist_map, &dist_map_func](const auto& c1, const unsigned src) @@ -76,8 +77,8 @@ TEST_CASE("Distance map", "[distance-map]") { const clk_lyt layout{aspect_ratio{4, 4}, cfe_clocking()}; - const auto dist_map = initialize_distance_map(layout, a_star_distance_functor{}); - const auto dist_map_func = distance_map_functor{dist_map}; + const auto dist_map = initialize_distance_map(layout, a_star_distance_functor{}); + const auto dist_map_func = distance_map_functor{dist_map}; layout.foreach_coordinate( [&layout, &dist_map, &dist_map_func](const auto& c1, const unsigned src) @@ -95,13 +96,14 @@ TEST_CASE("Distance map", "[distance-map]") TEST_CASE("Sparse distance map", "[distance-map]") { using clk_lyt = clocked_layout>; + using dist = uint64_t; SECTION("2DDWave clocking") { const clk_lyt layout{aspect_ratio{4, 4}, twoddwave_clocking()}; - const auto dist_map = initialize_sparse_distance_map(layout, a_star_distance_functor{}); - const auto dist_map_func = sparse_distance_map_functor{dist_map}; + const auto dist_map = initialize_sparse_distance_map(layout, a_star_distance_functor{}); + const auto dist_map_func = sparse_distance_map_functor{dist_map}; layout.foreach_coordinate( [&layout, &dist_map, &dist_map_func](const auto& c1) @@ -118,8 +120,8 @@ TEST_CASE("Sparse distance map", "[distance-map]") { const clk_lyt layout{aspect_ratio{4, 4}, use_clocking()}; - const auto dist_map = initialize_sparse_distance_map(layout, a_star_distance_functor{}); - const auto dist_map_func = sparse_distance_map_functor{dist_map}; + const auto dist_map = initialize_sparse_distance_map(layout, a_star_distance_functor{}); + const auto dist_map_func = sparse_distance_map_functor{dist_map}; layout.foreach_coordinate( [&layout, &dist_map, &dist_map_func](const auto& c1) @@ -136,8 +138,8 @@ TEST_CASE("Sparse distance map", "[distance-map]") { const clk_lyt layout{aspect_ratio{4, 4}, res_clocking()}; - const auto dist_map = initialize_sparse_distance_map(layout, a_star_distance_functor{}); - const auto dist_map_func = sparse_distance_map_functor{dist_map}; + const auto dist_map = initialize_sparse_distance_map(layout, a_star_distance_functor{}); + const auto dist_map_func = sparse_distance_map_functor{dist_map}; layout.foreach_coordinate( [&layout, &dist_map, &dist_map_func](const auto& c1) @@ -154,8 +156,8 @@ TEST_CASE("Sparse distance map", "[distance-map]") { const clk_lyt layout{aspect_ratio{4, 4}, cfe_clocking()}; - const auto dist_map = initialize_sparse_distance_map(layout, a_star_distance_functor{}); - const auto dist_map_func = sparse_distance_map_functor{dist_map}; + const auto dist_map = initialize_sparse_distance_map(layout, a_star_distance_functor{}); + const auto dist_map_func = sparse_distance_map_functor{dist_map}; layout.foreach_coordinate( [&layout, &dist_map, &dist_map_func](const auto& c1) From 1ea95644350b63d0e0ce92c90d53ad7732de2893 Mon Sep 17 00:00:00 2001 From: ClangFormat Date: Tue, 11 Jul 2023 15:56:56 +0000 Subject: [PATCH 09/17] :art: ClangFormat changes Signed-off-by: ClangFormat --- test/algorithms/path_finding/distance_map.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/algorithms/path_finding/distance_map.cpp b/test/algorithms/path_finding/distance_map.cpp index 467e0408f..275db19b6 100644 --- a/test/algorithms/path_finding/distance_map.cpp +++ b/test/algorithms/path_finding/distance_map.cpp @@ -17,7 +17,7 @@ using namespace fiction; TEST_CASE("Distance map", "[distance-map]") { using clk_lyt = clocked_layout>; - using dist = uint64_t; + using dist = uint64_t; SECTION("2DDWave clocking") { @@ -96,7 +96,7 @@ TEST_CASE("Distance map", "[distance-map]") TEST_CASE("Sparse distance map", "[distance-map]") { using clk_lyt = clocked_layout>; - using dist = uint64_t; + using dist = uint64_t; SECTION("2DDWave clocking") { From b1ab9d0f27e54ef5895b379c22bbc7361f721b24 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 12 Jul 2023 11:17:56 +0200 Subject: [PATCH 10/17] :white_check_mark: Passed failing test case --- test/algorithms/path_finding/distance.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/algorithms/path_finding/distance.cpp b/test/algorithms/path_finding/distance.cpp index 2fe6884a2..8568e3099 100644 --- a/test/algorithms/path_finding/distance.cpp +++ b/test/algorithms/path_finding/distance.cpp @@ -307,8 +307,9 @@ TEST_CASE("2DDWave distance functor", "[distance]") // negative coordinates CHECK(distance(layout, {0, 0}, {-1, -1}) == std::numeric_limits::max()); + CHECK(distance(layout, {-2, -8}, {-6, -4}) == std::numeric_limits::max()); + CHECK(distance(layout, {-6, -4}, {-2, -8}) == std::numeric_limits::max()); CHECK(distance(layout, {-4, -3}, {1, -1}) == 7); - CHECK(distance(layout, {-2, -8}, {-6, -4}) == 8); } } From bf984a3b388fdde0c59574693c097d96fe53413f Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 12 Jul 2023 12:16:10 +0200 Subject: [PATCH 11/17] :alembic: Added a benchmarking framework to measure code efficiency --- test/CMakeLists.txt | 8 +++++ test/benchmark/CMakeLists.txt | 12 +++++++ test/benchmark/distance_map.cpp | 58 +++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 test/benchmark/CMakeLists.txt create mode 100644 test/benchmark/distance_map.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 40db17758..4a17de344 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,6 +1,14 @@ +# Benchmarking (depends on Catch2) +option(FICTION_BENCHMARK "Build fiction benchmarks, which can evaluate the performance of certain code fragments" OFF) +if (FICTION_BENCHMARK) + message(STATUS "Building fiction benchmarks") + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/benchmark) +endif () + include_directories(.) file(GLOB_RECURSE FILENAMES */*.cpp) +list(FILTER FILENAMES EXCLUDE REGEX "benchmark/.*$") foreach (FILE IN LISTS FILENAMES) get_filename_component(NAME ${FILE} NAME_WE) diff --git a/test/benchmark/CMakeLists.txt b/test/benchmark/CMakeLists.txt new file mode 100644 index 000000000..36c9d4864 --- /dev/null +++ b/test/benchmark/CMakeLists.txt @@ -0,0 +1,12 @@ +file(GLOB_RECURSE FILENAMES *.cpp) + +foreach (FILE IN LISTS FILENAMES) + get_filename_component(NAME ${FILE} NAME_WE) + set(BENCH_NAME bench_${NAME}) + add_executable(${BENCH_NAME} ${FILE}) + target_compile_definitions(${BENCH_NAME} INTERFACE CATCH_CONFIG_NO_POSIX_SIGNALS) # make catch2 ignore SIGTERMs sent to applications when timeouts are reached + target_link_libraries(${BENCH_NAME} PRIVATE fiction_project_warnings fiction_project_options libfiction Catch2::Catch2WithMain) + + add_test(NAME ${NAME} COMMAND ${BENCH_NAME}) # group tests by file + # catch_discover_tests(${BENCH_NAME}) +endforeach () diff --git a/test/benchmark/distance_map.cpp b/test/benchmark/distance_map.cpp new file mode 100644 index 000000000..6b0fac94d --- /dev/null +++ b/test/benchmark/distance_map.cpp @@ -0,0 +1,58 @@ +// +// Created by marcel on 12.07.23. +// + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace fiction; + +template +Dist sum_distances(const Lyt& layout, const distance_functor& dist_func) noexcept +{ + Dist sum = 0; + layout.foreach_coordinate( + [&layout, &dist_func, &sum](const auto& c1) { + layout.foreach_coordinate([&layout, &dist_func, &sum, &c1](const auto& c2) + { sum += dist_func(layout, c1, c2); }); + }); + + return sum; +} + +TEST_CASE("Benchmark the benefit of distance maps", "[benchmark]") +{ + using clk_lyt = clocked_layout>; + using dist = uint64_t; + + const clk_lyt layout{aspect_ratio{5, 5}, use_clocking()}; + + BENCHMARK("without distance maps") + { + return sum_distances(layout, a_star_distance_functor{}); + }; + + const auto dist_map = initialize_distance_map(layout, a_star_distance_functor{}); + const auto dist_map_func = distance_map_functor{dist_map}; + + BENCHMARK("distance_map") + { + return sum_distances(layout, dist_map_func); + }; + + const auto sparse_dist_map = initialize_sparse_distance_map(layout, a_star_distance_functor{}); + const auto sparse_dist_map_func = sparse_distance_map_functor{sparse_dist_map}; + + BENCHMARK("sparse_distance_map") + { + return sum_distances(layout, sparse_dist_map_func); + }; +} From ead02eef8f691249627f9e257fd12a6d5e426ad8 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 12 Jul 2023 12:58:57 +0200 Subject: [PATCH 12/17] :alembic: Added benchmarking for the `smart_distance_cache_functor` --- .../algorithms/path_finding/distance_map.hpp | 4 +-- test/benchmark/distance_map.cpp | 26 ++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/include/fiction/algorithms/path_finding/distance_map.hpp b/include/fiction/algorithms/path_finding/distance_map.hpp index c8c7d7620..ccaa26f05 100644 --- a/include/fiction/algorithms/path_finding/distance_map.hpp +++ b/include/fiction/algorithms/path_finding/distance_map.hpp @@ -251,7 +251,7 @@ class smart_distance_cache_functor : public distance_functor // cache miss - const auto d = distance_functor(lyt, source, target); + const auto d = distance_functor::operator()(lyt, source, target); distance_cache[{source, target}] = d; @@ -262,7 +262,7 @@ class smart_distance_cache_functor : public distance_functor /** * Sparse distance map serving as a cache. */ - sparse_distance_map distance_cache{}; + mutable sparse_distance_map distance_cache{}; }; } // namespace fiction diff --git a/test/benchmark/distance_map.cpp b/test/benchmark/distance_map.cpp index 6b0fac94d..ff436e876 100644 --- a/test/benchmark/distance_map.cpp +++ b/test/benchmark/distance_map.cpp @@ -28,7 +28,7 @@ Dist sum_distances(const Lyt& layout, const distance_functor& dist_fu return sum; } -TEST_CASE("Benchmark the benefit of distance maps", "[benchmark]") +TEST_CASE("Benchmark distance maps", "[benchmark]") { using clk_lyt = clocked_layout>; using dist = uint64_t; @@ -56,3 +56,27 @@ TEST_CASE("Benchmark the benefit of distance maps", "[benchmark]") return sum_distances(layout, sparse_dist_map_func); }; } + +TEST_CASE("Benchmark smart distance cache", "[benchmark]") +{ + using clk_lyt = clocked_layout>; + using dist = uint64_t; + + const clk_lyt layout{aspect_ratio{5, 5}, use_clocking()}; + + BENCHMARK("smart_distance_cache (cold start)") + { + const auto dist_map_func = smart_distance_cache_functor{layout, &a_star_distance}; + + return sum_distances(layout, dist_map_func); + }; + + // warm up the cache + const auto dist_map_func = smart_distance_cache_functor{layout, &a_star_distance}; + sum_distances(layout, dist_map_func); + + BENCHMARK("smart_distance_cache (warm start)") + { + return sum_distances(layout, dist_map_func); + }; +} From 7ce10a8a9c9b4316c1adabaa08a41c06c8f38842 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Wed, 12 Jul 2023 13:49:37 +0200 Subject: [PATCH 13/17] :white_check_mark: Added test cases for the `smart_distance_cache_functor` --- test/algorithms/path_finding/distance_map.cpp | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/test/algorithms/path_finding/distance_map.cpp b/test/algorithms/path_finding/distance_map.cpp index 275db19b6..4847e67f3 100644 --- a/test/algorithms/path_finding/distance_map.cpp +++ b/test/algorithms/path_finding/distance_map.cpp @@ -171,3 +171,63 @@ TEST_CASE("Sparse distance map", "[distance-map]") }); } } + +TEST_CASE("Smart distance map functor", "[distance-map]") +{ + using clk_lyt = clocked_layout>; + using dist = uint64_t; + + SECTION("2DDWave clocking") + { + const clk_lyt layout{aspect_ratio{4, 4}, twoddwave_clocking()}; + + const auto dist_map_func = smart_distance_cache_functor{layout, &a_star_distance}; + + layout.foreach_coordinate( + [&layout, &dist_map_func](const auto& c1) + { + layout.foreach_coordinate( + [&layout, &dist_map_func, &c1](const auto& c2) + { CHECK(dist_map_func(layout, c1, c2) == twoddwave_distance(layout, c1, c2)); }); + }); + } + SECTION("USE clocking") + { + const clk_lyt layout{aspect_ratio{4, 4}, use_clocking()}; + + const auto dist_map_func = smart_distance_cache_functor{layout, &a_star_distance}; + + layout.foreach_coordinate( + [&layout, &dist_map_func](const auto& c1) + { + layout.foreach_coordinate([&layout, &dist_map_func, &c1](const auto& c2) + { CHECK(dist_map_func(layout, c1, c2) == a_star_distance(layout, c1, c2)); }); + }); + } + SECTION("RES clocking") + { + const clk_lyt layout{aspect_ratio{4, 4}, res_clocking()}; + + const auto dist_map_func = smart_distance_cache_functor{layout, &a_star_distance}; + + layout.foreach_coordinate( + [&layout, &dist_map_func](const auto& c1) + { + layout.foreach_coordinate([&layout, &dist_map_func, &c1](const auto& c2) + { CHECK(dist_map_func(layout, c1, c2) == a_star_distance(layout, c1, c2)); }); + }); + } + SECTION("CFE clocking") + { + const clk_lyt layout{aspect_ratio{4, 4}, cfe_clocking()}; + + const auto dist_map_func = smart_distance_cache_functor{layout, &a_star_distance}; + + layout.foreach_coordinate( + [&layout, &dist_map_func](const auto& c1) + { + layout.foreach_coordinate([&layout, &dist_map_func, &c1](const auto& c2) + { CHECK(dist_map_func(layout, c1, c2) == a_star_distance(layout, c1, c2)); }); + }); + } +} From 272c5b8a84ad1aa37a054df8d77ffee918be6196 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Sat, 15 Jul 2023 17:26:36 +0200 Subject: [PATCH 14/17] :white_check_mark: Added tests to ensure caching of `smart_distance_cache_functor` returns correct results --- test/algorithms/path_finding/distance_map.cpp | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/test/algorithms/path_finding/distance_map.cpp b/test/algorithms/path_finding/distance_map.cpp index 4847e67f3..8be5a483e 100644 --- a/test/algorithms/path_finding/distance_map.cpp +++ b/test/algorithms/path_finding/distance_map.cpp @@ -172,7 +172,7 @@ TEST_CASE("Sparse distance map", "[distance-map]") } } -TEST_CASE("Smart distance map functor", "[distance-map]") +TEST_CASE("Smart distance cache functor", "[distance-map]") { using clk_lyt = clocked_layout>; using dist = uint64_t; @@ -190,6 +190,15 @@ TEST_CASE("Smart distance map functor", "[distance-map]") [&layout, &dist_map_func, &c1](const auto& c2) { CHECK(dist_map_func(layout, c1, c2) == twoddwave_distance(layout, c1, c2)); }); }); + + // check that the cached distances are correct + layout.foreach_coordinate( + [&layout, &dist_map_func](const auto& c1) + { + layout.foreach_coordinate( + [&layout, &dist_map_func, &c1](const auto& c2) + { CHECK(dist_map_func(layout, c1, c2) == twoddwave_distance(layout, c1, c2)); }); + }); } SECTION("USE clocking") { @@ -203,6 +212,14 @@ TEST_CASE("Smart distance map functor", "[distance-map]") layout.foreach_coordinate([&layout, &dist_map_func, &c1](const auto& c2) { CHECK(dist_map_func(layout, c1, c2) == a_star_distance(layout, c1, c2)); }); }); + + // check that the cached distances are correct + layout.foreach_coordinate( + [&layout, &dist_map_func](const auto& c1) + { + layout.foreach_coordinate([&layout, &dist_map_func, &c1](const auto& c2) + { CHECK(dist_map_func(layout, c1, c2) == a_star_distance(layout, c1, c2)); }); + }); } SECTION("RES clocking") { @@ -216,6 +233,14 @@ TEST_CASE("Smart distance map functor", "[distance-map]") layout.foreach_coordinate([&layout, &dist_map_func, &c1](const auto& c2) { CHECK(dist_map_func(layout, c1, c2) == a_star_distance(layout, c1, c2)); }); }); + + // check that the cached distances are correct + layout.foreach_coordinate( + [&layout, &dist_map_func](const auto& c1) + { + layout.foreach_coordinate([&layout, &dist_map_func, &c1](const auto& c2) + { CHECK(dist_map_func(layout, c1, c2) == a_star_distance(layout, c1, c2)); }); + }); } SECTION("CFE clocking") { @@ -229,5 +254,13 @@ TEST_CASE("Smart distance map functor", "[distance-map]") layout.foreach_coordinate([&layout, &dist_map_func, &c1](const auto& c2) { CHECK(dist_map_func(layout, c1, c2) == a_star_distance(layout, c1, c2)); }); }); + + // check that the cached distances are correct + layout.foreach_coordinate( + [&layout, &dist_map_func](const auto& c1) + { + layout.foreach_coordinate([&layout, &dist_map_func, &c1](const auto& c2) + { CHECK(dist_map_func(layout, c1, c2) == a_star_distance(layout, c1, c2)); }); + }); } } From b2fcd63e9a32e161aafc3e10d39a807fd82780c8 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Sat, 15 Jul 2023 17:31:03 +0200 Subject: [PATCH 15/17] :memo: Added documentation for `smart_distance_cache_functor` --- docs/algorithms/path_finding.rst | 6 ++++-- include/fiction/algorithms/path_finding/distance_map.hpp | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/algorithms/path_finding.rst b/docs/algorithms/path_finding.rst index a9f17d934..fcc67599c 100644 --- a/docs/algorithms/path_finding.rst +++ b/docs/algorithms/path_finding.rst @@ -19,8 +19,8 @@ Distance Maps ------------- Distance maps can store the distance from a coordinate to all other coordinates. They are particularly useful when -repeatedly calling complex distance functions that are expensive to evaluate. The distance maps can serve as a cache for -these cases. +repeatedly calling complex distance functions that are expensive to evaluate. The distance maps can serve as a +lookup-table for these cases. **Header:** ``fiction/algorithms/path_finding/distance_map.hpp`` @@ -34,6 +34,8 @@ these cases. :members: .. doxygenclass:: fiction::sparse_distance_map_functor :members: +.. doxygenclass:: fiction::smart_distance_cache_functor + :members: Cost Functions -------------- diff --git a/include/fiction/algorithms/path_finding/distance_map.hpp b/include/fiction/algorithms/path_finding/distance_map.hpp index ccaa26f05..f7f02083e 100644 --- a/include/fiction/algorithms/path_finding/distance_map.hpp +++ b/include/fiction/algorithms/path_finding/distance_map.hpp @@ -23,6 +23,8 @@ namespace fiction * indices of the coordinates in the associated layout. A coordinate index \f$ i \f$ is calculated as follows: * \f$ i = y \cdot W + x \f$, with \f$ W \f$ being the width of the layout. As an example, in a layout of size * \f$ 3 \times 3 \f$, the coordinate index of the coordinate \f$ (2, 1) \f$ has index \f$ 1 \cdot 3 + 2 = 5 \f$. + * + * The `distance_map` is to be preferred over the `sparse_distance_map` in most cases when performance is the main goal. */ template using distance_map = std::vector>; @@ -30,6 +32,8 @@ using distance_map = std::vector>; * A sparse distance map is a flat hash map of distances between coordinates. The sparse distance map is accessed via * coordinate pairs. The sparse distance map is to be preferred over the distance map if only a small subset of the * distances between coordinates is to be cached. + * + * The `distance_map` is to be preferred over the `sparse_distance_map` in most cases when performance is the main goal. */ template using sparse_distance_map = phmap::parallel_flat_hash_map, coordinate>, Dist>; From e92730f0f7fdde49c866ab543ce493fa81c7a319 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Tue, 18 Jul 2023 17:07:18 +0200 Subject: [PATCH 16/17] :memo: Added documentation on the introduced code benchmarking feature --- docs/getting_started.rst | 12 ++++++++++++ .../fiction/algorithms/path_finding/distance_map.hpp | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/getting_started.rst b/docs/getting_started.rst index c6685661a..7a47e0848 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -153,6 +153,18 @@ linked against *fiction* and compiled as a stand-alone binary using the followin cmake --build . -j4 +Building code benchmarks +------------------------ + +Using ``Catch2``'s micro-benchmarking feature, you can compile and run code tests that evaluate the performance of +certain code constructs. The ``test/benchmark`` folder provides a selection of benchmarks we were running to evaluate +the performance of our code during development. Any ``*.cpp`` file that is placed in that folder is automatically +linked against *fiction* and compiled as a stand-alone binary using the following commands:: + + cmake . -B build -DFICTION_BENCHMARK=ON + cd build + cmake --build . -j4 + Uninstall --------- diff --git a/include/fiction/algorithms/path_finding/distance_map.hpp b/include/fiction/algorithms/path_finding/distance_map.hpp index f7f02083e..d2aac4ea1 100644 --- a/include/fiction/algorithms/path_finding/distance_map.hpp +++ b/include/fiction/algorithms/path_finding/distance_map.hpp @@ -31,7 +31,7 @@ using distance_map = std::vector>; /** * A sparse distance map is a flat hash map of distances between coordinates. The sparse distance map is accessed via * coordinate pairs. The sparse distance map is to be preferred over the distance map if only a small subset of the - * distances between coordinates is to be cached. + * distances between coordinates is to be stored. * * The `distance_map` is to be preferred over the `sparse_distance_map` in most cases when performance is the main goal. */ From 501e600e96799281cfb3613588ccd0cf49872c27 Mon Sep 17 00:00:00 2001 From: Marcel Walter Date: Tue, 18 Jul 2023 17:07:44 +0200 Subject: [PATCH 17/17] :construction_worker: Make sure the benchmarks compile on every CI system --- .github/workflows/macos.yml | 1 + .github/workflows/ubuntu.yml | 1 + .github/workflows/windows.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 523688f40..19ed60dab 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -99,6 +99,7 @@ jobs: -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DFICTION_CLI=ON -DFICTION_TEST=ON + -DFICTION_BENCHMARK=ON -DFICTION_EXPERIMENTS=ON -DFICTION_Z3=ON -DFICTION_PROGRESS_BARS=OFF diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 98c2a401f..9f462e99c 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -107,6 +107,7 @@ jobs: -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DFICTION_CLI=ON -DFICTION_TEST=ON + -DFICTION_BENCHMARK=ON -DFICTION_EXPERIMENTS=ON -DFICTION_Z3=ON -DFICTION_ENABLE_MUGEN=ON diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 3b457ace2..00054c4fd 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -83,6 +83,7 @@ jobs: -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DFICTION_CLI=ON -DFICTION_TEST=ON + -DFICTION_BENCHMARK=ON -DFICTION_EXPERIMENTS=ON -DFICTION_Z3=ON -DMOCKTURTLE_EXAMPLES=OFF