Skip to content

Commit

Permalink
✨ Added conversion functions for layouts of different coordinate types (
Browse files Browse the repository at this point in the history
#125)

* ✨ functions to convert layouts to a new layout based on a new coordinate type (fiction to SiQAD and vice versa)

* 🎨 put functions in the different header, added missing attributes, updated tests

* 📝 docu updated

* 🎨 new function to shift layout to positive coordinates

* 🎨 updated Marcel's suggestions

* 🎨 reformat code

* Delete sidb_surface_analysis.hpp

* 🎨 reformat code

* 🎨 updated Marcel's suggestions

* 📝 Adjusted docstrings

* 🎨 Renamed functions

* 🎨 Fixed code duplication

* 🎨 Restructured tests

* 🧪 Added failing tests

* 🎨 aspect-ratio added for the copy process

---------

Co-authored-by: Drewniok <[email protected]>
Co-authored-by: Marcel Walter <[email protected]>
  • Loading branch information
3 people committed Mar 2, 2023
1 parent da61221 commit c77a7c9
Show file tree
Hide file tree
Showing 4 changed files with 314 additions and 0 deletions.
4 changes: 4 additions & 0 deletions docs/utils/utils.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ Layout Utils
.. doxygenfunction:: fiction::num_adjacent_coordinates
.. doxygenfunction:: fiction::relative_to_absolute_cell_position
.. doxygenfunction:: fiction::port_direction_to_coordinate
.. doxygenfunction:: fiction::normalize_layout_coordinates
.. doxygenfunction:: fiction::convert_to_siqad_coordinates
.. doxygenfunction:: fiction::convert_to_fiction_coordinates



Placement Utils
Expand Down
4 changes: 4 additions & 0 deletions include/fiction/traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,10 @@ inline constexpr const bool has_inml_technology_v = std::is_same_v<technology<Ly
template <typename Lyt>
inline constexpr const bool has_sidb_technology_v = std::is_same_v<technology<Lyt>, sidb_technology>;
template <typename Lyt>
inline constexpr const bool has_offset_ucoord_v = std::is_same_v<coordinate<Lyt>, offset::ucoord_t>;
template <typename Lyt>
inline constexpr const bool has_cube_coord_v = std::is_same_v<coordinate<Lyt>, cube::coord_t>;
template <typename Lyt>
inline constexpr const bool has_siqad_coord_v = std::is_same_v<coordinate<Lyt>, siqad::coord_t>;

#pragma region is_cell_level_layout
Expand Down
126 changes: 126 additions & 0 deletions include/fiction/utils/layout_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@
#ifndef FICTION_LAYOUT_UTILS_HPP
#define FICTION_LAYOUT_UTILS_HPP

#include "fiction/layouts/cell_level_layout.hpp"
#include "fiction/layouts/coordinates.hpp"
#include "fiction/technology/cell_ports.hpp"
#include "fiction/traits.hpp"
#include "fiction/types.hpp"

#include <cassert>
#include <cstdint>
#include <limits>

namespace fiction
{
Expand All @@ -30,6 +34,7 @@ template <typename Lyt>

return static_cast<uint8_t>(lyt.adjacent_coordinates(c).size());
}

/**
* Converts a relative cell position within a tile to an absolute cell position within a layout. To compute the absolute
* position, the layout topology is taken into account.
Expand Down Expand Up @@ -163,6 +168,7 @@ template <uint16_t GateSizeX, uint16_t GateSizeY, typename GateLyt, typename Cel

return absolute_c;
}

/**
* Port directions address coordinates relative to each other by specifying cardinal directions. This function converts
* such a relative direction to an absolute coordinate when given a layout and a coordinate therein to consider. That
Expand Down Expand Up @@ -224,6 +230,126 @@ template <typename Lyt>
return {};
}

/**
* A new layout is constructed and returned that is equivalent to the given cell-level layout. However, its coordinates
* are normalized, i.e., start at `(0, 0)` and are all positive. To this end, all existing coordinates are shifted by an
* x and y offset.
*
* @tparam Lyt Cell-level layout type.
* @param lyt The layout which is to be normalized.
* @return New normalized equivalent layout.
*/
template <typename Lyt>
Lyt normalize_layout_coordinates(const Lyt& lyt) noexcept
{
static_assert(is_cartesian_layout_v<Lyt>, "Lyt is not a Cartesian layout");
static_assert(is_cell_level_layout_v<Lyt>, "Lyt is not a cell-level layout");

auto x_offset = std::numeric_limits<int32_t>::max();
auto y_offset = std::numeric_limits<int32_t>::max();

lyt.foreach_cell(
[&x_offset, &y_offset](const auto& c)
{
if (c.y <= y_offset)
{
y_offset = c.y;
}
if (c.x <= x_offset)
{
x_offset = c.x;
}
});

Lyt lyt_new{{lyt.x() - x_offset, lyt.y() - y_offset, lyt.z()},
lyt.get_layout_name(),
lyt.get_tile_size_x(),
lyt.get_tile_size_y()};

lyt.foreach_cell(
[&lyt_new, &lyt, &x_offset, &y_offset](const auto& c)
{
lyt_new.assign_cell_type({c.x - x_offset, c.y - y_offset}, lyt.get_cell_type(c));
lyt_new.assign_cell_mode({c.x - x_offset, c.y - y_offset}, lyt.get_cell_mode(c));
lyt_new.assign_cell_name({c.x - x_offset, c.y - y_offset}, lyt.get_cell_name(c));
});

return lyt_new;
}

/**
* Converts the coordinates of a given cell-level layout to SiQAD coordinates. A new equivalent layout based on SiQAD
* coordinates is returned.
*
* @tparam Lyt Cell-level layout type based on fiction coordinates, e.g., `offset::ucoord_t` or `cube::coord_t`.
* @param lyt The layout that is to be converted to a new layout based on SiQAD coordinates.
* @return A new equivalent layout based on SiQAD coordinates.
*/
template <typename Lyt>
sidb_cell_clk_lyt_siqad convert_to_siqad_coordinates(const Lyt& lyt) noexcept
{
static_assert(is_cartesian_layout_v<Lyt>, "Lyt is not a Cartesian layout");
static_assert(is_cell_level_layout_v<Lyt>, "Lyt is not a cell-level layout");
static_assert(has_sidb_technology_v<Lyt>, "Lyt is not an SiDB layout");

sidb_cell_clk_lyt_siqad lyt_new{{lyt.x(), lyt.y(), lyt.z()},
lyt.get_layout_name(),
lyt.get_tile_size_x(),
lyt.get_tile_size_y()};

lyt.foreach_cell(
[&lyt_new, &lyt](const auto& c)
{
lyt_new.assign_cell_type(siqad::to_siqad_coord<cell<Lyt>>(c), lyt.get_cell_type(c));
lyt_new.assign_cell_mode(siqad::to_siqad_coord<cell<Lyt>>(c), lyt.get_cell_mode(c));
lyt_new.assign_cell_name(siqad::to_siqad_coord<cell<Lyt>>(c), lyt.get_cell_name(c));
});

return lyt_new;
}

/**
* Converts the coordinates of a given cell-level layout to fiction coordinates, e.g., `offset::ucoord_t` or
* `cube::coord_t`. A new equivalent layout based on fiction coordinates is returned.
*
* @tparam Lyt Cell-level layout type based on fiction coordinates.
* @param lyt The layout that is to be converted to a new layout based on fiction coordinates.
* @return A new equivalent layout based on fiction coordinates.
*/
template <typename Lyt>
Lyt convert_to_fiction_coordinates(const sidb_cell_clk_lyt_siqad& lyt) noexcept
{
static_assert(is_cartesian_layout_v<Lyt>, "Lyt is not a Cartesian layout");
static_assert(is_cell_level_layout_v<Lyt>, "Lyt is not a cell-level layout");
static_assert(has_sidb_technology_v<Lyt>, "Lyt is not an SiDB layout");

Lyt lyt_new{{lyt.x(), lyt.y(), lyt.z()}, lyt.get_layout_name(), lyt.get_tile_size_x(), lyt.get_tile_size_y()};

const auto assign_coordinates = [&lyt_new](const auto& base_lyt) noexcept
{
base_lyt.foreach_cell(
[&lyt_new, &base_lyt](const auto& c)
{
lyt_new.assign_cell_type(siqad::to_fiction_coord<cell<Lyt>>(c), base_lyt.get_cell_type(c));
lyt_new.assign_cell_mode(siqad::to_fiction_coord<cell<Lyt>>(c), base_lyt.get_cell_mode(c));
lyt_new.assign_cell_name(siqad::to_fiction_coord<cell<Lyt>>(c), base_lyt.get_cell_name(c));
});
};

if (has_offset_ucoord_v<Lyt> && !lyt.is_empty())
{
auto lyt_normalized = normalize_layout_coordinates<sidb_cell_clk_lyt_siqad>(lyt);
assign_coordinates(lyt_normalized);
lyt_new.resize({lyt_normalized.x(), lyt_normalized.y(), lyt_normalized.z()});
}
else
{
assign_coordinates(lyt);
}

return lyt_new;
}

} // namespace fiction

#endif // FICTION_LAYOUT_UTILS_HPP
180 changes: 180 additions & 0 deletions test/utils/layout_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <catch2/catch_template_test_macros.hpp>

#include <fiction/layouts/cartesian_layout.hpp>
#include <fiction/layouts/cell_level_layout.hpp>
#include <fiction/layouts/coordinates.hpp>
#include <fiction/layouts/hexagonal_layout.hpp>
#include <fiction/utils/layout_utils.hpp>
Expand Down Expand Up @@ -38,3 +39,182 @@ TEMPLATE_TEST_CASE("Port directions to coordinates", "[layout-utils]", (cartesia
lyt.north_west(c));
});
}

TEMPLATE_TEST_CASE("siqad layout is normalized, shifted to positive coordinates", "[layout-utils]",
sidb_cell_clk_lyt_siqad)
{
SECTION("empty layout")
{
TestType lyt{{}, "layout based on siqad coordinates"};

lyt.assign_cell_type({-5, -1}, TestType::cell_type::NORMAL);
lyt.assign_cell_type({5, 1}, TestType::cell_type::NORMAL);

auto lyt_transformed = normalize_layout_coordinates<TestType>(lyt);

CHECK(lyt_transformed.get_layout_name() == "layout based on siqad coordinates");
CHECK(lyt_transformed.get_cell_type({0, 0}) == TestType::cell_type::NORMAL);
CHECK(lyt_transformed.get_cell_type({10, 2}) == TestType::cell_type::NORMAL);
CHECK(lyt_transformed.get_cell_type({-5, -1}) == TestType::cell_type::EMPTY);
}
}

TEMPLATE_TEST_CASE("Convert offset::ucoord_t layout to SiQAD coordinate layout", "[layout-utils]", sidb_cell_clk_lyt)
{
SECTION("empty layout")
{
TestType lyt{{10, 10}, "test"};

auto lyt_transformed = convert_to_siqad_coordinates<TestType>(lyt);

CHECK(lyt_transformed.is_empty());
CHECK(lyt_transformed.area() == static_cast<int64_t>(lyt.area()));
CHECK(lyt_transformed.get_layout_name() == lyt.get_layout_name());
}

SECTION("layout with one normal and one input cell")
{
TestType lyt{{5, 3}};

lyt.assign_cell_type({5, 3}, TestType::cell_type::NORMAL);
lyt.assign_cell_type({5, 1}, TestType::cell_type::INPUT);

auto lyt_transformed = convert_to_siqad_coordinates<TestType>(lyt);

CHECK(lyt_transformed.num_cells() == 2);
CHECK(lyt_transformed.area() == static_cast<int64_t>(lyt.area()));
CHECK(lyt_transformed.get_cell_type({5, 1, 1}) == TestType::cell_type::NORMAL);
CHECK(lyt_transformed.get_cell_type({5, 0, 1}) == TestType::cell_type::INPUT);
}

SECTION("layout with three cells")
{
TestType lyt{{5, 3}};

lyt.assign_cell_type({0, 0}, TestType::cell_type::NORMAL);
lyt.assign_cell_type({5, 3}, TestType::cell_type::INPUT);
lyt.assign_cell_type({5, 1}, TestType::cell_type::OUTPUT);
lyt.assign_cell_name({0, 0}, "normal cell");
lyt.assign_cell_name({5, 3}, "input cell");
lyt.assign_cell_name({5, 1}, "output cell");

auto lyt_transformed = convert_to_siqad_coordinates<TestType>(lyt);

CHECK(lyt_transformed.num_cells() == 3);
CHECK(lyt_transformed.area() == static_cast<int64_t>(lyt.area()));
CHECK(lyt_transformed.get_cell_type({0, 0, 0}) == TestType::cell_type::NORMAL);
CHECK(lyt_transformed.get_cell_type({5, 1, 1}) == TestType::cell_type::INPUT);
CHECK(lyt_transformed.get_cell_type({5, 0, 1}) == TestType::cell_type::OUTPUT);
CHECK(lyt_transformed.get_cell_name({0, 0, 0}) == "normal cell");
CHECK(lyt_transformed.get_cell_name({5, 1, 1}) == "input cell");
CHECK(lyt_transformed.get_cell_name({5, 0, 1}) == "output cell");
}
}

TEMPLATE_TEST_CASE("Convert SiQAD layout to offset::ucoord_t coordinate layout", "[layout-utils]", sidb_cell_clk_lyt)
{
SECTION("empty layout")
{
const sidb_cell_clk_lyt_siqad lyt{{}, "layout based on siqad coordinates"};

auto lyt_transformed = convert_to_fiction_coordinates<TestType>(lyt);

CHECK(lyt_transformed.is_empty());
CHECK(static_cast<int64_t>(lyt_transformed.area()) == lyt.area());
CHECK(lyt_transformed.get_layout_name() == lyt.get_layout_name());
}

SECTION("layout with one normal and one input cell")
{
sidb_cell_clk_lyt_siqad lyt{{5, 3}};

lyt.assign_cell_type({5, 3}, TestType::cell_type::NORMAL);
lyt.assign_cell_type({-5, -1}, TestType::cell_type::INPUT);
CHECK(lyt.x() == 5);
CHECK(lyt.y() == 3);

auto lyt_transformed = convert_to_fiction_coordinates<TestType>(lyt);

CHECK(lyt_transformed.x() == 10);
CHECK(lyt_transformed.y() == 4);
CHECK(lyt_transformed.num_cells() == 2);
CHECK(lyt_transformed.get_cell_type({10, 8}) == TestType::cell_type::NORMAL);
CHECK(lyt_transformed.get_cell_type({0, 0}) == TestType::cell_type::INPUT);
}

SECTION("layout with three cells")
{
sidb_cell_clk_lyt_siqad lyt{{5, 3}};

lyt.assign_cell_type({5, 3}, TestType::cell_type::NORMAL);
lyt.assign_cell_type({0, 0}, TestType::cell_type::INPUT);
lyt.assign_cell_type({5, 1}, TestType::cell_type::OUTPUT);
lyt.assign_cell_name({5, 3}, "normal cell");
lyt.assign_cell_name({0, 0}, "input cell");
lyt.assign_cell_name({5, 1}, "output cell");

auto lyt_transformed = convert_to_fiction_coordinates<TestType>(lyt);

CHECK(lyt_transformed.num_cells() == 3);
CHECK(static_cast<int64_t>(lyt_transformed.area()) == lyt.area());
CHECK(lyt_transformed.get_cell_type({5, 6}) == TestType::cell_type::NORMAL);
CHECK(lyt_transformed.get_cell_type({0, 0}) == TestType::cell_type::INPUT);
CHECK(lyt_transformed.get_cell_type({5, 2}) == TestType::cell_type::OUTPUT);
CHECK(lyt_transformed.get_cell_name({5, 6}) == "normal cell");
CHECK(lyt_transformed.get_cell_name({0, 0}) == "input cell");
CHECK(lyt_transformed.get_cell_name({5, 2}) == "output cell");
}
}

TEMPLATE_TEST_CASE("Convert SiQAD layout to cube::coord_t coordinate layout", "[layout-utils]",
(cell_level_layout<sidb_technology, clocked_layout<cartesian_layout<cube::coord_t>>>))
{
SECTION("empty layout")
{
const sidb_cell_clk_lyt_siqad lyt{{}, "layout based on siqad coordinates"};

auto lyt_transformed = convert_to_fiction_coordinates<TestType>(lyt);

CHECK(lyt_transformed.is_empty());
CHECK(lyt_transformed.area() == static_cast<int64_t>(lyt.area()));
CHECK(lyt_transformed.get_layout_name() == lyt.get_layout_name());
}

SECTION("layout with one normal and one input cell")
{
sidb_cell_clk_lyt_siqad lyt{{5, 1, 1}};

lyt.assign_cell_type({5, -1, 1}, TestType::cell_type::NORMAL);
lyt.assign_cell_type({5, 1, 0}, TestType::cell_type::INPUT);

auto lyt_transformed = convert_to_fiction_coordinates<TestType>(lyt);

CHECK(lyt_transformed.num_cells() == 2);
CHECK(lyt_transformed.area() == static_cast<int64_t>(lyt.area()));
CHECK(lyt_transformed.get_cell_type({5, -1}) == TestType::cell_type::NORMAL);
CHECK(lyt_transformed.get_cell_type({5, 2, 0}) == TestType::cell_type::INPUT);
}

SECTION("layout with three cells")
{
sidb_cell_clk_lyt_siqad lyt{{5, 3}};

lyt.assign_cell_type({5, -3}, TestType::cell_type::NORMAL);
lyt.assign_cell_type({0, 0}, TestType::cell_type::INPUT);
lyt.assign_cell_type({5, 3}, TestType::cell_type::OUTPUT);
lyt.assign_cell_name({5, -3}, "normal cell");
lyt.assign_cell_name({0, 0}, "input cell");
lyt.assign_cell_name({5, 3}, "output cell");

auto lyt_transformed = convert_to_fiction_coordinates<TestType>(lyt);

CHECK(lyt_transformed.num_cells() == 3);
CHECK(lyt_transformed.area() == static_cast<int64_t>(lyt.area()));
CHECK(lyt_transformed.get_cell_type({5, -6}) == TestType::cell_type::NORMAL);
CHECK(lyt_transformed.get_cell_type({0, 0}) == TestType::cell_type::INPUT);
CHECK(lyt_transformed.get_cell_type({5, 6}) == TestType::cell_type::OUTPUT);
CHECK(lyt_transformed.get_cell_name({5, -6}) == "normal cell");
CHECK(lyt_transformed.get_cell_name({0, 0}) == "input cell");
CHECK(lyt_transformed.get_cell_name({5, 6}) == "output cell");
}
}

0 comments on commit c77a7c9

Please sign in to comment.