From 01db414b6155cf6c1f972698118045976123d28e Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 2 Jan 2025 14:43:48 +0000 Subject: [PATCH 01/67] :memo: Update pyfiction docstrings Signed-off-by: GitHub Actions --- .../pyfiction/pybind11_mkdoc_docstrings.hpp | 222 ------------------ 1 file changed, 222 deletions(-) diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index 3cdf9930c4..2f8cdd7686 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -1122,56 +1122,6 @@ static const char *__doc_fiction_bounding_box_2d_x_size = R"doc(The horizontal s static const char *__doc_fiction_bounding_box_2d_y_size = R"doc(The vertical size of the bounding box in layout coordinates.)doc"; -static const char *__doc_fiction_branching_signal_container = -R"doc(A container class to help identify layout locations of branching nodes -like fanouts. When a node from a network is to placed in a layout, -fetching the node's fanins and looking for their locations in the -layout does not work properly when branching nodes like fanouts are -involved that got extended by wire nodes. This container solves that -issue. - -Template parameter ``Lyt``: - Gate-level layout type. - -Template parameter ``Ntk``: - Logic network type. - -Template parameter ``fanout_size``: - Maximum fanout size possible in the layout and/or the network.)doc"; - -static const char *__doc_fiction_branching_signal_container_branches = R"doc(Storage for all branches.)doc"; - -static const char *__doc_fiction_branching_signal_container_branching_signal = R"doc(Branch type.)doc"; - -static const char *__doc_fiction_branching_signal_container_branching_signal_branching_signal = R"doc()doc"; - -static const char *__doc_fiction_branching_signal_container_branching_signal_lyt_signal = R"doc()doc"; - -static const char *__doc_fiction_branching_signal_container_branching_signal_ntk_node = R"doc()doc"; - -static const char *__doc_fiction_branching_signal_container_operator_array = -R"doc(Accesses the branching container to find the location of a given node -`n`. Returns the signal to that location if it was already stored or -the default signal, otherwise. - -Parameter ``n``: - Node whose branching position is desired. - -Returns: - Signal to `n`'s layout location or the default signal if it wasn't - found.)doc"; - -static const char *__doc_fiction_branching_signal_container_update_branch = -R"doc(Updates the given node's branch by another layout signal, thereby, -creating a new branch or updating the position of an existing one, -e.g., if further wire segments were moving the head of the branch. - -Parameter ``ntk_node``: - Node whose branch is to be updated. - -Parameter ``lyt_signal``: - New signal pointing to the end of the branch.)doc"; - static const char *__doc_fiction_calculate_energy_and_state_type_with_kinks_accepted = R"doc(This function takes in an SiDB energy distribution. For each charge distribution, the state type is determined (i.e. erroneous, @@ -10202,20 +10152,6 @@ Parameter ``lyt``: Returns: List of all routing objectives in the given layout.)doc"; -static const char *__doc_fiction_fanin_container = -R"doc(Container that stores fanins of a node in a network, including whether -one of them is a constant. - -Note that this container assumes that each node has a maximum of one -constant fanin. - -Template parameter ``Ntk``: - `mockturtle` network type.)doc"; - -static const char *__doc_fiction_fanin_container_constant_fanin = -R"doc(Has a value if a fanin node is constant. In that case, it represents -the constant value.)doc"; - static const char *__doc_fiction_fanin_edge_container = R"doc(Container that stores fanin edges of a node in a network, including whether one of them is a constant. @@ -15189,158 +15125,6 @@ static const char *__doc_fiction_path_set_add = R"doc()doc"; static const char *__doc_fiction_path_set_contains = R"doc()doc"; static const char *__doc_fiction_place = -R"doc(Place 0-input gates. - -Template parameter ``Lyt``: - Gate-level layout type. - -Template parameter ``Ntk``: - Logic network type. - -Parameter ``lyt``: - Gate-level layout in which to place a 0-input gate. - -Parameter ``t``: - Tile in `lyt` to place the gate onto. - -Parameter ``ntk``: - Network whose node is to be placed. - -Parameter ``n``: - Node in `ntk` to place onto `t` in `lyt`. - -Returns: - Signal pointing to the placed gate in `lyt`.)doc"; - -static const char *__doc_fiction_place_2 = -R"doc(Place 1-input gates. - -Template parameter ``Lyt``: - Gate-level layout type. - -Template parameter ``Ntk``: - Logic network type. - -Parameter ``lyt``: - Gate-level layout in which to place a 1-input gate. - -Parameter ``t``: - Tile in `lyt` to place the gate onto. - -Parameter ``ntk``: - Network whose node is to be placed. - -Parameter ``n``: - Node in `ntk` to place onto `t` in `lyt`. - -Parameter ``a``: - Incoming signal to the newly placed gate in `lyt`. - -Returns: - Signal pointing to the placed gate in `lyt`.)doc"; - -static const char *__doc_fiction_place_3 = -R"doc(Place 2-input gates. - -Template parameter ``Lyt``: - Gate-level layout type. - -Template parameter ``Ntk``: - Logic network type. - -Parameter ``lyt``: - Gate-level layout in which to place a 2-input gate. - -Parameter ``t``: - Tile in `lyt` to place the gate onto. - -Parameter ``ntk``: - Network whose node is to be placed. - -Parameter ``n``: - Node in `ntk` to place onto `t` in `lyt`. - -Parameter ``a``: - First incoming signal to the newly placed gate in `lyt`. - -Parameter ``b``: - Second incoming signal to the newly placed gate in `lyt`. - -Parameter ``c``: - Third optional incoming constant value signal to the newly placed - gate in `lyt`. Might change the gate function when set, e.g., from - a MAJ to an AND if `c == false`. - -Returns: - Signal pointing to the placed gate in `lyt`.)doc"; - -static const char *__doc_fiction_place_4 = -R"doc(Place 3-input gates. - -Template parameter ``Lyt``: - Gate-level layout type. - -Template parameter ``Ntk``: - Logic network type. - -Parameter ``lyt``: - Gate-level layout in which to place a 3-input gate. - -Parameter ``t``: - Tile in `lyt` to place the gate onto. - -Parameter ``ntk``: - Network whose node is to be placed. - -Parameter ``n``: - Node in `ntk` to place onto `t` in `lyt`. - -Parameter ``a``: - First incoming signal to the newly placed gate in `lyt`. - -Parameter ``b``: - Second incoming signal to the newly placed gate in `lyt`. - -Parameter ``c``: - Third incoming signal to the newly placed gate in `lyt`. - -Returns: - Signal pointing to the placed gate in `lyt`.)doc"; - -static const char *__doc_fiction_place_5 = -R"doc(Place any gate from a network. This function automatically identifies -the arity of the passed node and fetches its incoming signals from the -given network and a provided `mockturtle::node_map`. This function -does not update the `mockturtle::node_map`. - -Template parameter ``Lyt``: - Gate-level layout type. - -Template parameter ``Ntk``: - Logic network type. - -Parameter ``lyt``: - Gate-level layout in which to place any gate. - -Parameter ``t``: - Tile in `lyt` to place the gate onto. - -Parameter ``ntk``: - Network whose node is to be placed. - -Parameter ``n``: - Node in `ntk` to place onto `t` in `lyt`. - -Parameter ``node2pos``: - Mapping from network nodes to layout signals, i.e., a pointer to - their position in the layout. The map is used to fetch location of - the fanins. The `mockturtle::node_map` is not updated by this - function. - -Returns: - Signal to the newly placed gate in `lyt`.)doc"; - -static const char *__doc_fiction_place_6 = R"doc(Place any gate from a network. This function automatically identifies the arity of the passed node and fetches its incoming signals from the given network and a provided branching_signal_container @@ -19323,12 +19107,6 @@ static const char *__doc_fmt_formatter_parse = R"doc()doc"; static const char *__doc_fmt_formatter_parse_2 = R"doc()doc"; -static const char *__doc_fmt_unnamed_struct_at_home_runner_work_fiction_fiction_include_fiction_layouts_coordinates_hpp_1090_8 = R"doc()doc"; - -static const char *__doc_fmt_unnamed_struct_at_home_runner_work_fiction_fiction_include_fiction_layouts_coordinates_hpp_1106_8 = R"doc()doc"; - -static const char *__doc_fmt_unnamed_struct_at_home_runner_work_fiction_fiction_include_fiction_technology_cell_ports_hpp_291_8 = R"doc()doc"; - static const char *__doc_mockturtle_detail_foreach_element_if_transform = R"doc()doc"; static const char *__doc_mockturtle_edge = From fea6fda92579cc2d4dc94f53543073e003163a37 Mon Sep 17 00:00:00 2001 From: benjamin Date: Thu, 2 Oct 2025 13:08:45 +0200 Subject: [PATCH 02/67] :heavy_plus_sign: starting files --- .../node_duplication_planarization.hpp | 757 +++++++++++ .../physical_design/orthogonal_planar.hpp | 1187 +++++++++++++++++ .../node_duplication_planarization.cpp | 179 +++ 3 files changed, 2123 insertions(+) create mode 100644 include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp create mode 100644 include/fiction/algorithms/physical_design/orthogonal_planar.hpp create mode 100644 test/algorithms/network_transformation/node_duplication_planarization.cpp diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp new file mode 100644 index 0000000000..1256a42fa0 --- /dev/null +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -0,0 +1,757 @@ +// +// Created by benjamin on 11.06.24. +// + +#ifndef FICTION_NODE_DUPLICATION_PLANARIZATION_HPP +#define FICTION_NODE_DUPLICATION_PLANARIZATION_HPP + +#include "fiction/algorithms/properties/check_planarity.hpp" +#include "fiction/networks/views/mutable_rank_view.hpp" +#include "fiction/networks/virtual_pi_network.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if (PROGRESS_BARS) +#include +#endif + +namespace fiction +{ + +/** + * Parameters for the node duplication algorithm. + */ +struct node_duplication_planarization_params +{ + /** + * The output order determines the starting layer for this algorithm. If this option is turned off, the output order + * remains the same as in the provided network. If it is turned on, the outputs are ordered randomly. + */ + enum class output_order : uint8_t + { + /** + * Keep the PO order from the input network. + */ + KEEP_PO_ORDER, + /** + * Randomize the PO order. + */ + RANDOM_PO_ORDER + }; + // bool random_output_order = false; + output_order po_order = output_order::KEEP_PO_ORDER; +}; + +namespace detail +{ + +/** + * A structure representing a pair of nodes in an H-graph. + * + * The nodes stored in this struct describe the fanin-edges of a node in an H-graph. + * A node pair object holds two nodes, which are saved in the member 'pair'. + * These two outer nodes are connected through zero or more 'middle_nodes'. + * The fanin order starts with the first node in 'pair', then proceeds through the 'middle_nodes', and ends with the + * second node in 'pair'. The order of 'middle_nodes' is arbitrary as they cannot be further connected to any other + * nodes. For the planarization, only the nodes inside the 'pair' are relevant. + * + * @tparam Ntk Network type for the nodes in the pair. + */ +template +struct node_pair +{ + /** + * Defines the beginning and end of the fanin-edged node. + */ + std::pair, mockturtle::node> pair; + /** + * Contains the nodes between the fanin-edges node; cannot be connected to any other node. + */ + std::vector> middle_nodes; + /** + * Shared pointer to another instance of node_pair detailing fanin-edge alignment. + */ + node_pair* fanin_pair; + /** + * Specifies the delay value for the node. + */ + uint64_t delay; + /** + * Standard constructor. + * + * @param node1 The first node of the fanin-edged node. + * @param node2 The second node of the fanin-edged node. + * @param delay_value The delay value for the node. + */ + node_pair(mockturtle::node node1, mockturtle::node node2, uint64_t delay_value) : + pair(node1, node2), + delay(delay_value), + fanin_pair(nullptr) + {} +}; + +/** + * Variant of the mockturtle::initialize_copy_network. This function helps with creating new networks from old + * networks. In the mockturtle/original version `old2new` is used to map nodes from the old network to nodes in the new + * network in a one to one relation. This variant allows old nodes to map to multiple nodes in order to represent + * relations to dulicated nodes. + + * A map (old2new) is created where old nodes from source network are mapped to new nodes in destination network. + * A destination network is created as a virtual_pi_network. + * + * @tparam Ntk Type of the network. + * @param src The source network. + * + * @return A pair of the destination network and a node map from the source to the destination network. + */ +template +std::pair, mockturtle::node_map>>, Ntk>> +initialize_copy_network_duplicates(Ntk const& src) +{ + static_assert(mockturtle::is_network_type_v>, "Ntk is not a network type"); + static_assert(mockturtle::is_network_type_v, "Ntk is not a network type"); + static_assert(mockturtle::has_get_constant_v>, + "Ntk does not implement the get_constant method"); + static_assert(mockturtle::has_create_pi_v>, "Ntk does not implement the create_pi method"); + static_assert(mockturtle::has_is_pi_v>, "Ntk does not implement the is_pi method"); + static_assert(mockturtle::has_create_not_v>, + "Ntk does not implement the create_not method"); + static_assert(mockturtle::has_get_constant_v, "Ntk does not implement the get_constant method"); + static_assert(mockturtle::has_get_node_v, "Ntk does not implement the get_node method"); + static_assert(mockturtle::has_foreach_pi_v, "Ntk does not implement the foreach_pi method"); + static_assert(mockturtle::has_foreach_po_v, "Ntk does not implement the foreach_po method"); + static_assert(mockturtle::has_is_complemented_v, "Ntk does not implement the is_complemented method"); + static_assert(mockturtle::has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method"); + + mockturtle::node_map>>, Ntk> old2new(src); + virtual_pi_network dest; + + old2new[src.get_constant(false)].push_back(dest.get_constant(false)); + if (src.get_node(src.get_constant(true)) != src.get_node(src.get_constant(false))) + { + old2new[src.get_constant(true)].push_back(dest.get_constant(true)); + } + src.foreach_pi([&](auto const& n) { old2new[n].push_back(dest.create_pi()); }); + return {dest, old2new}; +} + +/** + * The function gather_fanin_signals collects the fanin data for node n from the original ntk. + * For each node n there are the possible fanin candidates old2new_v[fn], which are the original node and all + * the nodes which are duplicates of this node. + * + * lvl[node_index] gives the current iterator at where the edge can be connected. To get the right signal, + * all nodes at old2new[n] need to be viewed. Match lvl[node_index] against all entries in old2new[n], + * then try lvl[node_index+1] then try lvl[node_index+2]. + * + * @param n Variable to process. + * @param lvl Level to process. + * @param edge_it Iterator for edge. + * @return Vector of fanins in the virtual_pi_network connected to the processed node. + */ +template +std::vector> +gather_fanin_signals(Ntk& ntk, NtkDest& ntk_dest_v, mockturtle::node n, + mockturtle::node_map>, Ntk>& old2new_v, + std::vector>& lvl, std::size_t& node_index) +{ + // Initialize variables + std::vector> children{}; + children.reserve(ntk.fanin_size(n)); + std::size_t local_node_index = 0; + + ntk.foreach_fanin( + n, + [&n, &ntk, &ntk_dest_v, &lvl, &old2new_v, &children, &node_index, &local_node_index](const auto& f, + const auto i) + { + // Get the vector of duplicated nodes of the original fan-in node fn. + const auto fn = ntk.get_node(f); + auto tgt_signal_v = old2new_v[fn]; + + assert(node_index < lvl.size() && "The fanin iterator is out of scope"); + + // The range indicates the number of candidate fan-ins. + std::size_t range = ntk.fanin_size(n) + 1; + + // Iterate through the candidate fan-ins. If a candidate fan-in matches the original fan-in or is a + // duplicate of it, add it to the children of the node n. + std::size_t end_index = std::min(node_index + range, lvl.size()); + for (std::size_t j = node_index; j < end_index; ++j) + { + // get the node from the newly generated network. + auto node_at_index = lvl[j]; + + // Check if the candidate matches the original fan-in or a duplicate. + // Also, verify if the candidate has already reached its fan-out limit. + if ((std::find(tgt_signal_v.cbegin(), tgt_signal_v.cend(), node_at_index) != tgt_signal_v.cend()) && + (ntk_dest_v.fanout_size(node_at_index) < ntk.fanout_size(fn))) + { + // Set the local node_index. + if (i == 0) + { + local_node_index = j; + } + else + { + local_node_index = std::max(local_node_index, j); + } + + // Add the matched candidate fan-in to the children. + children.emplace_back(node_at_index); + break; + } + } + }); + + // Set the node_index. + node_index = local_node_index; + + // Return the children of the node. + return children; +}; + +/** + * Constructs a planar `virtual_pi_network` based on the `ntk_lvls` array, which holds the ranks of the duplicated nodes + * for each level in the new network. This function creates new nodes for the duplicated ones and restores their fanin + * relations using the `gather_fanin_signals` function. + * + * For duplicated PIs (Primary Inputs), virtual PIs are created, and the original PI is stored in a map. + * + * The auxiliary function `gather_fanin_signals` collects fanin data for a node and matches it in the + * `virtual_pi_network`. + * + * Example: For a level (2, 3, 2, 4, 2), new nodes are created for duplications (e.g., 2) and stored in the `old2new_v` + * node_map. This map is used by `gather_fanin_signals` to establish the correct fanin relations. + * + * @tparam Ntk Network type. + * @param ntk Source network to be utilized for the creation of the virtual_pi_network. + * @param ntk_lvls Levels of nodes in the source network. + * @param ntk_lvls_new Levels of newly created nodes in the virtual_pi_network. + */ +template +virtual_pi_network +create_virtual_pi_ntk_from_duplicated_nodes(Ntk& ntk, std::vector>>& ntk_lvls, + std::vector>>& ntk_lvls_new) +{ + std::unordered_map, bool> node_status; + ntk_lvls_new.resize(ntk_lvls.size()); + + auto init_v = initialize_copy_network_duplicates(ntk); + auto& ntk_dest_v = init_v.first; + auto& old2new_v = init_v.second; + + for (std::size_t i = ntk_lvls.size(); i-- > 0;) + { + // The index of the node in the current node level. + std::size_t node_index = 0; + + // The current node level with duplicated nodes. + // Example vector: {3, 2, 3} + const auto& lvl = ntk_lvls[i]; + + // The current node level in the new network, where duplicated nodes are created as new nodes. + // Example vector {3, 2, 4} + auto& lvl_new = ntk_lvls_new[i]; + + // Create a node in the new network for each node contained in 'lvl'. + for (const auto& nd : lvl) + { + // If the node is a PI create virtual PIs for duplicates. + if (ntk.is_pi(nd)) + { + if (node_status[nd]) + { + const auto& new_node = ntk_dest_v.create_virtual_pi(nd); + lvl_new.push_back(new_node); + old2new_v[nd].push_back(new_node); + } + else + { + lvl_new.push_back(nd); + node_status[nd] = true; + } + } + else + { + const auto children = + gather_fanin_signals(ntk, ntk_dest_v, nd, old2new_v, ntk_lvls_new[i + 1], node_index); + + assert(!children.empty() && "The node has to have children"); + + const auto& new_node = ntk_dest_v.create_node(children, ntk.node_function(nd)); + lvl_new.push_back(new_node); + old2new_v[nd].push_back(new_node); + } + } + } + + ntk.foreach_po( + [&ntk, &ntk_dest_v, &old2new_v](const auto& po) + { + const auto tgt_signal_v = old2new_v[ntk.get_node(po)]; + + // POs are not duplicated as the algorithm starts at POs and duplicates other nodes based on their order + assert(tgt_signal_v.size() == 1 && "Multiple nodes mapped to PO"); + + const auto tgt_signal = tgt_signal_v[0]; + + const auto tgt_po = ntk.is_complemented(po) ? ntk_dest_v.create_not(tgt_signal) : tgt_signal; + + ntk_dest_v.create_po(tgt_po); + }); + + return ntk_dest_v; +} + +/** + * Calculates pairs of nodes from a given vector of nodes. + * + * This function takes a vector of nodes and returns a vector of node pairs. Each node pair consists of two nodes from + * the input vector and an optional vector of middle nodes. The delay of each node pair is initialized to infinity. + * + * @tparam Ntk The network type. + * @param nodes The vector of nodes. + * @return The vector of node pairs. + */ +template +[[nodiscard]] std::vector> calculate_pairs(const std::vector>& nodes) noexcept +{ + std::vector> pairwise_combinations{}; + pairwise_combinations.reserve(nodes.size() * (nodes.size() - 1)); + + if (nodes.size() == 1) + { + const node_pair pair = {nodes[0], nodes[0], + std::numeric_limits::max()}; // Initialize delay to inf + pairwise_combinations.push_back(pair); + return pairwise_combinations; + } + + for (auto it1 = nodes.cbegin(); it1 != nodes.cend(); ++it1) + { + for (auto it2 = it1 + 1; it2 != nodes.cend(); ++it2) + { + std::vector> middle_nodes{}; + middle_nodes.reserve(nodes.size() - 2); + + // fill middle_nodes with non-pair members + for (auto it = nodes.cbegin(); it != nodes.cend(); ++it) + { + if (it != it1 && it != it2) + { + middle_nodes.push_back(*it); + } + } + + node_pair pair1 = {*it1, *it2, std::numeric_limits::max()}; // Initialize delay to inf + node_pair pair2 = {*it2, *it1, std::numeric_limits::max()}; // Initialize delay to inf + + // Add middle_nodes to pairs + pair1.middle_nodes = middle_nodes; + pair2.middle_nodes = middle_nodes; + + pairwise_combinations.push_back(pair1); + pairwise_combinations.push_back(pair2); + } + } + + return pairwise_combinations; +} + +template +class node_duplication_planarization_impl +{ + public: + node_duplication_planarization_impl(const Ntk& src, const node_duplication_planarization_params& p) : + ntk(src), + ps{p} + {} + + /** + * The H-graph represents all possible orderings of node pairs within a single network level. + * A "slice" is created by adding all possible combinations of a `node_pair` to the H-graph of the level. + * These combinations are formed by selecting pairs of nodes from the fan-ins of the input node: + * - If the input node has only one fan-in, it is treated as a single combination. + * - If the input node has two fan-ins, there are two possible combinations. + * + * Each `node_pair` consists of a first and second element. The objective is to find an ordering of node pairs that + * maximizes the instances where the first element of a node_pair matches the second element of the preceding + * node_pair. This ordering is given as a linked list. + * + * This function computes the optimal ordering by calculating delays as follows: + * - All combinations of node pairs are iteratively added to a linked list. + * - For each combination, the first element of the current node_pair is compared with the last element of the + * preceding node_pairs. + * - If a connection exists between two node_pairs, the delay increases by 1; otherwise, it increases by 2. The + * default delay for the first node is 1. + * - If a node_pair lacks a connection, and its updated delay (increased by 2) is less than the existing delay, the + * node_pair's delay is updated accordingly. + * + * Processed node_pairs are stored in the `lvl_pairs` member for subsequent delay calculations. + * + * @param nd Node in the H-graph. + * @param border_pis A boolean indicating whether the input PIs (Primary Inputs) should be propagated to the next + */ + + void compute_slice_delays(const mockturtle::node& nd) + { + // Pis need to be propagated into the next level, since they have to be connected without crossings + if (ntk.is_pi(nd)) + { + fis.push_back(nd); + } + + ntk.foreach_fanin(nd, + [this](auto fi) + { + if (!ntk.is_constant(fi)) + { + fis.push_back(fi); + } + }); + + assert(fis.empty() == 0 && "Node is a buffered PI that is a PO"); + + // Compute the combinations in one slice + auto combinations = calculate_pairs(fis); + assert(!combinations.empty() && "Combinations are empty. There might be a dangling node"); + + if (!lvl_pairs.empty()) + { + std::vector>* combinations_last = &lvl_pairs.back(); + + for (auto& node_pair_cur : combinations) + { + for (auto& node_pair_last : *combinations_last) + { + // If there is a connection between the two node pairs the delay is calculated like this + if ((node_pair_cur.pair.first == node_pair_last.pair.second && + node_pair_last.delay + 1 < node_pair_cur.delay)) + { + node_pair_cur.fanin_pair = &node_pair_last; + node_pair_cur.delay = node_pair_last.delay + 1; + } + // If there is no connection between the two node pairs the delay is calculated like this + else if (node_pair_last.delay + 2 < node_pair_cur.delay) + { + node_pair_cur.fanin_pair = &node_pair_last; + node_pair_cur.delay = node_pair_last.delay + 2; + } + else if (node_pair_last.delay + 2 == node_pair_cur.delay) + { + // ToDo: If order doesnt matter, decide on minimal crossing view (mincross.c from graphviz) + + // This solves equal path delays, if they are connected in the next layer via a fanout + const auto fc0 = fanins(ntk, node_pair_last.pair.first); + if (node_pair_last.fanin_pair != nullptr) + { + const auto fc1 = fanins(ntk, node_pair_last.fanin_pair->pair.second); + for (const auto f0 : fc0.fanin_nodes) + { + for (const auto f1 : fc1.fanin_nodes) + { + if (f0 == f1) + { + node_pair_cur.fanin_pair = &node_pair_last; + break; + } + } + } + } + } + } + } + } + else + { + // The delay for the first node in the level is set to 1 + for (auto& node_pair : combinations) + { + node_pair.delay = 1; + } + } + + lvl_pairs.push_back(combinations); + } + + /** + * Inserts a node into a vector if it is unique. + * + * `This function inserts a node into a vector only if the vector is empty or the node is not equal to the first + * element of the vector. If the vector is not empty and the node is equal to the first element, it does nothing. + * An exception occurs if the node was skipped on the previous insertion attempt due to `vec.front() == node`; in + * that case, the node will be inserted this time. + * + * @param node The node to be inserted. + * @param vec The vector to insert the node into. + */ + void insert_if_not_first(const mockturtle::node& node, std::vector>& vec, + int& saturated_fanout_flag, int position) + { + if (vec.empty() || vec.front() != node) + { + vec.insert(vec.begin(), node); + saturated_fanout_flag = 0; + } + else if (position == 0) + { + if (saturated_fanout_flag == 1) + { + vec.insert(vec.begin(), node); + saturated_fanout_flag = 0; + } + else + { + saturated_fanout_flag = 1; + } + } + } + + /** + * Computes the order of nodes in the next level based on the delay (shortest path) in the H-graph of the level. + * + * This function computes the order of nodes in the next level based on their delay in the H-graph of the level. It + * selects the path with the least delay from the current level pairs and follows it via fanin relations. The nodes + * are inserted into the next level vector in the order they are encountered. + * + * @return The order of nodes in `next_level` + */ + std::vector> compute_node_order() + { + std::vector> next_level; + int saturated_fanout_flag = 0; + const auto& combinations = lvl_pairs.back(); + // select the path with the least delay and follow it via fanin relations + const auto minimum_it = + std::min_element(combinations.cbegin(), combinations.cend(), + [](const node_pair& a, const node_pair& b) { return a.delay < b.delay; }); + if (minimum_it != combinations.cend()) + { + const auto& min_combination = *minimum_it; + + // Insert the terminal node + insert_if_not_first(min_combination.pair.second, next_level, saturated_fanout_flag, 0); + + // insert middle_nodes + for (const auto& node : min_combination.middle_nodes) + { + insert_if_not_first(node, next_level, saturated_fanout_flag, 1); + } + + // Insert the first node + insert_if_not_first(min_combination.pair.first, next_level, saturated_fanout_flag, 1); + + auto fanin_combination = minimum_it->fanin_pair; + + while (fanin_combination) + { + // Insert the terminal node + if (ntk.is_pi(fanin_combination->pair.second)) + { + saturated_fanout_flag = 1; + } + insert_if_not_first(fanin_combination->pair.second, next_level, saturated_fanout_flag, 0); + + // Insert middle_nodes + for (const auto& node : fanin_combination->middle_nodes) + { + insert_if_not_first(node, next_level, saturated_fanout_flag, 1); + } + + // insert the first node + insert_if_not_first(fanin_combination->pair.first, next_level, saturated_fanout_flag, 1); + + fanin_combination = fanin_combination->fanin_pair; + } + } + return next_level; + } + + /** + * Checks if the given vector of nodes contains any non-primary inputs. + * + * This function iterates through each node in the vector and checks if it is a primary input. + * If a non-primary input is found, the `f_final_level` parameter is set to false and the loop is exited. + * + * @param v_next_level The vector of nodes to be checked. + */ + [[nodiscard]] bool check_final_level(const std::vector>& v_next_level) + { + for (const auto& nd : v_next_level) + { + if (!ntk.is_pi(nd)) + { + return false; + } + } + return true; + } + + [[nodiscard]] virtual_pi_network + run(std::vector>>& ntk_lvls_new) + { + // Initialize the POs + std::vector> pos{}; + pos.reserve(ntk.num_pos()); + ntk.foreach_node( + [this, &pos](const auto n) + { + if(ntk.is_po(n)) + { + const auto po = ntk.get_node(n); + if (std::find(pos.begin(), pos.end(), po) == pos.end()) + { + pos.push_back(po); + } + } + }); + + // Randomize the PO order + if (ps.po_order == node_duplication_planarization_params::output_order::RANDOM_PO_ORDER) + { + // Generate a random engine + static std::mt19937_64 generator(std::random_device{}()); + // Shuffle the pos vector + std::shuffle(pos.begin(), pos.end(), generator); + } + + // save the nodes of the next level + std::vector> next_level{}; + next_level.reserve(pos.size()); + + // Process the first level + for (const auto& po : pos) + { + fis.clear(); + compute_slice_delays(po); + next_level.push_back(po); + } + + ntk_lvls.push_back(next_level); + next_level.clear(); + + next_level = compute_node_order(); + + // check if the final/PI level is reached + bool f_final_level = check_final_level(next_level); + + // Process all other levels + while (!next_level.empty() && !f_final_level) + { + // Push the level to the node array + ntk_lvls.push_back(next_level); + lvl_pairs.clear(); + + // Store the nodes of the next level + for (const auto& cur_node : next_level) + { + fis.clear(); + + // There is one slice in the H-Graph for each node in the level + compute_slice_delays(cur_node); + } + // Clear before starting computations on the next level + next_level.clear(); + // Compute the next level + next_level = compute_node_order(); + // Check if we are at the final level + f_final_level = check_final_level(next_level); + } + // Push the final level (PIs) + if (f_final_level) + { + ntk_lvls.push_back(next_level); + } + + // create virtual pi network + auto virtual_ntk = create_virtual_pi_ntk_from_duplicated_nodes(ntk, ntk_lvls, ntk_lvls_new); + + // the ntk_levels were created in reverse order + std::reverse(ntk_lvls_new.begin(), ntk_lvls_new.end()); + + // assign the ranks in the virtual network based on ntk_lvls_new + virtual_ntk.update_ranks(); + virtual_ntk.set_all_ranks(ntk_lvls_new); + + return virtual_ntk; + } + + private: + /* + * The input network. + */ + Ntk ntk{}; + /* + * The currently node_pairs used in the current level. + */ + std::vector>> lvl_pairs{}; + /* + * The fanin nodes. + */ + std::vector> fis{}; + /* + * The network stored as levels. + */ + std::vector>> ntk_lvls{}; + /* + * The stats of the node_duplication class. + */ + node_duplication_planarization_params ps{}; +}; + +} // namespace detail + +/** + * Implements a planarization mechanism for networks using a H-Graph strategy for node duplication. + * + * The planarization achieved by this function solves the Node Duplication Crossing Minimization (NDCE) problem by + * finding the shortest x-y path in the H-graph for every level in the network. An H-graph describes edge relations + * between two levels in a network, with one level assumed as fixed, starting at the Primary Outputs (POs). By finding + * the shortest path from the source (x) to the sink (y) in this H-graph, an optimal solution for the NDCE problem for + * each level is found. The function constructs an H-graph that captures edge relations between two levels within the + * graph and computes the shortest x-y paths on the H-graph, traversing from the POs towards the Primary Inputs (PIs). + * + * @tparam NtkDest Destination network type. + * @tparam NtkSrc Source network type. + * @param ntk_src Source network to be utilized for the planarization. + * @param ps Node duplication parameters used in the computation. + * + * @return A view of the planarized virtual_pi_network created in the format of mutable_rank_view. + */ +template +[[nodiscard]] virtual_pi_network +node_duplication_planarization(const NtkSrc& ntk_src, const node_duplication_planarization_params& ps = {}) +{ + static_assert(mockturtle::is_network_type_v, "NtkSrc is not a network type"); + static_assert(mockturtle::has_create_node_v, "NtkSrc does not implement the create_node function"); + static_assert(mockturtle::has_rank_position_v, "NtkSrc does not implement the rank_position function"); + + assert(is_balanced(ntk_src) && "Networks have to be balanced for this duplication"); + + detail::node_duplication_planarization_impl p{ntk_src, ps}; + + std::vector>> ntk_lvls_new; + + auto result = p.run(ntk_lvls_new); + + assert(check_planarity(result) && "Planarization failed: Network should be planar"); + + return result; +} + +} // namespace fiction + +#endif // FICTION_NODE_DUPLICATION_PLANARIZATION_HPP diff --git a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp new file mode 100644 index 0000000000..de1e4bb24a --- /dev/null +++ b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp @@ -0,0 +1,1187 @@ +// +// Created by benjamin on 21.01.25. +// + +#ifndef FICTION_ORTHOGONAL_PLANAR_HPP +#define FICTION_ORTHOGONAL_PLANAR_HPP + +#include "fiction/algorithms/properties/check_planarity.hpp" +#include "fiction/layouts/clocking_scheme.hpp" +#include "fiction/traits.hpp" +#include "fiction/utils/network_utils.hpp" +#include "fiction/utils/placement_utils.hpp" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if (PROGRESS_BARS) +#include +#endif + +namespace fiction +{ + +namespace detail +{ + +// Define a 3D array using std::array and encapsulate it in a function +std::array, 4>, 3>, 3>, 2>& get_buffer_lookup() +{ + static std::array, 4>, 3>, 3>, 2> array = { + { // Array + {{ // Unconnected + {{// East + {{ + {0, 0}, + {1, 0}, + {1, 1}, + {0, 1} // gap 0 + }}, + {{ + {0, 0}, + {0, 0}, + {1, 0}, + {0, 0}, // gap 1 + + }}, + {{ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, // gap 2 and higher + + }}}}, + {{// South + {{ + {3, 0}, + {2, 0}, + {0, 0}, + {0, 0} // gap 0; only first two entries used + }}, + {{ + {3, 0}, + {2, 0}, + {0, 0}, + {0, 0} // gap 1 + }}, + {{ + {3, 0}, + {2, 0}, + {0, 0}, + {0, 0} // gap 2 + }}}}, + {{// Free + {{ + {0, 0}, + {1, 0}, + {2, 0}, + {3, 0} // gap 0 + }}, + {{ + {0, 0}, + {0, 0}, + {1, 0}, + {0, 0} // gap 1 + }}, + {{ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} // gap 2 and higher + }}}}}}, + {{ // Connected + {{// East + {{ + {0, 0}, + {0, 0}, + {0, 1}, + {0, 1} // gap 0 + }}, + {{ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} // gap 1 + }}, + {{ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} // gap 2 and higher + }}}}, + {{// South + {{ + {3, 0}, + {3, 0}, + {0, 0}, + {0, 0} // gap 0 + }}, + {{ + {3, 0}, + {3, 0}, + {0, 0}, + {0, 0} // gap 1 + }}, + {{ + {3, 0}, + {3, 0}, + {0, 0}, + {0, 0} // gap 2 and higher + }}}}, + {{// Free + {{ + {0, 0}, + {0, 0}, + {3, 0}, + {3, 0} // gap 0 + }}, + {{ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} // gap 1 + }}, + {{ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} // gap 2 + }}}}}}}}; + return array; +} + +// Define a 3D array using std::array and encapsulate it in a function +std::array, 4>, 3>, 3>, 4>& get_fanout_lookup() +{ + static std::array, 4>, 3>, 3>, 4> array = { + { // Array + {{ // Type Fo 1+2 + {{// East + {{ + {1, 0}, + {1, 1}, + {1, 2}, + {1, 1} // gap 0 + }}, + {{ + {1, 0}, + {1, 0}, + {1, 1}, + {1, 0} // gap 1 + }}, + {{ + {1, 0}, + {1, 0}, + {1, 0}, + {1, 0} // gap 2 + }}}}, + {{// South + {{ + {2, 0}, + {2, 1}, + {0, 0}, + {0, 0} // gap 0 + }}, + {{ + {2, 0}, + {2, 1}, + {0, 0}, + {0, 0} // gap 1 + }}, + {{ + {2, 0}, + {2, 1}, + {0, 0}, + {0, 0} // gap 2 + }}}}, + {{// Free + {{ + {1, 0}, + {2, 0}, + {2, 1}, + {2, 0} // gap 0 + }}, + {{ + {1, 0}, + {1, 0}, + {2, 0}, + {1, 0} // gap 1 + }}, + {{ + {1, 0}, + {1, 0}, + {1, 0}, + {1, 0} // gap 2 + }}}}}}, + {{ // Type F1 + {{// East + {{ + {0, 0}, + {0, 1}, + {0, 2}, + {0, 1} // gap 0 + }}, + {{ + {0, 0}, + {0, 0}, + {0, 1}, + {0, 0} // gap 1 + }}, + {{ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} // gap 2 + }}}}, + {{// South + {{ + {3, 0}, + {3, 1}, + {0, 0}, + {0, 0} // gap 0 + }}, + {{ + {3, 0}, + {3, 1}, + {0, 0}, + {0, 0} // gap 1 + }}, + {{ + {3, 0}, + {3, 1}, + {0, 0}, + {0, 0} // gap 2 + }}}}, + {{// Free + {{ + {0, 0}, + {3, 0}, + {3, 1}, + {3, 0} // gap 0 + }}, + {{ + {0, 0}, + {0, 0}, + {3, 0}, + {0, 0} // gap 1 + }}, + {{ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} // gap 2 + }}}}}}, + {{ // Type F2 + {{// East + {{ + {1, 0}, + {1, 0}, + {1, 1}, + {1, 1} // gap 0 + }}, + {{ + {1, 0}, + {1, 0}, + {1, 0}, + {1, 0} // gap 1 + }}, + {{ + {1, 0}, + {1, 0}, + {1, 0}, + {1, 0} // gap 2 + }}}}, + {{// South + {{ + {2, 0}, + {2, 0}, + {0, 0}, + {0, 0} // gap 0 + }}, + {{ + {2, 0}, + {2, 0}, + {0, 0}, + {0, 0} // gap 1 + }}, + {{ + {2, 0}, + {2, 0}, + {0, 0}, + {0, 0} // gap 2 + }}}}, + {{// Free + {{ + {1, 0}, + {1, 0}, + {2, 0}, + {2, 0} // gap 0 + }}, + {{ + {1, 0}, + {1, 0}, + {1, 0}, + {1, 0} // gap 1 + }}, + {{ + {1, 0}, + {1, 0}, + {1, 0}, + {1, 0} // gap 2 + }}}}}}, + {{ // Type F3 + {{// East + {{ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} // gap 0 + }}, + {{ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} // gap 1 + }}, + {{ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} // gap 2 + }}}}, + {{// South + {{ + {3, 0}, + {3, 0}, + {0, 0}, + {0, 0} // gap 0 + }}, + {{ + {3, 0}, + {3, 0}, + {0, 0}, + {0, 0} // gap 1 + }}, + {{ + {3, 0}, + {3, 0}, + {0, 0}, + {0, 0} /// gap 2 + }}}}, + {{// Free + {{ + {0, 0}, + {0, 0}, + {3, 0}, + {3, 0} // gap 0 + }}, + {{ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} // gap 1 + }}, + {{ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} // gap 2 + }}}}}}}}; + return array; +} + +template +uint64_t calculate_fanout_connection_type(const Ntk& ntk, mockturtle::node n) +{ + // order the POs + if (ntk.is_po(n)) + { + return 0; + } + auto fo = ntk.fanout(n); + assert(fo.size() == 2); + std::sort(fo.begin(), fo.end(), [&ntk](int a, int b) { return ntk.rank_position(a) < ntk.rank_position(b); }); + if (ntk.fanin_size(fo[0]) == 1 && ntk.fanin_size(fo[1]) == 1) + { + return 0; + } + if (ntk.fanin_size(fo[0]) == 1) + { + return 1; + } + if (ntk.fanin_size(fo[1]) == 1) + { + return 2; + } + // both fan-puts are connected with a neighbour + return 3; +} + +template +uint64_t calculate_predecessor_gap(const Ntk& ntk, mockturtle::node_map, Ntk>& node2pos, + uint64_t lvl, mockturtle::node n) +{ + // return if in the PI level + if (lvl == 0) + { + return 0; + } + + // calculate the rank of the predecessor node + auto fc = fanins(ntk, n); + + mockturtle::node nd = n; + if (fc.fanin_nodes.size() == 2) + { + std::sort(fc.fanin_nodes.begin(), fc.fanin_nodes.end(), + [&ntk](int a, int b) { return ntk.rank_position(a) < ntk.rank_position(b); }); + } + auto pre = fc.fanin_nodes[0]; + const auto r = ntk.rank_position(pre); + + // return if no neighbour + if (r == 0) + { + return 0; + } + + // calculate the level of the predecessor node + const auto l = lvl - 1; + + // get the neighbour with lower rank of the predecessor + const auto pre_neighbour = ntk.at_rank_position(l, r - 1); + + // calculate the gap size + auto pre1_t = static_cast>(node2pos[pre]); + auto pre2_t = static_cast>(node2pos[pre_neighbour]); + + assert(pre1_t.y > pre2_t.y); + return std::min(pre1_t.y - pre2_t.y - 1, 2); +} + +template +uint64_t calculate_connection(const Ntk& ntk, mockturtle::node n) +{ + if (ntk.is_po(n)) + { + return false; + } + auto fo = ntk.fanout(n); + assert(fo.size() == 1); + if (ntk.fanin_size(fo[0]) == 2) + { + auto fc = fanins(ntk, fo[0]); + auto pre = fc.fanin_nodes; + assert(pre.size() == 2); + std::sort(pre.begin(), pre.end(), [&ntk](int a, int b) { return ntk.rank_position(a) < ntk.rank_position(b); }); + if (pre[1] == n) + { + return 1; + } + } + return 0; +} + +template +uint64_t calculate_allowed_orientation(const Ntk& ntk, mockturtle::node n) +{ + auto fc = fanins(ntk, n); + assert(fc.fanin_nodes.size() == 1); + auto pre = fc.fanin_nodes[0]; + if (ntk.is_fanout(pre)) + { + auto fo = ntk.fanout(pre); + assert(fo.size() == 2); + std::sort(fo.begin(), fo.end(), [&ntk](int a, int b) { return ntk.rank_position(a) < ntk.rank_position(b); }); + if (n == fo[0]) // east + { + return 0; + } + assert(n == fo[1]); + return 1; + } + return 2; +} + +template +int calculate_start_orientation(Ntk& ntk, uint32_t lvl) +{ + int orientation = 0; + if (lvl == 0) + { + return orientation; + } + ntk.foreach_node_in_rank(lvl, + [&ntk, &orientation](const auto& n) + { + if (orientation > 0) + { + return; + } + if (ntk.fanin_size(n) == 2) + { + // it is 3 but return makes -1 + orientation = 4; + return; + } + if (const auto fc = fanins(ntk, n); fc.fanin_nodes.size() == 1) + { + const auto& pre = fc.fanin_nodes[0]; + if (ntk.is_fanout(pre) && ntk.fanout_size(pre) == 2) + { + // it is 0 but return makes -1 + orientation = 1; + return; + } + } + if(ntk.fanout_size(n) == 2) + { + // it is 0 but return makes -1 + orientation = 1; + return; + } + + }); + return (orientation == 0) ? 0 : (orientation - 1); + // instead of just returning 0, here the buffers could be wired in a way they are closer together and hence overhead + // produced by two input nodes is reduced +} + +template +std::tuple, std::vector> +compute_pr_variables(const Ntk& ntk, const Lyt& lyt, mockturtle::node_map, Ntk> node2pos, + uint32_t lvl) +{ + // ToDo: compute the starting orientation + std::vector orientation(ntk.rank_width(lvl)); + std::vector new_lines(ntk.rank_width(lvl)); + // get the lookup tables for the gate types + const auto& buffer_lu = get_buffer_lookup(); + const auto& fanout_lu = get_fanout_lookup(); + + ntk.foreach_node_in_rank( + lvl, + [&ntk, &node2pos, &lvl, &orientation, &new_lines, &buffer_lu, &fanout_lu](const auto& n, const auto& i) + { + // calculate the gap between the predecessors + uint64_t gap = calculate_predecessor_gap(ntk, node2pos, lvl, n); + + // needs gap and orientation as input + if (ntk.fanin_size(n) == 2) // complete + { + if (i != 0) + { + if (orientation[i - 1] == 2 && gap == 0) + { + orientation[i] = 1; + } + } + } + else if (ntk.fanin_size(n) == 1) + { + // allowed orientation Flag e = 0, s = 1, free = 2 + const uint64_t allowed_orientation = calculate_allowed_orientation(ntk, n); + assert(allowed_orientation < 3); + + // needs the type of connection (F1+2, F1, F2), allowed_orientation, gap, + // orientation, new_line as input + if (ntk.is_fanout(n)) + { + // calculate the type of connection F1+2 = 0, F1 = 1, F2 = 2, F0 = 3; + const uint64_t fanout_connection_type = calculate_fanout_connection_type(ntk, n); + assert(fanout_connection_type < 4); + if (i != 0) + { + const std::pair pair = + fanout_lu[fanout_connection_type][allowed_orientation][gap][orientation[i - 1]]; + orientation[i] = pair.first; + new_lines[i] = pair.second; + } + else if (fanout_connection_type == 0 || fanout_connection_type == 2) + { + // i == 0 + orientation[i] = 1; + } + } + // needs the type of connection (connected, unconnected), allowed_orientation, gap, + // orientation, new_line as input + else + { + if (i != 0) + { + // Connected Flag + uint64_t connected = calculate_connection(ntk, n); + const std::pair pair = + buffer_lu[connected][allowed_orientation][gap][orientation[i - 1]]; + orientation[i] = pair.first; + new_lines[i] = pair.second; + } + else + { + // i == 0 + orientation[i] = calculate_start_orientation(ntk, lvl); + } + } + } + }); + + const auto ret = std::make_tuple(orientation, new_lines); + return ret; +} + +template +std::vector compute_two_input_indices(const Ntk& ntk, uint64_t lvl) +{ + std::vector two_input_indices; + ntk.foreach_node_in_rank(lvl, + [&ntk, &two_input_indices, &lvl](const auto& n, const auto& i) + { + if (ntk.fanin_size(n) == 2) + { + two_input_indices.emplace_back(i); + } + }); + return two_input_indices; +} + +template +std::vector calculate_two_input_new_lines(const Ntk& ntk, + mockturtle::node_map, Ntk>& node2pos, + uint32_t lvl) +{ + std::vector cluster_new_lines; + ntk.foreach_node_in_rank(lvl, + [&ntk, &node2pos, &cluster_new_lines](const auto& n) + { + auto fc = fanins(ntk, n); + // calculate gaps due to AND gates + if (fc.fanin_nodes.size() == 2) + { + std::sort(fc.fanin_nodes.begin(), fc.fanin_nodes.end(), [&ntk](int a, int b) + { return ntk.rank_position(a) < ntk.rank_position(b); }); + // compute the max_gap for two fan-ins of anode + const auto &pre1 = fc.fanin_nodes[0], &pre2 = fc.fanin_nodes[1]; + + auto pre1_t = static_cast>(node2pos[pre1]), + pre2_t = static_cast>(node2pos[pre2]); + + cluster_new_lines.emplace_back(static_cast(pre2_t.y - pre1_t.y - 1)); + } + }); + return cluster_new_lines; +} + +void adjust_final_values(std::vector& x, std::vector& y, + const std::vector& two_input_indices, + const std::vector& two_input_new_lines) +{ + // Find the max element in two_input_new_lines and its index + auto max_it = std::max_element(two_input_new_lines.begin(), two_input_new_lines.end()); + const uint64_t max_new_lines_two_inputs = *max_it; + const auto max_new_lines_two_inputs_index = + static_cast(std::distance(two_input_new_lines.begin(), max_it)); + + // Find max sum in x + y and its index + auto max_xy_it = std::max_element(x.begin(), x.end(), + [&](std::size_t i, std::size_t j) { return (x[i] + y[i]) < (x[j] + y[j]); }); + + const uint64_t max_new_lines = x[*max_xy_it] + y[*max_xy_it]; + const std::size_t mnl_iterator = *max_xy_it; + + // Determine the center index + uint64_t center = 0, max = 0; + if (max_new_lines > max_new_lines_two_inputs) + { + center = mnl_iterator; + max = max_new_lines; + } + else + { + center = two_input_indices[max_new_lines_two_inputs_index]; + max = max_new_lines_two_inputs; + } + + // Create a lookup map for O(1) access instead of O(n) std::find() + std::unordered_map two_input_map; + for (std::size_t i = 0; i < two_input_indices.size(); ++i) + { + two_input_map[two_input_indices[i]] = i; + } + + // Iterate through x and y to update values efficiently + for (std::size_t i = 0; i < x.size(); ++i) + { + auto it = two_input_map.find(i); + if (it != two_input_map.end()) // Two fan-in node (found in two_input_indices) + { + const std::size_t idx = it->second; // Get the corresponding index + const uint64_t diff = max - x[i] - y[i] - two_input_new_lines[idx]; + + if (i < center) + { + y[i] += diff; + } + else if (i > center) + { + x[i] += diff; + } + else + { + assert(x[i] + y[i] + two_input_new_lines[idx] == max); + } + } + else // One fan-in node + { + const uint64_t diff = max - x[i] - y[i]; + + if (i < center) + { + y[i] += diff; + } + else if (i > center) + { + x[i] += diff; + } + else + { + assert(x[i] + y[i] == max); + } + } + } +} + +template +std::pair, std::vector> +compute_wiring(const Ntk& ntk, mockturtle::node_map, Ntk>& node2pos, + const std::vector& new_lines, uint64_t lvl) +{ + // Initialize 2-input indices + const auto two_input_indices = compute_two_input_indices(ntk, lvl); + const auto two_input_new_lines = calculate_two_input_new_lines(ntk, node2pos, lvl); + + // Initialize cluster indices + std::size_t cluster_index_start = 0; + + // Initialize the x and y vectors + std::vector x(ntk.rank_width(lvl)); + std::vector y(ntk.rank_width(lvl)); + + // Initialize the x and y vectors + std::vector cluster_new_lines(two_input_indices.size() + 1); + + // Example + /*std::vector two_input_indices = {0, 4, 9, 16, 20}; + std::vector two_input_new_lines = {0, 0, 2, 5, 0}; + std::vector x(21); + std::vector y(21); + std::vector cluster_new_lines(two_input_indices.size() + 1); + std::size_t cluster_index_start = 0; + std::vector new_lines = {0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0};*/ + + // if there is no two input gate, then there is only one cluster + if (two_input_indices.empty()) + { + for (std::size_t j = 0; j < ntk.rank_width(lvl); ++j) + { + x[j] = std::accumulate(new_lines.begin() + static_cast(j) + 1, + new_lines.begin() + static_cast(ntk.rank_width(lvl)), 0UL); + y[j] = (j == 0) ? new_lines[j] : y[j - 1] + new_lines[j]; + } + return std::make_pair(x, y); + } + + // Iterate over all cluster indices + uint64_t propagate_right = 0; + for (std::size_t i = 0; i < two_input_indices.size() + 1; ++i) + { + std::size_t cluster_index_end = 0; + if (i == two_input_indices.size()) + { + cluster_index_end = new_lines.size(); + } + else + { + cluster_index_end = two_input_indices[i]; + } + + // Compute x and y for the current cluster + for (std::size_t j = cluster_index_start; j < cluster_index_end; ++j) + { + // adjust x values + x[j] = std::accumulate(new_lines.begin() + static_cast(j) + 1, + new_lines.begin() + static_cast(cluster_index_end), 0UL); + + // adjust y values with propagation to the right (direction based on a 1D vector) + y[j] = (j == 0) ? new_lines[j] : y[j - 1] + new_lines[j]; + y[j] += (j == cluster_index_start) ? propagate_right : 0; + } + + // Also set the new lines for two input nodes + if (cluster_index_start != 0) + { + y[cluster_index_start - 1] += propagate_right; + } + + // Save the number of new lines in a cluster + if (cluster_index_start != cluster_index_end) + { + cluster_new_lines[i] = x[cluster_index_start]; + } + + // Save the right propagated new_lines + propagate_right = (two_input_new_lines[i] > cluster_new_lines[i] + propagate_right) ? + 0 : + propagate_right + cluster_new_lines[i] - two_input_new_lines[i]; + + // Move to the next cluster + cluster_index_start = cluster_index_end + 1; + } + + uint64_t propagate_left = 0; + // propagate left (direction based on a 1D vector) + for (std::size_t i = two_input_indices.size(); i > 0; --i) + { + std::size_t cluster_index_end = 0; + + cluster_index_end = two_input_indices[i - 1]; + + if (i == 1) + { + cluster_index_start = 0; + } + else + { + cluster_index_start = two_input_indices[i - 2] + 1; + } + + // Save the left propagated new_lines + propagate_left = (two_input_new_lines[i - 1] > cluster_new_lines[i] + propagate_left) ? + 0 : + propagate_left + cluster_new_lines[i] - two_input_new_lines[i - 1]; + + // ALso set the new lines for two input nodes + if (cluster_index_end != new_lines.size() - 1) + { + x[cluster_index_end] += propagate_left; + } + + for (std::size_t j = cluster_index_start; j < cluster_index_end; ++j) + { + x[j] += propagate_left; + } + } + + adjust_final_values(x, y, two_input_indices, two_input_new_lines); + + return std::make_pair(x, y); +} + +template +class orthogonal_planar_v2_impl +{ + public: + orthogonal_planar_v2_impl(const Ntk& src, const orthogonal_physical_design_params& p, + orthogonal_physical_design_stats& st) : + ntk{mockturtle::names_view(mockturtle::fanout_view(src))}, + ps{p}, + pst{st} + {} + + Lyt run() + { + // measure run time + mockturtle::stopwatch stop{pst.time_total}; + // + using node = typename Ntk::node; + + mockturtle::node_map, decltype(ntk)> node2pos{ntk}; + + aspect_ratio size_ = {0, 0}; + + // instantiate the layout + Lyt layout{size_, twoddwave_clocking(ps.number_of_clock_phases)}; + + // reserve PI nodes without positions + auto pi2node = reserve_input_nodes(layout, ntk); + + // first x-pos to use for gates is 1 because PIs take up the 0th column + tile latest_pos{1, 0}; + +#if (PROGRESS_BARS) + // initialize a progress bar + mockturtle::progress_bar bar{ctn.color_ntk.size(), "[i] arranging layout: |{0}|"}; +#endif + + tile place_t{0, 0}; + tile first_pos = {ntk.num_pis() - 1, 0}; + + for (uint32_t lvl = 0; lvl < ntk.depth() + 1; lvl++) + { + const auto variable_tuple = compute_pr_variables(ntk, layout, node2pos, lvl); + const auto orientation = std::get<0>(variable_tuple); + const auto new_lines = std::get<1>(variable_tuple); + + const auto wiring = compute_wiring(ntk, node2pos, new_lines, lvl); + const auto& x = wiring.first; + const auto& y = wiring.second; + + ntk.foreach_node_in_rank( + lvl, + [this, &layout, &pi2node, &node2pos, &orientation, &first_pos, &place_t, &x, &y](const auto& n, + const auto& i) + { + if (!ntk.is_constant(n)) + { + // if node is a PI, move it to its correct position + if (ntk.is_pi(n)) + { + if (ntk.rank_position(n) == 0) + { + node2pos[n] = layout.move_node(pi2node[n], first_pos); + place_t = first_pos; + } + else + { + place_t = {place_t.x - 1, place_t.y + 1}; + // node2pos[n] = layout.move_node(pi2node[n], prec_pos); + if (place_t.x == 0) + { + node2pos[n] = layout.move_node(pi2node[n], place_t); + } + else if (place_t.x < (first_pos.x / 2)) + { + node2pos[n] = layout.move_node(pi2node[n], {0, place_t.y}); + + node2pos[n] = + layout.create_buf(wire_east(layout, {0, place_t.y}, place_t), place_t); + } + else + { + node2pos[n] = layout.move_node(pi2node[n], {place_t.x, 0}); + + node2pos[n] = + layout.create_buf(wire_south(layout, {place_t.x, 0}, place_t), place_t); + } + } + } + // if n has only one fanin + else if (const auto fc = fanins(ntk, n); fc.fanin_nodes.size() == 1) + { + const auto& pre = fc.fanin_nodes[0]; + auto pre_t = static_cast>(node2pos[pre]); + + // Resolve new lines. Special case for the second fan-out of a fan-out node + if (!(ntk.is_fanout(pre) && (orientation[i] == 2 || orientation[i] == 3))) + { + if (x[i] != 0) + { + wire_east(layout, pre_t, {pre_t.x + x[i] + 1, pre_t.y}); + pre_t.x += x[i]; + } + if (y[i] != 0) + { + wire_south(layout, pre_t, {pre_t.x, pre_t.y + y[i] + 1}); + pre_t.y += y[i]; + } + } + // horizontal (corresponding to colored east) + if (orientation[i] == 0 || orientation[i] == 1) + { + place_t.y = pre_t.y; + place_t.x = pre_t.x + 1; + } + else + { + assert(orientation[i] == 2 || orientation[i] == 3); + + // Resolve new lines. Special case for the second fan-out of a fan-out node + if (ntk.is_fanout(pre) && (orientation[i] == 2 || orientation[i] == 3)) + { + // n the special case the + if (x[i] != 0) + { + pre_t.x += x[i]; + } + if (y[i] != 0) + { + wire_south(layout, pre_t, {pre_t.x, pre_t.y + y[i] + 1}); + pre_t.y += y[i]; + } + } + place_t.y = pre_t.y + 1; + place_t.x = pre_t.x; + } + + node2pos[n] = connect_and_place(layout, place_t, ntk, n, pre_t); + + if (ntk.rank_position(n) == 0) + { + first_pos = place_t; + } + } + else // if node has two fanins (or three fanins with one of them being constant) + { + const auto &pre1 = fc.fanin_nodes[0], pre2 = fc.fanin_nodes[1]; + + auto pre1_t = static_cast>(node2pos[pre1]), + pre2_t = static_cast>(node2pos[pre2]); + + // Resolve new lines + if (x[i] != 0) + { + wire_east(layout, pre1_t, {pre1_t.x + x[i] + 1, pre1_t.y}); + wire_east(layout, pre2_t, {pre2_t.x + x[i] + 1, pre2_t.y}); + pre1_t.x += x[i]; + pre2_t.x += x[i]; + } + if (y[i] != 0) + { + wire_south(layout, pre1_t, {pre1_t.x, pre1_t.y + y[i] + 1}); + wire_south(layout, pre2_t, {pre2_t.x, pre2_t.y + y[i] + 1}); + pre1_t.y += y[i]; + pre2_t.y += y[i]; + } + + // pre1_t is the northwards/eastern tile. + if (pre2_t.y < pre1_t.y) + { + std::swap(pre1_t, pre2_t); + } + + place_t = {pre1_t.x, pre2_t.y}; + + node2pos[n] = connect_and_place(layout, place_t, ntk, n, pre1_t, pre2_t, fc.constant_fanin); + + if (ntk.rank_position(n) == 0) + { + first_pos = place_t; + } + } + } + }); + } + + // layout.resize({first_pos.x + 1, place_t.y +3, 0}); + // debug::write_dot_layout(layout); + + std::unordered_map countMap; + int add_line = 0; + // the number of outputs on a node is limited to 2, due to fanout substitution + ntk.foreach_po( + [&](const auto& po) + { + if (!ntk.is_constant(po)) + { + const auto n_s = node2pos[po]; + auto po_tile = static_cast>(n_s); + if (countMap[po] < 2) // Check if the count is less than 2 + { + // Adjust the position based on whether it's the first or second occurrence + if (countMap[po] == 1) + { + if (po_tile.y == place_t.y) + { + add_line = 1; + } + po_tile = static_cast>(wire_south(layout, po_tile, {po_tile.x, po_tile.y + 2})); + } + const tile anker{po_tile}; + po_tile.x = first_pos.x + 1; + + // Create PO and increment the count + layout.create_po(wire_east(layout, anker, po_tile), + ntk.has_output_name(po_counter) ? ntk.get_output_name(po_counter++) : + fmt::format("po{}", po_counter++), + po_tile); + countMap[po]++; + } + else + { + assert(false); + } + } + }); + + layout.resize({first_pos.x + 1, place_t.y + add_line, 0}); + + // layout.resize({10, 10, 0}); + + // restore possibly set signal names + restore_names(ntk, layout, node2pos); + + // statistical information + pst.x_size = layout.x() + 1; + pst.y_size = layout.y() + 1; + pst.num_gates = layout.num_gates(); + pst.num_wires = layout.num_wires(); + + return layout; + } + + private: + mockturtle::names_view> ntk; + + orthogonal_physical_design_params ps; + orthogonal_physical_design_stats& pst; + + uint32_t po_counter{0}; +}; + +} // namespace detail + +/** + * Description + */ +template +Lyt orthogonal_planar_v2(const Ntk& ntk, orthogonal_physical_design_params ps = {}, + orthogonal_physical_design_stats* pst = nullptr) +{ + static_assert(is_gate_level_layout_v, "Lyt is not a gate-level layout"); + static_assert(mockturtle::is_network_type_v, + "Ntk is not a network type"); // Ntk is being converted to a topology_network anyway, therefore, + // this is the only relevant check here + + // check for input degree + if (has_high_degree_fanin_nodes(ntk, 2)) + { + throw high_degree_fanin_exception(); + } + + // check for planarity + if (!check_planarity(ntk)) + { + throw std::runtime_error("Input network has to be planar"); + } + + orthogonal_physical_design_stats st{}; + detail::orthogonal_planar_v2_impl p{ntk, ps, st}; + + auto result = p.run(); + + if (pst) + { + *pst = st; + } + + return result; +} + +} // namespace fiction +#endif // FICTION_ORTHOGONAL_PLANAR_HPP diff --git a/test/algorithms/network_transformation/node_duplication_planarization.cpp b/test/algorithms/network_transformation/node_duplication_planarization.cpp new file mode 100644 index 0000000000..cb71ea1f26 --- /dev/null +++ b/test/algorithms/network_transformation/node_duplication_planarization.cpp @@ -0,0 +1,179 @@ +// +// Created by benjamin on 11.06.24. +// + +#include + +#include "fiction/algorithms/network_transformation/fanout_substitution.hpp" +#include "fiction/algorithms/network_transformation/network_balancing.hpp" + +#include +#include +#include + +#include +#include + +using namespace fiction; + +TEST_CASE("Planarize technology ntk", "[node-duplication-planarization]") +{ + technology_network tec{}; + + const auto x1 = tec.create_pi(); + const auto x2 = tec.create_pi(); + const auto x3 = tec.create_pi(); + const auto x4 = tec.create_pi(); + const auto x5 = tec.create_pi(); + const auto f1 = tec.create_not(x2); + const auto f2 = tec.create_nary_and({x1, x2, x3, x4}); + const auto f3 = tec.create_nary_or({x3, x4, x5}); + tec.create_po(f1); + tec.create_po(f2); + tec.create_po(f3); + + network_balancing_params ps; + ps.unify_outputs = true; + + const auto tec_b = fiction::network_balancing( + fiction::fanout_substitution(tec), ps); + + const auto vpi_r = fiction::mutable_rank_view(tec_b); + + auto planarized_maj = node_duplication_planarization(vpi_r); + + mockturtle::equivalence_checking_stats st; + const auto cec_m = + mockturtle::equivalence_checking(*fiction::virtual_miter(tec, planarized_maj), {}, &st); + REQUIRE(cec_m.has_value()); + CHECK(*cec_m == 1); +} + +TEST_CASE("Planarize ntk with 3-ary node", "[node-duplication-planarization]") +{ + technology_network tec{}; + + const auto x1 = tec.create_pi(); + const auto x2 = tec.create_pi(); + const auto x3 = tec.create_pi(); + const auto x4 = tec.create_pi(); + const auto x5 = tec.create_pi(); + const auto f1 = tec.create_not(x2); + const auto f2 = tec.create_nary_and({x1, x2, x3, x4}); + const auto f3 = tec.create_nary_or({x3, x4, x5}); + const auto f4 = tec.create_maj(x1, x2, f3); + tec.create_po(f1); + tec.create_po(f2); + tec.create_po(f3); + tec.create_po(f4); + + network_balancing_params ps; + ps.unify_outputs = true; + + const auto tec_b = fiction::network_balancing( + fiction::fanout_substitution(tec), ps); + + const auto vpi_r = fiction::mutable_rank_view(tec_b); + + auto planarized_maj = node_duplication_planarization(vpi_r); + + mockturtle::equivalence_checking_stats st; + const auto cec_m = + mockturtle::equivalence_checking(*fiction::virtual_miter(tec, planarized_maj), {}, &st); + REQUIRE(cec_m.has_value()); + CHECK(*cec_m == 1); +} + +TEST_CASE("Buffer AIG and planarize technology_network", "[node-duplication-planarization]") +{ + mockturtle::aig_network aig{}; + + const auto x1 = aig.create_pi(); + const auto x2 = aig.create_pi(); + const auto x3 = aig.create_pi(); + const auto x4 = aig.create_pi(); + const auto x5 = aig.create_pi(); + const auto f1 = aig.create_not(x2); + const auto f2 = aig.create_nary_and({x1, x2, x3, x4}); + const auto f3 = aig.create_nary_or({x3, x4, x5}); + const auto f4 = aig.create_maj(x1, x2, f3); + aig.create_po(f1); + aig.create_po(f2); + aig.create_po(f3); + aig.create_po(f4); + + network_balancing_params ps; + ps.unify_outputs = true; + + const auto aig_b = fiction::network_balancing( + fiction::fanout_substitution(aig), ps); + + const auto vpi_r = fiction::mutable_rank_view(aig_b); + + auto planarized_maj = node_duplication_planarization(vpi_r); + + mockturtle::equivalence_checking_stats st; + const auto cec_m = + mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_maj), {}, &st); + REQUIRE(cec_m.has_value()); + CHECK(*cec_m == 1); +} + +TEST_CASE("Buffer AIG and planarize technology_network 2", "[node-duplication-planarization]") +{ + mockturtle::aig_network aig{}; + + const auto x1 = aig.create_pi(); + const auto x2 = aig.create_pi(); + const auto f1 = aig.create_and(x1, x2); + const auto f2 = aig.create_or(x1, x2); + aig.create_po(f1); + aig.create_po(f2); + + network_balancing_params ps; + ps.unify_outputs = true; + + const auto aig_b = fiction::network_balancing( + fiction::fanout_substitution(aig), ps); + + const auto vpi_r = fiction::mutable_rank_view(aig_b); + + auto planarized_maj = node_duplication_planarization(vpi_r); + + mockturtle::equivalence_checking_stats st; + const auto cec_m = + mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_maj), {}, &st); + REQUIRE(cec_m.has_value()); + CHECK(*cec_m == 1); +} + +TEST_CASE("Planarize multi output network", "[node-duplication-planarization]") +{ + mockturtle::aig_network aig{}; + + const auto x1 = aig.create_pi(); + const auto x2 = aig.create_pi(); + + const auto a1 = aig.create_and(x1, x2); + + aig.create_po(a1); + aig.create_po(a1); + aig.create_po(a1); + aig.create_po(a1); + + network_balancing_params ps; + ps.unify_outputs = true; + + const auto aig_b = fiction::network_balancing( + fiction::fanout_substitution(aig), ps); + + const auto vpi_r = fiction::mutable_rank_view(aig_b); + + auto planarized_maj = node_duplication_planarization(vpi_r); + + mockturtle::equivalence_checking_stats st; + const auto cec_m = + mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_maj), {}, &st); + REQUIRE(cec_m.has_value()); + CHECK(*cec_m == 1); +} From 12a817b538c912b1caff51c49140a1685ff34265 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 2 Oct 2025 11:09:48 +0000 Subject: [PATCH 03/67] :memo: Update pyfiction docstrings Signed-off-by: GitHub Actions --- .../pyfiction/pybind11_mkdoc_docstrings.hpp | 219 ++++++++++++++++++ 1 file changed, 219 insertions(+) diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index 0caccb74c3..b723f11157 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -4930,6 +4930,8 @@ Template parameter ``WiringReductionLyt``: Parameter ``lyt``: The wiring_reduction_layout to which obstructions will be added.)doc"; +static const char *__doc_fiction_detail_adjust_final_values = R"doc()doc"; + static const char *__doc_fiction_detail_adjust_tile = R"doc(This function adjusts the tile and gates in the layout after deleting wires. It shifts gates to fill the empty coordinates and adjusts the @@ -5115,6 +5117,12 @@ Parameter ``defect_lyt``: Returns: A `CellLyt` object representing the generated cell layout.)doc"; +static const char *__doc_fiction_detail_calculate_allowed_orientation = R"doc()doc"; + +static const char *__doc_fiction_detail_calculate_connection = R"doc()doc"; + +static const char *__doc_fiction_detail_calculate_fanout_connection_type = R"doc()doc"; + static const char *__doc_fiction_detail_calculate_offset_matrix = R"doc(Calculate an offset matrix based on a to-delete list in a `wiring_reduction_layout`. @@ -5141,6 +5149,29 @@ Parameter ``to_delete``: Returns: A 2D vector representing the calculated offset matrix.)doc"; +static const char *__doc_fiction_detail_calculate_pairs = +R"doc(Calculates pairs of nodes from a given vector of nodes. + +This function takes a vector of nodes and returns a vector of node +pairs. Each node pair consists of two nodes from the input vector and +an optional vector of middle nodes. The delay of each node pair is +initialized to infinity. + +Template parameter ``Ntk``: + The network type. + +Parameter ``nodes``: + The vector of nodes. + +Returns: + The vector of node pairs.)doc"; + +static const char *__doc_fiction_detail_calculate_predecessor_gap = R"doc()doc"; + +static const char *__doc_fiction_detail_calculate_start_orientation = R"doc()doc"; + +static const char *__doc_fiction_detail_calculate_two_input_new_lines = R"doc()doc"; + static const char *__doc_fiction_detail_check_and_optimize_po_positions = R"doc(Utility function that checks and optimizes PO positions after each gate relocation iteration. This function moves POs that are not @@ -5787,6 +5818,8 @@ Parameter ``lyt``: The number of primary outputs that are placed to the right of the middle primary output.)doc"; +static const char *__doc_fiction_detail_compute_two_input_indices = R"doc()doc"; + static const char *__doc_fiction_detail_connect_and_place = R"doc()doc"; static const char *__doc_fiction_detail_connect_and_place_2 = R"doc()doc"; @@ -5821,6 +5854,37 @@ static const char *__doc_fiction_detail_create_array = R"doc(From https://stackoverflow.com/questions/57756557/initializing-a- stdarray-with-a-constant-value)doc"; +static const char *__doc_fiction_detail_create_virtual_pi_ntk_from_duplicated_nodes = +R"doc(Constructs a planar `virtual_pi_network` based on the `ntk_lvls` +array, which holds the ranks of the duplicated nodes for each level in +the new network. This function creates new nodes for the duplicated +ones and restores their fanin relations using the +`gather_fanin_signals` function. + +For duplicated PIs (Primary Inputs), virtual PIs are created, and the +original PI is stored in a map. + +The auxiliary function `gather_fanin_signals` collects fanin data for +a node and matches it in the `virtual_pi_network`. + +Example: For a level (2, 3, 2, 4, 2), new nodes are created for +duplications (e.g., 2) and stored in the `old2new_v` node_map. This +map is used by `gather_fanin_signals` to establish the correct fanin +relations. + +Template parameter ``Ntk``: + Network type. + +Parameter ``ntk``: + Source network to be utilized for the creation of the + virtual_pi_network. + +Parameter ``ntk_lvls``: + Levels of nodes in the source network. + +Parameter ``ntk_lvls_new``: + Levels of newly created nodes in the virtual_pi_network.)doc"; + static const char *__doc_fiction_detail_create_wiring_reduction_layout = R"doc(Create a wiring_reduction_layout suitable for finding excess wiring based on a Cartesian layout. @@ -7695,6 +7759,10 @@ static const char *__doc_fiction_detail_generate_edge_intersection_graph_impl_ps static const char *__doc_fiction_detail_generate_edge_intersection_graph_impl_run = R"doc()doc"; +static const char *__doc_fiction_detail_get_buffer_lookup = R"doc()doc"; + +static const char *__doc_fiction_detail_get_fanout_lookup = R"doc()doc"; + static const char *__doc_fiction_detail_get_offset = R"doc(Utility function to calculate the offset that has to be subtracted from any x-coordinate on the hexagonal layout. @@ -9155,6 +9223,97 @@ static const char *__doc_fiction_detail_new_gate_location_NONE = R"doc(Do not ch static const char *__doc_fiction_detail_new_gate_location_SRC = R"doc(Check if the source tile is empty.)doc"; +static const char *__doc_fiction_detail_node_duplication_planarization_impl = R"doc()doc"; + +static const char *__doc_fiction_detail_node_duplication_planarization_impl_compute_slice_delays = +R"doc(The H-graph represents all possible orderings of node pairs within a +single network level. A "slice" is created by adding all possible +combinations of a `node_pair` to the H-graph of the level. These +combinations are formed by selecting pairs of nodes from the fan-ins +of the input node: - If the input node has only one fan-in, it is +treated as a single combination. - If the input node has two fan-ins, +there are two possible combinations. + +Each `node_pair` consists of a first and second element. The objective +is to find an ordering of node pairs that maximizes the instances +where the first element of a node_pair matches the second element of +the preceding node_pair. This ordering is given as a linked list. + +This function computes the optimal ordering by calculating delays as +follows: - All combinations of node pairs are iteratively added to a +linked list. - For each combination, the first element of the current +node_pair is compared with the last element of the preceding +node_pairs. - If a connection exists between two node_pairs, the delay +increases by 1; otherwise, it increases by 2. The default delay for +the first node is 1. - If a node_pair lacks a connection, and its +updated delay (increased by 2) is less than the existing delay, the +node_pair's delay is updated accordingly. + +Processed node_pairs are stored in the `lvl_pairs` member for +subsequent delay calculations. + +Parameter ``nd``: + Node in the H-graph. + +Parameter ``border_pis``: + A boolean indicating whether the input PIs (Primary Inputs) should + be propagated to the next)doc"; + +static const char *__doc_fiction_detail_node_duplication_planarization_impl_insert_if_not_first = +R"doc(Inserts a node into a vector if it is unique. + +`This function inserts a node into a vector only if the vector is +empty or the node is not equal to the first element of the vector. If +the vector is not empty and the node is equal to the first element, it +does nothing. An exception occurs if the node was skipped on the +previous insertion attempt due to `vec.front() == node`; in that case, +the node will be inserted this time. + +Parameter ``node``: + The node to be inserted. + +Parameter ``vec``: + The vector to insert the node into.)doc"; + +static const char *__doc_fiction_detail_node_duplication_planarization_impl_lvl_pairs = R"doc()doc"; + +static const char *__doc_fiction_detail_node_duplication_planarization_impl_node_duplication_planarization_impl = R"doc()doc"; + +static const char *__doc_fiction_detail_node_duplication_planarization_impl_ps = R"doc()doc"; + +static const char *__doc_fiction_detail_node_pair = +R"doc(A structure representing a pair of nodes in an H-graph. + +The nodes stored in this struct describe the fanin-edges of a node in +an H-graph. A node pair object holds two nodes, which are saved in the +member 'pair'. These two outer nodes are connected through zero or +more 'middle_nodes'. The fanin order starts with the first node in +'pair', then proceeds through the 'middle_nodes', and ends with the +second node in 'pair'. The order of 'middle_nodes' is arbitrary as +they cannot be further connected to any other nodes. For the +planarization, only the nodes inside the 'pair' are relevant. + +Template parameter ``Ntk``: + Network type for the nodes in the pair.)doc"; + +static const char *__doc_fiction_detail_node_pair_delay = R"doc(Specifies the delay value for the node.)doc"; + +static const char *__doc_fiction_detail_node_pair_fanin_pair = +R"doc(Shared pointer to another instance of node_pair detailing fanin-edge +alignment.)doc"; + +static const char *__doc_fiction_detail_node_pair_node_pair = +R"doc(Standard constructor. + +Parameter ``node1``: + The first node of the fanin-edged node. + +Parameter ``node2``: + The second node of the fanin-edged node. + +Parameter ``delay_value``: + The delay value for the node.)doc"; + static const char *__doc_fiction_detail_non_operationality_reason = R"doc(Reason why a layout is non-operational.)doc"; static const char *__doc_fiction_detail_non_operationality_reason_KINKS = R"doc(Kinks induced the layout to become non-operational.)doc"; @@ -9572,6 +9731,18 @@ static const char *__doc_fiction_detail_orthogonal_impl_pst = R"doc()doc"; static const char *__doc_fiction_detail_orthogonal_impl_run = R"doc()doc"; +static const char *__doc_fiction_detail_orthogonal_planar_v2_impl = R"doc()doc"; + +static const char *__doc_fiction_detail_orthogonal_planar_v2_impl_orthogonal_planar_v2_impl = R"doc()doc"; + +static const char *__doc_fiction_detail_orthogonal_planar_v2_impl_po_counter = R"doc()doc"; + +static const char *__doc_fiction_detail_orthogonal_planar_v2_impl_ps = R"doc()doc"; + +static const char *__doc_fiction_detail_orthogonal_planar_v2_impl_pst = R"doc()doc"; + +static const char *__doc_fiction_detail_orthogonal_planar_v2_impl_run = R"doc()doc"; + static const char *__doc_fiction_detail_physical_population_stability_impl = R"doc(This class implements the simulation of the population stability for a given SiDB layout. It determines the minimum electrostatic potential @@ -16767,6 +16938,52 @@ Parameter ``file``: Parameter ``rfun``: The actual parsing function.)doc"; +static const char *__doc_fiction_node_duplication_planarization = +R"doc(Implements a planarization mechanism for networks using a H-Graph +strategy for node duplication. + +The planarization achieved by this function solves the Node +Duplication Crossing Minimization (NDCE) problem by finding the +shortest x-y path in the H-graph for every level in the network. An +H-graph describes edge relations between two levels in a network, with +one level assumed as fixed, starting at the Primary Outputs (POs). By +finding the shortest path from the source (x) to the sink (y) in this +H-graph, an optimal solution for the NDCE problem for each level is +found. The function constructs an H-graph that captures edge relations +between two levels within the graph and computes the shortest x-y +paths on the H-graph, traversing from the POs towards the Primary +Inputs (PIs). + +Template parameter ``NtkDest``: + Destination network type. + +Template parameter ``NtkSrc``: + Source network type. + +Parameter ``ntk_src``: + Source network to be utilized for the planarization. + +Parameter ``ps``: + Node duplication parameters used in the computation. + +Returns: + A view of the planarized virtual_pi_network created in the format + of mutable_rank_view.)doc"; + +static const char *__doc_fiction_node_duplication_planarization_params = R"doc(Parameters for the node duplication algorithm.)doc"; + +static const char *__doc_fiction_node_duplication_planarization_params_output_order = +R"doc(The output order determines the starting layer for this algorithm. If +this option is turned off, the output order remains the same as in the +provided network. If it is turned on, the outputs are ordered +randomly.)doc"; + +static const char *__doc_fiction_node_duplication_planarization_params_output_order_KEEP_PO_ORDER = R"doc(Keep the PO order from the input network.)doc"; + +static const char *__doc_fiction_node_duplication_planarization_params_output_order_RANDOM_PO_ORDER = R"doc(Randomize the PO order.)doc"; + +static const char *__doc_fiction_node_duplication_planarization_params_po_order = R"doc()doc"; + static const char *__doc_fiction_normalize_layout_coordinates = R"doc(A new layout is constructed and returned that is equivalent to the given cell-level layout. However, its coordinates are normalized, @@ -17690,6 +17907,8 @@ static const char *__doc_fiction_orthogonal_physical_design_stats_x_size = R"doc static const char *__doc_fiction_orthogonal_physical_design_stats_y_size = R"doc()doc"; +static const char *__doc_fiction_orthogonal_planar_v2 = R"doc(Description)doc"; + static const char *__doc_fiction_out_of_cell_names_exception = R"doc()doc"; static const char *__doc_fiction_out_of_cell_names_exception_out_of_cell_names_exception = R"doc()doc"; From c2f2ca9740c0cc9e8409a76de26cd2d5629ef8bf Mon Sep 17 00:00:00 2001 From: benjamin Date: Thu, 2 Oct 2025 13:44:28 +0200 Subject: [PATCH 04/67] :heavy_plus_sign: starting files --- .../physical_design/orthogonal_planar.hpp | 472 +++++------------- .../algorithms/properties/check_planarity.hpp | 122 +++++ 2 files changed, 239 insertions(+), 355 deletions(-) create mode 100644 include/fiction/algorithms/properties/check_planarity.hpp diff --git a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp index de1e4bb24a..a2cb81af65 100644 --- a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp +++ b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp @@ -5,7 +5,6 @@ #ifndef FICTION_ORTHOGONAL_PLANAR_HPP #define FICTION_ORTHOGONAL_PLANAR_HPP -#include "fiction/algorithms/properties/check_planarity.hpp" #include "fiction/layouts/clocking_scheme.hpp" #include "fiction/traits.hpp" #include "fiction/utils/network_utils.hpp" @@ -45,125 +44,45 @@ namespace detail std::array, 4>, 3>, 3>, 2>& get_buffer_lookup() { static std::array, 4>, 3>, 3>, 2> array = { - { // Array - {{ // Unconnected - {{// East - {{ - {0, 0}, - {1, 0}, - {1, 1}, - {0, 1} // gap 0 - }}, - {{ - {0, 0}, - {0, 0}, - {1, 0}, - {0, 0}, // gap 1 - - }}, - {{ - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, // gap 2 and higher - - }}}}, - {{// South - {{ - {3, 0}, - {2, 0}, - {0, 0}, - {0, 0} // gap 0; only first two entries used - }}, - {{ - {3, 0}, - {2, 0}, - {0, 0}, - {0, 0} // gap 1 - }}, - {{ - {3, 0}, - {2, 0}, - {0, 0}, - {0, 0} // gap 2 - }}}}, - {{// Free - {{ - {0, 0}, - {1, 0}, - {2, 0}, - {3, 0} // gap 0 - }}, - {{ - {0, 0}, - {0, 0}, - {1, 0}, - {0, 0} // gap 1 - }}, - {{ - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0} // gap 2 and higher - }}}}}}, - {{ // Connected - {{// East - {{ - {0, 0}, - {0, 0}, - {0, 1}, - {0, 1} // gap 0 - }}, - {{ - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0} // gap 1 - }}, - {{ - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0} // gap 2 and higher - }}}}, - {{// South - {{ - {3, 0}, - {3, 0}, - {0, 0}, - {0, 0} // gap 0 - }}, - {{ - {3, 0}, - {3, 0}, - {0, 0}, - {0, 0} // gap 1 - }}, - {{ - {3, 0}, - {3, 0}, - {0, 0}, - {0, 0} // gap 2 and higher - }}}}, - {{// Free - {{ - {0, 0}, - {0, 0}, - {3, 0}, - {3, 0} // gap 0 - }}, - {{ - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0} // gap 1 - }}, - {{ - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0} // gap 2 - }}}}}}}}; + { // Array + {{// Unconnected + {{ + // East + {{{0, 0}, {1, 0}, {1, 1}, {0, 1}}}, // gap 0 + {{{0, 0}, {0, 0}, {1, 0}, {0, 0}}}, // gap 1 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 and higher + }}, + {{ + // South + {{{3, 0}, {2, 0}, {0, 0}, {0, 0}}}, // gap 0; only first two entries used + {{{3, 0}, {2, 0}, {0, 0}, {0, 0}}}, // gap 1 + {{{3, 0}, {2, 0}, {0, 0}, {0, 0}}} // gap 2 + }}, + {{ + // Free + {{{0, 0}, {1, 0}, {2, 0}, {3, 0}}}, // gap 0 + {{{0, 0}, {0, 0}, {1, 0}, {0, 0}}}, // gap 1 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 and higher + }}}}, + {{// Connected + {{ + // East + {{{0, 0}, {0, 0}, {0, 1}, {0, 1}}}, // gap 0 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}, // gap 1 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 and higher + }}, + {{ + // South + {{{3, 0}, {3, 0}, {0, 0}, {0, 0}}}, // gap 0 + {{{3, 0}, {3, 0}, {0, 0}, {0, 0}}}, // gap 1 + {{{3, 0}, {3, 0}, {0, 0}, {0, 0}}} // gap 2 and higher + }}, + {{ + // Free + {{{0, 0}, {0, 0}, {3, 0}, {3, 0}}}, // gap 0 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}, // gap 1 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 + }}}}}}; return array; } @@ -171,239 +90,83 @@ std::array, 4>, 3 std::array, 4>, 3>, 3>, 4>& get_fanout_lookup() { static std::array, 4>, 3>, 3>, 4> array = { - { // Array - {{ // Type Fo 1+2 - {{// East - {{ - {1, 0}, - {1, 1}, - {1, 2}, - {1, 1} // gap 0 - }}, - {{ - {1, 0}, - {1, 0}, - {1, 1}, - {1, 0} // gap 1 - }}, - {{ - {1, 0}, - {1, 0}, - {1, 0}, - {1, 0} // gap 2 - }}}}, - {{// South - {{ - {2, 0}, - {2, 1}, - {0, 0}, - {0, 0} // gap 0 - }}, - {{ - {2, 0}, - {2, 1}, - {0, 0}, - {0, 0} // gap 1 - }}, - {{ - {2, 0}, - {2, 1}, - {0, 0}, - {0, 0} // gap 2 - }}}}, - {{// Free - {{ - {1, 0}, - {2, 0}, - {2, 1}, - {2, 0} // gap 0 - }}, - {{ - {1, 0}, - {1, 0}, - {2, 0}, - {1, 0} // gap 1 - }}, - {{ - {1, 0}, - {1, 0}, - {1, 0}, - {1, 0} // gap 2 - }}}}}}, - {{ // Type F1 - {{// East - {{ - {0, 0}, - {0, 1}, - {0, 2}, - {0, 1} // gap 0 - }}, - {{ - {0, 0}, - {0, 0}, - {0, 1}, - {0, 0} // gap 1 - }}, - {{ - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0} // gap 2 - }}}}, - {{// South - {{ - {3, 0}, - {3, 1}, - {0, 0}, - {0, 0} // gap 0 - }}, - {{ - {3, 0}, - {3, 1}, - {0, 0}, - {0, 0} // gap 1 - }}, - {{ - {3, 0}, - {3, 1}, - {0, 0}, - {0, 0} // gap 2 - }}}}, - {{// Free - {{ - {0, 0}, - {3, 0}, - {3, 1}, - {3, 0} // gap 0 - }}, - {{ - {0, 0}, - {0, 0}, - {3, 0}, - {0, 0} // gap 1 - }}, - {{ - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0} // gap 2 - }}}}}}, - {{ // Type F2 - {{// East - {{ - {1, 0}, - {1, 0}, - {1, 1}, - {1, 1} // gap 0 - }}, - {{ - {1, 0}, - {1, 0}, - {1, 0}, - {1, 0} // gap 1 - }}, - {{ - {1, 0}, - {1, 0}, - {1, 0}, - {1, 0} // gap 2 - }}}}, - {{// South - {{ - {2, 0}, - {2, 0}, - {0, 0}, - {0, 0} // gap 0 - }}, - {{ - {2, 0}, - {2, 0}, - {0, 0}, - {0, 0} // gap 1 - }}, - {{ - {2, 0}, - {2, 0}, - {0, 0}, - {0, 0} // gap 2 - }}}}, - {{// Free - {{ - {1, 0}, - {1, 0}, - {2, 0}, - {2, 0} // gap 0 - }}, - {{ - {1, 0}, - {1, 0}, - {1, 0}, - {1, 0} // gap 1 - }}, - {{ - {1, 0}, - {1, 0}, - {1, 0}, - {1, 0} // gap 2 - }}}}}}, - {{ // Type F3 - {{// East - {{ - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0} // gap 0 - }}, - {{ - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0} // gap 1 - }}, - {{ - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0} // gap 2 - }}}}, - {{// South - {{ - {3, 0}, - {3, 0}, - {0, 0}, - {0, 0} // gap 0 - }}, - {{ - {3, 0}, - {3, 0}, - {0, 0}, - {0, 0} // gap 1 - }}, - {{ - {3, 0}, - {3, 0}, - {0, 0}, - {0, 0} /// gap 2 - }}}}, - {{// Free - {{ - {0, 0}, - {0, 0}, - {3, 0}, - {3, 0} // gap 0 - }}, - {{ - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0} // gap 1 - }}, - {{ - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0} // gap 2 - }}}}}}}}; + { // Array + {{// Type Fo 1+2 + {{ + // East + {{{1, 0}, {1, 1}, {1, 2}, {1, 1}}}, // gap 0 + {{{1, 0}, {1, 0}, {1, 1}, {1, 0}}}, // gap 1 + {{{1, 0}, {1, 0}, {1, 0}, {1, 0}}} // gap 2 + }}, + {{ + // South + {{{2, 0}, {2, 1}, {0, 0}, {0, 0}}}, // gap 0 + {{{2, 0}, {2, 1}, {0, 0}, {0, 0}}}, // gap 1 + {{{2, 0}, {2, 1}, {0, 0}, {0, 0}}} // gap 2 + }}, + {{ + // Free + {{{1, 0}, {2, 0}, {2, 1}, {2, 0}}}, // gap 0 + {{{1, 0}, {1, 0}, {2, 0}, {1, 0}}}, // gap 1 + {{{1, 0}, {1, 0}, {1, 0}, {1, 0}}} // gap 2 + }}}}, + {{// Type F1 + {{ + // East + {{{0, 0}, {0, 1}, {0, 2}, {0, 1}}}, // gap 0 + {{{0, 0}, {0, 0}, {0, 1}, {0, 0}}}, // gap 1 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 + }}, + {{ + // South + {{{3, 0}, {3, 1}, {0, 0}, {0, 0}}}, // gap 0 + {{{3, 0}, {3, 1}, {0, 0}, {0, 0}}}, // gap 1 + {{{3, 0}, {3, 1}, {0, 0}, {0, 0}}} // gap 2 + }}, + {{ + // Free + {{{0, 0}, {3, 0}, {3, 1}, {3, 0}}}, // gap 0 + {{{0, 0}, {0, 0}, {3, 0}, {0, 0}}}, // gap 1 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 + }}}}, + {{// Type F2 + {{ + // East + {{{1, 0}, {1, 0}, {1, 1}, {1, 1}}}, // gap 0 + {{{1, 0}, {1, 0}, {1, 0}, {1, 0}}}, // gap 1 + {{{1, 0}, {1, 0}, {1, 0}, {1, 0}}} // gap 2 + }}, + {{ + // South + {{{2, 0}, {2, 0}, {0, 0}, {0, 0}}}, // gap 0 + {{{2, 0}, {2, 0}, {0, 0}, {0, 0}}}, // gap 1 + {{{2, 0}, {2, 0}, {0, 0}, {0, 0}}} // gap 2 + }}, + {{ + // Free + {{{1, 0}, {1, 0}, {2, 0}, {2, 0}}}, // gap 0 + {{{1, 0}, {1, 0}, {1, 0}, {1, 0}}}, // gap 1 + {{{1, 0}, {1, 0}, {1, 0}, {1, 0}}} // gap 2 + }}}}, + {{// Type F3 + {{ + // East + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}, // gap 0 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}, // gap 1 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 + }}, + {{ + // South + {{{3, 0}, {3, 0}, {0, 0}, {0, 0}}}, // gap 0 + {{{3, 0}, {3, 0}, {0, 0}, {0, 0}}}, // gap 1 + {{{3, 0}, {3, 0}, {0, 0}, {0, 0}}} // gap 2 + }}, + {{ + // Free + {{{0, 0}, {0, 0}, {3, 0}, {3, 0}}}, // gap 0 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}, // gap 1 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 + }}}}}}; return array; } @@ -551,13 +314,12 @@ int calculate_start_orientation(Ntk& ntk, uint32_t lvl) return; } } - if(ntk.fanout_size(n) == 2) + if (ntk.fanout_size(n) == 2) { // it is 0 but return makes -1 orientation = 1; return; } - }); return (orientation == 0) ? 0 : (orientation - 1); // instead of just returning 0, here the buffers could be wired in a way they are closer together and hence overhead diff --git a/include/fiction/algorithms/properties/check_planarity.hpp b/include/fiction/algorithms/properties/check_planarity.hpp new file mode 100644 index 0000000000..97398237f9 --- /dev/null +++ b/include/fiction/algorithms/properties/check_planarity.hpp @@ -0,0 +1,122 @@ +// +// Created by benjamin on 17.07.24. +// + +#ifndef FICTION_CHECK_PLANARITY_HPP +#define FICTION_CHECK_PLANARITY_HPP + +#include "fiction/algorithms/network_transformation/network_balancing.hpp" + +#include + +#include +#include +#include + +namespace fiction +{ + +template +class check_planarity_impl +{ + public: + explicit check_planarity_impl(const Ntk& ntk) : ntk(ntk) {} + + /** + * Checks if a given network is planar. + * + * This function checks if the network represented by the variable `ntk` is planar. + * The network is planar if, for any edge with starting point \f$m\f$ and endpoint \f$n\f$ + * (represented by the node ranks), there is never another edge with starting point + * \f$m' > m\f$ and endpoint \f$n' < n\f$, or vice versa. When iterating through the + * ranks of one level, the endpoints are always increasing. Therefore, only the starting + * points need to be checked. Thus, the highest connected starting point in the fan-in + * gives a border \f$m_{\text{max}}\f$ for every subsequent edge. + * + * @return `true` if the network is planar, `false` otherwise. + */ + + bool run() + { + bool return_false = false; + for (uint32_t r = 1; r < ntk.depth() + 1; r++) + { + uint32_t bound = 0; + ntk.foreach_node_in_rank(r, + [this, &bound, &return_false](const auto& n) + { + uint32_t new_bound = bound; + ntk.foreach_fanin(n, + [this, &n, &bound, &new_bound, &return_false](const auto& fi) + { + const auto fi_n = ntk.get_node(fi); + if (!ntk.is_constant(fi_n)) + { + if (ntk.rank_position(fi_n) < bound) + { + return_false = true; + return false; // stop iterating + } + + new_bound = + std::max(new_bound, ntk.rank_position(fi_n)); + } + return true; // keep iterating + }); + if (return_false) + { + return; + } + bound = new_bound; + }); + if (return_false) + { + return false; + } + } + return true; + } + + private: + const Ntk ntk; +}; + +/** + * Checks if a logic network is planar for a network that is path balanced and has ranks assigned. + * + * If the network is not balanced, an exception is thrown. To balance the network, insert buffers to divide multi-level + * edges. + * + * It checks if the network represented by the variable `ntk` is planar. + * The network is planar if, for any edge with starting point \f$m\f$ and endpoint \f$n\f$ (represented by the node + * ranks), there is never another edge with starting point \f$m' > m\f$ and endpoint \f$n' < n\f$, or vice versa. When + * iterating through the ranks of one level, the endpoints are always increasing. Therefore, only the starting points + * need to be checked. Thus, the highest connected starting point in the fan-in gives a border \f$m_{\text{max}}\f$ for + * every subsequent edge. + * + * @tparam Ntk Logic network type. + * @param ntk The logic network to check for planarity. + * @return `true` if the network is planar, `false` otherwise. + */ +template +[[nodiscard]] bool check_planarity(const Ntk& ntk) +{ + static_assert(mockturtle::has_get_node_v, "Ntk does not implement the get_node function"); + static_assert(mockturtle::has_foreach_fanin_v, "Ntk does not implement the foreach_fanin function"); + static_assert(mockturtle::has_depth_v, "Ntk does not implement the depth function"); + static_assert(mockturtle::has_rank_position_v, "Ntk does not implement the rank_position function"); + static_assert(mockturtle::has_foreach_node_in_rank_v, + "Ntk does not implement the foreach_node_in_rank function"); + + assert(is_balanced(ntk) && "Network must be balanced"); + + check_planarity_impl p{ntk}; + + const auto result = p.run(); + + return result; +} + +} // namespace fiction + +#endif // FICTION_CHECK_PLANARITY_HPP From 78c11ceecf4cab252d78f271948dcf0bf51627f8 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 2 Oct 2025 11:45:35 +0000 Subject: [PATCH 05/67] :memo: Update pyfiction docstrings Signed-off-by: GitHub Actions --- .../pyfiction/pybind11_mkdoc_docstrings.hpp | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index b723f11157..473ccbf471 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -2588,6 +2588,54 @@ Template parameter ``Dist``: static const char *__doc_fiction_chebyshev_distance_functor_chebyshev_distance_functor = R"doc()doc"; +static const char *__doc_fiction_check_planarity = +R"doc(Checks if a logic network is planar for a network that is path +balanced and has ranks assigned. + +If the network is not balanced, an exception is thrown. To balance the +network, insert buffers to divide multi-level edges. + +It checks if the network represented by the variable `ntk` is planar. +The network is planar if, for any edge with starting point :math:`m` +and endpoint :math:`n` (represented by the node ranks), there is never +another edge with starting point :math:`m' > m` and endpoint :math:`n' +< n`, or vice versa. When iterating through the ranks of one level, +the endpoints are always increasing. Therefore, only the starting +points need to be checked. Thus, the highest connected starting point +in the fan-in gives a border :math:`m_{\text{max}}` for every +subsequent edge. + +Template parameter ``Ntk``: + Logic network type. + +Parameter ``ntk``: + The logic network to check for planarity. + +Returns: + `true` if the network is planar, `false` otherwise.)doc"; + +static const char *__doc_fiction_check_planarity_impl = R"doc()doc"; + +static const char *__doc_fiction_check_planarity_impl_check_planarity_impl = R"doc()doc"; + +static const char *__doc_fiction_check_planarity_impl_ntk = R"doc()doc"; + +static const char *__doc_fiction_check_planarity_impl_run = +R"doc(Checks if a given network is planar. + +This function checks if the network represented by the variable `ntk` +is planar. The network is planar if, for any edge with starting point +:math:`m` and endpoint :math:`n` (represented by the node ranks), +there is never another edge with starting point :math:`m' > m` and +endpoint :math:`n' < n`, or vice versa. When iterating through the +ranks of one level, the endpoints are always increasing. Therefore, +only the starting points need to be checked. Thus, the highest +connected starting point in the fan-in gives a border +:math:`m_{\text{max}}` for every subsequent edge. + +Returns: + `true` if the network is planar, `false` otherwise.)doc"; + static const char *__doc_fiction_check_simulation_results_for_equivalence = R"doc(This function compares two SiDB simulation results for equivalence. Two results are considered equivalent if they have the same number of From 1a6f5351b104e1bb8e0d285581bf8f97f94dd24d Mon Sep 17 00:00:00 2001 From: benjamin Date: Thu, 2 Oct 2025 13:53:34 +0200 Subject: [PATCH 06/67] :art: modified mutable_rank_view.hpp --- .../networks/views/mutable_rank_view.hpp | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/include/fiction/networks/views/mutable_rank_view.hpp b/include/fiction/networks/views/mutable_rank_view.hpp index d6a78ce6ae..ddbf0d85d7 100644 --- a/include/fiction/networks/views/mutable_rank_view.hpp +++ b/include/fiction/networks/views/mutable_rank_view.hpp @@ -256,6 +256,22 @@ class mutable_rank_view : public fiction::static_depth_view std::for_each(rank.cbegin(), rank.cend(), [this, i = 0u](auto const& n) mutable { rank_pos[n] = i++; }); } + /** + * Sets the associated rank positions for nodes in the network, given the order of `nodes`. + * + * @param level Level at which to replace nodes. + * @param nodes The new node order to be set at the given level. + */ + void set_all_ranks(const std::vector>& new_ranks) + { + assert(new_ranks.size() == ranks.size()); + + for (uint32_t level = 0; level < new_ranks.size(); ++level) + { + set_ranks(level, new_ranks[level]); + } + } + /** * Gets the associated nodes in rank order in a specific `level`. * @@ -269,6 +285,17 @@ class mutable_rank_view : public fiction::static_depth_view return ranks[level]; } + /** + * Gets the associated nodes in rank order in a specific `level`. + * + * @param level Level at which to return the nodes. + * @return The node order at the given level. + */ + [[nodiscard]] std::vector> get_all_ranks() const noexcept + { + return ranks; + } + /** * Fetches a node at a specific rank position * From 9c43e067aa2db7c035a61d11e06066ab2e511b73 Mon Sep 17 00:00:00 2001 From: benjamin Date: Mon, 6 Oct 2025 17:10:18 +0200 Subject: [PATCH 07/67] :art: updated all files --- .../node_duplication_planarization.hpp | 85 +++-- .../physical_design/orthogonal_planar.hpp | 317 ++++++++++++------ .../node_duplication_planarization.cpp | 32 +- .../physical_design/orthogonal_planar.cpp | 108 ++++++ .../algorithms/properties/check_planarity.cpp | 113 +++++++ 5 files changed, 505 insertions(+), 150 deletions(-) create mode 100644 test/algorithms/physical_design/orthogonal_planar.cpp create mode 100644 test/algorithms/properties/check_planarity.cpp diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index 1256a42fa0..0a4a96940e 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -6,7 +6,6 @@ #define FICTION_NODE_DUPLICATION_PLANARIZATION_HPP #include "fiction/algorithms/properties/check_planarity.hpp" -#include "fiction/networks/views/mutable_rank_view.hpp" #include "fiction/networks/virtual_pi_network.hpp" #include @@ -50,7 +49,6 @@ struct node_duplication_planarization_params */ RANDOM_PO_ORDER }; - // bool random_output_order = false; output_order po_order = output_order::KEEP_PO_ORDER; }; @@ -65,7 +63,7 @@ namespace detail * These two outer nodes are connected through zero or more 'middle_nodes'. * The fanin order starts with the first node in 'pair', then proceeds through the 'middle_nodes', and ends with the * second node in 'pair'. The order of 'middle_nodes' is arbitrary as they cannot be further connected to any other - * nodes. For the planarization, only the nodes inside the 'pair' are relevant. + * nodes. * * @tparam Ntk Network type for the nodes in the pair. */ @@ -76,6 +74,10 @@ struct node_pair * Defines the beginning and end of the fanin-edged node. */ std::pair, mockturtle::node> pair; + /** + * Specifies the delay value for the node. + */ + uint64_t delay; /** * Contains the nodes between the fanin-edges node; cannot be connected to any other node. */ @@ -84,10 +86,6 @@ struct node_pair * Shared pointer to another instance of node_pair detailing fanin-edge alignment. */ node_pair* fanin_pair; - /** - * Specifies the delay value for the node. - */ - uint64_t delay; /** * Standard constructor. * @@ -120,20 +118,13 @@ template std::pair, mockturtle::node_map>>, Ntk>> initialize_copy_network_duplicates(Ntk const& src) { - static_assert(mockturtle::is_network_type_v>, "Ntk is not a network type"); static_assert(mockturtle::is_network_type_v, "Ntk is not a network type"); + static_assert(mockturtle::has_get_constant_v, "Ntk does not implement the get_constant method"); static_assert(mockturtle::has_get_constant_v>, "Ntk does not implement the get_constant method"); - static_assert(mockturtle::has_create_pi_v>, "Ntk does not implement the create_pi method"); - static_assert(mockturtle::has_is_pi_v>, "Ntk does not implement the is_pi method"); - static_assert(mockturtle::has_create_not_v>, - "Ntk does not implement the create_not method"); - static_assert(mockturtle::has_get_constant_v, "Ntk does not implement the get_constant method"); static_assert(mockturtle::has_get_node_v, "Ntk does not implement the get_node method"); static_assert(mockturtle::has_foreach_pi_v, "Ntk does not implement the foreach_pi method"); - static_assert(mockturtle::has_foreach_po_v, "Ntk does not implement the foreach_po method"); - static_assert(mockturtle::has_is_complemented_v, "Ntk does not implement the is_complemented method"); - static_assert(mockturtle::has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method"); + static_assert(mockturtle::has_create_pi_v>, "Ntk does not implement the create_pi method"); mockturtle::node_map>>, Ntk> old2new(src); virtual_pi_network dest; @@ -163,9 +154,9 @@ initialize_copy_network_duplicates(Ntk const& src) */ template std::vector> -gather_fanin_signals(Ntk& ntk, NtkDest& ntk_dest_v, mockturtle::node n, - mockturtle::node_map>, Ntk>& old2new_v, - std::vector>& lvl, std::size_t& node_index) +gather_fanin_signals(const Ntk& ntk, const NtkDest& ntk_dest_v, const mockturtle::node n, + const mockturtle::node_map>, Ntk>& old2new_v, + const std::vector>& lvl, std::size_t& node_index) { // Initialize variables std::vector> children{}; @@ -179,20 +170,20 @@ gather_fanin_signals(Ntk& ntk, NtkDest& ntk_dest_v, mockturtle::node n, { // Get the vector of duplicated nodes of the original fan-in node fn. const auto fn = ntk.get_node(f); - auto tgt_signal_v = old2new_v[fn]; + const auto tgt_signal_v = old2new_v[fn]; assert(node_index < lvl.size() && "The fanin iterator is out of scope"); // The range indicates the number of candidate fan-ins. - std::size_t range = ntk.fanin_size(n) + 1; + const std::size_t range = ntk.fanin_size(n) + 1; // Iterate through the candidate fan-ins. If a candidate fan-in matches the original fan-in or is a // duplicate of it, add it to the children of the node n. - std::size_t end_index = std::min(node_index + range, lvl.size()); - for (std::size_t j = node_index; j < end_index; ++j) + const std::size_t end_index = std::min(node_index + range, lvl.size()); + for (auto j = node_index; j < end_index; ++j) { // get the node from the newly generated network. - auto node_at_index = lvl[j]; + const auto node_at_index = lvl[j]; // Check if the candidate matches the original fan-in or a duplicate. // Also, verify if the candidate has already reached its fan-out limit. @@ -243,8 +234,9 @@ gather_fanin_signals(Ntk& ntk, NtkDest& ntk_dest_v, mockturtle::node n, */ template virtual_pi_network -create_virtual_pi_ntk_from_duplicated_nodes(Ntk& ntk, std::vector>>& ntk_lvls, - std::vector>>& ntk_lvls_new) +create_virtual_pi_ntk_from_duplicated_nodes(const Ntk& ntk, + const std::vector>>& ntk_lvls, + std::vector>>& ntk_lvls_new) { std::unordered_map, bool> node_status; ntk_lvls_new.resize(ntk_lvls.size()); @@ -253,7 +245,7 @@ create_virtual_pi_ntk_from_duplicated_nodes(Ntk& ntk, std::vector 0;) + for (auto i = ntk_lvls.size(); i-- > 0;) { // The index of the node in the current node level. std::size_t node_index = 0; @@ -375,15 +367,15 @@ template class node_duplication_planarization_impl { public: - node_duplication_planarization_impl(const Ntk& src, const node_duplication_planarization_params& p) : + [[maybe_unused]] node_duplication_planarization_impl(const Ntk& src, const node_duplication_planarization_params& p) : ntk(src), ps{p} {} /** - * The H-graph represents all possible orderings of node pairs within a single network level. - * A "slice" is created by adding all possible combinations of a `node_pair` to the H-graph of the level. - * These combinations are formed by selecting pairs of nodes from the fan-ins of the input node: + * A "slice" describes one vertical layer in the H-graph. It is created by adding all possible combinations of a + * `node_pair` to the H-graph of the level. These combinations are formed by selecting pairs of nodes from the + * fan-ins of the input node: * - If the input node has only one fan-in, it is treated as a single combination. * - If the input node has two fan-ins, there are two possible combinations. * @@ -405,7 +397,6 @@ class node_duplication_planarization_impl * @param nd Node in the H-graph. * @param border_pis A boolean indicating whether the input PIs (Primary Inputs) should be propagated to the next */ - void compute_slice_delays(const mockturtle::node& nd) { // Pis need to be propagated into the next level, since they have to be connected without crossings @@ -423,7 +414,7 @@ class node_duplication_planarization_impl } }); - assert(fis.empty() == 0 && "Node is a buffered PI that is a PO"); + assert(fis.empty() == 0 && "There has to be at least one node in this level"); // Compute the combinations in one slice auto combinations = calculate_pairs(fis); @@ -452,8 +443,6 @@ class node_duplication_planarization_impl } else if (node_pair_last.delay + 2 == node_pair_cur.delay) { - // ToDo: If order doesnt matter, decide on minimal crossing view (mincross.c from graphviz) - // This solves equal path delays, if they are connected in the next layer via a fanout const auto fc0 = fanins(ntk, node_pair_last.pair.first); if (node_pair_last.fanin_pair != nullptr) @@ -497,9 +486,12 @@ class node_duplication_planarization_impl * * @param node The node to be inserted. * @param vec The vector to insert the node into. + * @param saturated_fanout_flag The flag which indicates that the maximum number of fanouts for this node is + * reached. + * @param position The position of the node (terminal node or not). */ void insert_if_not_first(const mockturtle::node& node, std::vector>& vec, - int& saturated_fanout_flag, int position) + int& saturated_fanout_flag, const int position) { if (vec.empty() || vec.front() != node) { @@ -583,9 +575,6 @@ class node_duplication_planarization_impl /** * Checks if the given vector of nodes contains any non-primary inputs. * - * This function iterates through each node in the vector and checks if it is a primary input. - * If a non-primary input is found, the `f_final_level` parameter is set to false and the loop is exited. - * * @param v_next_level The vector of nodes to be checked. */ [[nodiscard]] bool check_final_level(const std::vector>& v_next_level) @@ -600,8 +589,7 @@ class node_duplication_planarization_impl return true; } - [[nodiscard]] virtual_pi_network - run(std::vector>>& ntk_lvls_new) + [[nodiscard]] virtual_pi_network run(std::vector>>& ntk_lvls_new) { // Initialize the POs std::vector> pos{}; @@ -609,7 +597,7 @@ class node_duplication_planarization_impl ntk.foreach_node( [this, &pos](const auto n) { - if(ntk.is_po(n)) + if (ntk.is_po(n)) { const auto po = ntk.get_node(n); if (std::find(pos.begin(), pos.end(), po) == pos.end()) @@ -686,6 +674,10 @@ class node_duplication_planarization_impl virtual_ntk.update_ranks(); virtual_ntk.set_all_ranks(ntk_lvls_new); + // restore possibly set signal names + restore_network_name(ntk, virtual_ntk); + restore_output_names(ntk, virtual_ntk); + return virtual_ntk; } @@ -715,14 +707,15 @@ class node_duplication_planarization_impl } // namespace detail /** - * Implements a planarization mechanism for networks using a H-Graph strategy for node duplication. + * Implements a planarization mechanism for networks from the paper \"Fabricatable Interconnect and Molecular QCA + * Circuits\" by Amitabh Chaudhary, Danny Ziyi Chen, Xiaobo Sharon Hu, Michael T. Niemier, Ramprasad Ravichandran and + * Kevin Whitton in IEEE TRANSACTIONS ON COMPUTER-AIDED DESIGN OF INTEGRATED CIRCUITS AND SYSTEMS, Volume 26, 2007. * * The planarization achieved by this function solves the Node Duplication Crossing Minimization (NDCE) problem by * finding the shortest x-y path in the H-graph for every level in the network. An H-graph describes edge relations * between two levels in a network, with one level assumed as fixed, starting at the Primary Outputs (POs). By finding * the shortest path from the source (x) to the sink (y) in this H-graph, an optimal solution for the NDCE problem for - * each level is found. The function constructs an H-graph that captures edge relations between two levels within the - * graph and computes the shortest x-y paths on the H-graph, traversing from the POs towards the Primary Inputs (PIs). + * each level is found. The function traverses from the Primary Outputs (POs) towards the Primary Inputs (PIs). * * @tparam NtkDest Destination network type. * @tparam NtkSrc Source network type. @@ -733,7 +726,7 @@ class node_duplication_planarization_impl */ template [[nodiscard]] virtual_pi_network -node_duplication_planarization(const NtkSrc& ntk_src, const node_duplication_planarization_params& ps = {}) +node_duplication_planarization(const NtkSrc& ntk_src, node_duplication_planarization_params ps = {}) { static_assert(mockturtle::is_network_type_v, "NtkSrc is not a network type"); static_assert(mockturtle::has_create_node_v, "NtkSrc does not implement the create_node function"); diff --git a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp index a2cb81af65..8a3ea0d2b1 100644 --- a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp +++ b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp @@ -5,13 +5,12 @@ #ifndef FICTION_ORTHOGONAL_PLANAR_HPP #define FICTION_ORTHOGONAL_PLANAR_HPP +#include "fiction/algorithms/physical_design/orthogonal.hpp" #include "fiction/layouts/clocking_scheme.hpp" #include "fiction/traits.hpp" #include "fiction/utils/network_utils.hpp" #include "fiction/utils/placement_utils.hpp" -#include - #include #include #include @@ -25,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -40,7 +40,11 @@ namespace fiction namespace detail { -// Define a 3D array using std::array and encapsulate it in a function +/** + * Defines a 3D lookup table using `std::array` and encapsulates it within a function. This table encodes all possible + * combinations of the previous level, connection type, orientations, and surrounding spacing (gaps). Based on these + * inputs, it returns the corresponding orientation and spacing configuration for the current buffer. + */ std::array, 4>, 3>, 3>, 2>& get_buffer_lookup() { static std::array, 4>, 3>, 3>, 2> array = { @@ -86,7 +90,11 @@ std::array, 4>, 3 return array; } -// Define a 3D array using std::array and encapsulate it in a function +/** + * Defines a 3D lookup table using `std::array` and encapsulates it within a function. This table encodes all possible + * combinations of the previous level, connection type, orientations, and surrounding spacing (gaps). Based on these + * inputs, it returns the corresponding orientation and spacing configuration for the current fanout. + */ std::array, 4>, 3>, 3>, 4>& get_fanout_lookup() { static std::array, 4>, 3>, 3>, 4> array = { @@ -170,8 +178,23 @@ std::array, 4>, 3 return array; } +/** + * Computes the fan-out connection type of a node based on its successors' fan-in structures. + * Assumes exactly two fan-outs, which are ordered by rank position. + * + * Returns: + * - 0: Both fan-outs have a single fan-in. + * - 1: First fan-out has one fan-in, second has multiple. + * - 2: Second fan-out has one fan-in, first has multiple. + * - 3: Both fan-outs have multiple fan-ins. + * + * @tparam Ntk Logic network type. + * @param ntk Logic network containing the node. + * @param n Node for which to determine the fan-out connection type. + * @return Integer code (0–3) indicating the fan-out connection pattern. + */ template -uint64_t calculate_fanout_connection_type(const Ntk& ntk, mockturtle::node n) +uint64_t calculate_fanout_connection_type(const Ntk& ntk, const mockturtle::node& n) { // order the POs if (ntk.is_po(n)) @@ -197,9 +220,22 @@ uint64_t calculate_fanout_connection_type(const Ntk& ntk, mockturtle::node return 3; } +/** + * Computes the gap between the fan-in node and its preceding node, i.e., the node with a rank position one less than + * the current node. This value indicates the available spacing for placement. + * + * @tparam Ntk Logic network type. + * @tparam Lyt Layout type. + * @param ntk Logic network containing the node. + * @param node2pos Mapping from network nodes to layout tile positions. + * @param lvl Current level index of the node. + * @param n Node for which to compute the predecessor gap. + * @return Gap size (clamped to a maximum of 2). + */ + template -uint64_t calculate_predecessor_gap(const Ntk& ntk, mockturtle::node_map, Ntk>& node2pos, - uint64_t lvl, mockturtle::node n) +uint64_t calculate_predecessor_gap(const Ntk& ntk, const mockturtle::node_map, Ntk>& node2pos, + const uint64_t lvl, const mockturtle::node& n) { // return if in the PI level if (lvl == 0) @@ -210,7 +246,6 @@ uint64_t calculate_predecessor_gap(const Ntk& ntk, mockturtle::node_map nd = n; if (fc.fanin_nodes.size() == 2) { std::sort(fc.fanin_nodes.begin(), fc.fanin_nodes.end(), @@ -232,15 +267,24 @@ uint64_t calculate_predecessor_gap(const Ntk& ntk, mockturtle::node_map>(node2pos[pre]); - auto pre2_t = static_cast>(node2pos[pre_neighbour]); + const auto pre1_t = static_cast>(node2pos[pre]); + const auto pre2_t = static_cast>(node2pos[pre_neighbour]); assert(pre1_t.y > pre2_t.y); return std::min(pre1_t.y - pre2_t.y - 1, 2); } +/** + * Computes the buffer connection type for a given node. + * Determines whether the node serves as the second (rightmost) fan-in of its successor node. + * + * @tparam Ntk Logic network type. + * @param ntk Logic network containing the node. + * @param n Node to analyze. + * @return 1 if the node is the rightmost fan-in of its successor, otherwise 0. + */ template -uint64_t calculate_connection(const Ntk& ntk, mockturtle::node n) +uint64_t calculate_buffer_connection_type(const Ntk& ntk, const mockturtle::node& n) { if (ntk.is_po(n)) { @@ -262,8 +306,23 @@ uint64_t calculate_connection(const Ntk& ntk, mockturtle::node n) return 0; } +/** + * Determines the allowed orientation of a node based on its predecessor. + * For nodes driven by a fan-out, the orientation is defined by their relative rank position among the fan-out’s + * successors. + * + * Returns: + * - 0: Node is the first (east) successor of its predecessor. + * - 1: Node is the second (west) successor of its predecessor. + * - 2: Node is not driven by a fan-out. + * + * @tparam Ntk Logic network type. + * @param ntk Logic network containing the node. + * @param n Node for which to determine the allowed orientation. + * @return Orientation code (0–2) describing the node’s relative position. + */ template -uint64_t calculate_allowed_orientation(const Ntk& ntk, mockturtle::node n) +uint64_t calculate_allowed_orientation(const Ntk& ntk, const mockturtle::node& n) { auto fc = fanins(ntk, n); assert(fc.fanin_nodes.size() == 1); @@ -283,8 +342,23 @@ uint64_t calculate_allowed_orientation(const Ntk& ntk, mockturtle::node n) return 2; } +/** + * Determines the initial orientation for a given network level. + * The orientation is inferred from the structural pattern of nodes within the specified level, considering their fan-in + * and fan-out relationships. + * + * Returns: + * - 0: Default orientation (no specific structure found). + * - 1: Level starts with a fan-out or buffer structure. + * - 3: Level starts with a two-input (binary) gate. + * + * @tparam Ntk Logic network type. + * @param ntk Logic network to analyze. + * @param lvl Level index for which to compute the starting orientation. + * @return Orientation code (0, 1, or 3) defining the level’s initial direction. + */ template -int calculate_start_orientation(Ntk& ntk, uint32_t lvl) +int calculate_start_orientation(const Ntk& ntk, const uint32_t& lvl) { int orientation = 0; if (lvl == 0) @@ -300,7 +374,6 @@ int calculate_start_orientation(Ntk& ntk, uint32_t lvl) } if (ntk.fanin_size(n) == 2) { - // it is 3 but return makes -1 orientation = 4; return; } @@ -309,14 +382,12 @@ int calculate_start_orientation(Ntk& ntk, uint32_t lvl) const auto& pre = fc.fanin_nodes[0]; if (ntk.is_fanout(pre) && ntk.fanout_size(pre) == 2) { - // it is 0 but return makes -1 orientation = 1; return; } } if (ntk.fanout_size(n) == 2) { - // it is 0 but return makes -1 orientation = 1; return; } @@ -326,12 +397,27 @@ int calculate_start_orientation(Ntk& ntk, uint32_t lvl) // produced by two input nodes is reduced } +/** + * Computes orientation and routing line variables for a given network level. + * + * For each node in the specified level, the function determines: + * - The node’s orientation, based on its fan-in structure, gap spacing, and connection type. + * - Whether new routing lines must be introduced, derived from lookup tables for buffer and fan-out configurations. + * + * @tparam Ntk Logic network type. + * @tparam Lyt Layout type. + * @param ntk Logic network to analyze. + * @param node2pos Mapping from network nodes to layout tile positions. + * @param lvl Level index for which to compute the variables. + * @return Tuple containing: + * - `orientation`: orientation values for each node in the level. + * - `new_lines`: flags indicating newly added routing lines. + */ template std::tuple, std::vector> -compute_pr_variables(const Ntk& ntk, const Lyt& lyt, mockturtle::node_map, Ntk> node2pos, - uint32_t lvl) +compute_pr_variables(const Ntk& ntk, const mockturtle::node_map, Ntk>& node2pos, + const uint32_t& lvl) { - // ToDo: compute the starting orientation std::vector orientation(ntk.rank_width(lvl)); std::vector new_lines(ntk.rank_width(lvl)); // get the lookup tables for the gate types @@ -340,10 +426,10 @@ compute_pr_variables(const Ntk& ntk, const Lyt& lyt, mockturtle::node_map(ntk, node2pos, lvl, n); + const auto gap = calculate_predecessor_gap(ntk, node2pos, lvl, n); // needs gap and orientation as input if (ntk.fanin_size(n) == 2) // complete @@ -359,7 +445,7 @@ compute_pr_variables(const Ntk& ntk, const Lyt& lyt, mockturtle::node_map pair = - fanout_lu[fanout_connection_type][allowed_orientation][gap][orientation[i - 1]]; + const auto& pair = + fanout_lu.at(fanout_connection_type).at(allowed_orientation).at(gap).at(orientation[i - 1]); orientation[i] = pair.first; new_lines[i] = pair.second; } else if (fanout_connection_type == 0 || fanout_connection_type == 2) { - // i == 0 orientation[i] = 1; } } @@ -389,15 +474,15 @@ compute_pr_variables(const Ntk& ntk, const Lyt& lyt, mockturtle::node_map pair = - buffer_lu[connected][allowed_orientation][gap][orientation[i - 1]]; + const auto buffer_connection_type = calculate_buffer_connection_type(ntk, n); + const auto& pair = + buffer_lu.at(buffer_connection_type).at(allowed_orientation).at(gap).at(orientation[i - 1]); + orientation[i] = pair.first; new_lines[i] = pair.second; } else { - // i == 0 orientation[i] = calculate_start_orientation(ntk, lvl); } } @@ -408,10 +493,19 @@ compute_pr_variables(const Ntk& ntk, const Lyt& lyt, mockturtle::node_map -std::vector compute_two_input_indices(const Ntk& ntk, uint64_t lvl) +std::vector compute_two_input_indices(const Ntk& ntk, const uint64_t& lvl) { - std::vector two_input_indices; + std::vector two_input_indices{}; + two_input_indices.reserve(ntk.rank_width(lvl)); ntk.foreach_node_in_rank(lvl, [&ntk, &two_input_indices, &lvl](const auto& n, const auto& i) { @@ -423,12 +517,23 @@ std::vector compute_two_input_indices(const Ntk& ntk, uint64_t lvl) return two_input_indices; } +/** + * Computes the number of new routing lines required. + * + * @tparam Ntk Logic network type. + * @tparam Lyt Layout type. + * @param ntk Logic network to analyze. + * @param node2pos Mapping from network nodes to layout tile positions. + * @param lvl Level index to inspect. + * @return Vector of gap sizes (new line counts) for all two-input nodes in the level. + */ template -std::vector calculate_two_input_new_lines(const Ntk& ntk, - mockturtle::node_map, Ntk>& node2pos, - uint32_t lvl) +std::vector calculate_two_input_new_lines(const Ntk& ntk, + const mockturtle::node_map, Ntk>& node2pos, + const uint32_t& lvl) { - std::vector cluster_new_lines; + std::vector cluster_new_lines{}; + cluster_new_lines.reserve(ntk.rank_width(lvl)); ntk.foreach_node_in_rank(lvl, [&ntk, &node2pos, &cluster_new_lines](const auto& n) { @@ -441,8 +546,8 @@ std::vector calculate_two_input_new_lines(const Ntk& // compute the max_gap for two fan-ins of anode const auto &pre1 = fc.fanin_nodes[0], &pre2 = fc.fanin_nodes[1]; - auto pre1_t = static_cast>(node2pos[pre1]), - pre2_t = static_cast>(node2pos[pre2]); + const auto pre1_t = static_cast>(node2pos[pre1]), + pre2_t = static_cast>(node2pos[pre2]); cluster_new_lines.emplace_back(static_cast(pre2_t.y - pre1_t.y - 1)); } @@ -450,22 +555,34 @@ std::vector calculate_two_input_new_lines(const Ntk& return cluster_new_lines; } +/** + * Balances the final x and y wiring coordinates across all nodes in a level. + * + * The function identifies the center node or cluster with the maximum routing demand and adjusts all x and y + * coordinates such that the total wiring length is symmetric around this center. + * + * @param x Vector of x-coordinates to be adjusted. + * @param y Vector of y-coordinates to be adjusted. + * @param two_input_indices Indices of two-input nodes within the level. + * @param two_input_new_lines Number of new routing lines associated with each two-input node. + */ + void adjust_final_values(std::vector& x, std::vector& y, const std::vector& two_input_indices, const std::vector& two_input_new_lines) { // Find the max element in two_input_new_lines and its index - auto max_it = std::max_element(two_input_new_lines.begin(), two_input_new_lines.end()); - const uint64_t max_new_lines_two_inputs = *max_it; - const auto max_new_lines_two_inputs_index = - static_cast(std::distance(two_input_new_lines.begin(), max_it)); + const auto max_it = std::max_element(two_input_new_lines.begin(), two_input_new_lines.end()); + const auto max_new_lines_two_inputs = *max_it; + const auto max_new_lines_two_inputs_index = + static_cast(std::distance(two_input_new_lines.begin(), max_it)); // Find max sum in x + y and its index - auto max_xy_it = std::max_element(x.begin(), x.end(), - [&](std::size_t i, std::size_t j) { return (x[i] + y[i]) < (x[j] + y[j]); }); + const auto max_xy_it = std::max_element(x.begin(), x.end(), [&](std::size_t i, std::size_t j) + { return (x[i] + y[i]) < (x[j] + y[j]); }); - const uint64_t max_new_lines = x[*max_xy_it] + y[*max_xy_it]; - const std::size_t mnl_iterator = *max_xy_it; + const auto max_new_lines = x[*max_xy_it] + y[*max_xy_it]; + const auto mnl_iterator = *max_xy_it; // Determine the center index uint64_t center = 0, max = 0; @@ -493,8 +610,8 @@ void adjust_final_values(std::vector& x, std::vector& y, auto it = two_input_map.find(i); if (it != two_input_map.end()) // Two fan-in node (found in two_input_indices) { - const std::size_t idx = it->second; // Get the corresponding index - const uint64_t diff = max - x[i] - y[i] - two_input_new_lines[idx]; + const auto idx = it->second; // Get the corresponding index + const auto diff = max - x[i] - y[i] - two_input_new_lines[idx]; if (i < center) { @@ -511,7 +628,7 @@ void adjust_final_values(std::vector& x, std::vector& y, } else // One fan-in node { - const uint64_t diff = max - x[i] - y[i]; + const auto diff = max - x[i] - y[i]; if (i < center) { @@ -529,10 +646,25 @@ void adjust_final_values(std::vector& x, std::vector& y, } } +/** + * Computes the x and y wiring coordinates for nodes in a given level. + * + * The function divides the level into clusters separated by two-input gates and computes the relative wire offsets + * within and between these clusters. It accounts for new routing lines, right and left propagation, and spacing + * adjustments between connected clusters. + * + * @tparam Ntk Logic network type. + * @tparam Lyt Layout type. + * @param ntk Logic network to analyze. + * @param node2pos Mapping from network nodes to layout tile positions. + * @param new_lines Vector of newly introduced routing lines for each node. + * @param lvl Level index to process. + * @return Pair of vectors representing x and y wiring coordinates for each node in the level. + */ template std::pair, std::vector> -compute_wiring(const Ntk& ntk, mockturtle::node_map, Ntk>& node2pos, - const std::vector& new_lines, uint64_t lvl) +compute_wiring(const Ntk& ntk, const mockturtle::node_map, Ntk>& node2pos, + const std::vector& new_lines, const uint64_t& lvl) { // Initialize 2-input indices const auto two_input_indices = compute_two_input_indices(ntk, lvl); @@ -548,15 +680,6 @@ compute_wiring(const Ntk& ntk, mockturtle::node_map, Ntk // Initialize the x and y vectors std::vector cluster_new_lines(two_input_indices.size() + 1); - // Example - /*std::vector two_input_indices = {0, 4, 9, 16, 20}; - std::vector two_input_new_lines = {0, 0, 2, 5, 0}; - std::vector x(21); - std::vector y(21); - std::vector cluster_new_lines(two_input_indices.size() + 1); - std::size_t cluster_index_start = 0; - std::vector new_lines = {0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0};*/ - // if there is no two input gate, then there is only one cluster if (two_input_indices.empty()) { @@ -584,7 +707,7 @@ compute_wiring(const Ntk& ntk, mockturtle::node_map, Ntk } // Compute x and y for the current cluster - for (std::size_t j = cluster_index_start; j < cluster_index_end; ++j) + for (auto j = cluster_index_start; j < cluster_index_end; ++j) { // adjust x values x[j] = std::accumulate(new_lines.begin() + static_cast(j) + 1, @@ -618,11 +741,9 @@ compute_wiring(const Ntk& ntk, mockturtle::node_map, Ntk uint64_t propagate_left = 0; // propagate left (direction based on a 1D vector) - for (std::size_t i = two_input_indices.size(); i > 0; --i) + for (auto i = two_input_indices.size(); i > 0; --i) { - std::size_t cluster_index_end = 0; - - cluster_index_end = two_input_indices[i - 1]; + std::size_t cluster_index_end = two_input_indices[i - 1]; if (i == 1) { @@ -644,7 +765,7 @@ compute_wiring(const Ntk& ntk, mockturtle::node_map, Ntk x[cluster_index_end] += propagate_left; } - for (std::size_t j = cluster_index_start; j < cluster_index_end; ++j) + for (auto j = cluster_index_start; j < cluster_index_end; ++j) { x[j] += propagate_left; } @@ -656,29 +777,28 @@ compute_wiring(const Ntk& ntk, mockturtle::node_map, Ntk } template -class orthogonal_planar_v2_impl +class orthogonal_planar_impl { public: - orthogonal_planar_v2_impl(const Ntk& src, const orthogonal_physical_design_params& p, - orthogonal_physical_design_stats& st) : - ntk{mockturtle::names_view(mockturtle::fanout_view(src))}, + orthogonal_planar_impl(const Ntk& src, const orthogonal_physical_design_params& p, + orthogonal_physical_design_stats& st) : + ntk{mockturtle::fanout_view(src)}, ps{p}, pst{st} {} Lyt run() { + using node = typename Ntk::node; // measure run time mockturtle::stopwatch stop{pst.time_total}; - // - using node = typename Ntk::node; mockturtle::node_map, decltype(ntk)> node2pos{ntk}; - aspect_ratio size_ = {0, 0}; + aspect_ratio aspect_ratio = {0, 0}; // instantiate the layout - Lyt layout{size_, twoddwave_clocking(ps.number_of_clock_phases)}; + Lyt layout{aspect_ratio, twoddwave_clocking(ps.number_of_clock_phases)}; // reserve PI nodes without positions auto pi2node = reserve_input_nodes(layout, ntk); @@ -696,9 +816,10 @@ class orthogonal_planar_v2_impl for (uint32_t lvl = 0; lvl < ntk.depth() + 1; lvl++) { - const auto variable_tuple = compute_pr_variables(ntk, layout, node2pos, lvl); - const auto orientation = std::get<0>(variable_tuple); - const auto new_lines = std::get<1>(variable_tuple); + const auto variable_tuple = + compute_pr_variables, Lyt>(ntk, node2pos, lvl); + const auto orientation = std::get<0>(variable_tuple); + const auto new_lines = std::get<1>(variable_tuple); const auto wiring = compute_wiring(ntk, node2pos, new_lines, lvl); const auto& x = wiring.first; @@ -840,23 +961,20 @@ class orthogonal_planar_v2_impl }); } - // layout.resize({first_pos.x + 1, place_t.y +3, 0}); - // debug::write_dot_layout(layout); - - std::unordered_map countMap; + std::unordered_map count_map; int add_line = 0; // the number of outputs on a node is limited to 2, due to fanout substitution ntk.foreach_po( - [&](const auto& po) + [this, &layout, &first_pos, &place_t, &node2pos, &count_map, &add_line](const auto& po) { if (!ntk.is_constant(po)) { const auto n_s = node2pos[po]; auto po_tile = static_cast>(n_s); - if (countMap[po] < 2) // Check if the count is less than 2 + if (count_map[po] < 2) // Check if the count is less than 2 { // Adjust the position based on whether it's the first or second occurrence - if (countMap[po] == 1) + if (count_map[po] == 1) { if (po_tile.y == place_t.y) { @@ -872,7 +990,7 @@ class orthogonal_planar_v2_impl ntk.has_output_name(po_counter) ? ntk.get_output_name(po_counter++) : fmt::format("po{}", po_counter++), po_tile); - countMap[po]++; + count_map[po]++; } else { @@ -883,8 +1001,6 @@ class orthogonal_planar_v2_impl layout.resize({first_pos.x + 1, place_t.y + add_line, 0}); - // layout.resize({10, 10, 0}); - // restore possibly set signal names restore_names(ntk, layout, node2pos); @@ -898,7 +1014,7 @@ class orthogonal_planar_v2_impl } private: - mockturtle::names_view> ntk; + mockturtle::fanout_view ntk; orthogonal_physical_design_params ps; orthogonal_physical_design_stats& pst; @@ -909,11 +1025,24 @@ class orthogonal_planar_v2_impl } // namespace detail /** - * Description + * This algorithm performs a fully planar physical design flow for Field-Coupled Nanocomputing (FCN) circuits. It takes + * as input a logic network with a planar embedding, represented as a `mutable_rank_view`, and preserves this embedding + * during placement and routing. + * + * In this approach, each logic level of the network is mapped to a diagonal in the layout, while nodes within the same + * level are placed according to their rank positions in the planar embedding. This ensures a crossing-free, scalable, + * and layout-consistent mapping from logic to physical design. + * + * @tparam Lyt Gate-level layout type. + * @tparam Ntk Logic network type. + * @param ntk Planar logic network to be placed and routed. + * @param ps Configuration parameters for the physical design process. + * @param pst Optional statistics object to collect runtime and layout metrics. + * @return A fully planar gate-level layout of type `Lyt`. */ template -Lyt orthogonal_planar_v2(const Ntk& ntk, orthogonal_physical_design_params ps = {}, - orthogonal_physical_design_stats* pst = nullptr) +Lyt orthogonal_planar(const Ntk& ntk, orthogonal_physical_design_params ps = {}, + orthogonal_physical_design_stats* pst = nullptr) { static_assert(is_gate_level_layout_v, "Lyt is not a gate-level layout"); static_assert(mockturtle::is_network_type_v, @@ -929,11 +1058,11 @@ Lyt orthogonal_planar_v2(const Ntk& ntk, orthogonal_physical_design_params ps = // check for planarity if (!check_planarity(ntk)) { - throw std::runtime_error("Input network has to be planar"); + throw std::invalid_argument("Input network has to be planar"); } - orthogonal_physical_design_stats st{}; - detail::orthogonal_planar_v2_impl p{ntk, ps, st}; + orthogonal_physical_design_stats st{}; + detail::orthogonal_planar_impl p{ntk, ps, st}; auto result = p.run(); diff --git a/test/algorithms/network_transformation/node_duplication_planarization.cpp b/test/algorithms/network_transformation/node_duplication_planarization.cpp index cb71ea1f26..afdd1c68e8 100644 --- a/test/algorithms/network_transformation/node_duplication_planarization.cpp +++ b/test/algorithms/network_transformation/node_duplication_planarization.cpp @@ -6,6 +6,8 @@ #include "fiction/algorithms/network_transformation/fanout_substitution.hpp" #include "fiction/algorithms/network_transformation/network_balancing.hpp" +#include "fiction/algorithms/properties/check_planarity.hpp" +#include "fiction/networks/views/mutable_rank_view.hpp" #include #include @@ -40,11 +42,13 @@ TEST_CASE("Planarize technology ntk", "[node-duplication-planarization]") const auto vpi_r = fiction::mutable_rank_view(tec_b); - auto planarized_maj = node_duplication_planarization(vpi_r); + auto planarized_ntk = node_duplication_planarization(vpi_r); + + CHECK(check_planarity(planarized_ntk) == 1); mockturtle::equivalence_checking_stats st; const auto cec_m = - mockturtle::equivalence_checking(*fiction::virtual_miter(tec, planarized_maj), {}, &st); + mockturtle::equivalence_checking(*fiction::virtual_miter(tec, planarized_ntk), {}, &st); REQUIRE(cec_m.has_value()); CHECK(*cec_m == 1); } @@ -75,11 +79,13 @@ TEST_CASE("Planarize ntk with 3-ary node", "[node-duplication-planarization]") const auto vpi_r = fiction::mutable_rank_view(tec_b); - auto planarized_maj = node_duplication_planarization(vpi_r); + auto planarized_ntk = node_duplication_planarization(vpi_r); + + CHECK(check_planarity(planarized_ntk) == 1); mockturtle::equivalence_checking_stats st; const auto cec_m = - mockturtle::equivalence_checking(*fiction::virtual_miter(tec, planarized_maj), {}, &st); + mockturtle::equivalence_checking(*fiction::virtual_miter(tec, planarized_ntk), {}, &st); REQUIRE(cec_m.has_value()); CHECK(*cec_m == 1); } @@ -110,11 +116,13 @@ TEST_CASE("Buffer AIG and planarize technology_network", "[node-duplication-plan const auto vpi_r = fiction::mutable_rank_view(aig_b); - auto planarized_maj = node_duplication_planarization(vpi_r); + auto planarized_ntk = node_duplication_planarization(vpi_r); + + CHECK(check_planarity(planarized_ntk) == 1); mockturtle::equivalence_checking_stats st; const auto cec_m = - mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_maj), {}, &st); + mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_ntk), {}, &st); REQUIRE(cec_m.has_value()); CHECK(*cec_m == 1); } @@ -138,11 +146,13 @@ TEST_CASE("Buffer AIG and planarize technology_network 2", "[node-duplication-pl const auto vpi_r = fiction::mutable_rank_view(aig_b); - auto planarized_maj = node_duplication_planarization(vpi_r); + auto planarized_ntk = node_duplication_planarization(vpi_r); + + CHECK(check_planarity(planarized_ntk) == 1); mockturtle::equivalence_checking_stats st; const auto cec_m = - mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_maj), {}, &st); + mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_ntk), {}, &st); REQUIRE(cec_m.has_value()); CHECK(*cec_m == 1); } @@ -169,11 +179,13 @@ TEST_CASE("Planarize multi output network", "[node-duplication-planarization]") const auto vpi_r = fiction::mutable_rank_view(aig_b); - auto planarized_maj = node_duplication_planarization(vpi_r); + auto planarized_ntk = node_duplication_planarization(vpi_r); + + CHECK(check_planarity(planarized_ntk) == 1); mockturtle::equivalence_checking_stats st; const auto cec_m = - mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_maj), {}, &st); + mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_ntk), {}, &st); REQUIRE(cec_m.has_value()); CHECK(*cec_m == 1); } diff --git a/test/algorithms/physical_design/orthogonal_planar.cpp b/test/algorithms/physical_design/orthogonal_planar.cpp new file mode 100644 index 0000000000..059d87abad --- /dev/null +++ b/test/algorithms/physical_design/orthogonal_planar.cpp @@ -0,0 +1,108 @@ +#include + +#include "fiction/algorithms/physical_design/apply_gate_library.hpp" +#include "fiction/layouts/cell_level_layout.hpp" +#include "fiction/layouts/coordinates.hpp" +#include "fiction/technology/cell_technologies.hpp" +#include "utils/blueprints/network_blueprints.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace fiction; + +void check_stats(const orthogonal_physical_design_stats& st) noexcept +{ + CHECK(st.x_size > 0); + CHECK(st.y_size > 0); + CHECK(st.num_gates > 0); + CHECK(st.num_wires > 0); +} + +template +void check_ortho_planar(const Ntk& ntk) +{ + using gate_lyt = + fiction::gate_level_layout>>>; + using cell_layout = cell_level_layout>>; + + fiction::network_balancing_params b_ps; + b_ps.unify_outputs = true; + + const auto tec_balanced = fiction::network_balancing(fiction::fanout_substitution(ntk), b_ps); + + auto tec_ranked = fiction::mutable_rank_view(tec_balanced); + auto planarized_b = fiction::node_duplication_planarization(tec_ranked); + + mockturtle::equivalence_checking_stats eq_st; + const auto cec_m = mockturtle::equivalence_checking( + *fiction::virtual_miter(ntk, planarized_b), {}, &eq_st); + REQUIRE(cec_m.has_value()); + + orthogonal_physical_design_stats orthogonal_planar_stats{}; + + const auto gate_level_layout = fiction::orthogonal_planar(planarized_b, {}, &orthogonal_planar_stats); + + CHECK(gate_level_layout.num_crossings() == 0); + check_stats(orthogonal_planar_stats); + CHECK_NOTHROW(apply_gate_library(gate_level_layout)); +} + +TEST_CASE("Orthogonal planar layout tests", "[orthogonal-planar]") +{ + // Simple correctness and corner-case networks + check_ortho_planar(blueprints::se_coloring_corner_case_network()); + check_ortho_planar(blueprints::fanout_substitution_corner_case_network()); + check_ortho_planar(blueprints::clpl()); + + // Network with constant inputs + check_ortho_planar(blueprints::unbalanced_and_inv_network()); + + // Multi-output network + check_ortho_planar(blueprints::multi_output_network()); +} + +TEST_CASE("Name conservation after planar orthogonal physical design", "[orthogonal-planar]") +{ + using gate_lyt = gate_level_layout>>>; + + auto topolinano = blueprints::topolinano_network>(); + topolinano.set_network_name("topolinano"); + + fiction::network_balancing_params b_ps; + b_ps.unify_outputs = true; + + const auto topolinano_balanced = fiction::network_balancing>( + fiction::fanout_substitution>(topolinano), b_ps); + + auto topolinano_ranked = fiction::mutable_rank_view(topolinano_balanced); + auto planarized_b = fiction::node_duplication_planarization(topolinano_ranked); + + CHECK(planarized_b.get_network_name() == "topolinano"); + + orthogonal_physical_design_stats orthogonal_planar_stats{}; + + const auto layout = fiction::orthogonal_planar(planarized_b, {}, &orthogonal_planar_stats); + + // network name + CHECK(layout.get_layout_name() == "topolinano"); + + // PO names + CHECK(layout.get_output_name(0) == "f1"); + CHECK(layout.get_output_name(1) == "f2"); + CHECK(layout.get_output_name(2) == "f3"); +} \ No newline at end of file diff --git a/test/algorithms/properties/check_planarity.cpp b/test/algorithms/properties/check_planarity.cpp new file mode 100644 index 0000000000..306ab9f531 --- /dev/null +++ b/test/algorithms/properties/check_planarity.cpp @@ -0,0 +1,113 @@ +// +// Created by benjamin on 17.07.24. +// + +#include + +#include +#include +#include + +#include +#include + +#include + +using namespace fiction; + +TEST_CASE("Check planarity aig", "[check-planarity]") +{ + mockturtle::aig_network aig{}; + + CHECK(mockturtle::has_clear_visited_v); + CHECK(mockturtle::has_visited_v); + CHECK(mockturtle::has_set_visited_v); + + const auto x1 = aig.create_pi(); + const auto x2 = aig.create_pi(); + const auto x3 = aig.create_pi(); + + const auto f1 = aig.create_and(x1, x2); + const auto f2 = aig.create_and(x2, x3); + + const auto x4 = aig.create_pi(); + const auto x5 = aig.create_not(x4); + + aig.create_po(f1); + aig.create_po(f2); + aig.create_po(x5); + + mutable_rank_view tec_r(aig); + + const std::vector nodes_rank0{1, 2, 3, 6}; + const std::vector nodes_rank1{4, 5}; + + tec_r.set_ranks(0, nodes_rank0); + tec_r.set_ranks(1, nodes_rank1); + + const bool planar = check_planarity(tec_r); + + CHECK(planar == 1); +} + +TEST_CASE("Check planarity technology network", "[check-planarity]") +{ + technology_network tec{}; + + CHECK(mockturtle::has_clear_visited_v); + CHECK(mockturtle::has_visited_v); + CHECK(mockturtle::has_set_visited_v); + + const auto x1 = tec.create_pi(); + const auto x2 = tec.create_pi(); + const auto x3 = tec.create_pi(); + + const auto f1 = tec.create_and(x1, x2); + const auto f2 = tec.create_and(x2, x3); + + tec.create_po(f1); + tec.create_po(f2); + + mutable_rank_view tec_r(tec); + + const std::vector nodes_rank0{2, 3, 4}; + const std::vector nodes_rank1{5, 6}; + + tec_r.set_ranks(0, nodes_rank0); + tec_r.set_ranks(1, nodes_rank1); + + const bool planar = check_planarity(tec_r); + + CHECK(planar == 1); +} + +TEST_CASE("Check non-planarity technology network", "[check-planarity]") +{ + technology_network tec{}; + + CHECK(mockturtle::has_clear_visited_v); + CHECK(mockturtle::has_visited_v); + CHECK(mockturtle::has_set_visited_v); + + const auto x1 = tec.create_pi(); + const auto x2 = tec.create_pi(); + const auto x3 = tec.create_pi(); + + const auto f1 = tec.create_and(x1, x3); + const auto f2 = tec.create_and(x2, x3); + + tec.create_po(f1); + tec.create_po(f2); + + mutable_rank_view tec_r(tec); + + const std::vector nodes_rank0{2, 3, 4}; + const std::vector nodes_rank1{5, 6}; + + tec_r.set_ranks(0, nodes_rank0); + tec_r.set_ranks(1, nodes_rank1); + + const bool planar = check_planarity(tec_r); + + CHECK(planar == 0); +} From 263d0e3c3f34fbae71b1c963c19b793b7c54e690 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 6 Oct 2025 15:11:17 +0000 Subject: [PATCH 08/67] :memo: Update pyfiction docstrings Signed-off-by: GitHub Actions --- .../pyfiction/pybind11_mkdoc_docstrings.hpp | 263 +++++++++++++++--- 1 file changed, 230 insertions(+), 33 deletions(-) diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index 473ccbf471..cd154f3a12 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -4978,7 +4978,25 @@ Template parameter ``WiringReductionLyt``: Parameter ``lyt``: The wiring_reduction_layout to which obstructions will be added.)doc"; -static const char *__doc_fiction_detail_adjust_final_values = R"doc()doc"; +static const char *__doc_fiction_detail_adjust_final_values = +R"doc(Balances the final x and y wiring coordinates across all nodes in a +level. + +The function identifies the center node or cluster with the maximum +routing demand and adjusts all x and y coordinates such that the total +wiring length is symmetric around this center. + +Parameter ``x``: + Vector of x-coordinates to be adjusted. + +Parameter ``y``: + Vector of y-coordinates to be adjusted. + +Parameter ``two_input_indices``: + Indices of two-input nodes within the level. + +Parameter ``two_input_new_lines``: + Number of new routing lines associated with each two-input node.)doc"; static const char *__doc_fiction_detail_adjust_tile = R"doc(This function adjusts the tile and gates in the layout after deleting @@ -5165,11 +5183,65 @@ Parameter ``defect_lyt``: Returns: A `CellLyt` object representing the generated cell layout.)doc"; -static const char *__doc_fiction_detail_calculate_allowed_orientation = R"doc()doc"; +static const char *__doc_fiction_detail_calculate_allowed_orientation = +R"doc(Determines the allowed orientation of a node based on its predecessor. +For nodes driven by a fan-out, the orientation is defined by their +relative rank position among the fan-out’s successors. + +Returns: - 0: Node is the first (east) successor of its predecessor. - +1: Node is the second (west) successor of its predecessor. - 2: Node +is not driven by a fan-out. + +Template parameter ``Ntk``: + Logic network type. + +Parameter ``ntk``: + Logic network containing the node. + +Parameter ``n``: + Node for which to determine the allowed orientation. + +Returns: + Orientation code (0–2) describing the node’s relative position.)doc"; + +static const char *__doc_fiction_detail_calculate_buffer_connection_type = +R"doc(Computes the buffer connection type for a given node. Determines +whether the node serves as the second (rightmost) fan-in of its +successor node. + +Template parameter ``Ntk``: + Logic network type. + +Parameter ``ntk``: + Logic network containing the node. + +Parameter ``n``: + Node to analyze. + +Returns: + 1 if the node is the rightmost fan-in of its successor, otherwise + 0.)doc"; + +static const char *__doc_fiction_detail_calculate_fanout_connection_type = +R"doc(Computes the fan-out connection type of a node based on its +successors' fan-in structures. Assumes exactly two fan-outs, which are +ordered by rank position. -static const char *__doc_fiction_detail_calculate_connection = R"doc()doc"; +Returns: - 0: Both fan-outs have a single fan-in. - 1: First fan-out +has one fan-in, second has multiple. - 2: Second fan-out has one fan- +in, first has multiple. - 3: Both fan-outs have multiple fan-ins. -static const char *__doc_fiction_detail_calculate_fanout_connection_type = R"doc()doc"; +Template parameter ``Ntk``: + Logic network type. + +Parameter ``ntk``: + Logic network containing the node. + +Parameter ``n``: + Node for which to determine the fan-out connection type. + +Returns: + Integer code (0–3) indicating the fan-out connection pattern.)doc"; static const char *__doc_fiction_detail_calculate_offset_matrix = R"doc(Calculate an offset matrix based on a to-delete list in a @@ -5214,11 +5286,76 @@ Parameter ``nodes``: Returns: The vector of node pairs.)doc"; -static const char *__doc_fiction_detail_calculate_predecessor_gap = R"doc()doc"; +static const char *__doc_fiction_detail_calculate_predecessor_gap = +R"doc(Computes the gap between the fan-in node and its preceding node, i.e., +the node with a rank position one less than the current node. This +value indicates the available spacing for placement. + +Template parameter ``Ntk``: + Logic network type. + +Template parameter ``Lyt``: + Layout type. + +Parameter ``ntk``: + Logic network containing the node. + +Parameter ``node2pos``: + Mapping from network nodes to layout tile positions. + +Parameter ``lvl``: + Current level index of the node. + +Parameter ``n``: + Node for which to compute the predecessor gap. + +Returns: + Gap size (clamped to a maximum of 2).)doc"; + +static const char *__doc_fiction_detail_calculate_start_orientation = +R"doc(Determines the initial orientation for a given network level. The +orientation is inferred from the structural pattern of nodes within +the specified level, considering their fan-in and fan-out +relationships. -static const char *__doc_fiction_detail_calculate_start_orientation = R"doc()doc"; +Returns: - 0: Default orientation (no specific structure found). - 1: +Level starts with a fan-out or buffer structure. - 3: Level starts +with a two-input (binary) gate. + +Template parameter ``Ntk``: + Logic network type. + +Parameter ``ntk``: + Logic network to analyze. -static const char *__doc_fiction_detail_calculate_two_input_new_lines = R"doc()doc"; +Parameter ``lvl``: + Level index for which to compute the starting orientation. + +Returns: + Orientation code (0, 1, or 3) defining the level’s initial + direction.)doc"; + +static const char *__doc_fiction_detail_calculate_two_input_new_lines = +R"doc(Computes the number of new routing lines required. + +Template parameter ``Ntk``: + Logic network type. + +Template parameter ``Lyt``: + Layout type. + +Parameter ``ntk``: + Logic network to analyze. + +Parameter ``node2pos``: + Mapping from network nodes to layout tile positions. + +Parameter ``lvl``: + Level index to inspect. + +Returns: + Vector of gap sizes (new line counts) for all two-input nodes in + the level.)doc"; static const char *__doc_fiction_detail_check_and_optimize_po_positions = R"doc(Utility function that checks and optimizes PO positions after each @@ -5866,7 +6003,20 @@ Parameter ``lyt``: The number of primary outputs that are placed to the right of the middle primary output.)doc"; -static const char *__doc_fiction_detail_compute_two_input_indices = R"doc()doc"; +static const char *__doc_fiction_detail_compute_two_input_indices = +R"doc(Collects the indices of all two-input nodes in a given level. + +Template parameter ``Ntk``: + Logic network type. + +Parameter ``ntk``: + Logic network to analyze. + +Parameter ``lvl``: + Level index to inspect. + +Returns: + Vector of node indices with two fan-ins in the specified level.)doc"; static const char *__doc_fiction_detail_connect_and_place = R"doc()doc"; @@ -7807,9 +7957,19 @@ static const char *__doc_fiction_detail_generate_edge_intersection_graph_impl_ps static const char *__doc_fiction_detail_generate_edge_intersection_graph_impl_run = R"doc()doc"; -static const char *__doc_fiction_detail_get_buffer_lookup = R"doc()doc"; +static const char *__doc_fiction_detail_get_buffer_lookup = +R"doc(Defines a 3D lookup table using `std::array` and encapsulates it +within a function. This table encodes all possible combinations of the +previous level, connection type, orientations, and surrounding spacing +(gaps). Based on these inputs, it returns the corresponding +orientation and spacing configuration for the current buffer.)doc"; -static const char *__doc_fiction_detail_get_fanout_lookup = R"doc()doc"; +static const char *__doc_fiction_detail_get_fanout_lookup = +R"doc(Defines a 3D lookup table using `std::array` and encapsulates it +within a function. This table encodes all possible combinations of the +previous level, connection type, orientations, and surrounding spacing +(gaps). Based on these inputs, it returns the corresponding +orientation and spacing configuration for the current fanout.)doc"; static const char *__doc_fiction_detail_get_offset = R"doc(Utility function to calculate the offset that has to be subtracted @@ -9274,13 +9434,12 @@ static const char *__doc_fiction_detail_new_gate_location_SRC = R"doc(Check if t static const char *__doc_fiction_detail_node_duplication_planarization_impl = R"doc()doc"; static const char *__doc_fiction_detail_node_duplication_planarization_impl_compute_slice_delays = -R"doc(The H-graph represents all possible orderings of node pairs within a -single network level. A "slice" is created by adding all possible -combinations of a `node_pair` to the H-graph of the level. These -combinations are formed by selecting pairs of nodes from the fan-ins -of the input node: - If the input node has only one fan-in, it is -treated as a single combination. - If the input node has two fan-ins, -there are two possible combinations. +R"doc(A "slice" describes one vertical layer in the H-graph. It is created +by adding all possible combinations of a `node_pair` to the H-graph of +the level. These combinations are formed by selecting pairs of nodes +from the fan-ins of the input node: - If the input node has only one +fan-in, it is treated as a single combination. - If the input node has +two fan-ins, there are two possible combinations. Each `node_pair` consists of a first and second element. The objective is to find an ordering of node pairs that maximizes the instances @@ -9321,7 +9480,14 @@ Parameter ``node``: The node to be inserted. Parameter ``vec``: - The vector to insert the node into.)doc"; + The vector to insert the node into. + +Parameter ``saturated_fanout_flag``: + The flag which indicates that the maximum number of fanouts for + this node is reached. + +Parameter ``position``: + The position of the node (terminal node or not).)doc"; static const char *__doc_fiction_detail_node_duplication_planarization_impl_lvl_pairs = R"doc()doc"; @@ -9338,8 +9504,7 @@ member 'pair'. These two outer nodes are connected through zero or more 'middle_nodes'. The fanin order starts with the first node in 'pair', then proceeds through the 'middle_nodes', and ends with the second node in 'pair'. The order of 'middle_nodes' is arbitrary as -they cannot be further connected to any other nodes. For the -planarization, only the nodes inside the 'pair' are relevant. +they cannot be further connected to any other nodes. Template parameter ``Ntk``: Network type for the nodes in the pair.)doc"; @@ -9779,17 +9944,19 @@ static const char *__doc_fiction_detail_orthogonal_impl_pst = R"doc()doc"; static const char *__doc_fiction_detail_orthogonal_impl_run = R"doc()doc"; -static const char *__doc_fiction_detail_orthogonal_planar_v2_impl = R"doc()doc"; +static const char *__doc_fiction_detail_orthogonal_planar_impl = R"doc()doc"; + +static const char *__doc_fiction_detail_orthogonal_planar_impl_ntk = R"doc()doc"; -static const char *__doc_fiction_detail_orthogonal_planar_v2_impl_orthogonal_planar_v2_impl = R"doc()doc"; +static const char *__doc_fiction_detail_orthogonal_planar_impl_orthogonal_planar_impl = R"doc()doc"; -static const char *__doc_fiction_detail_orthogonal_planar_v2_impl_po_counter = R"doc()doc"; +static const char *__doc_fiction_detail_orthogonal_planar_impl_po_counter = R"doc()doc"; -static const char *__doc_fiction_detail_orthogonal_planar_v2_impl_ps = R"doc()doc"; +static const char *__doc_fiction_detail_orthogonal_planar_impl_ps = R"doc()doc"; -static const char *__doc_fiction_detail_orthogonal_planar_v2_impl_pst = R"doc()doc"; +static const char *__doc_fiction_detail_orthogonal_planar_impl_pst = R"doc()doc"; -static const char *__doc_fiction_detail_orthogonal_planar_v2_impl_run = R"doc()doc"; +static const char *__doc_fiction_detail_orthogonal_planar_impl_run = R"doc()doc"; static const char *__doc_fiction_detail_physical_population_stability_impl = R"doc(This class implements the simulation of the population stability for a @@ -16987,8 +17154,12 @@ Parameter ``rfun``: The actual parsing function.)doc"; static const char *__doc_fiction_node_duplication_planarization = -R"doc(Implements a planarization mechanism for networks using a H-Graph -strategy for node duplication. +R"doc(Implements a planarization mechanism for networks from the paper +\"Fabricatable Interconnect and Molecular QCA Circuits\" by Amitabh +Chaudhary, Danny Ziyi Chen, Xiaobo Sharon Hu, Michael T. Niemier, +Ramprasad Ravichandran and Kevin Whitton in IEEE TRANSACTIONS ON +COMPUTER-AIDED DESIGN OF INTEGRATED CIRCUITS AND SYSTEMS, Volume 26, +2007. The planarization achieved by this function solves the Node Duplication Crossing Minimization (NDCE) problem by finding the @@ -16997,10 +17168,8 @@ H-graph describes edge relations between two levels in a network, with one level assumed as fixed, starting at the Primary Outputs (POs). By finding the shortest path from the source (x) to the sink (y) in this H-graph, an optimal solution for the NDCE problem for each level is -found. The function constructs an H-graph that captures edge relations -between two levels within the graph and computes the shortest x-y -paths on the H-graph, traversing from the POs towards the Primary -Inputs (PIs). +found. The function traverses from the Primary Outputs (POs) towards +the Primary Inputs (PIs). Template parameter ``NtkDest``: Destination network type. @@ -17955,7 +18124,35 @@ static const char *__doc_fiction_orthogonal_physical_design_stats_x_size = R"doc static const char *__doc_fiction_orthogonal_physical_design_stats_y_size = R"doc()doc"; -static const char *__doc_fiction_orthogonal_planar_v2 = R"doc(Description)doc"; +static const char *__doc_fiction_orthogonal_planar = +R"doc(This algorithm performs a fully planar physical design flow for Field- +Coupled Nanocomputing (FCN) circuits. It takes as input a logic +network with a planar embedding, represented as a `mutable_rank_view`, +and preserves this embedding during placement and routing. + +In this approach, each logic level of the network is mapped to a +diagonal in the layout, while nodes within the same level are placed +according to their rank positions in the planar embedding. This +ensures a crossing-free, scalable, and layout-consistent mapping from +logic to physical design. + +Template parameter ``Lyt``: + Gate-level layout type. + +Template parameter ``Ntk``: + Logic network type. + +Parameter ``ntk``: + Planar logic network to be placed and routed. + +Parameter ``ps``: + Configuration parameters for the physical design process. + +Parameter ``pst``: + Optional statistics object to collect runtime and layout metrics. + +Returns: + A fully planar gate-level layout of type `Lyt`.)doc"; static const char *__doc_fiction_out_of_cell_names_exception = R"doc()doc"; From a73295cc01e5e9588913bc90a2dfd8457990fc4c Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 7 Oct 2025 11:57:14 +0200 Subject: [PATCH 09/67] :art: updated docstring --- .../physical_design/orthogonal_planar.hpp | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp index 8a3ea0d2b1..96da62c12d 100644 --- a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp +++ b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp @@ -743,7 +743,7 @@ compute_wiring(const Ntk& ntk, const mockturtle::node_map 0; --i) { - std::size_t cluster_index_end = two_input_indices[i - 1]; + std::size_t cluster_index_end = two_input_indices[i - 1]; if (i == 1) { @@ -776,6 +776,21 @@ compute_wiring(const Ntk& ntk, const mockturtle::node_map class orthogonal_planar_impl { @@ -816,10 +831,9 @@ class orthogonal_planar_impl for (uint32_t lvl = 0; lvl < ntk.depth() + 1; lvl++) { - const auto variable_tuple = - compute_pr_variables, Lyt>(ntk, node2pos, lvl); - const auto orientation = std::get<0>(variable_tuple); - const auto new_lines = std::get<1>(variable_tuple); + const auto variable_tuple = compute_pr_variables, Lyt>(ntk, node2pos, lvl); + const auto orientation = std::get<0>(variable_tuple); + const auto new_lines = std::get<1>(variable_tuple); const auto wiring = compute_wiring(ntk, node2pos, new_lines, lvl); const auto& x = wiring.first; From ee39d0169736e850c42cb0a5585d298c971ca134 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 7 Oct 2025 09:58:31 +0000 Subject: [PATCH 10/67] :memo: Update pyfiction docstrings Signed-off-by: GitHub Actions --- .../pyfiction/pybind11_mkdoc_docstrings.hpp | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index cd154f3a12..e950509afa 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -9944,7 +9944,31 @@ static const char *__doc_fiction_detail_orthogonal_impl_pst = R"doc()doc"; static const char *__doc_fiction_detail_orthogonal_impl_run = R"doc()doc"; -static const char *__doc_fiction_detail_orthogonal_planar_impl = R"doc()doc"; +static const char *__doc_fiction_detail_orthogonal_planar_impl = +R"doc(Implements the general planar layout generation algorithm. + +The algorithm performs placement and routing level by level, starting +from the primary inputs and proceeding toward the outputs. For each +level, node placement depends on the orientation and excess wiring +computed in auxiliary routines such as `compute_pr_variables` and +`compute_wiring`. Nodes are positioned according to their fan-in +structure and routing constraints to ensure planarity of the resulting +layout. + +Template parameter ``Lyt``: + Gate-level layout type. + +Template parameter ``Ntk``: + Logic network type. + +Parameter ``src``: + Source network to be placed and routed. + +Parameter ``p``: + Parameters controlling layout generation and clocking. + +Parameter ``st``: + Statistics object used to collect runtime and layout information.)doc"; static const char *__doc_fiction_detail_orthogonal_planar_impl_ntk = R"doc()doc"; From 875f136f0139a9dc9eecc5f236763cee14bbf0f1 Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 7 Oct 2025 12:10:55 +0200 Subject: [PATCH 11/67] :art: updated docstring --- .../physical_design/orthogonal_planar.hpp | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp index 96da62c12d..63ae17b5ad 100644 --- a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp +++ b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp @@ -430,9 +430,8 @@ compute_pr_variables(const Ntk& ntk, const mockturtle::node_map(ntk, node2pos, lvl, n); - - // needs gap and orientation as input - if (ntk.fanin_size(n) == 2) // complete + // calculate the orientation and new_lines depending on the type of nodes and available space + if (ntk.fanin_size(n) == 2) { if (i != 0) { @@ -558,15 +557,14 @@ std::vector calculate_two_input_new_lines(const Ntk& /** * Balances the final x and y wiring coordinates across all nodes in a level. * - * The function identifies the center node or cluster with the maximum routing demand and adjusts all x and y - * coordinates such that the total wiring length is symmetric around this center. + * The function identifies the minimal routing demand, which means the closest diagonal, where all nodes can be placed + * and can be routed without conflicts. * * @param x Vector of x-coordinates to be adjusted. * @param y Vector of y-coordinates to be adjusted. * @param two_input_indices Indices of two-input nodes within the level. * @param two_input_new_lines Number of new routing lines associated with each two-input node. */ - void adjust_final_values(std::vector& x, std::vector& y, const std::vector& two_input_indices, const std::vector& two_input_new_lines) @@ -807,17 +805,14 @@ class orthogonal_planar_impl using node = typename Ntk::node; // measure run time mockturtle::stopwatch stop{pst.time_total}; - + // initialize mapping from nodes to positions mockturtle::node_map, decltype(ntk)> node2pos{ntk}; - + // initialize the aspect ratio aspect_ratio aspect_ratio = {0, 0}; - // instantiate the layout Lyt layout{aspect_ratio, twoddwave_clocking(ps.number_of_clock_phases)}; - // reserve PI nodes without positions auto pi2node = reserve_input_nodes(layout, ntk); - // first x-pos to use for gates is 1 because PIs take up the 0th column tile latest_pos{1, 0}; @@ -828,7 +823,7 @@ class orthogonal_planar_impl tile place_t{0, 0}; tile first_pos = {ntk.num_pis() - 1, 0}; - + // place and route the nodes in ascending level order for (uint32_t lvl = 0; lvl < ntk.depth() + 1; lvl++) { const auto variable_tuple = compute_pr_variables, Lyt>(ntk, node2pos, lvl); @@ -838,7 +833,7 @@ class orthogonal_planar_impl const auto wiring = compute_wiring(ntk, node2pos, new_lines, lvl); const auto& x = wiring.first; const auto& y = wiring.second; - + // place and route the nodes in ascending rank order ntk.foreach_node_in_rank( lvl, [this, &layout, &pi2node, &node2pos, &orientation, &first_pos, &place_t, &x, &y](const auto& n, @@ -933,7 +928,8 @@ class orthogonal_planar_impl first_pos = place_t; } } - else // if node has two fanins (or three fanins with one of them being constant) + // if node has two fanins (or three fanins with one of them being constant) + else { const auto &pre1 = fc.fanin_nodes[0], pre2 = fc.fanin_nodes[1]; @@ -975,6 +971,7 @@ class orthogonal_planar_impl }); } + // place and route POs std::unordered_map count_map; int add_line = 0; // the number of outputs on a node is limited to 2, due to fanout substitution From 0d9677dcb031841b4f10520593bd443f054abfb1 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 7 Oct 2025 10:13:52 +0000 Subject: [PATCH 12/67] :memo: Update pyfiction docstrings Signed-off-by: GitHub Actions --- .../include/pyfiction/pybind11_mkdoc_docstrings.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index e950509afa..3466c414a4 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -4982,9 +4982,9 @@ static const char *__doc_fiction_detail_adjust_final_values = R"doc(Balances the final x and y wiring coordinates across all nodes in a level. -The function identifies the center node or cluster with the maximum -routing demand and adjusts all x and y coordinates such that the total -wiring length is symmetric around this center. +The function identifies the minimal routing demand, which means the +closest diagonal, where all nodes can be placed and can be routed +without conflicts. Parameter ``x``: Vector of x-coordinates to be adjusted. From 47121e66f4016011801189b1e9d3961ff57f1c63 Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 7 Oct 2025 13:26:02 +0200 Subject: [PATCH 13/67] :art: updated name conservation --- .../node_duplication_planarization.hpp | 10 +++++----- .../physical_design/orthogonal_planar.cpp | 14 +++++++++++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index 0a4a96940e..9138345232 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -367,7 +367,8 @@ template class node_duplication_planarization_impl { public: - [[maybe_unused]] node_duplication_planarization_impl(const Ntk& src, const node_duplication_planarization_params& p) : + [[maybe_unused]] node_duplication_planarization_impl(const Ntk& src, + const node_duplication_planarization_params& p) : ntk(src), ps{p} {} @@ -675,8 +676,7 @@ class node_duplication_planarization_impl virtual_ntk.set_all_ranks(ntk_lvls_new); // restore possibly set signal names - restore_network_name(ntk, virtual_ntk); - restore_output_names(ntk, virtual_ntk); + restore_names(ntk, virtual_ntk); return virtual_ntk; } @@ -725,8 +725,8 @@ class node_duplication_planarization_impl * @return A view of the planarized virtual_pi_network created in the format of mutable_rank_view. */ template -[[nodiscard]] virtual_pi_network -node_duplication_planarization(const NtkSrc& ntk_src, node_duplication_planarization_params ps = {}) +[[nodiscard]] virtual_pi_network node_duplication_planarization(const NtkSrc& ntk_src, + node_duplication_planarization_params ps = {}) { static_assert(mockturtle::is_network_type_v, "NtkSrc is not a network type"); static_assert(mockturtle::has_create_node_v, "NtkSrc does not implement the create_node function"); diff --git a/test/algorithms/physical_design/orthogonal_planar.cpp b/test/algorithms/physical_design/orthogonal_planar.cpp index 059d87abad..b9c8460ac0 100644 --- a/test/algorithms/physical_design/orthogonal_planar.cpp +++ b/test/algorithms/physical_design/orthogonal_planar.cpp @@ -4,6 +4,7 @@ #include "fiction/layouts/cell_level_layout.hpp" #include "fiction/layouts/coordinates.hpp" #include "fiction/technology/cell_technologies.hpp" +#include "fiction/utils/debug/network_writer.hpp" #include "utils/blueprints/network_blueprints.hpp" #include @@ -89,18 +90,29 @@ TEST_CASE("Name conservation after planar orthogonal physical design", "[orthogo const auto topolinano_balanced = fiction::network_balancing>( fiction::fanout_substitution>(topolinano), b_ps); + debug::write_dot_network(topolinano_balanced, "balanced"); + auto topolinano_ranked = fiction::mutable_rank_view(topolinano_balanced); auto planarized_b = fiction::node_duplication_planarization(topolinano_ranked); + CHECK(planarized_b.get_name(2) == "x1"); + CHECK(planarized_b.get_name(3) == "x2"); + + debug::write_dot_network(planarized_b, "planarized"); + CHECK(planarized_b.get_network_name() == "topolinano"); orthogonal_physical_design_stats orthogonal_planar_stats{}; const auto layout = fiction::orthogonal_planar(planarized_b, {}, &orthogonal_planar_stats); + debug::write_dot_layout(layout); + // network name CHECK(layout.get_layout_name() == "topolinano"); - + // PI names (they are ordered corresponding to their placement in the layout) + CHECK(layout.get_input_name(1) == "x1"); + CHECK(layout.get_input_name(0) == "x2"); // PO names CHECK(layout.get_output_name(0) == "f1"); CHECK(layout.get_output_name(1) == "f2"); From ecc189aa6f735ca8fe70ad5cd84bf49c1e6dfe09 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 7 Oct 2025 12:01:08 +0000 Subject: [PATCH 14/67] =?UTF-8?q?=F0=9F=8E=A8=20Incorporated=20pre-commit?= =?UTF-8?q?=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../algorithms/physical_design/orthogonal_planar.hpp | 8 ++++---- test/algorithms/physical_design/orthogonal_planar.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp index 63ae17b5ad..6ad21119d2 100644 --- a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp +++ b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp @@ -308,7 +308,7 @@ uint64_t calculate_buffer_connection_type(const Ntk& ntk, const mockturtle::node /** * Determines the allowed orientation of a node based on its predecessor. - * For nodes driven by a fan-out, the orientation is defined by their relative rank position among the fan-out’s + * For nodes driven by a fan-out, the orientation is defined by their relative rank position among the fan-out's * successors. * * Returns: @@ -319,7 +319,7 @@ uint64_t calculate_buffer_connection_type(const Ntk& ntk, const mockturtle::node * @tparam Ntk Logic network type. * @param ntk Logic network containing the node. * @param n Node for which to determine the allowed orientation. - * @return Orientation code (0–2) describing the node’s relative position. + * @return Orientation code (0–2) describing the node's relative position. */ template uint64_t calculate_allowed_orientation(const Ntk& ntk, const mockturtle::node& n) @@ -355,7 +355,7 @@ uint64_t calculate_allowed_orientation(const Ntk& ntk, const mockturtle::node int calculate_start_orientation(const Ntk& ntk, const uint32_t& lvl) @@ -401,7 +401,7 @@ int calculate_start_orientation(const Ntk& ntk, const uint32_t& lvl) * Computes orientation and routing line variables for a given network level. * * For each node in the specified level, the function determines: - * - The node’s orientation, based on its fan-in structure, gap spacing, and connection type. + * - The node's orientation, based on its fan-in structure, gap spacing, and connection type. * - Whether new routing lines must be introduced, derived from lookup tables for buffer and fan-out configurations. * * @tparam Ntk Logic network type. diff --git a/test/algorithms/physical_design/orthogonal_planar.cpp b/test/algorithms/physical_design/orthogonal_planar.cpp index b9c8460ac0..fa1dd5cd7b 100644 --- a/test/algorithms/physical_design/orthogonal_planar.cpp +++ b/test/algorithms/physical_design/orthogonal_planar.cpp @@ -117,4 +117,4 @@ TEST_CASE("Name conservation after planar orthogonal physical design", "[orthogo CHECK(layout.get_output_name(0) == "f1"); CHECK(layout.get_output_name(1) == "f2"); CHECK(layout.get_output_name(2) == "f3"); -} \ No newline at end of file +} From d2aeef507614a306e0737a403ea01e53f38046e7 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 7 Oct 2025 12:02:08 +0000 Subject: [PATCH 15/67] :memo: Update pyfiction docstrings Signed-off-by: GitHub Actions --- .../include/pyfiction/pybind11_mkdoc_docstrings.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index 3466c414a4..085e78c08e 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -5186,7 +5186,7 @@ Parameter ``defect_lyt``: static const char *__doc_fiction_detail_calculate_allowed_orientation = R"doc(Determines the allowed orientation of a node based on its predecessor. For nodes driven by a fan-out, the orientation is defined by their -relative rank position among the fan-out’s successors. +relative rank position among the fan-out's successors. Returns: - 0: Node is the first (east) successor of its predecessor. - 1: Node is the second (west) successor of its predecessor. - 2: Node @@ -5202,7 +5202,7 @@ Parameter ``n``: Node for which to determine the allowed orientation. Returns: - Orientation code (0–2) describing the node’s relative position.)doc"; + Orientation code (0–2) describing the node's relative position.)doc"; static const char *__doc_fiction_detail_calculate_buffer_connection_type = R"doc(Computes the buffer connection type for a given node. Determines @@ -5332,7 +5332,7 @@ Parameter ``lvl``: Level index for which to compute the starting orientation. Returns: - Orientation code (0, 1, or 3) defining the level’s initial + Orientation code (0, 1, or 3) defining the level's initial direction.)doc"; static const char *__doc_fiction_detail_calculate_two_input_new_lines = From b0be14ab4b8cefa7af73df57295ab63f2b9bdfa1 Mon Sep 17 00:00:00 2001 From: benjamin Date: Mon, 13 Oct 2025 14:55:39 +0200 Subject: [PATCH 16/67] :art: nitpick comments --- .../node_duplication_planarization.hpp | 56 +++++++++++-------- .../physical_design/orthogonal_planar.hpp | 8 +-- .../node_duplication_planarization.cpp | 10 ++-- .../physical_design/orthogonal_planar.cpp | 8 --- 4 files changed, 41 insertions(+), 41 deletions(-) diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index 9138345232..f1b0711cf1 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -5,6 +5,7 @@ #ifndef FICTION_NODE_DUPLICATION_PLANARIZATION_HPP #define FICTION_NODE_DUPLICATION_PLANARIZATION_HPP +#include "fiction/algorithms/network_transformation/network_balancing.hpp" #include "fiction/algorithms/properties/check_planarity.hpp" #include "fiction/networks/virtual_pi_network.hpp" @@ -18,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -153,13 +155,13 @@ initialize_copy_network_duplicates(Ntk const& src) * @return Vector of fanins in the virtual_pi_network connected to the processed node. */ template -std::vector> +std::vector> gather_fanin_signals(const Ntk& ntk, const NtkDest& ntk_dest_v, const mockturtle::node n, - const mockturtle::node_map>, Ntk>& old2new_v, - const std::vector>& lvl, std::size_t& node_index) + const mockturtle::node_map>, Ntk>& old2new_v, + const std::vector>& lvl, std::size_t& node_index) { // Initialize variables - std::vector> children{}; + std::vector> children{}; children.reserve(ntk.fanin_size(n)); std::size_t local_node_index = 0; @@ -169,8 +171,8 @@ gather_fanin_signals(const Ntk& ntk, const NtkDest& ntk_dest_v, const mockturtle const auto i) { // Get the vector of duplicated nodes of the original fan-in node fn. - const auto fn = ntk.get_node(f); - const auto tgt_signal_v = old2new_v[fn]; + const auto fn = ntk.get_node(f); + const auto& tgt_signal_v = old2new_v[fn]; assert(node_index < lvl.size() && "The fanin iterator is out of scope"); @@ -184,10 +186,11 @@ gather_fanin_signals(const Ntk& ntk, const NtkDest& ntk_dest_v, const mockturtle { // get the node from the newly generated network. const auto node_at_index = lvl[j]; + const auto candidate_sig = ntk_dest_v.make_signal(node_at_index); // Check if the candidate matches the original fan-in or a duplicate. // Also, verify if the candidate has already reached its fan-out limit. - if ((std::find(tgt_signal_v.cbegin(), tgt_signal_v.cend(), node_at_index) != tgt_signal_v.cend()) && + if ((std::find(tgt_signal_v.cbegin(), tgt_signal_v.cend(), candidate_sig) != tgt_signal_v.cend()) && (ntk_dest_v.fanout_size(node_at_index) < ntk.fanout_size(fn))) { // Set the local node_index. @@ -201,7 +204,7 @@ gather_fanin_signals(const Ntk& ntk, const NtkDest& ntk_dest_v, const mockturtle } // Add the matched candidate fan-in to the children. - children.emplace_back(node_at_index); + children.emplace_back(candidate_sig); break; } } @@ -233,10 +236,9 @@ gather_fanin_signals(const Ntk& ntk, const NtkDest& ntk_dest_v, const mockturtle * @param ntk_lvls_new Levels of newly created nodes in the virtual_pi_network. */ template -virtual_pi_network -create_virtual_pi_ntk_from_duplicated_nodes(const Ntk& ntk, - const std::vector>>& ntk_lvls, - std::vector>>& ntk_lvls_new) +virtual_pi_network create_virtual_pi_ntk_from_duplicated_nodes( + const Ntk& ntk, const std::vector>>& ntk_lvls, + std::vector>>>& ntk_lvls_new) { std::unordered_map, bool> node_status; ntk_lvls_new.resize(ntk_lvls.size()); @@ -266,7 +268,8 @@ create_virtual_pi_ntk_from_duplicated_nodes(const Ntk& { if (node_status[nd]) { - const auto& new_node = ntk_dest_v.create_virtual_pi(nd); + const auto& new_signal = ntk_dest_v.create_virtual_pi(nd); + const auto& new_node = ntk_dest_v.get_node(new_signal); lvl_new.push_back(new_node); old2new_v[nd].push_back(new_node); } @@ -278,6 +281,8 @@ create_virtual_pi_ntk_from_duplicated_nodes(const Ntk& } else { + assert(i != ntk_lvls.size() && "Node, which is not marked as PI is in the PI rank"); + const auto children = gather_fanin_signals(ntk, ntk_dest_v, nd, old2new_v, ntk_lvls_new[i + 1], node_index); @@ -396,7 +401,6 @@ class node_duplication_planarization_impl * Processed node_pairs are stored in the `lvl_pairs` member for subsequent delay calculations. * * @param nd Node in the H-graph. - * @param border_pis A boolean indicating whether the input PIs (Primary Inputs) should be propagated to the next */ void compute_slice_delays(const mockturtle::node& nd) { @@ -415,7 +419,7 @@ class node_duplication_planarization_impl } }); - assert(fis.empty() == 0 && "There has to be at least one node in this level"); + assert(!fis.empty() && "There has to be at least one node in this level"); // Compute the combinations in one slice auto combinations = calculate_pairs(fis); @@ -514,8 +518,6 @@ class node_duplication_planarization_impl } /** - * Computes the order of nodes in the next level based on the delay (shortest path) in the H-graph of the level. - * * This function computes the order of nodes in the next level based on their delay in the H-graph of the level. It * selects the path with the least delay from the current level pairs and follows it via fanin relations. The nodes * are inserted into the next level vector in the order they are encountered. @@ -590,9 +592,9 @@ class node_duplication_planarization_impl return true; } - [[nodiscard]] virtual_pi_network run(std::vector>>& ntk_lvls_new) + [[nodiscard]] virtual_pi_network run() { - // Initialize the POs + // Initialize the POs with foreach_node to retain the rank_view order std::vector> pos{}; pos.reserve(ntk.num_pos()); ntk.foreach_node( @@ -665,6 +667,8 @@ class node_duplication_planarization_impl ntk_lvls.push_back(next_level); } + std::vector>>> ntk_lvls_new{}; + // create virtual pi network auto virtual_ntk = create_virtual_pi_ntk_from_duplicated_nodes(ntk, ntk_lvls, ntk_lvls_new); @@ -732,15 +736,19 @@ template static_assert(mockturtle::has_create_node_v, "NtkSrc does not implement the create_node function"); static_assert(mockturtle::has_rank_position_v, "NtkSrc does not implement the rank_position function"); - assert(is_balanced(ntk_src) && "Networks have to be balanced for this duplication"); + if (!is_balanced(ntk_src)) + { + throw std::invalid_argument("Networks have to be balanced for this duplication"); + } detail::node_duplication_planarization_impl p{ntk_src, ps}; - std::vector>> ntk_lvls_new; - - auto result = p.run(ntk_lvls_new); + auto result = p.run(); - assert(check_planarity(result) && "Planarization failed: Network should be planar"); + if (!check_planarity(result)) + { + throw std::runtime_error("Planarization failed: resulting network is not planar"); + } return result; } diff --git a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp index 6ad21119d2..2dfcc74d4b 100644 --- a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp +++ b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp @@ -683,8 +683,8 @@ compute_wiring(const Ntk& ntk, const mockturtle::node_map(j) + 1, - new_lines.begin() + static_cast(ntk.rank_width(lvl)), 0UL); + x[j] = std::accumulate(new_lines.begin() + static_cast(j + 1), + new_lines.begin() + ntk.rank_width(lvl), 0UL); y[j] = (j == 0) ? new_lines[j] : y[j - 1] + new_lines[j]; } return std::make_pair(x, y); @@ -708,8 +708,8 @@ compute_wiring(const Ntk& ntk, const mockturtle::node_map(j) + 1, - new_lines.begin() + static_cast(cluster_index_end), 0UL); + x[j] = std::accumulate(new_lines.begin() + static_cast(j + 1), + new_lines.begin() + static_cast(cluster_index_end), 0UL); // adjust y values with propagation to the right (direction based on a 1D vector) y[j] = (j == 0) ? new_lines[j] : y[j - 1] + new_lines[j]; diff --git a/test/algorithms/network_transformation/node_duplication_planarization.cpp b/test/algorithms/network_transformation/node_duplication_planarization.cpp index afdd1c68e8..4266aca095 100644 --- a/test/algorithms/network_transformation/node_duplication_planarization.cpp +++ b/test/algorithms/network_transformation/node_duplication_planarization.cpp @@ -44,7 +44,7 @@ TEST_CASE("Planarize technology ntk", "[node-duplication-planarization]") auto planarized_ntk = node_duplication_planarization(vpi_r); - CHECK(check_planarity(planarized_ntk) == 1); + CHECK(check_planarity(planarized_ntk)); mockturtle::equivalence_checking_stats st; const auto cec_m = @@ -81,7 +81,7 @@ TEST_CASE("Planarize ntk with 3-ary node", "[node-duplication-planarization]") auto planarized_ntk = node_duplication_planarization(vpi_r); - CHECK(check_planarity(planarized_ntk) == 1); + CHECK(check_planarity(planarized_ntk)); mockturtle::equivalence_checking_stats st; const auto cec_m = @@ -118,7 +118,7 @@ TEST_CASE("Buffer AIG and planarize technology_network", "[node-duplication-plan auto planarized_ntk = node_duplication_planarization(vpi_r); - CHECK(check_planarity(planarized_ntk) == 1); + CHECK(check_planarity(planarized_ntk)); mockturtle::equivalence_checking_stats st; const auto cec_m = @@ -148,7 +148,7 @@ TEST_CASE("Buffer AIG and planarize technology_network 2", "[node-duplication-pl auto planarized_ntk = node_duplication_planarization(vpi_r); - CHECK(check_planarity(planarized_ntk) == 1); + CHECK(check_planarity(planarized_ntk)); mockturtle::equivalence_checking_stats st; const auto cec_m = @@ -181,7 +181,7 @@ TEST_CASE("Planarize multi output network", "[node-duplication-planarization]") auto planarized_ntk = node_duplication_planarization(vpi_r); - CHECK(check_planarity(planarized_ntk) == 1); + CHECK(check_planarity(planarized_ntk)); mockturtle::equivalence_checking_stats st; const auto cec_m = diff --git a/test/algorithms/physical_design/orthogonal_planar.cpp b/test/algorithms/physical_design/orthogonal_planar.cpp index fa1dd5cd7b..c26558bbb2 100644 --- a/test/algorithms/physical_design/orthogonal_planar.cpp +++ b/test/algorithms/physical_design/orthogonal_planar.cpp @@ -4,7 +4,6 @@ #include "fiction/layouts/cell_level_layout.hpp" #include "fiction/layouts/coordinates.hpp" #include "fiction/technology/cell_technologies.hpp" -#include "fiction/utils/debug/network_writer.hpp" #include "utils/blueprints/network_blueprints.hpp" #include @@ -90,24 +89,17 @@ TEST_CASE("Name conservation after planar orthogonal physical design", "[orthogo const auto topolinano_balanced = fiction::network_balancing>( fiction::fanout_substitution>(topolinano), b_ps); - debug::write_dot_network(topolinano_balanced, "balanced"); - auto topolinano_ranked = fiction::mutable_rank_view(topolinano_balanced); auto planarized_b = fiction::node_duplication_planarization(topolinano_ranked); CHECK(planarized_b.get_name(2) == "x1"); CHECK(planarized_b.get_name(3) == "x2"); - - debug::write_dot_network(planarized_b, "planarized"); - CHECK(planarized_b.get_network_name() == "topolinano"); orthogonal_physical_design_stats orthogonal_planar_stats{}; const auto layout = fiction::orthogonal_planar(planarized_b, {}, &orthogonal_planar_stats); - debug::write_dot_layout(layout); - // network name CHECK(layout.get_layout_name() == "topolinano"); // PI names (they are ordered corresponding to their placement in the layout) From 18d3440c1b0656e6b9471f1c8e3f63df5950e875 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 13 Oct 2025 12:56:42 +0000 Subject: [PATCH 17/67] :memo: Update pyfiction docstrings Signed-off-by: GitHub Actions --- .../include/pyfiction/pybind11_mkdoc_docstrings.hpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index 085e78c08e..4b1efe8ce7 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -9460,11 +9460,7 @@ Processed node_pairs are stored in the `lvl_pairs` member for subsequent delay calculations. Parameter ``nd``: - Node in the H-graph. - -Parameter ``border_pis``: - A boolean indicating whether the input PIs (Primary Inputs) should - be propagated to the next)doc"; + Node in the H-graph.)doc"; static const char *__doc_fiction_detail_node_duplication_planarization_impl_insert_if_not_first = R"doc(Inserts a node into a vector if it is unique. From 29c85a2f5b924336ca3781c119b67833b79a19d7 Mon Sep 17 00:00:00 2001 From: benjamin Date: Mon, 13 Oct 2025 16:44:10 +0200 Subject: [PATCH 18/67] :art: coderabbitai comments --- .../node_duplication_planarization.hpp | 21 ++-- .../physical_design/orthogonal_planar.hpp | 98 ++++++++++--------- .../algorithms/properties/check_planarity.hpp | 10 +- .../physical_design/orthogonal_planar.cpp | 1 + 4 files changed, 72 insertions(+), 58 deletions(-) diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index f1b0711cf1..be1e5405c8 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -268,14 +268,15 @@ virtual_pi_network create_virtual_pi_ntk_from_duplicated_nodes( { if (node_status[nd]) { - const auto& new_signal = ntk_dest_v.create_virtual_pi(nd); - const auto& new_node = ntk_dest_v.get_node(new_signal); - lvl_new.push_back(new_node); - old2new_v[nd].push_back(new_node); + const auto new_sig = ntk_dest_v.create_virtual_pi(nd); + lvl_new.push_back(ntk_dest_v.get_node(new_sig)); + old2new_v[nd].push_back(new_sig); } else { - lvl_new.push_back(nd); + const auto& sigs = old2new_v[nd]; + assert(!sigs.empty()); + lvl_new.push_back(ntk_dest_v.get_node(sigs.front())); node_status[nd] = true; } } @@ -288,9 +289,9 @@ virtual_pi_network create_virtual_pi_ntk_from_duplicated_nodes( assert(!children.empty() && "The node has to have children"); - const auto& new_node = ntk_dest_v.create_node(children, ntk.node_function(nd)); - lvl_new.push_back(new_node); - old2new_v[nd].push_back(new_node); + const auto new_sig = ntk_dest_v.create_node(children, ntk.node_function(nd)); + lvl_new.push_back(ntk_dest_v.get_node(new_sig)); + old2new_v[nd].push_back(new_sig); } } } @@ -415,7 +416,7 @@ class node_duplication_planarization_impl { if (!ntk.is_constant(fi)) { - fis.push_back(fi); + fis.push_back(ntk.get_node(fi)); } }); @@ -484,7 +485,7 @@ class node_duplication_planarization_impl /** * Inserts a node into a vector if it is unique. * - * `This function inserts a node into a vector only if the vector is empty or the node is not equal to the first + * This function inserts a node into a vector only if the vector is empty or the node is not equal to the first * element of the vector. If the vector is not empty and the node is equal to the first element, it does nothing. * An exception occurs if the node was skipped on the previous insertion attempt due to `vec.front() == node`; in * that case, the node will be inserted this time. diff --git a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp index 2dfcc74d4b..6f8bc8c12d 100644 --- a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp +++ b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp @@ -6,11 +6,13 @@ #define FICTION_ORTHOGONAL_PLANAR_HPP #include "fiction/algorithms/physical_design/orthogonal.hpp" +#include "fiction/algorithms/properties/check_planarity.hpp" #include "fiction/layouts/clocking_scheme.hpp" #include "fiction/traits.hpp" #include "fiction/utils/network_utils.hpp" #include "fiction/utils/placement_utils.hpp" +#include #include #include #include @@ -566,51 +568,55 @@ std::vector calculate_two_input_new_lines(const Ntk& * @param two_input_new_lines Number of new routing lines associated with each two-input node. */ void adjust_final_values(std::vector& x, std::vector& y, - const std::vector& two_input_indices, - const std::vector& two_input_new_lines) + const std::vector& two_input_indices, + const std::vector& two_input_new_lines) { - // Find the max element in two_input_new_lines and its index - const auto max_it = std::max_element(two_input_new_lines.begin(), two_input_new_lines.end()); - const auto max_new_lines_two_inputs = *max_it; - const auto max_new_lines_two_inputs_index = - static_cast(std::distance(two_input_new_lines.begin(), max_it)); - - // Find max sum in x + y and its index - const auto max_xy_it = std::max_element(x.begin(), x.end(), [&](std::size_t i, std::size_t j) - { return (x[i] + y[i]) < (x[j] + y[j]); }); - - const auto max_new_lines = x[*max_xy_it] + y[*max_xy_it]; - const auto mnl_iterator = *max_xy_it; - + // Max element in two_input_new_lines and its index + const auto max_it = std::max_element(two_input_new_lines.begin(), two_input_new_lines.end()); + const uint64_t max_new_lines_two_inputs = (max_it != two_input_new_lines.end()) ? *max_it : 0; + const std::size_t max_two_inputs_idx = + (max_it != two_input_new_lines.end()) ? + static_cast(std::distance(two_input_new_lines.begin(), max_it)) : + 0; + // Find index with max x+y + std::size_t max_xy_idx = 0; + uint64_t max_xy_sum = 0; + for (std::size_t i = 0; i < x.size(); ++i) + { + const uint64_t s = x[i] + y[i]; + if (i == 0 || s > max_xy_sum) + { + max_xy_sum = s; + max_xy_idx = i; + } + } // Determine the center index - uint64_t center = 0, max = 0; - if (max_new_lines > max_new_lines_two_inputs) + std::size_t center = 0; + uint64_t max = 0; + if (max_xy_sum > max_new_lines_two_inputs) { - center = mnl_iterator; - max = max_new_lines; + center = max_xy_idx; + max = max_xy_sum; } else { - center = two_input_indices[max_new_lines_two_inputs_index]; + center = two_input_indices.empty() ? 0 : two_input_indices[max_two_inputs_idx]; max = max_new_lines_two_inputs; } - // Create a lookup map for O(1) access instead of O(n) std::find() std::unordered_map two_input_map; for (std::size_t i = 0; i < two_input_indices.size(); ++i) { two_input_map[two_input_indices[i]] = i; } - // Iterate through x and y to update values efficiently for (std::size_t i = 0; i < x.size(); ++i) { auto it = two_input_map.find(i); - if (it != two_input_map.end()) // Two fan-in node (found in two_input_indices) - { - const auto idx = it->second; // Get the corresponding index + if (it != two_input_map.end()) + { // Two fan-in node + const auto idx = it->second; const auto diff = max - x[i] - y[i] - two_input_new_lines[idx]; - if (i < center) { y[i] += diff; @@ -624,10 +630,9 @@ void adjust_final_values(std::vector& x, std::vector& y, assert(x[i] + y[i] + two_input_new_lines[idx] == max); } } - else // One fan-in node - { + else + { // One fan-in node const auto diff = max - x[i] - y[i]; - if (i < center) { y[i] += diff; @@ -729,9 +734,9 @@ compute_wiring(const Ntk& ntk, const mockturtle::node_map cluster_new_lines[i] + propagate_right) ? - 0 : - propagate_right + cluster_new_lines[i] - two_input_new_lines[i]; + const uint64_t til = (i < two_input_new_lines.size()) ? two_input_new_lines[i] : 0; + propagate_right = + (til > cluster_new_lines[i] + propagate_right) ? 0 : propagate_right + cluster_new_lines[i] - til; // Move to the next cluster cluster_index_start = cluster_index_end + 1; @@ -795,7 +800,7 @@ class orthogonal_planar_impl public: orthogonal_planar_impl(const Ntk& src, const orthogonal_physical_design_params& p, orthogonal_physical_design_stats& st) : - ntk{mockturtle::fanout_view(src)}, + ntk{mockturtle::fanout_view{mockturtle::names_view{src}}}, ps{p}, pst{st} {} @@ -818,17 +823,19 @@ class orthogonal_planar_impl #if (PROGRESS_BARS) // initialize a progress bar - mockturtle::progress_bar bar{ctn.color_ntk.size(), "[i] arranging layout: |{0}|"}; + mockturtle::progress_bar bar{ntk.depth() + 1, "[i] arranging layout: |{0}|"}; #endif tile place_t{0, 0}; + assert(ntk.num_pis() > 0); tile first_pos = {ntk.num_pis() - 1, 0}; // place and route the nodes in ascending level order for (uint32_t lvl = 0; lvl < ntk.depth() + 1; lvl++) { - const auto variable_tuple = compute_pr_variables, Lyt>(ntk, node2pos, lvl); - const auto orientation = std::get<0>(variable_tuple); - const auto new_lines = std::get<1>(variable_tuple); + const auto variable_tuple = + compute_pr_variables>, Lyt>(ntk, node2pos, lvl); + const auto orientation = std::get<0>(variable_tuple); + const auto new_lines = std::get<1>(variable_tuple); const auto wiring = compute_wiring(ntk, node2pos, new_lines, lvl); const auto& x = wiring.first; @@ -972,20 +979,21 @@ class orthogonal_planar_impl } // place and route POs - std::unordered_map count_map; - int add_line = 0; + std::unordered_map::node, int> count_map; + int add_line = 0; // the number of outputs on a node is limited to 2, due to fanout substitution ntk.foreach_po( [this, &layout, &first_pos, &place_t, &node2pos, &count_map, &add_line](const auto& po) { if (!ntk.is_constant(po)) { - const auto n_s = node2pos[po]; - auto po_tile = static_cast>(n_s); - if (count_map[po] < 2) // Check if the count is less than 2 + const auto drv = ntk.get_node(po); + auto po_tile = static_cast>(node2pos[drv]); + auto& cnt = count_map[drv]; + if (cnt < 2) // Check if the count is less than 2 { // Adjust the position based on whether it's the first or second occurrence - if (count_map[po] == 1) + if (cnt == 1) { if (po_tile.y == place_t.y) { @@ -1001,7 +1009,7 @@ class orthogonal_planar_impl ntk.has_output_name(po_counter) ? ntk.get_output_name(po_counter++) : fmt::format("po{}", po_counter++), po_tile); - count_map[po]++; + ++cnt; } else { @@ -1025,7 +1033,7 @@ class orthogonal_planar_impl } private: - mockturtle::fanout_view ntk; + mockturtle::fanout_view> ntk; orthogonal_physical_design_params ps; orthogonal_physical_design_stats& pst; diff --git a/include/fiction/algorithms/properties/check_planarity.hpp b/include/fiction/algorithms/properties/check_planarity.hpp index 97398237f9..b2070868b0 100644 --- a/include/fiction/algorithms/properties/check_planarity.hpp +++ b/include/fiction/algorithms/properties/check_planarity.hpp @@ -36,7 +36,7 @@ class check_planarity_impl * @return `true` if the network is planar, `false` otherwise. */ - bool run() + [[nodiscard]] bool run() const { bool return_false = false; for (uint32_t r = 1; r < ntk.depth() + 1; r++) @@ -78,7 +78,7 @@ class check_planarity_impl } private: - const Ntk ntk; + const Ntk& ntk; }; /** @@ -107,8 +107,12 @@ template static_assert(mockturtle::has_rank_position_v, "Ntk does not implement the rank_position function"); static_assert(mockturtle::has_foreach_node_in_rank_v, "Ntk does not implement the foreach_node_in_rank function"); + static_assert(mockturtle::has_is_constant_v, "Ntk does not implement the is_constant function"); - assert(is_balanced(ntk) && "Network must be balanced"); + if (!is_balanced(ntk)) + { + throw std::invalid_argument("Network must be balanced"); + } check_planarity_impl p{ntk}; diff --git a/test/algorithms/physical_design/orthogonal_planar.cpp b/test/algorithms/physical_design/orthogonal_planar.cpp index c26558bbb2..3b07194e4c 100644 --- a/test/algorithms/physical_design/orthogonal_planar.cpp +++ b/test/algorithms/physical_design/orthogonal_planar.cpp @@ -52,6 +52,7 @@ void check_ortho_planar(const Ntk& ntk) const auto cec_m = mockturtle::equivalence_checking( *fiction::virtual_miter(ntk, planarized_b), {}, &eq_st); REQUIRE(cec_m.has_value()); + CHECK(*cec_m == 1); orthogonal_physical_design_stats orthogonal_planar_stats{}; From aa1e9fdbcf7717071685ccea1be5eae46ec0ddb8 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 13 Oct 2025 14:45:19 +0000 Subject: [PATCH 19/67] :memo: Update pyfiction docstrings Signed-off-by: GitHub Actions --- .../include/pyfiction/pybind11_mkdoc_docstrings.hpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index 4b1efe8ce7..15708d5c90 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -9465,9 +9465,9 @@ Parameter ``nd``: static const char *__doc_fiction_detail_node_duplication_planarization_impl_insert_if_not_first = R"doc(Inserts a node into a vector if it is unique. -`This function inserts a node into a vector only if the vector is -empty or the node is not equal to the first element of the vector. If -the vector is not empty and the node is equal to the first element, it +This function inserts a node into a vector only if the vector is empty +or the node is not equal to the first element of the vector. If the +vector is not empty and the node is equal to the first element, it does nothing. An exception occurs if the node was skipped on the previous insertion attempt due to `vec.front() == node`; in that case, the node will be inserted this time. @@ -9966,8 +9966,6 @@ Parameter ``p``: Parameter ``st``: Statistics object used to collect runtime and layout information.)doc"; -static const char *__doc_fiction_detail_orthogonal_planar_impl_ntk = R"doc()doc"; - static const char *__doc_fiction_detail_orthogonal_planar_impl_orthogonal_planar_impl = R"doc()doc"; static const char *__doc_fiction_detail_orthogonal_planar_impl_po_counter = R"doc()doc"; From 6115d6713bbc0f1adb1d2e8ba90a5d2c9d44ebb2 Mon Sep 17 00:00:00 2001 From: benjamin Date: Mon, 27 Oct 2025 14:33:26 +0100 Subject: [PATCH 20/67] :repeat: replaced check_planarity with mincross --- .../node_duplication_planarization.hpp | 10 +- .../physical_design/orthogonal_planar.hpp | 9 +- .../algorithms/properties/check_planarity.hpp | 126 ------------------ .../node_duplication_planarization.cpp | 37 ++++- .../algorithms/properties/check_planarity.cpp | 113 ---------------- 5 files changed, 46 insertions(+), 249 deletions(-) delete mode 100644 include/fiction/algorithms/properties/check_planarity.hpp delete mode 100644 test/algorithms/properties/check_planarity.cpp diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index be1e5405c8..2af2f0d06f 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -6,7 +6,7 @@ #define FICTION_NODE_DUPLICATION_PLANARIZATION_HPP #include "fiction/algorithms/network_transformation/network_balancing.hpp" -#include "fiction/algorithms/properties/check_planarity.hpp" +#include "fiction/algorithms/graph/mincross.hpp" #include "fiction/networks/virtual_pi_network.hpp" #include @@ -746,7 +746,13 @@ template auto result = p.run(); - if (!check_planarity(result)) + // check for planarity + mincross_stats st_min{}; + mincross_params p_min{}; + p_min.optimize = false; + + auto ntk_min = mincross(result, p_min, &st_min); // counts crossings + if (st_min.num_crossings != 0) { throw std::runtime_error("Planarization failed: resulting network is not planar"); } diff --git a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp index 6f8bc8c12d..5555bcc6df 100644 --- a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp +++ b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp @@ -6,7 +6,7 @@ #define FICTION_ORTHOGONAL_PLANAR_HPP #include "fiction/algorithms/physical_design/orthogonal.hpp" -#include "fiction/algorithms/properties/check_planarity.hpp" +#include "fiction/algorithms/graph/mincross.hpp" #include "fiction/layouts/clocking_scheme.hpp" #include "fiction/traits.hpp" #include "fiction/utils/network_utils.hpp" @@ -1075,7 +1075,12 @@ Lyt orthogonal_planar(const Ntk& ntk, orthogonal_physical_design_params ps = {}, } // check for planarity - if (!check_planarity(ntk)) + mincross_stats st_min{}; + mincross_params p_min{}; + p_min.optimize = false; + + auto ntk_min = mincross(ntk, p_min, &st_min); // counts crossings + if (st_min.num_crossings != 0) { throw std::invalid_argument("Input network has to be planar"); } diff --git a/include/fiction/algorithms/properties/check_planarity.hpp b/include/fiction/algorithms/properties/check_planarity.hpp deleted file mode 100644 index b2070868b0..0000000000 --- a/include/fiction/algorithms/properties/check_planarity.hpp +++ /dev/null @@ -1,126 +0,0 @@ -// -// Created by benjamin on 17.07.24. -// - -#ifndef FICTION_CHECK_PLANARITY_HPP -#define FICTION_CHECK_PLANARITY_HPP - -#include "fiction/algorithms/network_transformation/network_balancing.hpp" - -#include - -#include -#include -#include - -namespace fiction -{ - -template -class check_planarity_impl -{ - public: - explicit check_planarity_impl(const Ntk& ntk) : ntk(ntk) {} - - /** - * Checks if a given network is planar. - * - * This function checks if the network represented by the variable `ntk` is planar. - * The network is planar if, for any edge with starting point \f$m\f$ and endpoint \f$n\f$ - * (represented by the node ranks), there is never another edge with starting point - * \f$m' > m\f$ and endpoint \f$n' < n\f$, or vice versa. When iterating through the - * ranks of one level, the endpoints are always increasing. Therefore, only the starting - * points need to be checked. Thus, the highest connected starting point in the fan-in - * gives a border \f$m_{\text{max}}\f$ for every subsequent edge. - * - * @return `true` if the network is planar, `false` otherwise. - */ - - [[nodiscard]] bool run() const - { - bool return_false = false; - for (uint32_t r = 1; r < ntk.depth() + 1; r++) - { - uint32_t bound = 0; - ntk.foreach_node_in_rank(r, - [this, &bound, &return_false](const auto& n) - { - uint32_t new_bound = bound; - ntk.foreach_fanin(n, - [this, &n, &bound, &new_bound, &return_false](const auto& fi) - { - const auto fi_n = ntk.get_node(fi); - if (!ntk.is_constant(fi_n)) - { - if (ntk.rank_position(fi_n) < bound) - { - return_false = true; - return false; // stop iterating - } - - new_bound = - std::max(new_bound, ntk.rank_position(fi_n)); - } - return true; // keep iterating - }); - if (return_false) - { - return; - } - bound = new_bound; - }); - if (return_false) - { - return false; - } - } - return true; - } - - private: - const Ntk& ntk; -}; - -/** - * Checks if a logic network is planar for a network that is path balanced and has ranks assigned. - * - * If the network is not balanced, an exception is thrown. To balance the network, insert buffers to divide multi-level - * edges. - * - * It checks if the network represented by the variable `ntk` is planar. - * The network is planar if, for any edge with starting point \f$m\f$ and endpoint \f$n\f$ (represented by the node - * ranks), there is never another edge with starting point \f$m' > m\f$ and endpoint \f$n' < n\f$, or vice versa. When - * iterating through the ranks of one level, the endpoints are always increasing. Therefore, only the starting points - * need to be checked. Thus, the highest connected starting point in the fan-in gives a border \f$m_{\text{max}}\f$ for - * every subsequent edge. - * - * @tparam Ntk Logic network type. - * @param ntk The logic network to check for planarity. - * @return `true` if the network is planar, `false` otherwise. - */ -template -[[nodiscard]] bool check_planarity(const Ntk& ntk) -{ - static_assert(mockturtle::has_get_node_v, "Ntk does not implement the get_node function"); - static_assert(mockturtle::has_foreach_fanin_v, "Ntk does not implement the foreach_fanin function"); - static_assert(mockturtle::has_depth_v, "Ntk does not implement the depth function"); - static_assert(mockturtle::has_rank_position_v, "Ntk does not implement the rank_position function"); - static_assert(mockturtle::has_foreach_node_in_rank_v, - "Ntk does not implement the foreach_node_in_rank function"); - static_assert(mockturtle::has_is_constant_v, "Ntk does not implement the is_constant function"); - - if (!is_balanced(ntk)) - { - throw std::invalid_argument("Network must be balanced"); - } - - check_planarity_impl p{ntk}; - - const auto result = p.run(); - - return result; -} - -} // namespace fiction - -#endif // FICTION_CHECK_PLANARITY_HPP diff --git a/test/algorithms/network_transformation/node_duplication_planarization.cpp b/test/algorithms/network_transformation/node_duplication_planarization.cpp index 4266aca095..d544b973d6 100644 --- a/test/algorithms/network_transformation/node_duplication_planarization.cpp +++ b/test/algorithms/network_transformation/node_duplication_planarization.cpp @@ -6,7 +6,7 @@ #include "fiction/algorithms/network_transformation/fanout_substitution.hpp" #include "fiction/algorithms/network_transformation/network_balancing.hpp" -#include "fiction/algorithms/properties/check_planarity.hpp" +#include "fiction/algorithms/graph/mincross.hpp" #include "fiction/networks/views/mutable_rank_view.hpp" #include @@ -44,7 +44,12 @@ TEST_CASE("Planarize technology ntk", "[node-duplication-planarization]") auto planarized_ntk = node_duplication_planarization(vpi_r); - CHECK(check_planarity(planarized_ntk)); + mincross_stats st_min{}; + mincross_params p_min{}; + p_min.optimize = false; + + auto ntk = mincross(planarized_ntk, p_min, &st_min); // counts crossings + CHECK(st_min.num_crossings == 0); mockturtle::equivalence_checking_stats st; const auto cec_m = @@ -81,7 +86,12 @@ TEST_CASE("Planarize ntk with 3-ary node", "[node-duplication-planarization]") auto planarized_ntk = node_duplication_planarization(vpi_r); - CHECK(check_planarity(planarized_ntk)); + mincross_stats st_min{}; + mincross_params p_min{}; + p_min.optimize = false; + + auto ntk = mincross(planarized_ntk, p_min, &st_min); // counts crossings + CHECK(st_min.num_crossings == 0); mockturtle::equivalence_checking_stats st; const auto cec_m = @@ -118,7 +128,12 @@ TEST_CASE("Buffer AIG and planarize technology_network", "[node-duplication-plan auto planarized_ntk = node_duplication_planarization(vpi_r); - CHECK(check_planarity(planarized_ntk)); + mincross_stats st_min{}; + mincross_params p_min{}; + p_min.optimize = false; + + auto ntk = mincross(planarized_ntk, p_min, &st_min); // counts crossings + CHECK(st_min.num_crossings == 0); mockturtle::equivalence_checking_stats st; const auto cec_m = @@ -148,7 +163,12 @@ TEST_CASE("Buffer AIG and planarize technology_network 2", "[node-duplication-pl auto planarized_ntk = node_duplication_planarization(vpi_r); - CHECK(check_planarity(planarized_ntk)); + mincross_stats st_min{}; + mincross_params p_min{}; + p_min.optimize = false; + + auto ntk = mincross(planarized_ntk, p_min, &st_min); // counts crossings + CHECK(st_min.num_crossings == 0); mockturtle::equivalence_checking_stats st; const auto cec_m = @@ -181,7 +201,12 @@ TEST_CASE("Planarize multi output network", "[node-duplication-planarization]") auto planarized_ntk = node_duplication_planarization(vpi_r); - CHECK(check_planarity(planarized_ntk)); + mincross_stats st_min{}; + mincross_params p_min{}; + p_min.optimize = false; + + auto ntk = mincross(planarized_ntk, p_min, &st_min); // counts crossings + CHECK(st_min.num_crossings == 0); mockturtle::equivalence_checking_stats st; const auto cec_m = diff --git a/test/algorithms/properties/check_planarity.cpp b/test/algorithms/properties/check_planarity.cpp deleted file mode 100644 index 306ab9f531..0000000000 --- a/test/algorithms/properties/check_planarity.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// -// Created by benjamin on 17.07.24. -// - -#include - -#include -#include -#include - -#include -#include - -#include - -using namespace fiction; - -TEST_CASE("Check planarity aig", "[check-planarity]") -{ - mockturtle::aig_network aig{}; - - CHECK(mockturtle::has_clear_visited_v); - CHECK(mockturtle::has_visited_v); - CHECK(mockturtle::has_set_visited_v); - - const auto x1 = aig.create_pi(); - const auto x2 = aig.create_pi(); - const auto x3 = aig.create_pi(); - - const auto f1 = aig.create_and(x1, x2); - const auto f2 = aig.create_and(x2, x3); - - const auto x4 = aig.create_pi(); - const auto x5 = aig.create_not(x4); - - aig.create_po(f1); - aig.create_po(f2); - aig.create_po(x5); - - mutable_rank_view tec_r(aig); - - const std::vector nodes_rank0{1, 2, 3, 6}; - const std::vector nodes_rank1{4, 5}; - - tec_r.set_ranks(0, nodes_rank0); - tec_r.set_ranks(1, nodes_rank1); - - const bool planar = check_planarity(tec_r); - - CHECK(planar == 1); -} - -TEST_CASE("Check planarity technology network", "[check-planarity]") -{ - technology_network tec{}; - - CHECK(mockturtle::has_clear_visited_v); - CHECK(mockturtle::has_visited_v); - CHECK(mockturtle::has_set_visited_v); - - const auto x1 = tec.create_pi(); - const auto x2 = tec.create_pi(); - const auto x3 = tec.create_pi(); - - const auto f1 = tec.create_and(x1, x2); - const auto f2 = tec.create_and(x2, x3); - - tec.create_po(f1); - tec.create_po(f2); - - mutable_rank_view tec_r(tec); - - const std::vector nodes_rank0{2, 3, 4}; - const std::vector nodes_rank1{5, 6}; - - tec_r.set_ranks(0, nodes_rank0); - tec_r.set_ranks(1, nodes_rank1); - - const bool planar = check_planarity(tec_r); - - CHECK(planar == 1); -} - -TEST_CASE("Check non-planarity technology network", "[check-planarity]") -{ - technology_network tec{}; - - CHECK(mockturtle::has_clear_visited_v); - CHECK(mockturtle::has_visited_v); - CHECK(mockturtle::has_set_visited_v); - - const auto x1 = tec.create_pi(); - const auto x2 = tec.create_pi(); - const auto x3 = tec.create_pi(); - - const auto f1 = tec.create_and(x1, x3); - const auto f2 = tec.create_and(x2, x3); - - tec.create_po(f1); - tec.create_po(f2); - - mutable_rank_view tec_r(tec); - - const std::vector nodes_rank0{2, 3, 4}; - const std::vector nodes_rank1{5, 6}; - - tec_r.set_ranks(0, nodes_rank0); - tec_r.set_ranks(1, nodes_rank1); - - const bool planar = check_planarity(tec_r); - - CHECK(planar == 0); -} From 14ae01558c9331a57c907280dc67ad3b09a9ec06 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 Oct 2025 13:33:57 +0000 Subject: [PATCH 21/67] =?UTF-8?q?=F0=9F=8E=A8=20Incorporated=20pre-commit?= =?UTF-8?q?=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../network_transformation/node_duplication_planarization.hpp | 2 +- .../fiction/algorithms/physical_design/orthogonal_planar.hpp | 2 +- .../network_transformation/node_duplication_planarization.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index 2af2f0d06f..58e5982ea1 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -5,8 +5,8 @@ #ifndef FICTION_NODE_DUPLICATION_PLANARIZATION_HPP #define FICTION_NODE_DUPLICATION_PLANARIZATION_HPP -#include "fiction/algorithms/network_transformation/network_balancing.hpp" #include "fiction/algorithms/graph/mincross.hpp" +#include "fiction/algorithms/network_transformation/network_balancing.hpp" #include "fiction/networks/virtual_pi_network.hpp" #include diff --git a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp index 5555bcc6df..c17264454e 100644 --- a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp +++ b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp @@ -5,8 +5,8 @@ #ifndef FICTION_ORTHOGONAL_PLANAR_HPP #define FICTION_ORTHOGONAL_PLANAR_HPP -#include "fiction/algorithms/physical_design/orthogonal.hpp" #include "fiction/algorithms/graph/mincross.hpp" +#include "fiction/algorithms/physical_design/orthogonal.hpp" #include "fiction/layouts/clocking_scheme.hpp" #include "fiction/traits.hpp" #include "fiction/utils/network_utils.hpp" diff --git a/test/algorithms/network_transformation/node_duplication_planarization.cpp b/test/algorithms/network_transformation/node_duplication_planarization.cpp index d544b973d6..37afa97247 100644 --- a/test/algorithms/network_transformation/node_duplication_planarization.cpp +++ b/test/algorithms/network_transformation/node_duplication_planarization.cpp @@ -4,9 +4,9 @@ #include +#include "fiction/algorithms/graph/mincross.hpp" #include "fiction/algorithms/network_transformation/fanout_substitution.hpp" #include "fiction/algorithms/network_transformation/network_balancing.hpp" -#include "fiction/algorithms/graph/mincross.hpp" #include "fiction/networks/views/mutable_rank_view.hpp" #include From 58b8af74d8dd1823ef1d58c0cb67f7ef22c2abc9 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 27 Oct 2025 14:10:54 +0000 Subject: [PATCH 22/67] :memo: Update pyfiction docstrings Signed-off-by: GitHub Actions --- .../pyfiction/pybind11_mkdoc_docstrings.hpp | 48 ------------------- 1 file changed, 48 deletions(-) diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index 0b2688f9ff..9aaaa9758b 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -2690,54 +2690,6 @@ Template parameter ``Dist``: static const char *__doc_fiction_chebyshev_distance_functor_chebyshev_distance_functor = R"doc()doc"; -static const char *__doc_fiction_check_planarity = -R"doc(Checks if a logic network is planar for a network that is path -balanced and has ranks assigned. - -If the network is not balanced, an exception is thrown. To balance the -network, insert buffers to divide multi-level edges. - -It checks if the network represented by the variable `ntk` is planar. -The network is planar if, for any edge with starting point :math:`m` -and endpoint :math:`n` (represented by the node ranks), there is never -another edge with starting point :math:`m' > m` and endpoint :math:`n' -< n`, or vice versa. When iterating through the ranks of one level, -the endpoints are always increasing. Therefore, only the starting -points need to be checked. Thus, the highest connected starting point -in the fan-in gives a border :math:`m_{\text{max}}` for every -subsequent edge. - -Template parameter ``Ntk``: - Logic network type. - -Parameter ``ntk``: - The logic network to check for planarity. - -Returns: - `true` if the network is planar, `false` otherwise.)doc"; - -static const char *__doc_fiction_check_planarity_impl = R"doc()doc"; - -static const char *__doc_fiction_check_planarity_impl_check_planarity_impl = R"doc()doc"; - -static const char *__doc_fiction_check_planarity_impl_ntk = R"doc()doc"; - -static const char *__doc_fiction_check_planarity_impl_run = -R"doc(Checks if a given network is planar. - -This function checks if the network represented by the variable `ntk` -is planar. The network is planar if, for any edge with starting point -:math:`m` and endpoint :math:`n` (represented by the node ranks), -there is never another edge with starting point :math:`m' > m` and -endpoint :math:`n' < n`, or vice versa. When iterating through the -ranks of one level, the endpoints are always increasing. Therefore, -only the starting points need to be checked. Thus, the highest -connected starting point in the fan-in gives a border -:math:`m_{\text{max}}` for every subsequent edge. - -Returns: - `true` if the network is planar, `false` otherwise.)doc"; - static const char *__doc_fiction_check_simulation_results_for_equivalence = R"doc(This function compares two SiDB simulation results for equivalence. Two results are considered equivalent if they have the same number of From ccb370419cacb5a6db69ecfdf021260239932f75 Mon Sep 17 00:00:00 2001 From: benjamin Date: Mon, 27 Oct 2025 15:46:19 +0100 Subject: [PATCH 23/67] :adhesive_bandage: small changes to also include complemented signals --- .../node_duplication_planarization.hpp | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index 2af2f0d06f..cf32050221 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -5,8 +5,8 @@ #ifndef FICTION_NODE_DUPLICATION_PLANARIZATION_HPP #define FICTION_NODE_DUPLICATION_PLANARIZATION_HPP -#include "fiction/algorithms/network_transformation/network_balancing.hpp" #include "fiction/algorithms/graph/mincross.hpp" +#include "fiction/algorithms/network_transformation/network_balancing.hpp" #include "fiction/networks/virtual_pi_network.hpp" #include @@ -156,7 +156,7 @@ initialize_copy_network_duplicates(Ntk const& src) */ template std::vector> -gather_fanin_signals(const Ntk& ntk, const NtkDest& ntk_dest_v, const mockturtle::node n, +gather_fanin_signals(const Ntk& ntk, NtkDest& ntk_dest_v, const mockturtle::node n, const mockturtle::node_map>, Ntk>& old2new_v, const std::vector>& lvl, std::size_t& node_index) { @@ -204,7 +204,8 @@ gather_fanin_signals(const Ntk& ntk, const NtkDest& ntk_dest_v, const mockturtle } // Add the matched candidate fan-in to the children. - children.emplace_back(candidate_sig); + children.emplace_back(ntk.is_complemented(f) ? ntk_dest_v.create_not(candidate_sig) : + candidate_sig); break; } } @@ -218,22 +219,26 @@ gather_fanin_signals(const Ntk& ntk, const NtkDest& ntk_dest_v, const mockturtle }; /** - * Constructs a planar `virtual_pi_network` based on the `ntk_lvls` array, which holds the ranks of the duplicated nodes - * for each level in the new network. This function creates new nodes for the duplicated ones and restores their fanin - * relations using the `gather_fanin_signals` function. + * Constructs a planar `virtual_pi_network` based on duplicated nodes derived from the source network. * - * For duplicated PIs (Primary Inputs), virtual PIs are created, and the original PI is stored in a map. + * The input `ntk_lvls` contains per-level vectors of original node ranks in the source network. For each level, this + * function creates corresponding nodes (including duplicates) in a new `virtual_pi_network` and restores their fanin + * relations using the `gather_fanin_signals` helper function. * - * The auxiliary function `gather_fanin_signals` collects fanin data for a node and matches it in the - * `virtual_pi_network`. + * For duplicated PIs (Primary Inputs), virtual PIs are created, and the original PI is stored in a mapping structure. + * The auxiliary function `gather_fanin_signals` collects fanin data for each node and matches it to its corresponding + * nodes in the `virtual_pi_network`. * - * Example: For a level (2, 3, 2, 4, 2), new nodes are created for duplications (e.g., 2) and stored in the `old2new_v` - * node_map. This map is used by `gather_fanin_signals` to establish the correct fanin relations. + * Example: For a level {2, 3, 2, 4, 2}, new nodes are created for each duplicated occurrence (e.g., node 2) and stored + * in the `old2new_v` node map. This map is then used by `gather_fanin_signals` to correctly establish fanin + * relationships between newly created nodes. * * @tparam Ntk Network type. - * @param ntk Source network to be utilized for the creation of the virtual_pi_network. - * @param ntk_lvls Levels of nodes in the source network. - * @param ntk_lvls_new Levels of newly created nodes in the virtual_pi_network. + * @param ntk Source network used to construct the `virtual_pi_network`. + * @param ntk_lvls Per-level vectors of original node ranks in the source network used to derive node duplications. + * @param ntk_lvls_new Per-level vectors of newly created nodes' ranks in the constructed `virtual_pi_network`. + * @return The constructed planar `virtual_pi_network` containing duplicated nodes with restored fanin and fanout + * relations. */ template virtual_pi_network create_virtual_pi_ntk_from_duplicated_nodes( @@ -486,15 +491,16 @@ class node_duplication_planarization_impl * Inserts a node into a vector if it is unique. * * This function inserts a node into a vector only if the vector is empty or the node is not equal to the first - * element of the vector. If the vector is not empty and the node is equal to the first element, it does nothing. - * An exception occurs if the node was skipped on the previous insertion attempt due to `vec.front() == node`; in - * that case, the node will be inserted this time. + * element of the vector. If the vector is not empty and the node is equal to the first element, insertion depends + * on the `saturated_fanout_flag` and the node’s `position`: when `position == 0`, a repeated insertion attempt will + * succeed only if the node was previously skipped (indicated by `saturated_fanout_flag == 1`); otherwise, the flag + * is set to 1 and the node is skipped for this call. No exception is thrown during this process. * * @param node The node to be inserted. * @param vec The vector to insert the node into. - * @param saturated_fanout_flag The flag which indicates that the maximum number of fanouts for this node is - * reached. - * @param position The position of the node (terminal node or not). + * @param saturated_fanout_flag A state flag toggled when consecutive duplicate insertions occur. Set to 1 when a + * node is skipped and reset to 0 when a node is successfully inserted. + * @param position The position of the node (0 indicates a terminal node; controls duplicate insertion behavior). */ void insert_if_not_first(const mockturtle::node& node, std::vector>& vec, int& saturated_fanout_flag, const int position) From 3c40183759481a55839f814c08987691d8885f1b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 Oct 2025 14:47:08 +0000 Subject: [PATCH 24/67] =?UTF-8?q?=F0=9F=8E=A8=20Incorporated=20pre-commit?= =?UTF-8?q?=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../network_transformation/node_duplication_planarization.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index cf32050221..3a3e0e60cc 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -492,7 +492,7 @@ class node_duplication_planarization_impl * * This function inserts a node into a vector only if the vector is empty or the node is not equal to the first * element of the vector. If the vector is not empty and the node is equal to the first element, insertion depends - * on the `saturated_fanout_flag` and the node’s `position`: when `position == 0`, a repeated insertion attempt will + * on the `saturated_fanout_flag` and the node's `position`: when `position == 0`, a repeated insertion attempt will * succeed only if the node was previously skipped (indicated by `saturated_fanout_flag == 1`); otherwise, the flag * is set to 1 and the node is skipped for this call. No exception is thrown during this process. * From db8fd96330ec0c3516d20782b0fd432d0a629a4e Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 27 Oct 2025 14:48:20 +0000 Subject: [PATCH 25/67] :memo: Update pyfiction docstrings Signed-off-by: GitHub Actions --- .../pyfiction/pybind11_mkdoc_docstrings.hpp | 60 +++++++++++-------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index 9aaaa9758b..c1c17015ad 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -6107,35 +6107,42 @@ R"doc(From https://stackoverflow.com/questions/57756557/initializing-a- stdarray-with-a-constant-value)doc"; static const char *__doc_fiction_detail_create_virtual_pi_ntk_from_duplicated_nodes = -R"doc(Constructs a planar `virtual_pi_network` based on the `ntk_lvls` -array, which holds the ranks of the duplicated nodes for each level in -the new network. This function creates new nodes for the duplicated -ones and restores their fanin relations using the -`gather_fanin_signals` function. +R"doc(Constructs a planar `virtual_pi_network` based on duplicated nodes +derived from the source network. -For duplicated PIs (Primary Inputs), virtual PIs are created, and the -original PI is stored in a map. +The input `ntk_lvls` contains per-level vectors of original node ranks +in the source network. For each level, this function creates +corresponding nodes (including duplicates) in a new +`virtual_pi_network` and restores their fanin relations using the +`gather_fanin_signals` helper function. -The auxiliary function `gather_fanin_signals` collects fanin data for -a node and matches it in the `virtual_pi_network`. +For duplicated PIs (Primary Inputs), virtual PIs are created, and the +original PI is stored in a mapping structure. The auxiliary function +`gather_fanin_signals` collects fanin data for each node and matches +it to its corresponding nodes in the `virtual_pi_network`. -Example: For a level (2, 3, 2, 4, 2), new nodes are created for -duplications (e.g., 2) and stored in the `old2new_v` node_map. This -map is used by `gather_fanin_signals` to establish the correct fanin -relations. +Example: For a level {2, 3, 2, 4, 2}, new nodes are created for each +duplicated occurrence (e.g., node 2) and stored in the `old2new_v` +node map. This map is then used by `gather_fanin_signals` to correctly +establish fanin relationships between newly created nodes. Template parameter ``Ntk``: Network type. Parameter ``ntk``: - Source network to be utilized for the creation of the - virtual_pi_network. + Source network used to construct the `virtual_pi_network`. Parameter ``ntk_lvls``: - Levels of nodes in the source network. + Per-level vectors of original node ranks in the source network + used to derive node duplications. Parameter ``ntk_lvls_new``: - Levels of newly created nodes in the virtual_pi_network.)doc"; + Per-level vectors of newly created nodes' ranks in the constructed + `virtual_pi_network`. + +Returns: + The constructed planar `virtual_pi_network` containing duplicated + nodes with restored fanin and fanout relations.)doc"; static const char *__doc_fiction_detail_create_wiring_reduction_layout = R"doc(Create a wiring_reduction_layout suitable for finding excess wiring @@ -9521,10 +9528,13 @@ R"doc(Inserts a node into a vector if it is unique. This function inserts a node into a vector only if the vector is empty or the node is not equal to the first element of the vector. If the -vector is not empty and the node is equal to the first element, it -does nothing. An exception occurs if the node was skipped on the -previous insertion attempt due to `vec.front() == node`; in that case, -the node will be inserted this time. +vector is not empty and the node is equal to the first element, +insertion depends on the `saturated_fanout_flag` and the node's +`position`: when `position == 0`, a repeated insertion attempt will +succeed only if the node was previously skipped (indicated by +`saturated_fanout_flag == 1`); otherwise, the flag is set to 1 and the +node is skipped for this call. No exception is thrown during this +process. Parameter ``node``: The node to be inserted. @@ -9533,11 +9543,13 @@ Parameter ``vec``: The vector to insert the node into. Parameter ``saturated_fanout_flag``: - The flag which indicates that the maximum number of fanouts for - this node is reached. + A state flag toggled when consecutive duplicate insertions occur. + Set to 1 when a node is skipped and reset to 0 when a node is + successfully inserted. Parameter ``position``: - The position of the node (terminal node or not).)doc"; + The position of the node (0 indicates a terminal node; controls + duplicate insertion behavior).)doc"; static const char *__doc_fiction_detail_node_duplication_planarization_impl_lvl_pairs = R"doc()doc"; From cb7db4f649e24a642ed8056d350f8df723813bca Mon Sep 17 00:00:00 2001 From: benjamin Date: Mon, 27 Oct 2025 17:25:32 +0100 Subject: [PATCH 26/67] :repeat: refactored using raw pointers to indexed values in node_pair --- .../node_duplication_planarization.hpp | 66 ++++++++++++------- .../physical_design/orthogonal_planar.hpp | 9 ++- .../physical_design/orthogonal_planar.cpp | 7 +- 3 files changed, 51 insertions(+), 31 deletions(-) diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index cf32050221..ee5ed27dee 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -87,7 +87,7 @@ struct node_pair /** * Shared pointer to another instance of node_pair detailing fanin-edge alignment. */ - node_pair* fanin_pair; + std::size_t fanin_it{}; /** * Standard constructor. * @@ -97,8 +97,7 @@ struct node_pair */ node_pair(mockturtle::node node1, mockturtle::node node2, uint64_t delay_value) : pair(node1, node2), - delay(delay_value), - fanin_pair(nullptr) + delay(delay_value) {} }; @@ -288,6 +287,7 @@ virtual_pi_network create_virtual_pi_ntk_from_duplicated_nodes( else { assert(i != ntk_lvls.size() && "Node, which is not marked as PI is in the PI rank"); + assert(i + 1 < ntk_lvls_new.size() && "Next level does not exist"); const auto children = gather_fanin_signals(ntk, ntk_dest_v, nd, old2new_v, ntk_lvls_new[i + 1], node_index); @@ -435,44 +435,51 @@ class node_duplication_planarization_impl { std::vector>* combinations_last = &lvl_pairs.back(); - for (auto& node_pair_cur : combinations) + for (std::size_t cur_idx = 0; cur_idx < combinations.size(); ++cur_idx) { - for (auto& node_pair_last : *combinations_last) + auto& node_pair_cur = combinations[cur_idx]; + + for (std::size_t last_idx = 0; last_idx < combinations_last->size(); ++last_idx) { + auto& node_pair_last = (*combinations_last)[last_idx]; + // If there is a connection between the two node pairs the delay is calculated like this if ((node_pair_cur.pair.first == node_pair_last.pair.second && node_pair_last.delay + 1 < node_pair_cur.delay)) { - node_pair_cur.fanin_pair = &node_pair_last; - node_pair_cur.delay = node_pair_last.delay + 1; + node_pair_cur.fanin_it = last_idx; + node_pair_cur.delay = node_pair_last.delay + 1; } // If there is no connection between the two node pairs the delay is calculated like this else if (node_pair_last.delay + 2 < node_pair_cur.delay) { - node_pair_cur.fanin_pair = &node_pair_last; - node_pair_cur.delay = node_pair_last.delay + 2; + node_pair_cur.fanin_it = last_idx; + node_pair_cur.delay = node_pair_last.delay + 2; } else if (node_pair_last.delay + 2 == node_pair_cur.delay) { // This solves equal path delays, if they are connected in the next layer via a fanout const auto fc0 = fanins(ntk, node_pair_last.pair.first); - if (node_pair_last.fanin_pair != nullptr) + if (node_pair_last.fanin_it < combinations_last->size()) { - const auto fc1 = fanins(ntk, node_pair_last.fanin_pair->pair.second); + const auto& parent_pair = (*combinations_last)[node_pair_last.fanin_it]; + const auto fc1 = fanins(ntk, parent_pair.pair.second); + for (const auto f0 : fc0.fanin_nodes) { for (const auto f1 : fc1.fanin_nodes) { if (f0 == f1) { - node_pair_cur.fanin_pair = &node_pair_last; - break; + node_pair_cur.fanin_it = last_idx; + goto next_combination; } } } } } } + next_combination:; } } else @@ -535,11 +542,14 @@ class node_duplication_planarization_impl { std::vector> next_level; int saturated_fanout_flag = 0; - const auto& combinations = lvl_pairs.back(); - // select the path with the least delay and follow it via fanin relations + + const auto& combinations = lvl_pairs.back(); + + // Select the path with the least delay and follow it via fanin relations const auto minimum_it = std::min_element(combinations.cbegin(), combinations.cend(), [](const node_pair& a, const node_pair& b) { return a.delay < b.delay; }); + if (minimum_it != combinations.cend()) { const auto& min_combination = *minimum_it; @@ -547,7 +557,7 @@ class node_duplication_planarization_impl // Insert the terminal node insert_if_not_first(min_combination.pair.second, next_level, saturated_fanout_flag, 0); - // insert middle_nodes + // Insert middle_nodes for (const auto& node : min_combination.middle_nodes) { insert_if_not_first(node, next_level, saturated_fanout_flag, 1); @@ -556,29 +566,37 @@ class node_duplication_planarization_impl // Insert the first node insert_if_not_first(min_combination.pair.first, next_level, saturated_fanout_flag, 1); - auto fanin_combination = minimum_it->fanin_pair; + // Start with index instead of pointer + std::size_t level = lvl_pairs.size() - 1; + std::size_t fanin_it = minimum_it->fanin_it; - while (fanin_combination) + // Follow chain while index is valid + while (level > 0 && fanin_it < lvl_pairs[level - 1].size()) { + const auto& fanin_combination = lvl_pairs[level - 1][fanin_it]; + // Insert the terminal node - if (ntk.is_pi(fanin_combination->pair.second)) + if (ntk.is_pi(fanin_combination.pair.second)) { saturated_fanout_flag = 1; } - insert_if_not_first(fanin_combination->pair.second, next_level, saturated_fanout_flag, 0); + insert_if_not_first(fanin_combination.pair.second, next_level, saturated_fanout_flag, 0); // Insert middle_nodes - for (const auto& node : fanin_combination->middle_nodes) + for (const auto& node : fanin_combination.middle_nodes) { insert_if_not_first(node, next_level, saturated_fanout_flag, 1); } - // insert the first node - insert_if_not_first(fanin_combination->pair.first, next_level, saturated_fanout_flag, 1); + // Insert the first node + insert_if_not_first(fanin_combination.pair.first, next_level, saturated_fanout_flag, 1); - fanin_combination = fanin_combination->fanin_pair; + // Move one level up + --level; + fanin_it = fanin_combination.fanin_it; } } + return next_level; } diff --git a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp index c17264454e..155be98029 100644 --- a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp +++ b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include @@ -800,7 +799,7 @@ class orthogonal_planar_impl public: orthogonal_planar_impl(const Ntk& src, const orthogonal_physical_design_params& p, orthogonal_physical_design_stats& st) : - ntk{mockturtle::fanout_view{mockturtle::names_view{src}}}, + ntk{mockturtle::fanout_view{src}}, ps{p}, pst{st} {} @@ -811,7 +810,7 @@ class orthogonal_planar_impl // measure run time mockturtle::stopwatch stop{pst.time_total}; // initialize mapping from nodes to positions - mockturtle::node_map, decltype(ntk)> node2pos{ntk}; + mockturtle::node_map, mockturtle::fanout_view> node2pos{ntk}; // initialize the aspect ratio aspect_ratio aspect_ratio = {0, 0}; // instantiate the layout @@ -833,7 +832,7 @@ class orthogonal_planar_impl for (uint32_t lvl = 0; lvl < ntk.depth() + 1; lvl++) { const auto variable_tuple = - compute_pr_variables>, Lyt>(ntk, node2pos, lvl); + compute_pr_variables, Lyt>(ntk, node2pos, lvl); const auto orientation = std::get<0>(variable_tuple); const auto new_lines = std::get<1>(variable_tuple); @@ -1033,7 +1032,7 @@ class orthogonal_planar_impl } private: - mockturtle::fanout_view> ntk; + mockturtle::fanout_view ntk; orthogonal_physical_design_params ps; orthogonal_physical_design_stats& pst; diff --git a/test/algorithms/physical_design/orthogonal_planar.cpp b/test/algorithms/physical_design/orthogonal_planar.cpp index 3b07194e4c..11881d79f0 100644 --- a/test/algorithms/physical_design/orthogonal_planar.cpp +++ b/test/algorithms/physical_design/orthogonal_planar.cpp @@ -96,6 +96,9 @@ TEST_CASE("Name conservation after planar orthogonal physical design", "[orthogo CHECK(planarized_b.get_name(2) == "x1"); CHECK(planarized_b.get_name(3) == "x2"); CHECK(planarized_b.get_network_name() == "topolinano"); + CHECK(planarized_b.get_output_name(0) == "f1"); + CHECK(planarized_b.get_output_name(1) == "f2"); + CHECK(planarized_b.get_output_name(2) == "f3"); orthogonal_physical_design_stats orthogonal_planar_stats{}; @@ -104,8 +107,8 @@ TEST_CASE("Name conservation after planar orthogonal physical design", "[orthogo // network name CHECK(layout.get_layout_name() == "topolinano"); // PI names (they are ordered corresponding to their placement in the layout) - CHECK(layout.get_input_name(1) == "x1"); - CHECK(layout.get_input_name(0) == "x2"); + CHECK(layout.get_input_name(0) == "x1"); + CHECK(layout.get_input_name(1) == "x2"); // PO names CHECK(layout.get_output_name(0) == "f1"); CHECK(layout.get_output_name(1) == "f2"); From cbb94ca526fd26f190d16b72c44355aa0e3846d1 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 27 Oct 2025 16:26:41 +0000 Subject: [PATCH 27/67] :memo: Update pyfiction docstrings Signed-off-by: GitHub Actions --- .../pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index c1c17015ad..98474a0896 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -9573,7 +9573,7 @@ Template parameter ``Ntk``: static const char *__doc_fiction_detail_node_pair_delay = R"doc(Specifies the delay value for the node.)doc"; -static const char *__doc_fiction_detail_node_pair_fanin_pair = +static const char *__doc_fiction_detail_node_pair_fanin_it = R"doc(Shared pointer to another instance of node_pair detailing fanin-edge alignment.)doc"; @@ -10032,6 +10032,8 @@ Parameter ``p``: Parameter ``st``: Statistics object used to collect runtime and layout information.)doc"; +static const char *__doc_fiction_detail_orthogonal_planar_impl_ntk = R"doc()doc"; + static const char *__doc_fiction_detail_orthogonal_planar_impl_orthogonal_planar_impl = R"doc()doc"; static const char *__doc_fiction_detail_orthogonal_planar_impl_po_counter = R"doc()doc"; From 2b0d49b4f4bca62b080d965b50493ab342b86a39 Mon Sep 17 00:00:00 2001 From: benjamin Date: Mon, 27 Oct 2025 17:30:11 +0100 Subject: [PATCH 28/67] :art: clang-format --- .../node_duplication_planarization.hpp | 1 - .../algorithms/physical_design/orthogonal_planar.hpp | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index e535ea2a70..fa435d4384 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -286,7 +286,6 @@ virtual_pi_network create_virtual_pi_ntk_from_duplicated_nodes( } else { - assert(i != ntk_lvls.size() && "Node, which is not marked as PI is in the PI rank"); assert(i + 1 < ntk_lvls_new.size() && "Next level does not exist"); const auto children = diff --git a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp index 155be98029..04a3b0becf 100644 --- a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp +++ b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp @@ -831,10 +831,9 @@ class orthogonal_planar_impl // place and route the nodes in ascending level order for (uint32_t lvl = 0; lvl < ntk.depth() + 1; lvl++) { - const auto variable_tuple = - compute_pr_variables, Lyt>(ntk, node2pos, lvl); - const auto orientation = std::get<0>(variable_tuple); - const auto new_lines = std::get<1>(variable_tuple); + const auto variable_tuple = compute_pr_variables, Lyt>(ntk, node2pos, lvl); + const auto orientation = std::get<0>(variable_tuple); + const auto new_lines = std::get<1>(variable_tuple); const auto wiring = compute_wiring(ntk, node2pos, new_lines, lvl); const auto& x = wiring.first; From 51b404d0b8bd94b6613aba6f93d28d5a04ca629a Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 28 Oct 2025 11:37:19 +0100 Subject: [PATCH 29/67] :art: coderabbitai --- .../physical_design/orthogonal_planar.hpp | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp index 04a3b0becf..e5e8f17144 100644 --- a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp +++ b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp @@ -46,7 +46,7 @@ namespace detail * combinations of the previous level, connection type, orientations, and surrounding spacing (gaps). Based on these * inputs, it returns the corresponding orientation and spacing configuration for the current buffer. */ -std::array, 4>, 3>, 3>, 2>& get_buffer_lookup() +inline std::array, 4>, 3>, 3>, 2>& get_buffer_lookup() { static std::array, 4>, 3>, 3>, 2> array = { { // Array @@ -96,7 +96,7 @@ std::array, 4>, 3 * combinations of the previous level, connection type, orientations, and surrounding spacing (gaps). Based on these * inputs, it returns the corresponding orientation and spacing configuration for the current fanout. */ -std::array, 4>, 3>, 3>, 4>& get_fanout_lookup() +inline std::array, 4>, 3>, 3>, 4>& get_fanout_lookup() { static std::array, 4>, 3>, 3>, 4> array = { { // Array @@ -566,9 +566,9 @@ std::vector calculate_two_input_new_lines(const Ntk& * @param two_input_indices Indices of two-input nodes within the level. * @param two_input_new_lines Number of new routing lines associated with each two-input node. */ -void adjust_final_values(std::vector& x, std::vector& y, - const std::vector& two_input_indices, - const std::vector& two_input_new_lines) +inline void adjust_final_values(std::vector& x, std::vector& y, + const std::vector& two_input_indices, + const std::vector& two_input_new_lines) { // Max element in two_input_new_lines and its index const auto max_it = std::max_element(two_input_new_lines.begin(), two_input_new_lines.end()); @@ -1003,10 +1003,19 @@ class orthogonal_planar_impl po_tile.x = first_pos.x + 1; // Create PO and increment the count - layout.create_po(wire_east(layout, anker, po_tile), - ntk.has_output_name(po_counter) ? ntk.get_output_name(po_counter++) : - fmt::format("po{}", po_counter++), - po_tile); + if constexpr (mockturtle::has_has_output_name_v) + { + layout.create_po(wire_east(layout, anker, po_tile), + ntk.has_output_name(po_counter) ? ntk.get_output_name(po_counter++) : + fmt::format("po{}", po_counter++), + po_tile); + } + else + { + layout.create_po(wire_east(layout, anker, po_tile), fmt::format("po{}", po_counter++), + po_tile); + } + ++cnt; } else From daf845fbf7b2abef6c9713d9475e06c3f4321939 Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 28 Oct 2025 12:17:28 +0100 Subject: [PATCH 30/67] :art: codecoverage --- .../node_duplication_planarization.hpp | 3 +- .../physical_design/orthogonal_planar.cpp | 28 +++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index fa435d4384..8d281e1f75 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -704,7 +704,8 @@ class node_duplication_planarization_impl virtual_ntk.set_all_ranks(ntk_lvls_new); // restore possibly set signal names - restore_names(ntk, virtual_ntk); + restore_network_name(ntk, virtual_ntk); + restore_output_names(ntk, virtual_ntk); return virtual_ntk; } diff --git a/test/algorithms/physical_design/orthogonal_planar.cpp b/test/algorithms/physical_design/orthogonal_planar.cpp index 11881d79f0..ae7e5792eb 100644 --- a/test/algorithms/physical_design/orthogonal_planar.cpp +++ b/test/algorithms/physical_design/orthogonal_planar.cpp @@ -1,5 +1,6 @@ #include +#include "catch2/matchers/catch_matchers.hpp" #include "fiction/algorithms/physical_design/apply_gate_library.hpp" #include "fiction/layouts/cell_level_layout.hpp" #include "fiction/layouts/coordinates.hpp" @@ -63,6 +64,25 @@ void check_ortho_planar(const Ntk& ntk) CHECK_NOTHROW(apply_gate_library(gate_level_layout)); } +TEST_CASE("Check exceptions", "[orthogonal-planar]") +{ + using gate_lyt = + fiction::gate_level_layout>>>; + fiction::network_balancing_params b_ps; + b_ps.unify_outputs = true; + + auto maj = blueprints::maj1_network(); + auto maj_ranked = fiction::mutable_rank_view(maj); + orthogonal_physical_design_stats orthogonal_planar_stats{}; + CHECK_THROWS_WITH(fiction::orthogonal_planar(maj_ranked, {}, &orthogonal_planar_stats), + "network contains nodes that exceed the supported fanin size"); + + auto ao = blueprints::and_or_network(); + auto ao_ranked = fiction::mutable_rank_view(ao); + CHECK_THROWS_WITH(fiction::orthogonal_planar(ao_ranked, {}, &orthogonal_planar_stats), + "Input network has to be planar"); +} + TEST_CASE("Orthogonal planar layout tests", "[orthogonal-planar]") { // Simple correctness and corner-case networks @@ -75,6 +95,9 @@ TEST_CASE("Orthogonal planar layout tests", "[orthogonal-planar]") // Multi-output network check_ortho_planar(blueprints::multi_output_network()); + + // Parity network + check_ortho_planar(blueprints::parity_network()); } TEST_CASE("Name conservation after planar orthogonal physical design", "[orthogonal-planar]") @@ -93,8 +116,6 @@ TEST_CASE("Name conservation after planar orthogonal physical design", "[orthogo auto topolinano_ranked = fiction::mutable_rank_view(topolinano_balanced); auto planarized_b = fiction::node_duplication_planarization(topolinano_ranked); - CHECK(planarized_b.get_name(2) == "x1"); - CHECK(planarized_b.get_name(3) == "x2"); CHECK(planarized_b.get_network_name() == "topolinano"); CHECK(planarized_b.get_output_name(0) == "f1"); CHECK(planarized_b.get_output_name(1) == "f2"); @@ -106,9 +127,6 @@ TEST_CASE("Name conservation after planar orthogonal physical design", "[orthogo // network name CHECK(layout.get_layout_name() == "topolinano"); - // PI names (they are ordered corresponding to their placement in the layout) - CHECK(layout.get_input_name(0) == "x1"); - CHECK(layout.get_input_name(1) == "x2"); // PO names CHECK(layout.get_output_name(0) == "f1"); CHECK(layout.get_output_name(1) == "f2"); From feca457b4e553cc59cb7c36f237e7a4223fe497c Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 28 Oct 2025 12:53:28 +0100 Subject: [PATCH 31/67] :art: codecoverage --- .../node_duplication_planarization.hpp | 2 +- .../node_duplication_planarization.cpp | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index 8d281e1f75..c41f1af1c4 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -215,7 +215,7 @@ gather_fanin_signals(const Ntk& ntk, NtkDest& ntk_dest_v, const mockturtle::node // Return the children of the node. return children; -}; +} /** * Constructs a planar `virtual_pi_network` based on duplicated nodes derived from the source network. diff --git a/test/algorithms/network_transformation/node_duplication_planarization.cpp b/test/algorithms/network_transformation/node_duplication_planarization.cpp index 37afa97247..d078dddffa 100644 --- a/test/algorithms/network_transformation/node_duplication_planarization.cpp +++ b/test/algorithms/network_transformation/node_duplication_planarization.cpp @@ -4,6 +4,7 @@ #include +#include "catch2/matchers/catch_matchers.hpp" #include "fiction/algorithms/graph/mincross.hpp" #include "fiction/algorithms/network_transformation/fanout_substitution.hpp" #include "fiction/algorithms/network_transformation/network_balancing.hpp" @@ -18,6 +19,23 @@ using namespace fiction; +TEST_CASE("Check exceptions", "[node-duplication-planarization]") +{ + technology_network tec{}; + + const auto x1 = tec.create_pi(); + const auto x2 = tec.create_pi(); + const auto b1 = tec.create_buf(x2); + const auto f1 = tec.create_and(x1, b1); + + tec.create_po(f1); + + const auto vpi_r = fiction::mutable_rank_view(tec); + + CHECK_THROWS_WITH(node_duplication_planarization(vpi_r), + "Networks have to be balanced for this duplication"); +} + TEST_CASE("Planarize technology ntk", "[node-duplication-planarization]") { technology_network tec{}; @@ -84,6 +102,9 @@ TEST_CASE("Planarize ntk with 3-ary node", "[node-duplication-planarization]") const auto vpi_r = fiction::mutable_rank_view(tec_b); + node_duplication_planarization_params params; + params.po_order = node_duplication_planarization_params::output_order::RANDOM_PO_ORDER; + auto planarized_ntk = node_duplication_planarization(vpi_r); mincross_stats st_min{}; From 56eed96e968d329f03c296f2f7659be71202ca89 Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 28 Oct 2025 13:20:42 +0100 Subject: [PATCH 32/67] :art: coderabbitai --- .../fiction/algorithms/physical_design/orthogonal_planar.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp index e5e8f17144..e4d1a889c4 100644 --- a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp +++ b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp @@ -1020,7 +1020,7 @@ class orthogonal_planar_impl } else { - assert(false); + throw std::logic_error("Node has more than 2 primary outputs after fanout substitution"); } } }); From de57c619785fbdad5a6a1835290b7db94a39027a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 12:20:50 +0000 Subject: [PATCH 33/67] =?UTF-8?q?=F0=9F=8E=A8=20Incorporated=20pre-commit?= =?UTF-8?q?=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../network_transformation/node_duplication_planarization.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/algorithms/network_transformation/node_duplication_planarization.cpp b/test/algorithms/network_transformation/node_duplication_planarization.cpp index d078dddffa..f9db47f0e6 100644 --- a/test/algorithms/network_transformation/node_duplication_planarization.cpp +++ b/test/algorithms/network_transformation/node_duplication_planarization.cpp @@ -32,8 +32,7 @@ TEST_CASE("Check exceptions", "[node-duplication-planarization]") const auto vpi_r = fiction::mutable_rank_view(tec); - CHECK_THROWS_WITH(node_duplication_planarization(vpi_r), - "Networks have to be balanced for this duplication"); + CHECK_THROWS_WITH(node_duplication_planarization(vpi_r), "Networks have to be balanced for this duplication"); } TEST_CASE("Planarize technology ntk", "[node-duplication-planarization]") From 767863808ff89cd80ed0f2a54e8f75f5798b0c9f Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 28 Oct 2025 13:54:53 +0100 Subject: [PATCH 34/67] :book: docstring updated --- .../network_transformation/node_duplication_planarization.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index c41f1af1c4..d26fea64e0 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -85,7 +85,7 @@ struct node_pair */ std::vector> middle_nodes; /** - * Shared pointer to another instance of node_pair detailing fanin-edge alignment. + * Index into the previous level's node_pair vector, used to track fanin-edge alignment. */ std::size_t fanin_it{}; /** From 4af955273bca3fe02a7643dadaa07c08a43092ca Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 28 Oct 2025 12:56:35 +0000 Subject: [PATCH 35/67] :memo: Update pyfiction docstrings Signed-off-by: GitHub Actions --- .../pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index 98474a0896..1983a5d3d7 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -9574,8 +9574,8 @@ Template parameter ``Ntk``: static const char *__doc_fiction_detail_node_pair_delay = R"doc(Specifies the delay value for the node.)doc"; static const char *__doc_fiction_detail_node_pair_fanin_it = -R"doc(Shared pointer to another instance of node_pair detailing fanin-edge -alignment.)doc"; +R"doc(Index into the previous level's node_pair vector, used to track fanin- +edge alignment.)doc"; static const char *__doc_fiction_detail_node_pair_node_pair = R"doc(Standard constructor. From 013407e2ede8541d5cf20eeb3d4076facfe245df Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 28 Oct 2025 14:11:29 +0100 Subject: [PATCH 36/67] :art: coderabbitai --- .../node_duplication_planarization.hpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index d26fea64e0..4027fed48e 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -122,10 +122,11 @@ initialize_copy_network_duplicates(Ntk const& src) static_assert(mockturtle::is_network_type_v, "Ntk is not a network type"); static_assert(mockturtle::has_get_constant_v, "Ntk does not implement the get_constant method"); static_assert(mockturtle::has_get_constant_v>, - "Ntk does not implement the get_constant method"); + "virtual_pi_network does not implement get_constant"); static_assert(mockturtle::has_get_node_v, "Ntk does not implement the get_node method"); static_assert(mockturtle::has_foreach_pi_v, "Ntk does not implement the foreach_pi method"); - static_assert(mockturtle::has_create_pi_v>, "Ntk does not implement the create_pi method"); + static_assert(mockturtle::has_create_pi_v>, + "virtual_pi_network does not implement create_pi"); mockturtle::node_map>>, Ntk> old2new(src); virtual_pi_network dest; @@ -244,6 +245,12 @@ virtual_pi_network create_virtual_pi_ntk_from_duplicated_nodes( const Ntk& ntk, const std::vector>>& ntk_lvls, std::vector>>>& ntk_lvls_new) { + static_assert(mockturtle::has_create_node_v>, "virtual_pi_network lacks create_node"); + static_assert(mockturtle::has_get_node_v>, "virtual_pi_network lacks get_node"); + static_assert(mockturtle::has_fanout_size_v>, "virtual_pi_network lacks fanout_size"); + static_assert(mockturtle::has_create_po_v>, "virtual_pi_network lacks create_po"); + static_assert(mockturtle::has_create_not_v>, "virtual_pi_network lacks create_not"); + std::unordered_map, bool> node_status; ntk_lvls_new.resize(ntk_lvls.size()); @@ -291,7 +298,9 @@ virtual_pi_network create_virtual_pi_ntk_from_duplicated_nodes( const auto children = gather_fanin_signals(ntk, ntk_dest_v, nd, old2new_v, ntk_lvls_new[i + 1], node_index); - assert(!children.empty() && "The node has to have children"); + // Ensure child count matches function arity (including 0-fanin constants) + assert(children.size() == ntk.fanin_size(nd) && + "Mismatch between gathered children and node fanin count"); const auto new_sig = ntk_dest_v.create_node(children, ntk.node_function(nd)); lvl_new.push_back(ntk_dest_v.get_node(new_sig)); From 85fefbb30a5f9b29dc8eebbc740e74364b3fcbe4 Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 28 Oct 2025 14:36:30 +0100 Subject: [PATCH 37/67] :art: suppress unchecked access to optional value --- .../node_duplication_planarization.cpp | 5 +++++ test/algorithms/physical_design/orthogonal_planar.cpp | 1 + 2 files changed, 6 insertions(+) diff --git a/test/algorithms/network_transformation/node_duplication_planarization.cpp b/test/algorithms/network_transformation/node_duplication_planarization.cpp index f9db47f0e6..478fcb5811 100644 --- a/test/algorithms/network_transformation/node_duplication_planarization.cpp +++ b/test/algorithms/network_transformation/node_duplication_planarization.cpp @@ -72,6 +72,7 @@ TEST_CASE("Planarize technology ntk", "[node-duplication-planarization]") const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(tec, planarized_ntk), {}, &st); REQUIRE(cec_m.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) CHECK(*cec_m == 1); } @@ -117,6 +118,7 @@ TEST_CASE("Planarize ntk with 3-ary node", "[node-duplication-planarization]") const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(tec, planarized_ntk), {}, &st); REQUIRE(cec_m.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) CHECK(*cec_m == 1); } @@ -159,6 +161,7 @@ TEST_CASE("Buffer AIG and planarize technology_network", "[node-duplication-plan const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_ntk), {}, &st); REQUIRE(cec_m.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) CHECK(*cec_m == 1); } @@ -194,6 +197,7 @@ TEST_CASE("Buffer AIG and planarize technology_network 2", "[node-duplication-pl const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_ntk), {}, &st); REQUIRE(cec_m.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) CHECK(*cec_m == 1); } @@ -232,5 +236,6 @@ TEST_CASE("Planarize multi output network", "[node-duplication-planarization]") const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_ntk), {}, &st); REQUIRE(cec_m.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) CHECK(*cec_m == 1); } diff --git a/test/algorithms/physical_design/orthogonal_planar.cpp b/test/algorithms/physical_design/orthogonal_planar.cpp index ae7e5792eb..f55c54224f 100644 --- a/test/algorithms/physical_design/orthogonal_planar.cpp +++ b/test/algorithms/physical_design/orthogonal_planar.cpp @@ -53,6 +53,7 @@ void check_ortho_planar(const Ntk& ntk) const auto cec_m = mockturtle::equivalence_checking( *fiction::virtual_miter(ntk, planarized_b), {}, &eq_st); REQUIRE(cec_m.has_value()); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) CHECK(*cec_m == 1); orthogonal_physical_design_stats orthogonal_planar_stats{}; From 3cde343cb14f4b8ff17ffd9bf77e33ed9dc919f3 Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 28 Oct 2025 14:46:06 +0100 Subject: [PATCH 38/67] :art: suppress unchecked access to optional value --- .../node_duplication_planarization.cpp | 5 +++++ test/algorithms/physical_design/orthogonal_planar.cpp | 1 + 2 files changed, 6 insertions(+) diff --git a/test/algorithms/network_transformation/node_duplication_planarization.cpp b/test/algorithms/network_transformation/node_duplication_planarization.cpp index 478fcb5811..33f5fb31ba 100644 --- a/test/algorithms/network_transformation/node_duplication_planarization.cpp +++ b/test/algorithms/network_transformation/node_duplication_planarization.cpp @@ -71,6 +71,7 @@ TEST_CASE("Planarize technology ntk", "[node-duplication-planarization]") mockturtle::equivalence_checking_stats st; const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(tec, planarized_ntk), {}, &st); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) REQUIRE(cec_m.has_value()); // NOLINTNEXTLINE(bugprone-unchecked-optional-access) CHECK(*cec_m == 1); @@ -117,6 +118,7 @@ TEST_CASE("Planarize ntk with 3-ary node", "[node-duplication-planarization]") mockturtle::equivalence_checking_stats st; const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(tec, planarized_ntk), {}, &st); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) REQUIRE(cec_m.has_value()); // NOLINTNEXTLINE(bugprone-unchecked-optional-access) CHECK(*cec_m == 1); @@ -160,6 +162,7 @@ TEST_CASE("Buffer AIG and planarize technology_network", "[node-duplication-plan mockturtle::equivalence_checking_stats st; const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_ntk), {}, &st); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) REQUIRE(cec_m.has_value()); // NOLINTNEXTLINE(bugprone-unchecked-optional-access) CHECK(*cec_m == 1); @@ -196,6 +199,7 @@ TEST_CASE("Buffer AIG and planarize technology_network 2", "[node-duplication-pl mockturtle::equivalence_checking_stats st; const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_ntk), {}, &st); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) REQUIRE(cec_m.has_value()); // NOLINTNEXTLINE(bugprone-unchecked-optional-access) CHECK(*cec_m == 1); @@ -235,6 +239,7 @@ TEST_CASE("Planarize multi output network", "[node-duplication-planarization]") mockturtle::equivalence_checking_stats st; const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_ntk), {}, &st); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) REQUIRE(cec_m.has_value()); // NOLINTNEXTLINE(bugprone-unchecked-optional-access) CHECK(*cec_m == 1); diff --git a/test/algorithms/physical_design/orthogonal_planar.cpp b/test/algorithms/physical_design/orthogonal_planar.cpp index f55c54224f..cfcddc580d 100644 --- a/test/algorithms/physical_design/orthogonal_planar.cpp +++ b/test/algorithms/physical_design/orthogonal_planar.cpp @@ -52,6 +52,7 @@ void check_ortho_planar(const Ntk& ntk) mockturtle::equivalence_checking_stats eq_st; const auto cec_m = mockturtle::equivalence_checking( *fiction::virtual_miter(ntk, planarized_b), {}, &eq_st); + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) REQUIRE(cec_m.has_value()); // NOLINTNEXTLINE(bugprone-unchecked-optional-access) CHECK(*cec_m == 1); From 46884ab9304951089592a56d261018e6403c3391 Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 28 Oct 2025 14:59:57 +0100 Subject: [PATCH 39/67] :art: suppress unchecked access to optional value --- .../node_duplication_planarization.cpp | 40 ++++++++++++------- .../physical_design/orthogonal_planar.cpp | 8 ++-- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/test/algorithms/network_transformation/node_duplication_planarization.cpp b/test/algorithms/network_transformation/node_duplication_planarization.cpp index 33f5fb31ba..2409607a6a 100644 --- a/test/algorithms/network_transformation/node_duplication_planarization.cpp +++ b/test/algorithms/network_transformation/node_duplication_planarization.cpp @@ -71,10 +71,12 @@ TEST_CASE("Planarize technology ntk", "[node-duplication-planarization]") mockturtle::equivalence_checking_stats st; const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(tec, planarized_ntk), {}, &st); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + // clang-tidy bugprone-unchecked-optional-access false positive in tests + // NOLINTBEGIN(bugprone-unchecked-optional-access) REQUIRE(cec_m.has_value()); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - CHECK(*cec_m == 1); + const auto result = *cec_m; + CHECK(result == 1); + // NOLINTEND(bugprone-unchecked-optional-access) } TEST_CASE("Planarize ntk with 3-ary node", "[node-duplication-planarization]") @@ -118,10 +120,12 @@ TEST_CASE("Planarize ntk with 3-ary node", "[node-duplication-planarization]") mockturtle::equivalence_checking_stats st; const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(tec, planarized_ntk), {}, &st); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + // clang-tidy bugprone-unchecked-optional-access false positive in tests + // NOLINTBEGIN(bugprone-unchecked-optional-access) REQUIRE(cec_m.has_value()); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - CHECK(*cec_m == 1); + const auto result = *cec_m; + CHECK(result == 1); + // NOLINTEND(bugprone-unchecked-optional-access) } TEST_CASE("Buffer AIG and planarize technology_network", "[node-duplication-planarization]") @@ -162,10 +166,12 @@ TEST_CASE("Buffer AIG and planarize technology_network", "[node-duplication-plan mockturtle::equivalence_checking_stats st; const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_ntk), {}, &st); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + // clang-tidy bugprone-unchecked-optional-access false positive in tests + // NOLINTBEGIN(bugprone-unchecked-optional-access) REQUIRE(cec_m.has_value()); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - CHECK(*cec_m == 1); + const auto result = *cec_m; + CHECK(result == 1); + // NOLINTEND(bugprone-unchecked-optional-access) } TEST_CASE("Buffer AIG and planarize technology_network 2", "[node-duplication-planarization]") @@ -199,10 +205,12 @@ TEST_CASE("Buffer AIG and planarize technology_network 2", "[node-duplication-pl mockturtle::equivalence_checking_stats st; const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_ntk), {}, &st); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + // clang-tidy bugprone-unchecked-optional-access false positive in tests + // NOLINTBEGIN(bugprone-unchecked-optional-access) REQUIRE(cec_m.has_value()); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - CHECK(*cec_m == 1); + const auto result = *cec_m; + CHECK(result == 1); + // NOLINTEND(bugprone-unchecked-optional-access) } TEST_CASE("Planarize multi output network", "[node-duplication-planarization]") @@ -239,8 +247,10 @@ TEST_CASE("Planarize multi output network", "[node-duplication-planarization]") mockturtle::equivalence_checking_stats st; const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_ntk), {}, &st); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + // clang-tidy bugprone-unchecked-optional-access false positive in tests + // NOLINTBEGIN(bugprone-unchecked-optional-access) REQUIRE(cec_m.has_value()); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - CHECK(*cec_m == 1); + const auto result = *cec_m; + CHECK(result == 1); + // NOLINTEND(bugprone-unchecked-optional-access) } diff --git a/test/algorithms/physical_design/orthogonal_planar.cpp b/test/algorithms/physical_design/orthogonal_planar.cpp index cfcddc580d..5f6cc44573 100644 --- a/test/algorithms/physical_design/orthogonal_planar.cpp +++ b/test/algorithms/physical_design/orthogonal_planar.cpp @@ -52,10 +52,12 @@ void check_ortho_planar(const Ntk& ntk) mockturtle::equivalence_checking_stats eq_st; const auto cec_m = mockturtle::equivalence_checking( *fiction::virtual_miter(ntk, planarized_b), {}, &eq_st); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + // clang-tidy bugprone-unchecked-optional-access false positive in tests + // NOLINTBEGIN(bugprone-unchecked-optional-access) REQUIRE(cec_m.has_value()); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - CHECK(*cec_m == 1); + const auto result = *cec_m; + CHECK(result == 1); + // NOLINTEND(bugprone-unchecked-optional-access) orthogonal_physical_design_stats orthogonal_planar_stats{}; From be38caaa7611b0f24fda44eec7b4b4034e3b3f69 Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 28 Oct 2025 15:19:37 +0100 Subject: [PATCH 40/67] :art: suppress unchecked access to optional value --- .../node_duplication_planarization.cpp | 20 +++++++++---------- .../physical_design/orthogonal_planar.cpp | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/test/algorithms/network_transformation/node_duplication_planarization.cpp b/test/algorithms/network_transformation/node_duplication_planarization.cpp index 2409607a6a..d27b6c66d0 100644 --- a/test/algorithms/network_transformation/node_duplication_planarization.cpp +++ b/test/algorithms/network_transformation/node_duplication_planarization.cpp @@ -68,11 +68,11 @@ TEST_CASE("Planarize technology ntk", "[node-duplication-planarization]") auto ntk = mincross(planarized_ntk, p_min, &st_min); // counts crossings CHECK(st_min.num_crossings == 0); + // clang-tidy bugprone-unchecked-optional-access false positive in tests + // NOLINTBEGIN(bugprone-unchecked-optional-access) mockturtle::equivalence_checking_stats st; const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(tec, planarized_ntk), {}, &st); - // clang-tidy bugprone-unchecked-optional-access false positive in tests - // NOLINTBEGIN(bugprone-unchecked-optional-access) REQUIRE(cec_m.has_value()); const auto result = *cec_m; CHECK(result == 1); @@ -117,11 +117,11 @@ TEST_CASE("Planarize ntk with 3-ary node", "[node-duplication-planarization]") auto ntk = mincross(planarized_ntk, p_min, &st_min); // counts crossings CHECK(st_min.num_crossings == 0); + // clang-tidy bugprone-unchecked-optional-access false positive in tests + // NOLINTBEGIN(bugprone-unchecked-optional-access) mockturtle::equivalence_checking_stats st; const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(tec, planarized_ntk), {}, &st); - // clang-tidy bugprone-unchecked-optional-access false positive in tests - // NOLINTBEGIN(bugprone-unchecked-optional-access) REQUIRE(cec_m.has_value()); const auto result = *cec_m; CHECK(result == 1); @@ -163,11 +163,11 @@ TEST_CASE("Buffer AIG and planarize technology_network", "[node-duplication-plan auto ntk = mincross(planarized_ntk, p_min, &st_min); // counts crossings CHECK(st_min.num_crossings == 0); + // clang-tidy bugprone-unchecked-optional-access false positive in tests + // NOLINTBEGIN(bugprone-unchecked-optional-access) mockturtle::equivalence_checking_stats st; const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_ntk), {}, &st); - // clang-tidy bugprone-unchecked-optional-access false positive in tests - // NOLINTBEGIN(bugprone-unchecked-optional-access) REQUIRE(cec_m.has_value()); const auto result = *cec_m; CHECK(result == 1); @@ -202,11 +202,11 @@ TEST_CASE("Buffer AIG and planarize technology_network 2", "[node-duplication-pl auto ntk = mincross(planarized_ntk, p_min, &st_min); // counts crossings CHECK(st_min.num_crossings == 0); + // clang-tidy bugprone-unchecked-optional-access false positive in tests + // NOLINTBEGIN(bugprone-unchecked-optional-access) mockturtle::equivalence_checking_stats st; const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_ntk), {}, &st); - // clang-tidy bugprone-unchecked-optional-access false positive in tests - // NOLINTBEGIN(bugprone-unchecked-optional-access) REQUIRE(cec_m.has_value()); const auto result = *cec_m; CHECK(result == 1); @@ -244,11 +244,11 @@ TEST_CASE("Planarize multi output network", "[node-duplication-planarization]") auto ntk = mincross(planarized_ntk, p_min, &st_min); // counts crossings CHECK(st_min.num_crossings == 0); + // clang-tidy bugprone-unchecked-optional-access false positive in tests + // NOLINTBEGIN(bugprone-unchecked-optional-access) mockturtle::equivalence_checking_stats st; const auto cec_m = mockturtle::equivalence_checking(*fiction::virtual_miter(aig, planarized_ntk), {}, &st); - // clang-tidy bugprone-unchecked-optional-access false positive in tests - // NOLINTBEGIN(bugprone-unchecked-optional-access) REQUIRE(cec_m.has_value()); const auto result = *cec_m; CHECK(result == 1); diff --git a/test/algorithms/physical_design/orthogonal_planar.cpp b/test/algorithms/physical_design/orthogonal_planar.cpp index 5f6cc44573..66e0938fcc 100644 --- a/test/algorithms/physical_design/orthogonal_planar.cpp +++ b/test/algorithms/physical_design/orthogonal_planar.cpp @@ -49,11 +49,11 @@ void check_ortho_planar(const Ntk& ntk) auto tec_ranked = fiction::mutable_rank_view(tec_balanced); auto planarized_b = fiction::node_duplication_planarization(tec_ranked); + // clang-tidy bugprone-unchecked-optional-access false positive in tests + // NOLINTBEGIN(bugprone-unchecked-optional-access) mockturtle::equivalence_checking_stats eq_st; const auto cec_m = mockturtle::equivalence_checking( *fiction::virtual_miter(ntk, planarized_b), {}, &eq_st); - // clang-tidy bugprone-unchecked-optional-access false positive in tests - // NOLINTBEGIN(bugprone-unchecked-optional-access) REQUIRE(cec_m.has_value()); const auto result = *cec_m; CHECK(result == 1); From 40fda747d78cb95b8d9f9ff039f26ff7b5008a4b Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 28 Oct 2025 15:42:15 +0100 Subject: [PATCH 41/67] :art: clang-tidy --- .../node_duplication_planarization.hpp | 4 ---- .../fiction/algorithms/physical_design/orthogonal_planar.hpp | 5 ++++- test/algorithms/physical_design/orthogonal_planar.cpp | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index 4027fed48e..458f1b90b2 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -24,10 +24,6 @@ #include #include -#if (PROGRESS_BARS) -#include -#endif - namespace fiction { diff --git a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp index e4d1a889c4..39c580a584 100644 --- a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp +++ b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp @@ -806,7 +806,6 @@ class orthogonal_planar_impl Lyt run() { - using node = typename Ntk::node; // measure run time mockturtle::stopwatch stop{pst.time_total}; // initialize mapping from nodes to positions @@ -974,6 +973,10 @@ class orthogonal_planar_impl } } }); +#if (PROGRESS_BARS) + // update progress + bar(i); +#endif } // place and route POs diff --git a/test/algorithms/physical_design/orthogonal_planar.cpp b/test/algorithms/physical_design/orthogonal_planar.cpp index 66e0938fcc..bc63fcef8b 100644 --- a/test/algorithms/physical_design/orthogonal_planar.cpp +++ b/test/algorithms/physical_design/orthogonal_planar.cpp @@ -26,7 +26,7 @@ using namespace fiction; -void check_stats(const orthogonal_physical_design_stats& st) noexcept +static void check_stats(const orthogonal_physical_design_stats& st) noexcept { CHECK(st.x_size > 0); CHECK(st.y_size > 0); @@ -35,7 +35,7 @@ void check_stats(const orthogonal_physical_design_stats& st) noexcept } template -void check_ortho_planar(const Ntk& ntk) +static void check_ortho_planar(const Ntk& ntk) { using gate_lyt = fiction::gate_level_layout>>>; From f0eb038ddca980063caa3ef8dc6218edcdd394b7 Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 28 Oct 2025 16:05:01 +0100 Subject: [PATCH 42/67] :art: clang-tidy --- .../fiction/algorithms/physical_design/orthogonal_planar.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp index 39c580a584..c6be14296f 100644 --- a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp +++ b/include/fiction/algorithms/physical_design/orthogonal_planar.hpp @@ -975,7 +975,7 @@ class orthogonal_planar_impl }); #if (PROGRESS_BARS) // update progress - bar(i); + bar(lvl); #endif } From b12e84c292b5f9cddaaa5cb75403e2dfc77da16e Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 28 Oct 2025 16:13:39 +0100 Subject: [PATCH 43/67] :art: clang-tidy --- .../network_transformation/node_duplication_planarization.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/algorithms/network_transformation/node_duplication_planarization.cpp b/test/algorithms/network_transformation/node_duplication_planarization.cpp index d27b6c66d0..d6002af15b 100644 --- a/test/algorithms/network_transformation/node_duplication_planarization.cpp +++ b/test/algorithms/network_transformation/node_duplication_planarization.cpp @@ -108,7 +108,7 @@ TEST_CASE("Planarize ntk with 3-ary node", "[node-duplication-planarization]") node_duplication_planarization_params params; params.po_order = node_duplication_planarization_params::output_order::RANDOM_PO_ORDER; - auto planarized_ntk = node_duplication_planarization(vpi_r); + auto planarized_ntk = node_duplication_planarization(vpi_r, params); mincross_stats st_min{}; mincross_params p_min{}; From 7abfccc3b33681d0e56e4694a2dc620d8602c65b Mon Sep 17 00:00:00 2001 From: benjamin Date: Fri, 21 Nov 2025 13:26:51 +0100 Subject: [PATCH 44/67] :heavy_plus_sign: commands added --- .../physical_design/cmd_physical_design.hpp | 2 + cli/cmd/physical_design/include/plane.hpp | 44 +++++++ cli/cmd/physical_design/src/plane.cpp | 120 ++++++++++++++++++ docs/changelog.rst | 6 + .../node_duplication_planarization.cpp | 49 ------- 5 files changed, 172 insertions(+), 49 deletions(-) create mode 100644 cli/cmd/physical_design/include/plane.hpp create mode 100644 cli/cmd/physical_design/src/plane.cpp diff --git a/cli/cmd/physical_design/cmd_physical_design.hpp b/cli/cmd/physical_design/cmd_physical_design.hpp index 8449d39661..f6422598cd 100644 --- a/cli/cmd/physical_design/cmd_physical_design.hpp +++ b/cli/cmd/physical_design/cmd_physical_design.hpp @@ -18,6 +18,7 @@ #endif #include "include/optimize.hpp" #include "include/ortho.hpp" +#include "include/plane.hpp" // NOLINTEND(misc-include-cleaner) namespace alice @@ -36,6 +37,7 @@ ALICE_ADD_COMMAND(onepass, FICTION_CLI_CATEGORY_PHYSICAL_DESIGN) #endif ALICE_ADD_COMMAND(optimize, FICTION_CLI_CATEGORY_PHYSICAL_DESIGN) ALICE_ADD_COMMAND(ortho, FICTION_CLI_CATEGORY_PHYSICAL_DESIGN) +ALICE_ADD_COMMAND(plane, FICTION_CLI_CATEGORY_PHYSICAL_DESIGN) } // namespace alice diff --git a/cli/cmd/physical_design/include/plane.hpp b/cli/cmd/physical_design/include/plane.hpp new file mode 100644 index 0000000000..22b4c79937 --- /dev/null +++ b/cli/cmd/physical_design/include/plane.hpp @@ -0,0 +1,44 @@ +// +// Created by benjamin on 21.11.25. +// + +#ifndef FICTION_CMD_PLANE_HPP +#define FICTION_CMD_PLANE_HPP + +#include +#include +#include +#include + +#include +#include + +#include + +namespace alice +{ + +class plane_command final : public command +{ + public: + explicit plane_command(const environment::ptr& e); + + protected: + void execute() override; + + // Optional: enable JSON logging like ortho + // nlohmann::json log() const override; + + private: + fiction::fanout_substitution_params fan_ps{}; + fiction::network_balancing_params bal_ps{}; + fiction::node_duplication_planarization_params dup_ps{}; + fiction::orthogonal_physical_design_stats orth_stats{}; + + uint32_t seed{0u}; + uint32_t po_order{0u}; +}; + +} // namespace alice + +#endif // FICTION_CMD_PLANE_HPP diff --git a/cli/cmd/physical_design/src/plane.cpp b/cli/cmd/physical_design/src/plane.cpp new file mode 100644 index 0000000000..65bdf791c7 --- /dev/null +++ b/cli/cmd/physical_design/src/plane.cpp @@ -0,0 +1,120 @@ +// +// Created by benjamin on 21.11.25. +// + +#include "cmd/physical_design/include/plane.hpp" + +#include "stores.hpp" // NOLINT(misc-include-cleaner) + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace alice +{ + +plane_command::plane_command(const environment::ptr& e) : + command(e, "Performs fanout substitution, path balancing, planarization (node duplication), " + "and produces an orthogonal planar layout.") +{ + // fanout substitution options + add_option("--degree,-d", fan_ps.degree, "Maximum number of outputs a fan-out node can have", true) + ->set_type_name("{2, 3}"); + + add_option("--strategy,-s", fan_ps.strategy, + "Chain fan-outs in balanced tree (breadth=0), DFS tree (depth=1), or random (2)", true) + ->set_type_name("{0,1,2}"); + + add_option("--threshold,-t", fan_ps.threshold, + "Maximum number of outputs any gate can have before substitution applies", true); + + add_option("--seed,-r", seed, "Random seed used for random fanout substitution."); + + // balancing option + bal_ps.unify_outputs = true; + + // planarization option + add_option("--po-order, -p", po_order, "PO order: keep=0, random=1")->set_type_name("{0,1}"); +} + +void plane_command::execute() +{ + auto& ns = store(); + auto& ls = store(); + + if (ns.empty()) + { + env->out() << "[w] no logic network in store\n"; + return; + } + + if (is_set("seed")) + { + fan_ps.seed = seed; + } + + using po_enum = decltype(dup_ps.po_order); + switch (po_order) + { + case 0u: dup_ps.po_order = po_enum::KEEP_PO_ORDER; break; + case 1u: dup_ps.po_order = po_enum::RANDOM_PO_ORDER; break; + default: + env->out() << "[w] invalid --po-order, defaulting to keep\n"; + dup_ps.po_order = po_enum::KEEP_PO_ORDER; + break; + } + + const auto perform_fanouts_and_balance = [this](auto&& ntk_ptr) + { + auto tec_f = fiction::fanout_substitution(*ntk_ptr, fan_ps); + return std::make_shared(fiction::network_balancing(tec_f, bal_ps)); + }; + + auto tec_b = std::visit(perform_fanouts_and_balance, ns.current()); + + const fiction::mutable_rank_view vpi_r(*tec_b); + + auto planarized_ntk = fiction::node_duplication_planarization(vpi_r, dup_ps); + + try + { + ls.extend() = std::make_shared( + fiction::orthogonal_planar(planarized_ntk, {}, &orth_stats)); + + env->out() << fmt::format("[i] Layout generated: {} × {} | gates: {} | wires: {} | crossings: {}\n", + orth_stats.x_size, orth_stats.y_size, orth_stats.num_gates, orth_stats.num_wires, + orth_stats.num_crossings); + } + catch (const fiction::high_degree_fanin_exception& e) + { + env->out() << fmt::format("[e] {}\n", e.what()); + } + catch (const std::invalid_argument& e) + { + env->out() << fmt::format("[e] {}\n", e.what()); + } + catch (const std::exception& e) + { + env->out() << fmt::format("[e] unexpected error: {}\n", e.what()); + } + + fan_ps = {}; + bal_ps = {}; + dup_ps = {}; +} + +} // namespace alice diff --git a/docs/changelog.rst b/docs/changelog.rst index 9445f4c5d3..5e9ab6cb21 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -8,6 +8,12 @@ The format is based on `Keep a Changelog ` Unreleased ---------- +Added +##### +- Algorithms: + - Node Duplication Planarization + - PLANE: Planar Layout from Network Embedding + Changed ####### - Build system: diff --git a/test/algorithms/network_transformation/node_duplication_planarization.cpp b/test/algorithms/network_transformation/node_duplication_planarization.cpp index d6002af15b..c23f229de3 100644 --- a/test/algorithms/network_transformation/node_duplication_planarization.cpp +++ b/test/algorithms/network_transformation/node_duplication_planarization.cpp @@ -79,55 +79,6 @@ TEST_CASE("Planarize technology ntk", "[node-duplication-planarization]") // NOLINTEND(bugprone-unchecked-optional-access) } -TEST_CASE("Planarize ntk with 3-ary node", "[node-duplication-planarization]") -{ - technology_network tec{}; - - const auto x1 = tec.create_pi(); - const auto x2 = tec.create_pi(); - const auto x3 = tec.create_pi(); - const auto x4 = tec.create_pi(); - const auto x5 = tec.create_pi(); - const auto f1 = tec.create_not(x2); - const auto f2 = tec.create_nary_and({x1, x2, x3, x4}); - const auto f3 = tec.create_nary_or({x3, x4, x5}); - const auto f4 = tec.create_maj(x1, x2, f3); - tec.create_po(f1); - tec.create_po(f2); - tec.create_po(f3); - tec.create_po(f4); - - network_balancing_params ps; - ps.unify_outputs = true; - - const auto tec_b = fiction::network_balancing( - fiction::fanout_substitution(tec), ps); - - const auto vpi_r = fiction::mutable_rank_view(tec_b); - - node_duplication_planarization_params params; - params.po_order = node_duplication_planarization_params::output_order::RANDOM_PO_ORDER; - - auto planarized_ntk = node_duplication_planarization(vpi_r, params); - - mincross_stats st_min{}; - mincross_params p_min{}; - p_min.optimize = false; - - auto ntk = mincross(planarized_ntk, p_min, &st_min); // counts crossings - CHECK(st_min.num_crossings == 0); - - // clang-tidy bugprone-unchecked-optional-access false positive in tests - // NOLINTBEGIN(bugprone-unchecked-optional-access) - mockturtle::equivalence_checking_stats st; - const auto cec_m = - mockturtle::equivalence_checking(*fiction::virtual_miter(tec, planarized_ntk), {}, &st); - REQUIRE(cec_m.has_value()); - const auto result = *cec_m; - CHECK(result == 1); - // NOLINTEND(bugprone-unchecked-optional-access) -} - TEST_CASE("Buffer AIG and planarize technology_network", "[node-duplication-planarization]") { mockturtle::aig_network aig{}; From f93aa9aed4d19667a6830b729130c80dfc985282 Mon Sep 17 00:00:00 2001 From: benjamin Date: Fri, 21 Nov 2025 13:41:23 +0100 Subject: [PATCH 45/67] :art: clang-tidy --- cli/cmd/physical_design/src/plane.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/cmd/physical_design/src/plane.cpp b/cli/cmd/physical_design/src/plane.cpp index 65bdf791c7..3cda0a30f5 100644 --- a/cli/cmd/physical_design/src/plane.cpp +++ b/cli/cmd/physical_design/src/plane.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include From ba10e4e8a5cbc8f9a867d09f0521c573a1be441d Mon Sep 17 00:00:00 2001 From: benjamin Date: Mon, 24 Nov 2025 10:16:34 +0100 Subject: [PATCH 46/67] :pencil2: renaming --- cli/cmd/physical_design/src/plane.cpp | 2 +- ... planar_layout_from_network_embedding.hpp} | 17 ++++++----- ...planar_layout_from_network_embedding..cpp} | 30 +++++++++---------- 3 files changed, 25 insertions(+), 24 deletions(-) rename include/fiction/algorithms/physical_design/{orthogonal_planar.hpp => planar_layout_from_network_embedding.hpp} (98%) rename test/algorithms/physical_design/{orthogonal_planar.cpp => planar_layout_from_network_embedding..cpp} (80%) diff --git a/cli/cmd/physical_design/src/plane.cpp b/cli/cmd/physical_design/src/plane.cpp index 3cda0a30f5..aac5a2995b 100644 --- a/cli/cmd/physical_design/src/plane.cpp +++ b/cli/cmd/physical_design/src/plane.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp b/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp similarity index 98% rename from include/fiction/algorithms/physical_design/orthogonal_planar.hpp rename to include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp index c6be14296f..4fce31c777 100644 --- a/include/fiction/algorithms/physical_design/orthogonal_planar.hpp +++ b/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp @@ -2,8 +2,8 @@ // Created by benjamin on 21.01.25. // -#ifndef FICTION_ORTHOGONAL_PLANAR_HPP -#define FICTION_ORTHOGONAL_PLANAR_HPP +#ifndef FICTION_PLANAR_LAYOUT_FROM_NETWORK_EMBEDDING_HPP +#define FICTION_PLANAR_LAYOUT_FROM_NETWORK_EMBEDDING_HPP #include "fiction/algorithms/graph/mincross.hpp" #include "fiction/algorithms/physical_design/orthogonal.hpp" @@ -272,7 +272,7 @@ uint64_t calculate_predecessor_gap(const Ntk& ntk, const mockturtle::node_map>(node2pos[pre_neighbour]); assert(pre1_t.y > pre2_t.y); - return std::min(pre1_t.y - pre2_t.y - 1, 2); + return std::min(pre1_t.y - pre2_t.y - 1, 2); } /** @@ -794,10 +794,10 @@ compute_wiring(const Ntk& ntk, const mockturtle::node_map -class orthogonal_planar_impl +class plane_impl { public: - orthogonal_planar_impl(const Ntk& src, const orthogonal_physical_design_params& p, + plane_impl(const Ntk& src, const orthogonal_physical_design_params& p, orthogonal_physical_design_stats& st) : ntk{mockturtle::fanout_view{src}}, ps{p}, @@ -1054,6 +1054,7 @@ class orthogonal_planar_impl } // namespace detail /** + * This algorithm constructs a planar layout from a planar embedding of a logic network. Ehcne, the name "planar_layout form entwork embedding" (PLANE). * This algorithm performs a fully planar physical design flow for Field-Coupled Nanocomputing (FCN) circuits. It takes * as input a logic network with a planar embedding, represented as a `mutable_rank_view`, and preserves this embedding * during placement and routing. @@ -1070,7 +1071,7 @@ class orthogonal_planar_impl * @return A fully planar gate-level layout of type `Lyt`. */ template -Lyt orthogonal_planar(const Ntk& ntk, orthogonal_physical_design_params ps = {}, +Lyt plane(const Ntk& ntk, orthogonal_physical_design_params ps = {}, orthogonal_physical_design_stats* pst = nullptr) { static_assert(is_gate_level_layout_v, "Lyt is not a gate-level layout"); @@ -1096,7 +1097,7 @@ Lyt orthogonal_planar(const Ntk& ntk, orthogonal_physical_design_params ps = {}, } orthogonal_physical_design_stats st{}; - detail::orthogonal_planar_impl p{ntk, ps, st}; + detail::plane_impl p{ntk, ps, st}; auto result = p.run(); @@ -1109,4 +1110,4 @@ Lyt orthogonal_planar(const Ntk& ntk, orthogonal_physical_design_params ps = {}, } } // namespace fiction -#endif // FICTION_ORTHOGONAL_PLANAR_HPP +#endif // FICTION_PLANAR_LAYOUT_FROM_NETWORK_EMBEDDING_HPP diff --git a/test/algorithms/physical_design/orthogonal_planar.cpp b/test/algorithms/physical_design/planar_layout_from_network_embedding..cpp similarity index 80% rename from test/algorithms/physical_design/orthogonal_planar.cpp rename to test/algorithms/physical_design/planar_layout_from_network_embedding..cpp index bc63fcef8b..d55440bb06 100644 --- a/test/algorithms/physical_design/orthogonal_planar.cpp +++ b/test/algorithms/physical_design/planar_layout_from_network_embedding..cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include @@ -35,7 +35,7 @@ static void check_stats(const orthogonal_physical_design_stats& st) noexcept } template -static void check_ortho_planar(const Ntk& ntk) +static void check_plane(const Ntk& ntk) { using gate_lyt = fiction::gate_level_layout>>>; @@ -61,14 +61,14 @@ static void check_ortho_planar(const Ntk& ntk) orthogonal_physical_design_stats orthogonal_planar_stats{}; - const auto gate_level_layout = fiction::orthogonal_planar(planarized_b, {}, &orthogonal_planar_stats); + const auto gate_level_layout = fiction::plane(planarized_b, {}, &orthogonal_planar_stats); CHECK(gate_level_layout.num_crossings() == 0); check_stats(orthogonal_planar_stats); CHECK_NOTHROW(apply_gate_library(gate_level_layout)); } -TEST_CASE("Check exceptions", "[orthogonal-planar]") +TEST_CASE("Check exceptions", "[planar-layout-from-network-embedding]") { using gate_lyt = fiction::gate_level_layout>>>; @@ -78,33 +78,33 @@ TEST_CASE("Check exceptions", "[orthogonal-planar]") auto maj = blueprints::maj1_network(); auto maj_ranked = fiction::mutable_rank_view(maj); orthogonal_physical_design_stats orthogonal_planar_stats{}; - CHECK_THROWS_WITH(fiction::orthogonal_planar(maj_ranked, {}, &orthogonal_planar_stats), + CHECK_THROWS_WITH(fiction::plane(maj_ranked, {}, &orthogonal_planar_stats), "network contains nodes that exceed the supported fanin size"); auto ao = blueprints::and_or_network(); auto ao_ranked = fiction::mutable_rank_view(ao); - CHECK_THROWS_WITH(fiction::orthogonal_planar(ao_ranked, {}, &orthogonal_planar_stats), + CHECK_THROWS_WITH(fiction::plane(ao_ranked, {}, &orthogonal_planar_stats), "Input network has to be planar"); } -TEST_CASE("Orthogonal planar layout tests", "[orthogonal-planar]") +TEST_CASE("Orthogonal planar layout tests", "[planar-layout-from-network-embedding]") { // Simple correctness and corner-case networks - check_ortho_planar(blueprints::se_coloring_corner_case_network()); - check_ortho_planar(blueprints::fanout_substitution_corner_case_network()); - check_ortho_planar(blueprints::clpl()); + check_plane(blueprints::se_coloring_corner_case_network()); + check_plane(blueprints::fanout_substitution_corner_case_network()); + check_plane(blueprints::clpl()); // Network with constant inputs - check_ortho_planar(blueprints::unbalanced_and_inv_network()); + check_plane(blueprints::unbalanced_and_inv_network()); // Multi-output network - check_ortho_planar(blueprints::multi_output_network()); + check_plane(blueprints::multi_output_network()); // Parity network - check_ortho_planar(blueprints::parity_network()); + check_plane(blueprints::parity_network()); } -TEST_CASE("Name conservation after planar orthogonal physical design", "[orthogonal-planar]") +TEST_CASE("Name conservation after planar orthogonal physical design", "[planar-layout-from-network-embedding]") { using gate_lyt = gate_level_layout>>>; @@ -127,7 +127,7 @@ TEST_CASE("Name conservation after planar orthogonal physical design", "[orthogo orthogonal_physical_design_stats orthogonal_planar_stats{}; - const auto layout = fiction::orthogonal_planar(planarized_b, {}, &orthogonal_planar_stats); + const auto layout = fiction::plane(planarized_b, {}, &orthogonal_planar_stats); // network name CHECK(layout.get_layout_name() == "topolinano"); From 360cca558d82a438e347b731a9476055132dcacb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 09:17:12 +0000 Subject: [PATCH 47/67] =?UTF-8?q?=F0=9F=8E=A8=20Incorporated=20pre-commit?= =?UTF-8?q?=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../planar_layout_from_network_embedding.hpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp b/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp index 4fce31c777..fa44e001aa 100644 --- a/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp +++ b/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp @@ -797,8 +797,7 @@ template class plane_impl { public: - plane_impl(const Ntk& src, const orthogonal_physical_design_params& p, - orthogonal_physical_design_stats& st) : + plane_impl(const Ntk& src, const orthogonal_physical_design_params& p, orthogonal_physical_design_stats& st) : ntk{mockturtle::fanout_view{src}}, ps{p}, pst{st} @@ -1054,10 +1053,10 @@ class plane_impl } // namespace detail /** - * This algorithm constructs a planar layout from a planar embedding of a logic network. Ehcne, the name "planar_layout form entwork embedding" (PLANE). - * This algorithm performs a fully planar physical design flow for Field-Coupled Nanocomputing (FCN) circuits. It takes - * as input a logic network with a planar embedding, represented as a `mutable_rank_view`, and preserves this embedding - * during placement and routing. + * This algorithm constructs a planar layout from a planar embedding of a logic network. Ehcne, the name "planar_layout + * form entwork embedding" (PLANE). This algorithm performs a fully planar physical design flow for Field-Coupled + * Nanocomputing (FCN) circuits. It takes as input a logic network with a planar embedding, represented as a + * `mutable_rank_view`, and preserves this embedding during placement and routing. * * In this approach, each logic level of the network is mapped to a diagonal in the layout, while nodes within the same * level are placed according to their rank positions in the planar embedding. This ensures a crossing-free, scalable, @@ -1071,8 +1070,7 @@ class plane_impl * @return A fully planar gate-level layout of type `Lyt`. */ template -Lyt plane(const Ntk& ntk, orthogonal_physical_design_params ps = {}, - orthogonal_physical_design_stats* pst = nullptr) +Lyt plane(const Ntk& ntk, orthogonal_physical_design_params ps = {}, orthogonal_physical_design_stats* pst = nullptr) { static_assert(is_gate_level_layout_v, "Lyt is not a gate-level layout"); static_assert(mockturtle::is_network_type_v, @@ -1096,8 +1094,8 @@ Lyt plane(const Ntk& ntk, orthogonal_physical_design_params ps = {}, throw std::invalid_argument("Input network has to be planar"); } - orthogonal_physical_design_stats st{}; - detail::plane_impl p{ntk, ps, st}; + orthogonal_physical_design_stats st{}; + detail::plane_impl p{ntk, ps, st}; auto result = p.run(); From e948de210fa244f19fb00da490e67b4fa8b421cf Mon Sep 17 00:00:00 2001 From: benjamin Date: Mon, 24 Nov 2025 10:17:15 +0100 Subject: [PATCH 48/67] :pencil2: renaming --- cli/cmd/physical_design/src/plane.cpp | 2 +- .../planar_layout_from_network_embedding.hpp | 18 ++++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/cli/cmd/physical_design/src/plane.cpp b/cli/cmd/physical_design/src/plane.cpp index aac5a2995b..21e01084a8 100644 --- a/cli/cmd/physical_design/src/plane.cpp +++ b/cli/cmd/physical_design/src/plane.cpp @@ -94,7 +94,7 @@ void plane_command::execute() try { ls.extend() = std::make_shared( - fiction::orthogonal_planar(planarized_ntk, {}, &orth_stats)); + fiction::plane(planarized_ntk, {}, &orth_stats)); env->out() << fmt::format("[i] Layout generated: {} × {} | gates: {} | wires: {} | crossings: {}\n", orth_stats.x_size, orth_stats.y_size, orth_stats.num_gates, orth_stats.num_wires, diff --git a/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp b/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp index 4fce31c777..fa44e001aa 100644 --- a/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp +++ b/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp @@ -797,8 +797,7 @@ template class plane_impl { public: - plane_impl(const Ntk& src, const orthogonal_physical_design_params& p, - orthogonal_physical_design_stats& st) : + plane_impl(const Ntk& src, const orthogonal_physical_design_params& p, orthogonal_physical_design_stats& st) : ntk{mockturtle::fanout_view{src}}, ps{p}, pst{st} @@ -1054,10 +1053,10 @@ class plane_impl } // namespace detail /** - * This algorithm constructs a planar layout from a planar embedding of a logic network. Ehcne, the name "planar_layout form entwork embedding" (PLANE). - * This algorithm performs a fully planar physical design flow for Field-Coupled Nanocomputing (FCN) circuits. It takes - * as input a logic network with a planar embedding, represented as a `mutable_rank_view`, and preserves this embedding - * during placement and routing. + * This algorithm constructs a planar layout from a planar embedding of a logic network. Ehcne, the name "planar_layout + * form entwork embedding" (PLANE). This algorithm performs a fully planar physical design flow for Field-Coupled + * Nanocomputing (FCN) circuits. It takes as input a logic network with a planar embedding, represented as a + * `mutable_rank_view`, and preserves this embedding during placement and routing. * * In this approach, each logic level of the network is mapped to a diagonal in the layout, while nodes within the same * level are placed according to their rank positions in the planar embedding. This ensures a crossing-free, scalable, @@ -1071,8 +1070,7 @@ class plane_impl * @return A fully planar gate-level layout of type `Lyt`. */ template -Lyt plane(const Ntk& ntk, orthogonal_physical_design_params ps = {}, - orthogonal_physical_design_stats* pst = nullptr) +Lyt plane(const Ntk& ntk, orthogonal_physical_design_params ps = {}, orthogonal_physical_design_stats* pst = nullptr) { static_assert(is_gate_level_layout_v, "Lyt is not a gate-level layout"); static_assert(mockturtle::is_network_type_v, @@ -1096,8 +1094,8 @@ Lyt plane(const Ntk& ntk, orthogonal_physical_design_params ps = {}, throw std::invalid_argument("Input network has to be planar"); } - orthogonal_physical_design_stats st{}; - detail::plane_impl p{ntk, ps, st}; + orthogonal_physical_design_stats st{}; + detail::plane_impl p{ntk, ps, st}; auto result = p.run(); From d56ede7c091d63ebd0ec7ea0ddb564180932e820 Mon Sep 17 00:00:00 2001 From: benjamin Date: Mon, 24 Nov 2025 10:35:11 +0100 Subject: [PATCH 49/67] :pencil2: renaming --- .../planar_layout_from_network_embedding.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp b/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp index fa44e001aa..2ebc6d2021 100644 --- a/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp +++ b/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp @@ -1053,10 +1053,10 @@ class plane_impl } // namespace detail /** - * This algorithm constructs a planar layout from a planar embedding of a logic network. Ehcne, the name "planar_layout - * form entwork embedding" (PLANE). This algorithm performs a fully planar physical design flow for Field-Coupled - * Nanocomputing (FCN) circuits. It takes as input a logic network with a planar embedding, represented as a - * `mutable_rank_view`, and preserves this embedding during placement and routing. + * This algorithm constructs a planar layout from the planar embedding of a logic network, forming the Planar Layout + * from Network Embedding (PLANE) methodology. It provides a fully planar physical design flow for Field-Coupled + * Nanocomputing (FCN) circuits. The algorithm operates on a logic network with an existing planar embedding, + * represented as a `mutable_rank_view`, and preserves this embedding during placement and routing. * * In this approach, each logic level of the network is mapped to a diagonal in the layout, while nodes within the same * level are placed according to their rank positions in the planar embedding. This ensures a crossing-free, scalable, From 342a7bdc04974c7d233e14310bb8b1d10538fab5 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 24 Nov 2025 09:36:26 +0000 Subject: [PATCH 50/67] :memo: Update pyfiction docstrings Signed-off-by: GitHub Actions --- .../pyfiction/pybind11_mkdoc_docstrings.hpp | 139 +++++++++--------- 1 file changed, 71 insertions(+), 68 deletions(-) diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index fd2baf67f8..b9b891a792 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -10006,44 +10006,6 @@ static const char *__doc_fiction_detail_orthogonal_impl_pst = R"doc()doc"; static const char *__doc_fiction_detail_orthogonal_impl_run = R"doc()doc"; -static const char *__doc_fiction_detail_orthogonal_planar_impl = -R"doc(Implements the general planar layout generation algorithm. - -The algorithm performs placement and routing level by level, starting -from the primary inputs and proceeding toward the outputs. For each -level, node placement depends on the orientation and excess wiring -computed in auxiliary routines such as `compute_pr_variables` and -`compute_wiring`. Nodes are positioned according to their fan-in -structure and routing constraints to ensure planarity of the resulting -layout. - -Template parameter ``Lyt``: - Gate-level layout type. - -Template parameter ``Ntk``: - Logic network type. - -Parameter ``src``: - Source network to be placed and routed. - -Parameter ``p``: - Parameters controlling layout generation and clocking. - -Parameter ``st``: - Statistics object used to collect runtime and layout information.)doc"; - -static const char *__doc_fiction_detail_orthogonal_planar_impl_ntk = R"doc()doc"; - -static const char *__doc_fiction_detail_orthogonal_planar_impl_orthogonal_planar_impl = R"doc()doc"; - -static const char *__doc_fiction_detail_orthogonal_planar_impl_po_counter = R"doc()doc"; - -static const char *__doc_fiction_detail_orthogonal_planar_impl_ps = R"doc()doc"; - -static const char *__doc_fiction_detail_orthogonal_planar_impl_pst = R"doc()doc"; - -static const char *__doc_fiction_detail_orthogonal_planar_impl_run = R"doc()doc"; - static const char *__doc_fiction_detail_physical_population_stability_impl = R"doc(This class implements the simulation of the population stability for a given SiDB layout. It determines the minimum electrostatic potential @@ -10221,6 +10183,44 @@ static const char *__doc_fiction_detail_placement_info_current_po = R"doc(The in static const char *__doc_fiction_detail_placement_info_node2pos = R"doc(Mapping of nodes to their positions in the layout.)doc"; +static const char *__doc_fiction_detail_plane_impl = +R"doc(Implements the general planar layout generation algorithm. + +The algorithm performs placement and routing level by level, starting +from the primary inputs and proceeding toward the outputs. For each +level, node placement depends on the orientation and excess wiring +computed in auxiliary routines such as `compute_pr_variables` and +`compute_wiring`. Nodes are positioned according to their fan-in +structure and routing constraints to ensure planarity of the resulting +layout. + +Template parameter ``Lyt``: + Gate-level layout type. + +Template parameter ``Ntk``: + Logic network type. + +Parameter ``src``: + Source network to be placed and routed. + +Parameter ``p``: + Parameters controlling layout generation and clocking. + +Parameter ``st``: + Statistics object used to collect runtime and layout information.)doc"; + +static const char *__doc_fiction_detail_plane_impl_ntk = R"doc()doc"; + +static const char *__doc_fiction_detail_plane_impl_plane_impl = R"doc()doc"; + +static const char *__doc_fiction_detail_plane_impl_po_counter = R"doc()doc"; + +static const char *__doc_fiction_detail_plane_impl_ps = R"doc()doc"; + +static const char *__doc_fiction_detail_plane_impl_pst = R"doc()doc"; + +static const char *__doc_fiction_detail_plane_impl_run = R"doc()doc"; + static const char *__doc_fiction_detail_post_layout_optimization_impl = R"doc()doc"; static const char *__doc_fiction_detail_post_layout_optimization_impl_add_fanin_to_route = @@ -18428,36 +18428,6 @@ static const char *__doc_fiction_orthogonal_physical_design_stats_x_size = R"doc static const char *__doc_fiction_orthogonal_physical_design_stats_y_size = R"doc()doc"; -static const char *__doc_fiction_orthogonal_planar = -R"doc(This algorithm performs a fully planar physical design flow for Field- -Coupled Nanocomputing (FCN) circuits. It takes as input a logic -network with a planar embedding, represented as a `mutable_rank_view`, -and preserves this embedding during placement and routing. - -In this approach, each logic level of the network is mapped to a -diagonal in the layout, while nodes within the same level are placed -according to their rank positions in the planar embedding. This -ensures a crossing-free, scalable, and layout-consistent mapping from -logic to physical design. - -Template parameter ``Lyt``: - Gate-level layout type. - -Template parameter ``Ntk``: - Logic network type. - -Parameter ``ntk``: - Planar logic network to be placed and routed. - -Parameter ``ps``: - Configuration parameters for the physical design process. - -Parameter ``pst``: - Optional statistics object to collect runtime and layout metrics. - -Returns: - A fully planar gate-level layout of type `Lyt`.)doc"; - static const char *__doc_fiction_out_of_cell_names_exception = R"doc()doc"; static const char *__doc_fiction_out_of_cell_names_exception_out_of_cell_names_exception = R"doc()doc"; @@ -18864,6 +18834,39 @@ Parameter ``node2pos``: Returns: Signal to the newly placed gate in `lyt`.)doc"; +static const char *__doc_fiction_plane = +R"doc(This algorithm constructs a planar layout from the planar embedding of +a logic network, forming the Planar Layout from Network Embedding +(PLANE) methodology. It provides a fully planar physical design flow +for Field-Coupled Nanocomputing (FCN) circuits. The algorithm operates +on a logic network with an existing planar embedding, represented as a +`mutable_rank_view`, and preserves this embedding during placement and +routing. + +In this approach, each logic level of the network is mapped to a +diagonal in the layout, while nodes within the same level are placed +according to their rank positions in the planar embedding. This +ensures a crossing-free, scalable, and layout-consistent mapping from +logic to physical design. + +Template parameter ``Lyt``: + Gate-level layout type. + +Template parameter ``Ntk``: + Logic network type. + +Parameter ``ntk``: + Planar logic network to be placed and routed. + +Parameter ``ps``: + Configuration parameters for the physical design process. + +Parameter ``pst``: + Optional statistics object to collect runtime and layout metrics. + +Returns: + A fully planar gate-level layout of type `Lyt`.)doc"; + static const char *__doc_fiction_pointy_top_hex = R"doc(\verbatim / \ / \ | | | | \ / \ / \endverbatim)doc"; static const char *__doc_fiction_population_stability_information = From 33973c9704e132609c71979ae162cc9adabde08d Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 9 Dec 2025 13:35:55 +0100 Subject: [PATCH 51/67] :art: added small changes --- cli/cmd/physical_design/include/plane.hpp | 55 +++++++-- cli/cmd/physical_design/src/plane.cpp | 67 +++++++---- docs/changelog.rst | 4 +- .../node_duplication_planarization.hpp | 112 +++++++++--------- .../planar_layout_from_network_embedding.hpp | 84 ++++++++++++- .../planar_layout_from_network_embedding..cpp | 25 ++-- 6 files changed, 236 insertions(+), 111 deletions(-) diff --git a/cli/cmd/physical_design/include/plane.hpp b/cli/cmd/physical_design/include/plane.hpp index 22b4c79937..04f3d13baf 100644 --- a/cli/cmd/physical_design/include/plane.hpp +++ b/cli/cmd/physical_design/include/plane.hpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include @@ -17,25 +17,62 @@ namespace alice { - +/** + * Executes a physical design approach using a planar embedding of a logic network. + * + * See include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp for more details. + */ class plane_command final : public command { public: + /** + * Standard constructor. Adds descriptive information, options, and flags. + * + * @param e alice::environment that specifies stores etc. + */ explicit plane_command(const environment::ptr& e); protected: + /** + * Function to perform the physical design call. Generates a placed and routed FCN gate layout. + */ void execute() override; - // Optional: enable JSON logging like ortho - // nlohmann::json log() const override; + /** + * Logs the resulting information in a log file. + * + * @return JSON object containing information about the physical design process. + */ + nlohmann::json log() const override; private: - fiction::fanout_substitution_params fan_ps{}; - fiction::network_balancing_params bal_ps{}; + /** + * Parameters for fanout substitution. + */ + fiction::fanout_substitution_params fan_ps{}; + /** + * Parameters for network path balancing. + */ + fiction::network_balancing_params bal_ps{}; + /** + * Parameters for planarization via node duplication. + */ fiction::node_duplication_planarization_params dup_ps{}; - fiction::orthogonal_physical_design_stats orth_stats{}; - - uint32_t seed{0u}; + /** + * Parameters for planar layout generation from the network embedding. + */ + fiction::planar_layout_from_network_embedding_params ps{}; + /** + * Statistics collected during planar layout generation. + */ + fiction::planar_layout_from_network_embedding_stats st{}; + /** + * Number of clock phases used in the layout. + */ + uint8_t num_clock_phases = 4u; + /** + * Determines whether primary output order is kept (0) or randomized (1). + */ uint32_t po_order{0u}; }; diff --git a/cli/cmd/physical_design/src/plane.cpp b/cli/cmd/physical_design/src/plane.cpp index 21e01084a8..6185cecdac 100644 --- a/cli/cmd/physical_design/src/plane.cpp +++ b/cli/cmd/physical_design/src/plane.cpp @@ -9,14 +9,15 @@ #include #include #include -#include #include +#include #include #include #include #include #include +#include #include #include @@ -29,27 +30,20 @@ namespace alice { plane_command::plane_command(const environment::ptr& e) : - command(e, "Performs fanout substitution, path balancing, planarization (node duplication), " - "and produces an orthogonal planar layout.") + command(e, "First, a planar embedding is obtained by performing fanout substitution, path balancing, and " + "planarization (node duplication). Then, scalable placement and routing are carried out on the " + "planar layout of the current logic network in store. Compared to the gold algorithm, the " + "resulting layout may occupy more area, but it is generated in reasonable runtime.") { // fanout substitution options - add_option("--degree,-d", fan_ps.degree, "Maximum number of outputs a fan-out node can have", true) - ->set_type_name("{2, 3}"); - - add_option("--strategy,-s", fan_ps.strategy, - "Chain fan-outs in balanced tree (breadth=0), DFS tree (depth=1), or random (2)", true) - ->set_type_name("{0,1,2}"); - - add_option("--threshold,-t", fan_ps.threshold, - "Maximum number of outputs any gate can have before substitution applies", true); - - add_option("--seed,-r", seed, "Random seed used for random fanout substitution."); - + fan_ps.degree = 2; // balancing option bal_ps.unify_outputs = true; - // planarization option add_option("--po-order, -p", po_order, "PO order: keep=0, random=1")->set_type_name("{0,1}"); + // plane options + add_option("--clock_numbers,-n", num_clock_phases, "Number of clock phases to be used {3 or 4}"); + add_flag("--verbose,-v", ps.verbose, "Be verbose"); } void plane_command::execute() @@ -62,21 +56,37 @@ void plane_command::execute() env->out() << "[w] no logic network in store\n"; return; } - - if (is_set("seed")) + // error case: phases out of range + if (num_clock_phases != 3u && num_clock_phases != 4u) { - fan_ps.seed = seed; + env->out() << "[e] only 3- and 4-phase clocking schemes are supported\n"; + ps = {}; + return; } + ps.number_of_clock_phases = num_clock_phases == 3 ? fiction::num_clks::THREE : fiction::num_clks::FOUR; + + using po_enum = decltype(dup_ps.po_order); using po_enum = decltype(dup_ps.po_order); + switch (po_order) { - case 0u: dup_ps.po_order = po_enum::KEEP_PO_ORDER; break; - case 1u: dup_ps.po_order = po_enum::RANDOM_PO_ORDER; break; + case 0u: + { + dup_ps.po_order = po_enum::KEEP_PO_ORDER; + break; + } + case 1u: + { + dup_ps.po_order = po_enum::RANDOM_PO_ORDER; + break; + } default: + { env->out() << "[w] invalid --po-order, defaulting to keep\n"; dup_ps.po_order = po_enum::KEEP_PO_ORDER; break; + } } const auto perform_fanouts_and_balance = [this](auto&& ntk_ptr) @@ -94,11 +104,7 @@ void plane_command::execute() try { ls.extend() = std::make_shared( - fiction::plane(planarized_ntk, {}, &orth_stats)); - - env->out() << fmt::format("[i] Layout generated: {} × {} | gates: {} | wires: {} | crossings: {}\n", - orth_stats.x_size, orth_stats.y_size, orth_stats.num_gates, orth_stats.num_wires, - orth_stats.num_crossings); + fiction::plane(planarized_ntk, ps, &st)); } catch (const fiction::high_degree_fanin_exception& e) { @@ -118,4 +124,13 @@ void plane_command::execute() dup_ps = {}; } +nlohmann::json plane_command::log() const +{ + return nlohmann::json{{"runtime in seconds", mockturtle::to_seconds(st.time_total)}, + {"number of gates", st.num_gates}, + {"number of wires", st.num_wires}, + {"number of crossings", st.num_crossings}, + {"layout", {{"x-size", st.x_size}, {"y-size", st.y_size}, {"area", st.x_size * st.y_size}}}}; +} + } // namespace alice diff --git a/docs/changelog.rst b/docs/changelog.rst index 5e9ab6cb21..85bfeeea50 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -11,8 +11,8 @@ Unreleased Added ##### - Algorithms: - - Node Duplication Planarization - - PLANE: Planar Layout from Network Embedding + - Node Duplication Planarization, reimplemented from “Fabricatable Interconnect and Molecular QCA Circuits” by Amitabh Chaudhary, Danny Ziyi Chen, Xiaobo Sharon Hu, Michael T. Niemier, Ramprasad Ravichandran, and Kevin Whitton. + - Planar Layout from Network Embedding (PLANE) for generating planar 2DDWave-clocked Cartesian gate-level layouts in a fast and scalable fashion. Changed ####### diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index 458f1b90b2..df2f544ef6 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -33,8 +33,7 @@ namespace fiction struct node_duplication_planarization_params { /** - * The output order determines the starting layer for this algorithm. If this option is turned off, the output order - * remains the same as in the provided network. If it is turned on, the outputs are ordered randomly. + * Controls how output nodes are ordered before starting the algorithm. */ enum class output_order : uint8_t { @@ -47,6 +46,9 @@ struct node_duplication_planarization_params */ RANDOM_PO_ORDER }; + /** + * The output order used. Defaults to KEEP_PO_ORDER. + */ output_order po_order = output_order::KEEP_PO_ORDER; }; @@ -54,53 +56,51 @@ namespace detail { /** - * A structure representing a pair of nodes in an H-graph. + * Represents one node in the H-graph used for crossing minimization. * - * The nodes stored in this struct describe the fanin-edges of a node in an H-graph. - * A node pair object holds two nodes, which are saved in the member 'pair'. - * These two outer nodes are connected through zero or more 'middle_nodes'. - * The fanin order starts with the first node in 'pair', then proceeds through the 'middle_nodes', and ends with the - * second node in 'pair'. The order of 'middle_nodes' is arbitrary as they cannot be further connected to any other - * nodes. + * For a node in level l of the input network, all possible orderings of its fanins from layer l−1 are enumerated. + * Each such ordering is represented by an H-graph node. The first and last fanins of the ordering are stored, since + * these determine the delay in the H-graph. The remaining fanins are placed in middle. Their mutual order is irrelevant + * for this algorithm. * - * @tparam Ntk Network type for the nodes in the pair. + * @tparam Ntk Network type from which node types are drawn. */ template -struct node_pair +struct hgraph_node { /** - * Defines the beginning and end of the fanin-edged node. + * First and last fanin. */ - std::pair, mockturtle::node> pair; + std::pair, mockturtle::node> outer_fanins; /** - * Specifies the delay value for the node. + * All remaining fanins. */ - uint64_t delay; + std::vector> middle_fanins; /** - * Contains the nodes between the fanin-edges node; cannot be connected to any other node. + * Specifies the delay value for the hgraph_node. */ - std::vector> middle_nodes; + uint64_t delay; /** - * Index into the previous level's node_pair vector, used to track fanin-edge alignment. + * Index of the predecessor H-graph node. */ std::size_t fanin_it{}; /** - * Standard constructor. + * Constructs an H-graph node with given first and last fanins and delay. * - * @param node1 The first node of the fanin-edged node. - * @param node2 The second node of the fanin-edged node. + * @param first The first (leftmost) fanin in the ordering. + * @param last The last (rightmost) fanin in the ordering. * @param delay_value The delay value for the node. */ - node_pair(mockturtle::node node1, mockturtle::node node2, uint64_t delay_value) : - pair(node1, node2), + hgraph_node(const mockturtle::node& first, const mockturtle::node& last, const uint64_t delay_value) : + outer_fanins(first, last), delay(delay_value) {} }; /** - * Variant of the mockturtle::initialize_copy_network. This function helps with creating new networks from old + * Variant of the `mockturtle::initialize_copy_network`. This function helps with creating new networks from old * networks. In the mockturtle/original version `old2new` is used to map nodes from the old network to nodes in the new - * network in a one to one relation. This variant allows old nodes to map to multiple nodes in order to represent + * network in a one-to-one relation. This variant allows old nodes to map to multiple nodes in order to represent * relations to dulicated nodes. * A map (old2new) is created where old nodes from source network are mapped to new nodes in destination network. @@ -173,11 +173,11 @@ gather_fanin_signals(const Ntk& ntk, NtkDest& ntk_dest_v, const mockturtle::node assert(node_index < lvl.size() && "The fanin iterator is out of scope"); // The range indicates the number of candidate fan-ins. - const std::size_t range = ntk.fanin_size(n) + 1; + const std::size_t max_candidates = ntk.fanin_size(n) + 1; // Iterate through the candidate fan-ins. If a candidate fan-in matches the original fan-in or is a // duplicate of it, add it to the children of the node n. - const std::size_t end_index = std::min(node_index + range, lvl.size()); + const std::size_t end_index = std::min(node_index + max_candidates, lvl.size()); for (auto j = node_index; j < end_index; ++j) { // get the node from the newly generated network. @@ -334,15 +334,15 @@ virtual_pi_network create_virtual_pi_ntk_from_duplicated_nodes( * @return The vector of node pairs. */ template -[[nodiscard]] std::vector> calculate_pairs(const std::vector>& nodes) noexcept +[[nodiscard]] std::vector> calculate_pairs(const std::vector>& nodes) noexcept { - std::vector> pairwise_combinations{}; + std::vector> pairwise_combinations{}; pairwise_combinations.reserve(nodes.size() * (nodes.size() - 1)); if (nodes.size() == 1) { - const node_pair pair = {nodes[0], nodes[0], - std::numeric_limits::max()}; // Initialize delay to inf + const hgraph_node pair = {nodes[0], nodes[0], + std::numeric_limits::max()}; // Initialize delay to inf pairwise_combinations.push_back(pair); return pairwise_combinations; } @@ -351,24 +351,24 @@ template { for (auto it2 = it1 + 1; it2 != nodes.cend(); ++it2) { - std::vector> middle_nodes{}; - middle_nodes.reserve(nodes.size() - 2); + std::vector> middle_fanins{}; + middle_fanins.reserve(nodes.size() - 2); - // fill middle_nodes with non-pair members + // fill middle_fanins with non-pair members for (auto it = nodes.cbegin(); it != nodes.cend(); ++it) { if (it != it1 && it != it2) { - middle_nodes.push_back(*it); + middle_fanins.push_back(*it); } } - node_pair pair1 = {*it1, *it2, std::numeric_limits::max()}; // Initialize delay to inf - node_pair pair2 = {*it2, *it1, std::numeric_limits::max()}; // Initialize delay to inf + hgraph_node pair1 = {*it1, *it2, std::numeric_limits::max()}; // Initialize delay to inf + hgraph_node pair2 = {*it2, *it1, std::numeric_limits::max()}; // Initialize delay to inf - // Add middle_nodes to pairs - pair1.middle_nodes = middle_nodes; - pair2.middle_nodes = middle_nodes; + // Add middle_fanins to pairs + pair1.middle_fanins = middle_fanins; + pair2.middle_fanins = middle_fanins; pairwise_combinations.push_back(pair1); pairwise_combinations.push_back(pair2); @@ -437,7 +437,7 @@ class node_duplication_planarization_impl if (!lvl_pairs.empty()) { - std::vector>* combinations_last = &lvl_pairs.back(); + std::vector>* combinations_last = &lvl_pairs.back(); for (std::size_t cur_idx = 0; cur_idx < combinations.size(); ++cur_idx) { @@ -448,7 +448,7 @@ class node_duplication_planarization_impl auto& node_pair_last = (*combinations_last)[last_idx]; // If there is a connection between the two node pairs the delay is calculated like this - if ((node_pair_cur.pair.first == node_pair_last.pair.second && + if ((node_pair_cur.outer_fanins.first == node_pair_last.outer_fanins.second && node_pair_last.delay + 1 < node_pair_cur.delay)) { node_pair_cur.fanin_it = last_idx; @@ -463,11 +463,11 @@ class node_duplication_planarization_impl else if (node_pair_last.delay + 2 == node_pair_cur.delay) { // This solves equal path delays, if they are connected in the next layer via a fanout - const auto fc0 = fanins(ntk, node_pair_last.pair.first); + const auto fc0 = fanins(ntk, node_pair_last.outer_fanins.first); if (node_pair_last.fanin_it < combinations_last->size()) { const auto& parent_pair = (*combinations_last)[node_pair_last.fanin_it]; - const auto fc1 = fanins(ntk, parent_pair.pair.second); + const auto fc1 = fanins(ntk, parent_pair.outer_fanins.second); for (const auto f0 : fc0.fanin_nodes) { @@ -552,23 +552,23 @@ class node_duplication_planarization_impl // Select the path with the least delay and follow it via fanin relations const auto minimum_it = std::min_element(combinations.cbegin(), combinations.cend(), - [](const node_pair& a, const node_pair& b) { return a.delay < b.delay; }); + [](const hgraph_node& a, const hgraph_node& b) { return a.delay < b.delay; }); if (minimum_it != combinations.cend()) { const auto& min_combination = *minimum_it; // Insert the terminal node - insert_if_not_first(min_combination.pair.second, next_level, saturated_fanout_flag, 0); + insert_if_not_first(min_combination.outer_fanins.second, next_level, saturated_fanout_flag, 0); - // Insert middle_nodes - for (const auto& node : min_combination.middle_nodes) + // Insert middle_fanins + for (const auto& node : min_combination.middle_fanins) { insert_if_not_first(node, next_level, saturated_fanout_flag, 1); } // Insert the first node - insert_if_not_first(min_combination.pair.first, next_level, saturated_fanout_flag, 1); + insert_if_not_first(min_combination.outer_fanins.first, next_level, saturated_fanout_flag, 1); // Start with index instead of pointer std::size_t level = lvl_pairs.size() - 1; @@ -580,20 +580,20 @@ class node_duplication_planarization_impl const auto& fanin_combination = lvl_pairs[level - 1][fanin_it]; // Insert the terminal node - if (ntk.is_pi(fanin_combination.pair.second)) + if (ntk.is_pi(fanin_combination.outer_fanins.second)) { saturated_fanout_flag = 1; } - insert_if_not_first(fanin_combination.pair.second, next_level, saturated_fanout_flag, 0); + insert_if_not_first(fanin_combination.outer_fanins.second, next_level, saturated_fanout_flag, 0); - // Insert middle_nodes - for (const auto& node : fanin_combination.middle_nodes) + // Insert middle_fanins + for (const auto& node : fanin_combination.middle_fanins) { insert_if_not_first(node, next_level, saturated_fanout_flag, 1); } // Insert the first node - insert_if_not_first(fanin_combination.pair.first, next_level, saturated_fanout_flag, 1); + insert_if_not_first(fanin_combination.outer_fanins.first, next_level, saturated_fanout_flag, 1); // Move one level up --level; @@ -723,7 +723,7 @@ class node_duplication_planarization_impl /* * The currently node_pairs used in the current level. */ - std::vector>> lvl_pairs{}; + std::vector>> lvl_pairs{}; /* * The fanin nodes. */ @@ -743,7 +743,7 @@ class node_duplication_planarization_impl /** * Implements a planarization mechanism for networks from the paper \"Fabricatable Interconnect and Molecular QCA * Circuits\" by Amitabh Chaudhary, Danny Ziyi Chen, Xiaobo Sharon Hu, Michael T. Niemier, Ramprasad Ravichandran and - * Kevin Whitton in IEEE TRANSACTIONS ON COMPUTER-AIDED DESIGN OF INTEGRATED CIRCUITS AND SYSTEMS, Volume 26, 2007. + * Kevin Whitton in IEEE Transactions on Computer-Aided Design of Integrated Circuits and Systems, Volume 26, 2007. * * The planarization achieved by this function solves the Node Duplication Crossing Minimization (NDCE) problem by * finding the shortest x-y path in the H-graph for every level in the network. An H-graph describes edge relations diff --git a/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp b/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp index 2ebc6d2021..084dc426bb 100644 --- a/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp +++ b/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp @@ -38,6 +38,64 @@ namespace fiction { +/** + * Parameters for the planar layout from network embedding algorithm. + */ +struct planar_layout_from_network_embedding_params +{ + /** + * Number of clock phases to use. 3 and 4 are supported. + */ + num_clks number_of_clock_phases = num_clks::FOUR; + /** + * Verbosity. + */ + bool verbose = false; +}; +/** + * This struct stores statistics about the planar layout design process. + */ +struct planar_layout_from_network_embedding_stats +{ + /** + * Runtime of the planar layout design process. + */ + mockturtle::stopwatch<>::duration time_total{}; + /** + * Layout width. + */ + uint64_t x_size{0ull}; + /** + * Layout height. + */ + uint64_t y_size{0ull}; + /** + * Number of gates. + */ + uint64_t num_gates{0ull}; + /** + * Number of wires. + */ + uint64_t num_wires{0ull}; + /** + * Number of crossings. + */ + uint64_t num_crossings{0ull}; + /** + * Reports the statistics to the given output stream. + * + * @param out Output stream. + */ + void report(std::ostream& out = std::cout) const + { + out << fmt::format("[i] total time = {:.2f} secs\n", mockturtle::to_seconds(time_total)); + out << fmt::format("[i] layout size = {} × {}\n", x_size, y_size); + out << fmt::format("[i] num. gates = {}\n", num_gates); + out << fmt::format("[i] num. wires = {}\n", num_wires); + out << fmt::format("[i] num. crossings = {}\n", num_crossings); + } +}; + namespace detail { @@ -797,7 +855,8 @@ template class plane_impl { public: - plane_impl(const Ntk& src, const orthogonal_physical_design_params& p, orthogonal_physical_design_stats& st) : + plane_impl(const Ntk& src, const planar_layout_from_network_embedding_params& p, + planar_layout_from_network_embedding_stats& st) : ntk{mockturtle::fanout_view{src}}, ps{p}, pst{st} @@ -1038,14 +1097,26 @@ class plane_impl pst.num_gates = layout.num_gates(); pst.num_wires = layout.num_wires(); + if (ps.verbose) + { + std::cout << "\n[i] Layout generated:\n"; + + std::cout << fmt::format("[i] Layout dimension: {} × {} = {}\n", pst.x_size, pst.y_size, + pst.x_size * pst.y_size); + + std::cout << fmt::format("[i] #Gates: {}\n", pst.num_gates); + std::cout << fmt::format("[i] #Wires: {}\n", pst.num_wires); + std::cout << fmt::format("[i] #Crossings: {}\n", pst.num_crossings); + } + return layout; } private: mockturtle::fanout_view ntk; - orthogonal_physical_design_params ps; - orthogonal_physical_design_stats& pst; + planar_layout_from_network_embedding_params ps; + planar_layout_from_network_embedding_stats& pst; uint32_t po_counter{0}; }; @@ -1070,7 +1141,8 @@ class plane_impl * @return A fully planar gate-level layout of type `Lyt`. */ template -Lyt plane(const Ntk& ntk, orthogonal_physical_design_params ps = {}, orthogonal_physical_design_stats* pst = nullptr) +Lyt plane(const Ntk& ntk, planar_layout_from_network_embedding_params ps = {}, + planar_layout_from_network_embedding_stats* pst = nullptr) { static_assert(is_gate_level_layout_v, "Lyt is not a gate-level layout"); static_assert(mockturtle::is_network_type_v, @@ -1094,8 +1166,8 @@ Lyt plane(const Ntk& ntk, orthogonal_physical_design_params ps = {}, orthogonal_ throw std::invalid_argument("Input network has to be planar"); } - orthogonal_physical_design_stats st{}; - detail::plane_impl p{ntk, ps, st}; + planar_layout_from_network_embedding_stats st{}; + detail::plane_impl p{ntk, ps, st}; auto result = p.run(); diff --git a/test/algorithms/physical_design/planar_layout_from_network_embedding..cpp b/test/algorithms/physical_design/planar_layout_from_network_embedding..cpp index d55440bb06..8d3d6301ea 100644 --- a/test/algorithms/physical_design/planar_layout_from_network_embedding..cpp +++ b/test/algorithms/physical_design/planar_layout_from_network_embedding..cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -26,7 +25,7 @@ using namespace fiction; -static void check_stats(const orthogonal_physical_design_stats& st) noexcept +static void check_stats(const planar_layout_from_network_embedding_stats& st) noexcept { CHECK(st.x_size > 0); CHECK(st.y_size > 0); @@ -59,12 +58,12 @@ static void check_plane(const Ntk& ntk) CHECK(result == 1); // NOLINTEND(bugprone-unchecked-optional-access) - orthogonal_physical_design_stats orthogonal_planar_stats{}; + planar_layout_from_network_embedding_stats planar_layout_from_network_embedding_stats{}; - const auto gate_level_layout = fiction::plane(planarized_b, {}, &orthogonal_planar_stats); + const auto gate_level_layout = fiction::plane(planarized_b, {}, &planar_layout_from_network_embedding_stats); CHECK(gate_level_layout.num_crossings() == 0); - check_stats(orthogonal_planar_stats); + check_stats(planar_layout_from_network_embedding_stats); CHECK_NOTHROW(apply_gate_library(gate_level_layout)); } @@ -77,17 +76,17 @@ TEST_CASE("Check exceptions", "[planar-layout-from-network-embedding]") auto maj = blueprints::maj1_network(); auto maj_ranked = fiction::mutable_rank_view(maj); - orthogonal_physical_design_stats orthogonal_planar_stats{}; - CHECK_THROWS_WITH(fiction::plane(maj_ranked, {}, &orthogonal_planar_stats), + planar_layout_from_network_embedding_stats planar_layout_from_network_embedding_stats{}; + CHECK_THROWS_WITH(fiction::plane(maj_ranked, {}, &planar_layout_from_network_embedding_stats), "network contains nodes that exceed the supported fanin size"); auto ao = blueprints::and_or_network(); auto ao_ranked = fiction::mutable_rank_view(ao); - CHECK_THROWS_WITH(fiction::plane(ao_ranked, {}, &orthogonal_planar_stats), + CHECK_THROWS_WITH(fiction::plane(ao_ranked, {}, &planar_layout_from_network_embedding_stats), "Input network has to be planar"); } -TEST_CASE("Orthogonal planar layout tests", "[planar-layout-from-network-embedding]") +TEST_CASE("Planar layout tests", "[planar-layout-from-network-embedding]") { // Simple correctness and corner-case networks check_plane(blueprints::se_coloring_corner_case_network()); @@ -104,7 +103,7 @@ TEST_CASE("Orthogonal planar layout tests", "[planar-layout-from-network-embeddi check_plane(blueprints::parity_network()); } -TEST_CASE("Name conservation after planar orthogonal physical design", "[planar-layout-from-network-embedding]") +TEST_CASE("Name conservation after planar physical design", "[planar-layout-from-network-embedding]") { using gate_lyt = gate_level_layout>>>; @@ -125,9 +124,11 @@ TEST_CASE("Name conservation after planar orthogonal physical design", "[planar- CHECK(planarized_b.get_output_name(1) == "f2"); CHECK(planarized_b.get_output_name(2) == "f3"); - orthogonal_physical_design_stats orthogonal_planar_stats{}; + planar_layout_from_network_embedding_stats planar_layout_from_network_embedding_stats{}; + planar_layout_from_network_embedding_params ps{}; + ps.verbose = true; - const auto layout = fiction::plane(planarized_b, {}, &orthogonal_planar_stats); + const auto layout = fiction::plane(planarized_b, ps, &planar_layout_from_network_embedding_stats); // network name CHECK(layout.get_layout_name() == "topolinano"); From 7dfe32932dcfb2ce409b8fb2cc4053fb075d1e2d Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 9 Dec 2025 12:36:57 +0000 Subject: [PATCH 52/67] :memo: Update pyfiction docstrings Signed-off-by: GitHub Actions --- .../pyfiction/pybind11_mkdoc_docstrings.hpp | 99 +++++++++++-------- 1 file changed, 59 insertions(+), 40 deletions(-) diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index b9b891a792..f60ddc1ff5 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -8915,6 +8915,35 @@ static const char *__doc_fiction_detail_hexagonalization_impl_pst = R"doc(Hexago static const char *__doc_fiction_detail_hexagonalization_impl_run = R"doc()doc"; +static const char *__doc_fiction_detail_hgraph_node = +R"doc(Represents one node in the H-graph used for crossing minimization. + +For a node in level l of the input network, all possible orderings of +its fanins from layer l−1 are enumerated. Each such ordering is +represented by an H-graph node. The first and last fanins of the +ordering are stored, since these determine the delay in the H-graph. +The remaining fanins are placed in middle. Their mutual order is +irrelevant for this algorithm. + +Template parameter ``Ntk``: + Network type from which node types are drawn.)doc"; + +static const char *__doc_fiction_detail_hgraph_node_delay = R"doc(Specifies the delay value for the hgraph_node.)doc"; + +static const char *__doc_fiction_detail_hgraph_node_fanin_it = R"doc(Index of the predecessor H-graph node.)doc"; + +static const char *__doc_fiction_detail_hgraph_node_hgraph_node = +R"doc(Constructs an H-graph node with given first and last fanins and delay. + +Parameter ``first``: + The first (leftmost) fanin in the ordering. + +Parameter ``last``: + The last (rightmost) fanin in the ordering. + +Parameter ``delay_value``: + The delay value for the node.)doc"; + static const char *__doc_fiction_detail_is_balanced_impl = R"doc()doc"; static const char *__doc_fiction_detail_is_balanced_impl_balanced = R"doc()doc"; @@ -9557,38 +9586,6 @@ static const char *__doc_fiction_detail_node_duplication_planarization_impl_node static const char *__doc_fiction_detail_node_duplication_planarization_impl_ps = R"doc()doc"; -static const char *__doc_fiction_detail_node_pair = -R"doc(A structure representing a pair of nodes in an H-graph. - -The nodes stored in this struct describe the fanin-edges of a node in -an H-graph. A node pair object holds two nodes, which are saved in the -member 'pair'. These two outer nodes are connected through zero or -more 'middle_nodes'. The fanin order starts with the first node in -'pair', then proceeds through the 'middle_nodes', and ends with the -second node in 'pair'. The order of 'middle_nodes' is arbitrary as -they cannot be further connected to any other nodes. - -Template parameter ``Ntk``: - Network type for the nodes in the pair.)doc"; - -static const char *__doc_fiction_detail_node_pair_delay = R"doc(Specifies the delay value for the node.)doc"; - -static const char *__doc_fiction_detail_node_pair_fanin_it = -R"doc(Index into the previous level's node_pair vector, used to track fanin- -edge alignment.)doc"; - -static const char *__doc_fiction_detail_node_pair_node_pair = -R"doc(Standard constructor. - -Parameter ``node1``: - The first node of the fanin-edged node. - -Parameter ``node2``: - The second node of the fanin-edged node. - -Parameter ``delay_value``: - The delay value for the node.)doc"; - static const char *__doc_fiction_detail_non_operationality_reason = R"doc(Reason why a layout is non-operational.)doc"; static const char *__doc_fiction_detail_non_operationality_reason_KINKS = R"doc(Kinks induced the layout to become non-operational.)doc"; @@ -17461,8 +17458,8 @@ static const char *__doc_fiction_node_duplication_planarization = R"doc(Implements a planarization mechanism for networks from the paper \"Fabricatable Interconnect and Molecular QCA Circuits\" by Amitabh Chaudhary, Danny Ziyi Chen, Xiaobo Sharon Hu, Michael T. Niemier, -Ramprasad Ravichandran and Kevin Whitton in IEEE TRANSACTIONS ON -COMPUTER-AIDED DESIGN OF INTEGRATED CIRCUITS AND SYSTEMS, Volume 26, +Ramprasad Ravichandran and Kevin Whitton in IEEE Transactions on +Computer-Aided Design of Integrated Circuits and Systems, Volume 26, 2007. The planarization achieved by this function solves the Node @@ -17493,17 +17490,13 @@ Parameter ``ps``: static const char *__doc_fiction_node_duplication_planarization_params = R"doc(Parameters for the node duplication algorithm.)doc"; -static const char *__doc_fiction_node_duplication_planarization_params_output_order = -R"doc(The output order determines the starting layer for this algorithm. If -this option is turned off, the output order remains the same as in the -provided network. If it is turned on, the outputs are ordered -randomly.)doc"; +static const char *__doc_fiction_node_duplication_planarization_params_output_order = R"doc(Controls how output nodes are ordered before starting the algorithm.)doc"; static const char *__doc_fiction_node_duplication_planarization_params_output_order_KEEP_PO_ORDER = R"doc(Keep the PO order from the input network.)doc"; static const char *__doc_fiction_node_duplication_planarization_params_output_order_RANDOM_PO_ORDER = R"doc(Randomize the PO order.)doc"; -static const char *__doc_fiction_node_duplication_planarization_params_po_order = R"doc()doc"; +static const char *__doc_fiction_node_duplication_planarization_params_po_order = R"doc(The output order used. Defaults to KEEP_PO_ORDER.)doc"; static const char *__doc_fiction_normalize_layout_coordinates = R"doc(A new layout is constructed and returned that is equivalent to the @@ -18834,6 +18827,32 @@ Parameter ``node2pos``: Returns: Signal to the newly placed gate in `lyt`.)doc"; +static const char *__doc_fiction_planar_layout_from_network_embedding_params = R"doc(Parameters for the planar layout from network embedding algorithm.)doc"; + +static const char *__doc_fiction_planar_layout_from_network_embedding_params_number_of_clock_phases = R"doc(Number of clock phases to use. 3 and 4 are supported.)doc"; + +static const char *__doc_fiction_planar_layout_from_network_embedding_params_verbose = R"doc(Verbosity.)doc"; + +static const char *__doc_fiction_planar_layout_from_network_embedding_stats = R"doc(This struct stores statistics about the planar layout design process.)doc"; + +static const char *__doc_fiction_planar_layout_from_network_embedding_stats_duration = R"doc(Runtime of the planar layout design process.)doc"; + +static const char *__doc_fiction_planar_layout_from_network_embedding_stats_num_crossings = R"doc(Number of crossings.)doc"; + +static const char *__doc_fiction_planar_layout_from_network_embedding_stats_num_gates = R"doc(Number of gates.)doc"; + +static const char *__doc_fiction_planar_layout_from_network_embedding_stats_num_wires = R"doc(Number of wires.)doc"; + +static const char *__doc_fiction_planar_layout_from_network_embedding_stats_report = +R"doc(Reports the statistics to the given output stream. + +Parameter ``out``: + Output stream.)doc"; + +static const char *__doc_fiction_planar_layout_from_network_embedding_stats_x_size = R"doc(Layout width.)doc"; + +static const char *__doc_fiction_planar_layout_from_network_embedding_stats_y_size = R"doc(Layout height.)doc"; + static const char *__doc_fiction_plane = R"doc(This algorithm constructs a planar layout from the planar embedding of a logic network, forming the Planar Layout from Network Embedding From 4f4ddc3ffc3c1adbc3664c407ea797d0ff76a747 Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 9 Dec 2025 13:56:18 +0100 Subject: [PATCH 53/67] :art: small change for node order in equal delay cases --- .../node_duplication_planarization.hpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index df2f544ef6..f90bb9e8f9 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -420,12 +420,20 @@ class node_duplication_planarization_impl fis.push_back(nd); } + // Respect the rank order. If two combinations have the same delay and have no seen advantage then the one from + // the original ranking is used, since it is inserted and not overwritten afterward. ntk.foreach_fanin(nd, - [this](auto fi) + [&](auto fi) { if (!ntk.is_constant(fi)) { - fis.push_back(ntk.get_node(fi)); + auto n = ntk.get_node(fi); + + auto it = + std::lower_bound(fis.begin(), fis.end(), n, [&](auto const& a, auto const& b) + { return ntk.rank_position(a) < ntk.rank_position(b); }); + + fis.insert(it, n); } }); @@ -463,11 +471,10 @@ class node_duplication_planarization_impl else if (node_pair_last.delay + 2 == node_pair_cur.delay) { // This solves equal path delays, if they are connected in the next layer via a fanout - const auto fc0 = fanins(ntk, node_pair_last.outer_fanins.first); + const auto fc0 = fanins(ntk, node_pair_cur.outer_fanins.first); if (node_pair_last.fanin_it < combinations_last->size()) { - const auto& parent_pair = (*combinations_last)[node_pair_last.fanin_it]; - const auto fc1 = fanins(ntk, parent_pair.outer_fanins.second); + const auto fc1 = fanins(ntk, node_pair_last.outer_fanins.second); for (const auto f0 : fc0.fanin_nodes) { From 1c9fde5fd37165167beee159c5ce94ffca1b5e29 Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 9 Dec 2025 14:21:26 +0100 Subject: [PATCH 54/67] :art: nitpick comments --- cli/cmd/physical_design/src/plane.cpp | 2 -- .../node_duplication_planarization.hpp | 10 ++++---- .../planar_layout_from_network_embedding.hpp | 4 ++-- ... planar_layout_from_network_embedding.cpp} | 23 ++++++++++--------- 4 files changed, 19 insertions(+), 20 deletions(-) rename test/algorithms/physical_design/{planar_layout_from_network_embedding..cpp => planar_layout_from_network_embedding.cpp} (88%) diff --git a/cli/cmd/physical_design/src/plane.cpp b/cli/cmd/physical_design/src/plane.cpp index 6185cecdac..ef56ada088 100644 --- a/cli/cmd/physical_design/src/plane.cpp +++ b/cli/cmd/physical_design/src/plane.cpp @@ -67,8 +67,6 @@ void plane_command::execute() ps.number_of_clock_phases = num_clock_phases == 3 ? fiction::num_clks::THREE : fiction::num_clks::FOUR; using po_enum = decltype(dup_ps.po_order); - using po_enum = decltype(dup_ps.po_order); - switch (po_order) { case 0u: diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index f90bb9e8f9..de52532baa 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -723,23 +723,23 @@ class node_duplication_planarization_impl } private: - /* + /** * The input network. */ Ntk ntk{}; - /* + /** * The currently node_pairs used in the current level. */ std::vector>> lvl_pairs{}; - /* + /** * The fanin nodes. */ std::vector> fis{}; - /* + /** * The network stored as levels. */ std::vector>> ntk_lvls{}; - /* + /** * The stats of the node_duplication class. */ node_duplication_planarization_params ps{}; diff --git a/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp b/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp index 084dc426bb..a4ccbd4bd7 100644 --- a/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp +++ b/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp @@ -347,7 +347,7 @@ uint64_t calculate_buffer_connection_type(const Ntk& ntk, const mockturtle::node { if (ntk.is_po(n)) { - return false; + return 0; } auto fo = ntk.fanout(n); assert(fo.size() == 1); @@ -417,7 +417,7 @@ uint64_t calculate_allowed_orientation(const Ntk& ntk, const mockturtle::node -int calculate_start_orientation(const Ntk& ntk, const uint32_t& lvl) +int calculate_start_orientation(const Ntk& ntk, const uint32_t lvl) { int orientation = 0; if (lvl == 0) diff --git a/test/algorithms/physical_design/planar_layout_from_network_embedding..cpp b/test/algorithms/physical_design/planar_layout_from_network_embedding.cpp similarity index 88% rename from test/algorithms/physical_design/planar_layout_from_network_embedding..cpp rename to test/algorithms/physical_design/planar_layout_from_network_embedding.cpp index 8d3d6301ea..439d0f713e 100644 --- a/test/algorithms/physical_design/planar_layout_from_network_embedding..cpp +++ b/test/algorithms/physical_design/planar_layout_from_network_embedding.cpp @@ -1,28 +1,28 @@ #include - -#include "catch2/matchers/catch_matchers.hpp" -#include "fiction/algorithms/physical_design/apply_gate_library.hpp" -#include "fiction/layouts/cell_level_layout.hpp" -#include "fiction/layouts/coordinates.hpp" -#include "fiction/technology/cell_technologies.hpp" -#include "utils/blueprints/network_blueprints.hpp" +#include #include #include #include +#include #include #include #include +#include #include +#include #include #include #include #include +#include #include #include #include +#include + using namespace fiction; static void check_stats(const planar_layout_from_network_embedding_stats& st) noexcept @@ -60,7 +60,8 @@ static void check_plane(const Ntk& ntk) planar_layout_from_network_embedding_stats planar_layout_from_network_embedding_stats{}; - const auto gate_level_layout = fiction::plane(planarized_b, {}, &planar_layout_from_network_embedding_stats); + const auto gate_level_layout = + fiction::plane(planarized_b, {}, &planar_layout_from_network_embedding_stats); CHECK(gate_level_layout.num_crossings() == 0); check_stats(planar_layout_from_network_embedding_stats); @@ -74,8 +75,8 @@ TEST_CASE("Check exceptions", "[planar-layout-from-network-embedding]") fiction::network_balancing_params b_ps; b_ps.unify_outputs = true; - auto maj = blueprints::maj1_network(); - auto maj_ranked = fiction::mutable_rank_view(maj); + auto maj = blueprints::maj1_network(); + auto maj_ranked = fiction::mutable_rank_view(maj); planar_layout_from_network_embedding_stats planar_layout_from_network_embedding_stats{}; CHECK_THROWS_WITH(fiction::plane(maj_ranked, {}, &planar_layout_from_network_embedding_stats), "network contains nodes that exceed the supported fanin size"); @@ -124,7 +125,7 @@ TEST_CASE("Name conservation after planar physical design", "[planar-layout-from CHECK(planarized_b.get_output_name(1) == "f2"); CHECK(planarized_b.get_output_name(2) == "f3"); - planar_layout_from_network_embedding_stats planar_layout_from_network_embedding_stats{}; + planar_layout_from_network_embedding_stats planar_layout_from_network_embedding_stats{}; planar_layout_from_network_embedding_params ps{}; ps.verbose = true; From c18035963ea227c4b1047a90788987b2d9e4de62 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 9 Dec 2025 13:22:31 +0000 Subject: [PATCH 55/67] :memo: Update pyfiction docstrings Signed-off-by: GitHub Actions --- .../pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index f60ddc1ff5..33e3ba3356 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -9580,11 +9580,11 @@ Parameter ``position``: The position of the node (0 indicates a terminal node; controls duplicate insertion behavior).)doc"; -static const char *__doc_fiction_detail_node_duplication_planarization_impl_lvl_pairs = R"doc()doc"; +static const char *__doc_fiction_detail_node_duplication_planarization_impl_lvl_pairs = R"doc(The currently node_pairs used in the current level.)doc"; static const char *__doc_fiction_detail_node_duplication_planarization_impl_node_duplication_planarization_impl = R"doc()doc"; -static const char *__doc_fiction_detail_node_duplication_planarization_impl_ps = R"doc()doc"; +static const char *__doc_fiction_detail_node_duplication_planarization_impl_ps = R"doc(The stats of the node_duplication class.)doc"; static const char *__doc_fiction_detail_non_operationality_reason = R"doc(Reason why a layout is non-operational.)doc"; From 643925f4ea1186d87c493be20ff8399b25e63115 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 13:50:37 +0000 Subject: [PATCH 56/67] =?UTF-8?q?=F0=9F=8E=A8=20Incorporated=20pre-commit?= =?UTF-8?q?=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index e4e863b643..fa493c2c1d 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -11,7 +11,7 @@ Unreleased Added ##### - Algorithms: - - Node Duplication Planarization, reimplemented from “Fabricatable Interconnect and Molecular QCA Circuits” by Amitabh Chaudhary, Danny Ziyi Chen, Xiaobo Sharon Hu, Michael T. Niemier, Ramprasad Ravichandran, and Kevin Whitton. + - Node Duplication Planarization, reimplemented from "Fabricatable Interconnect and Molecular QCA Circuits" by Amitabh Chaudhary, Danny Ziyi Chen, Xiaobo Sharon Hu, Michael T. Niemier, Ramprasad Ravichandran, and Kevin Whitton. - Planar Layout from Network Embedding (PLANE) for generating planar 2DDWave-clocked Cartesian gate-level layouts in a fast and scalable fashion. - Documentation: - Added ``AGENTS.md`` to guide AI agents in the repository From e3c229dc5186559460c3909029dceae5b61551c9 Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 9 Dec 2025 15:36:55 +0100 Subject: [PATCH 57/67] :art: clang-tidy --- .../physical_design/planar_layout_from_network_embedding.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp b/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp index a4ccbd4bd7..060d0d7e7c 100644 --- a/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp +++ b/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include From 9228a06cf1c3d29d8744459abbb469980f9c5b3f Mon Sep 17 00:00:00 2001 From: benjamin Date: Thu, 18 Dec 2025 10:48:46 +0100 Subject: [PATCH 58/67] :art: added more verbose docstring to plane.hpp --- cli/cmd/physical_design/include/plane.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/cmd/physical_design/include/plane.hpp b/cli/cmd/physical_design/include/plane.hpp index 04f3d13baf..219165a704 100644 --- a/cli/cmd/physical_design/include/plane.hpp +++ b/cli/cmd/physical_design/include/plane.hpp @@ -18,7 +18,8 @@ namespace alice { /** - * Executes a physical design approach using a planar embedding of a logic network. + * Executes the physical design approach Planar Layout from Network Embedding (PLANE), which finds a valid placement and + * routing without crossings given a planar network embedding. * * See include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp for more details. */ From 0d0aac43870625b074c36d6a6cfc284127716a2a Mon Sep 17 00:00:00 2001 From: benjamin Date: Thu, 18 Dec 2025 11:06:18 +0100 Subject: [PATCH 59/67] :art: corrected naming convention --- cli/cmd/physical_design/src/plane.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cli/cmd/physical_design/src/plane.cpp b/cli/cmd/physical_design/src/plane.cpp index ef56ada088..dfc8f3168c 100644 --- a/cli/cmd/physical_design/src/plane.cpp +++ b/cli/cmd/physical_design/src/plane.cpp @@ -48,10 +48,10 @@ plane_command::plane_command(const environment::ptr& e) : void plane_command::execute() { - auto& ns = store(); - auto& ls = store(); + auto& lns = store(); + auto& gls = store(); - if (ns.empty()) + if (lns.empty()) { env->out() << "[w] no logic network in store\n"; return; @@ -93,7 +93,7 @@ void plane_command::execute() return std::make_shared(fiction::network_balancing(tec_f, bal_ps)); }; - auto tec_b = std::visit(perform_fanouts_and_balance, ns.current()); + auto tec_b = std::visit(perform_fanouts_and_balance, lns.current()); const fiction::mutable_rank_view vpi_r(*tec_b); @@ -101,7 +101,7 @@ void plane_command::execute() try { - ls.extend() = std::make_shared( + gls.extend() = std::make_shared( fiction::plane(planarized_ntk, ps, &st)); } catch (const fiction::high_degree_fanin_exception& e) From 8a4b48d0571f7ed6586a5da22e6b70589cad29da Mon Sep 17 00:00:00 2001 From: benjamin Date: Thu, 18 Dec 2025 11:11:25 +0100 Subject: [PATCH 60/67] :art: improved readability --- cli/cmd/physical_design/src/plane.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cli/cmd/physical_design/src/plane.cpp b/cli/cmd/physical_design/src/plane.cpp index dfc8f3168c..968f5cdcac 100644 --- a/cli/cmd/physical_design/src/plane.cpp +++ b/cli/cmd/physical_design/src/plane.cpp @@ -66,23 +66,22 @@ void plane_command::execute() ps.number_of_clock_phases = num_clock_phases == 3 ? fiction::num_clks::THREE : fiction::num_clks::FOUR; - using po_enum = decltype(dup_ps.po_order); switch (po_order) { case 0u: { - dup_ps.po_order = po_enum::KEEP_PO_ORDER; + dup_ps.po_order = fiction::node_duplication_planarization_params::output_order::KEEP_PO_ORDER; break; } case 1u: { - dup_ps.po_order = po_enum::RANDOM_PO_ORDER; + dup_ps.po_order = fiction::node_duplication_planarization_params::output_order::RANDOM_PO_ORDER; break; } default: { env->out() << "[w] invalid --po-order, defaulting to keep\n"; - dup_ps.po_order = po_enum::KEEP_PO_ORDER; + dup_ps.po_order = fiction::node_duplication_planarization_params::output_order::KEEP_PO_ORDER; break; } } From 45fa8a18e293dff592a351510edea8f2190090cd Mon Sep 17 00:00:00 2001 From: benjamin Date: Thu, 18 Dec 2025 11:15:07 +0100 Subject: [PATCH 61/67] :art: revert accidental changes in changelog.rst --- docs/changelog.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index fa493c2c1d..ba2a918b50 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -59,6 +59,11 @@ Changed - Dependencies: - Updated all dependencies to their latest versions +Removed +####### +- Continuous integration: + - macOS 13 has been removed, along with support for the x86_64 (Intel) architecture + Fixed ##### - Data structures: From f7a554be12d253d692e5660b6637985731c65976 Mon Sep 17 00:00:00 2001 From: benjamin Date: Thu, 18 Dec 2025 14:40:43 +0100 Subject: [PATCH 62/67] :art: refactored enums for readability --- .../node_duplication_planarization.hpp | 95 +++++++++++-------- 1 file changed, 57 insertions(+), 38 deletions(-) diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index de52532baa..1ff6c6fd30 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -55,6 +55,9 @@ struct node_duplication_planarization_params namespace detail { +template +using levelized_node_order = std::vector>>; + /** * Represents one node in the H-graph used for crossing minimization. * @@ -145,10 +148,17 @@ initialize_copy_network_duplicates(Ntk const& src) * all nodes at old2new[n] need to be viewed. Match lvl[node_index] against all entries in old2new[n], * then try lvl[node_index+1] then try lvl[node_index+2]. * - * @param n Variable to process. - * @param lvl Level to process. - * @param edge_it Iterator for edge. - * @return Vector of fanins in the virtual_pi_network connected to the processed node. + * @tparam Ntk Source network type. + * @tparam NtkDest Destination network type. + * @param ntk Source network from which fanins are collected. + * @param ntk_dest_v Destination network in which the corresponding signals are created. + * @param n Node in the source network whose fanins are processed. + * @param old2new_v Mapping from source nodes to their corresponding signals (including duplicates) + * in the destination network. + * @param lvl Vector of nodes at the current level in the destination network. + * @param node_index Reference to the current index into `lvl`, used to determine valid fanin connections + * and potentially updated during processing. + * @return Vector of fanin signals in the destination network corresponding to the fanins of node `n`. */ template std::vector> @@ -237,9 +247,9 @@ gather_fanin_signals(const Ntk& ntk, NtkDest& ntk_dest_v, const mockturtle::node * relations. */ template -virtual_pi_network create_virtual_pi_ntk_from_duplicated_nodes( - const Ntk& ntk, const std::vector>>& ntk_lvls, - std::vector>>>& ntk_lvls_new) +virtual_pi_network +create_virtual_pi_ntk_from_duplicated_nodes(const Ntk& ntk, const levelized_node_order& ntk_lvls, + levelized_node_order>& ntk_lvls_new) { static_assert(mockturtle::has_create_node_v>, "virtual_pi_network lacks create_node"); static_assert(mockturtle::has_get_node_v>, "virtual_pi_network lacks get_node"); @@ -289,8 +299,6 @@ virtual_pi_network create_virtual_pi_ntk_from_duplicated_nodes( } else { - assert(i + 1 < ntk_lvls_new.size() && "Next level does not exist"); - const auto children = gather_fanin_signals(ntk, ntk_dest_v, nd, old2new_v, ntk_lvls_new[i + 1], node_index); @@ -505,6 +513,18 @@ class node_duplication_planarization_impl lvl_pairs.push_back(combinations); } + enum class fanout_state : uint8_t + { + NORMAL, + SATURATED + }; + + enum class insertion_position : uint8_t + { + TERMINAL, + INNER + }; + /** * Inserts a node into a vector if it is unique. * @@ -521,23 +541,23 @@ class node_duplication_planarization_impl * @param position The position of the node (0 indicates a terminal node; controls duplicate insertion behavior). */ void insert_if_not_first(const mockturtle::node& node, std::vector>& vec, - int& saturated_fanout_flag, const int position) + fanout_state& fo_st, const insertion_position position) { if (vec.empty() || vec.front() != node) { vec.insert(vec.begin(), node); - saturated_fanout_flag = 0; + fo_st = fanout_state::NORMAL; } - else if (position == 0) + else if (position == insertion_position::TERMINAL) { - if (saturated_fanout_flag == 1) + if (fo_st == fanout_state::SATURATED) { vec.insert(vec.begin(), node); - saturated_fanout_flag = 0; + fo_st = fanout_state::NORMAL; } else { - saturated_fanout_flag = 1; + fo_st = fanout_state::SATURATED; } } } @@ -552,7 +572,7 @@ class node_duplication_planarization_impl std::vector> compute_node_order() { std::vector> next_level; - int saturated_fanout_flag = 0; + fanout_state fo_st = fanout_state::NORMAL; const auto& combinations = lvl_pairs.back(); @@ -566,16 +586,16 @@ class node_duplication_planarization_impl const auto& min_combination = *minimum_it; // Insert the terminal node - insert_if_not_first(min_combination.outer_fanins.second, next_level, saturated_fanout_flag, 0); + insert_if_not_first(min_combination.outer_fanins.second, next_level, fo_st, insertion_position::TERMINAL); // Insert middle_fanins for (const auto& node : min_combination.middle_fanins) { - insert_if_not_first(node, next_level, saturated_fanout_flag, 1); + insert_if_not_first(node, next_level, fo_st, insertion_position::INNER); } // Insert the first node - insert_if_not_first(min_combination.outer_fanins.first, next_level, saturated_fanout_flag, 1); + insert_if_not_first(min_combination.outer_fanins.first, next_level, fo_st, insertion_position::INNER); // Start with index instead of pointer std::size_t level = lvl_pairs.size() - 1; @@ -589,18 +609,18 @@ class node_duplication_planarization_impl // Insert the terminal node if (ntk.is_pi(fanin_combination.outer_fanins.second)) { - saturated_fanout_flag = 1; + fo_st = fanout_state::SATURATED; } - insert_if_not_first(fanin_combination.outer_fanins.second, next_level, saturated_fanout_flag, 0); + insert_if_not_first(fanin_combination.outer_fanins.second, next_level, fo_st, insertion_position::TERMINAL); // Insert middle_fanins for (const auto& node : fanin_combination.middle_fanins) { - insert_if_not_first(node, next_level, saturated_fanout_flag, 1); + insert_if_not_first(node, next_level, fo_st, insertion_position::INNER); } // Insert the first node - insert_if_not_first(fanin_combination.outer_fanins.first, next_level, saturated_fanout_flag, 1); + insert_if_not_first(fanin_combination.outer_fanins.first, next_level, fo_st, insertion_position::INNER); // Move one level up --level; @@ -703,7 +723,7 @@ class node_duplication_planarization_impl ntk_lvls.push_back(next_level); } - std::vector>>> ntk_lvls_new{}; + levelized_node_order> ntk_lvls_new{}; // create virtual pi network auto virtual_ntk = create_virtual_pi_ntk_from_duplicated_nodes(ntk, ntk_lvls, ntk_lvls_new); @@ -738,7 +758,7 @@ class node_duplication_planarization_impl /** * The network stored as levels. */ - std::vector>> ntk_lvls{}; + levelized_node_order ntk_lvls{}; /** * The stats of the node_duplication class. */ @@ -758,27 +778,26 @@ class node_duplication_planarization_impl * the shortest path from the source (x) to the sink (y) in this H-graph, an optimal solution for the NDCE problem for * each level is found. The function traverses from the Primary Outputs (POs) towards the Primary Inputs (PIs). * - * @tparam NtkDest Destination network type. - * @tparam NtkSrc Source network type. - * @param ntk_src Source network to be utilized for the planarization. + * @tparam Ntk Source network type. + * @param ntk Source network to be utilized for the planarization. * @param ps Node duplication parameters used in the computation. * - * @return A view of the planarized virtual_pi_network created in the format of mutable_rank_view. + * @return A planarized virtual_pi_network. */ -template -[[nodiscard]] virtual_pi_network node_duplication_planarization(const NtkSrc& ntk_src, - node_duplication_planarization_params ps = {}) +template +[[nodiscard]] virtual_pi_network node_duplication_planarization(const Ntk& ntk, + node_duplication_planarization_params ps = {}) { - static_assert(mockturtle::is_network_type_v, "NtkSrc is not a network type"); - static_assert(mockturtle::has_create_node_v, "NtkSrc does not implement the create_node function"); - static_assert(mockturtle::has_rank_position_v, "NtkSrc does not implement the rank_position function"); + static_assert(mockturtle::is_network_type_v, "NtkSrc is not a network type"); + static_assert(mockturtle::has_create_node_v, "NtkSrc does not implement the create_node function"); + static_assert(mockturtle::has_rank_position_v, "NtkSrc does not implement the rank_position function"); - if (!is_balanced(ntk_src)) + if (!is_balanced(ntk)) { throw std::invalid_argument("Networks have to be balanced for this duplication"); } - detail::node_duplication_planarization_impl p{ntk_src, ps}; + detail::node_duplication_planarization_impl p{ntk, ps}; auto result = p.run(); @@ -787,7 +806,7 @@ template mincross_params p_min{}; p_min.optimize = false; - auto ntk_min = mincross(result, p_min, &st_min); // counts crossings + mincross(result, p_min, &st_min); // counts crossings if (st_min.num_crossings != 0) { throw std::runtime_error("Planarization failed: resulting network is not planar"); From 97bde8c6c799c30a161bc7afb173104bf4272e0c Mon Sep 17 00:00:00 2001 From: benjamin Date: Thu, 18 Dec 2025 14:50:56 +0100 Subject: [PATCH 63/67] :art: refactored enums for readability --- .../node_duplication_planarization.hpp | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index 1ff6c6fd30..c07fcdb10f 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -420,17 +420,17 @@ class node_duplication_planarization_impl * * @param nd Node in the H-graph. */ - void compute_slice_delays(const mockturtle::node& nd) + void compute_slice_delays(const mockturtle::node& n) { // Pis need to be propagated into the next level, since they have to be connected without crossings - if (ntk.is_pi(nd)) + if (ntk.is_pi(n)) { - fis.push_back(nd); + fis.push_back(n); } // Respect the rank order. If two combinations have the same delay and have no seen advantage then the one from // the original ranking is used, since it is inserted and not overwritten afterward. - ntk.foreach_fanin(nd, + ntk.foreach_fanin(n, [&](auto fi) { if (!ntk.is_constant(fi)) @@ -513,15 +513,36 @@ class node_duplication_planarization_impl lvl_pairs.push_back(combinations); } + /** + * Represents the current state of fanout saturation during node insertion. + * A fanout is saturated if it has reached its maximum fanout limit. + */ enum class fanout_state : uint8_t { + /** + * No fanout saturation is active. + */ NORMAL, + + /** + * Fanout saturation is active. + */ SATURATED }; + /** + * Specifies the position of a node during insertion. + */ enum class insertion_position : uint8_t { + /** + * The node is a terminal node of a fanin relation. + */ TERMINAL, + + /** + * The node is an inner (non-terminal) fanin node. + */ INNER }; @@ -572,7 +593,7 @@ class node_duplication_planarization_impl std::vector> compute_node_order() { std::vector> next_level; - fanout_state fo_st = fanout_state::NORMAL; + fanout_state fo_st = fanout_state::NORMAL; const auto& combinations = lvl_pairs.back(); @@ -611,7 +632,8 @@ class node_duplication_planarization_impl { fo_st = fanout_state::SATURATED; } - insert_if_not_first(fanin_combination.outer_fanins.second, next_level, fo_st, insertion_position::TERMINAL); + insert_if_not_first(fanin_combination.outer_fanins.second, next_level, fo_st, + insertion_position::TERMINAL); // Insert middle_fanins for (const auto& node : fanin_combination.middle_fanins) From 76857e8972f503c10b23c0c53168194298c99bda Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 18 Dec 2025 13:52:00 +0000 Subject: [PATCH 64/67] :memo: Update pyfiction docstrings Signed-off-by: GitHub Actions --- .../pyfiction/pybind11_mkdoc_docstrings.hpp | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index 33e3ba3356..6d612f1929 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -9552,6 +9552,15 @@ subsequent delay calculations. Parameter ``nd``: Node in the H-graph.)doc"; +static const char *__doc_fiction_detail_node_duplication_planarization_impl_fanout_state = +R"doc(Represents the current state of fanout saturation during node +insertion. A fanout is saturated if it has reached its maximum fanout +limit.)doc"; + +static const char *__doc_fiction_detail_node_duplication_planarization_impl_fanout_state_NORMAL = R"doc(No fanout saturation is active.)doc"; + +static const char *__doc_fiction_detail_node_duplication_planarization_impl_fanout_state_SATURATED = R"doc(Fanout saturation is active.)doc"; + static const char *__doc_fiction_detail_node_duplication_planarization_impl_insert_if_not_first = R"doc(Inserts a node into a vector if it is unique. @@ -9580,10 +9589,18 @@ Parameter ``position``: The position of the node (0 indicates a terminal node; controls duplicate insertion behavior).)doc"; +static const char *__doc_fiction_detail_node_duplication_planarization_impl_insertion_position = R"doc(Specifies the position of a node during insertion.)doc"; + +static const char *__doc_fiction_detail_node_duplication_planarization_impl_insertion_position_INNER = R"doc(The node is an inner (non-terminal) fanin node.)doc"; + +static const char *__doc_fiction_detail_node_duplication_planarization_impl_insertion_position_TERMINAL = R"doc(The node is a terminal node of a fanin relation.)doc"; + static const char *__doc_fiction_detail_node_duplication_planarization_impl_lvl_pairs = R"doc(The currently node_pairs used in the current level.)doc"; static const char *__doc_fiction_detail_node_duplication_planarization_impl_node_duplication_planarization_impl = R"doc()doc"; +static const char *__doc_fiction_detail_node_duplication_planarization_impl_ntk_lvls = R"doc(The network stored as levels.)doc"; + static const char *__doc_fiction_detail_node_duplication_planarization_impl_ps = R"doc(The stats of the node_duplication class.)doc"; static const char *__doc_fiction_detail_non_operationality_reason = R"doc(Reason why a layout is non-operational.)doc"; @@ -17472,21 +17489,17 @@ H-graph, an optimal solution for the NDCE problem for each level is found. The function traverses from the Primary Outputs (POs) towards the Primary Inputs (PIs). -Template parameter ``NtkDest``: - Destination network type. - -Template parameter ``NtkSrc``: +Template parameter ``Ntk``: Source network type. -Parameter ``ntk_src``: +Parameter ``ntk``: Source network to be utilized for the planarization. Parameter ``ps``: Node duplication parameters used in the computation. Returns: - A view of the planarized virtual_pi_network created in the format - of mutable_rank_view.)doc"; + A planarized virtual_pi_network.)doc"; static const char *__doc_fiction_node_duplication_planarization_params = R"doc(Parameters for the node duplication algorithm.)doc"; From 02f28ec2a7b63c189bd624e62d2ffd0199f433d4 Mon Sep 17 00:00:00 2001 From: benjamin Date: Thu, 18 Dec 2025 16:02:48 +0100 Subject: [PATCH 65/67] :art: small modifications --- .../planar_layout_from_network_embedding.hpp | 312 +++++++++--------- 1 file changed, 156 insertions(+), 156 deletions(-) diff --git a/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp b/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp index 060d0d7e7c..0bc648459b 100644 --- a/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp +++ b/include/fiction/algorithms/physical_design/planar_layout_from_network_embedding.hpp @@ -105,138 +105,130 @@ namespace detail * combinations of the previous level, connection type, orientations, and surrounding spacing (gaps). Based on these * inputs, it returns the corresponding orientation and spacing configuration for the current buffer. */ -inline std::array, 4>, 3>, 3>, 2>& get_buffer_lookup() -{ - static std::array, 4>, 3>, 3>, 2> array = { - { // Array - {{// Unconnected - {{ - // East - {{{0, 0}, {1, 0}, {1, 1}, {0, 1}}}, // gap 0 - {{{0, 0}, {0, 0}, {1, 0}, {0, 0}}}, // gap 1 - {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 and higher - }}, - {{ - // South - {{{3, 0}, {2, 0}, {0, 0}, {0, 0}}}, // gap 0; only first two entries used - {{{3, 0}, {2, 0}, {0, 0}, {0, 0}}}, // gap 1 - {{{3, 0}, {2, 0}, {0, 0}, {0, 0}}} // gap 2 - }}, - {{ - // Free - {{{0, 0}, {1, 0}, {2, 0}, {3, 0}}}, // gap 0 - {{{0, 0}, {0, 0}, {1, 0}, {0, 0}}}, // gap 1 - {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 and higher - }}}}, - {{// Connected - {{ - // East - {{{0, 0}, {0, 0}, {0, 1}, {0, 1}}}, // gap 0 - {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}, // gap 1 - {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 and higher - }}, - {{ - // South - {{{3, 0}, {3, 0}, {0, 0}, {0, 0}}}, // gap 0 - {{{3, 0}, {3, 0}, {0, 0}, {0, 0}}}, // gap 1 - {{{3, 0}, {3, 0}, {0, 0}, {0, 0}}} // gap 2 and higher - }}, - {{ - // Free - {{{0, 0}, {0, 0}, {3, 0}, {3, 0}}}, // gap 0 - {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}, // gap 1 - {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 - }}}}}}; - return array; -} +inline constexpr std::array, 4>, 3>, 3>, 2> + BUFFER_LOOKUP = {{ // Array + {{// Unconnected + {{ + // East + {{{0, 0}, {1, 0}, {1, 1}, {0, 1}}}, // gap 0 + {{{0, 0}, {0, 0}, {1, 0}, {0, 0}}}, // gap 1 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 and higher + }}, + {{ + // South + {{{3, 0}, {2, 0}, {0, 0}, {0, 0}}}, // gap 0; only first two entries used + {{{3, 0}, {2, 0}, {0, 0}, {0, 0}}}, // gap 1 + {{{3, 0}, {2, 0}, {0, 0}, {0, 0}}} // gap 2 + }}, + {{ + // Free + {{{0, 0}, {1, 0}, {2, 0}, {3, 0}}}, // gap 0 + {{{0, 0}, {0, 0}, {1, 0}, {0, 0}}}, // gap 1 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 and higher + }}}}, + {{// Connected + {{ + // East + {{{0, 0}, {0, 0}, {0, 1}, {0, 1}}}, // gap 0 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}, // gap 1 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 and higher + }}, + {{ + // South + {{{3, 0}, {3, 0}, {0, 0}, {0, 0}}}, // gap 0 + {{{3, 0}, {3, 0}, {0, 0}, {0, 0}}}, // gap 1 + {{{3, 0}, {3, 0}, {0, 0}, {0, 0}}} // gap 2 and higher + }}, + {{ + // Free + {{{0, 0}, {0, 0}, {3, 0}, {3, 0}}}, // gap 0 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}, // gap 1 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 + }}}}}}; /** * Defines a 3D lookup table using `std::array` and encapsulates it within a function. This table encodes all possible * combinations of the previous level, connection type, orientations, and surrounding spacing (gaps). Based on these * inputs, it returns the corresponding orientation and spacing configuration for the current fanout. */ -inline std::array, 4>, 3>, 3>, 4>& get_fanout_lookup() -{ - static std::array, 4>, 3>, 3>, 4> array = { - { // Array - {{// Type Fo 1+2 - {{ - // East - {{{1, 0}, {1, 1}, {1, 2}, {1, 1}}}, // gap 0 - {{{1, 0}, {1, 0}, {1, 1}, {1, 0}}}, // gap 1 - {{{1, 0}, {1, 0}, {1, 0}, {1, 0}}} // gap 2 - }}, - {{ - // South - {{{2, 0}, {2, 1}, {0, 0}, {0, 0}}}, // gap 0 - {{{2, 0}, {2, 1}, {0, 0}, {0, 0}}}, // gap 1 - {{{2, 0}, {2, 1}, {0, 0}, {0, 0}}} // gap 2 - }}, - {{ - // Free - {{{1, 0}, {2, 0}, {2, 1}, {2, 0}}}, // gap 0 - {{{1, 0}, {1, 0}, {2, 0}, {1, 0}}}, // gap 1 - {{{1, 0}, {1, 0}, {1, 0}, {1, 0}}} // gap 2 - }}}}, - {{// Type F1 - {{ - // East - {{{0, 0}, {0, 1}, {0, 2}, {0, 1}}}, // gap 0 - {{{0, 0}, {0, 0}, {0, 1}, {0, 0}}}, // gap 1 - {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 - }}, - {{ - // South - {{{3, 0}, {3, 1}, {0, 0}, {0, 0}}}, // gap 0 - {{{3, 0}, {3, 1}, {0, 0}, {0, 0}}}, // gap 1 - {{{3, 0}, {3, 1}, {0, 0}, {0, 0}}} // gap 2 - }}, - {{ - // Free - {{{0, 0}, {3, 0}, {3, 1}, {3, 0}}}, // gap 0 - {{{0, 0}, {0, 0}, {3, 0}, {0, 0}}}, // gap 1 - {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 - }}}}, - {{// Type F2 - {{ - // East - {{{1, 0}, {1, 0}, {1, 1}, {1, 1}}}, // gap 0 - {{{1, 0}, {1, 0}, {1, 0}, {1, 0}}}, // gap 1 - {{{1, 0}, {1, 0}, {1, 0}, {1, 0}}} // gap 2 - }}, - {{ - // South - {{{2, 0}, {2, 0}, {0, 0}, {0, 0}}}, // gap 0 - {{{2, 0}, {2, 0}, {0, 0}, {0, 0}}}, // gap 1 - {{{2, 0}, {2, 0}, {0, 0}, {0, 0}}} // gap 2 - }}, - {{ - // Free - {{{1, 0}, {1, 0}, {2, 0}, {2, 0}}}, // gap 0 - {{{1, 0}, {1, 0}, {1, 0}, {1, 0}}}, // gap 1 - {{{1, 0}, {1, 0}, {1, 0}, {1, 0}}} // gap 2 - }}}}, - {{// Type F3 - {{ - // East - {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}, // gap 0 - {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}, // gap 1 - {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 - }}, - {{ - // South - {{{3, 0}, {3, 0}, {0, 0}, {0, 0}}}, // gap 0 - {{{3, 0}, {3, 0}, {0, 0}, {0, 0}}}, // gap 1 - {{{3, 0}, {3, 0}, {0, 0}, {0, 0}}} // gap 2 - }}, - {{ - // Free - {{{0, 0}, {0, 0}, {3, 0}, {3, 0}}}, // gap 0 - {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}, // gap 1 - {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 - }}}}}}; - return array; -} +inline constexpr std::array, 4>, 3>, 3>, 4> + FANOUT_LOOKUP = {{ // Array + {{// Type Fo 1+2 + {{ + // East + {{{1, 0}, {1, 1}, {1, 2}, {1, 1}}}, // gap 0 + {{{1, 0}, {1, 0}, {1, 1}, {1, 0}}}, // gap 1 + {{{1, 0}, {1, 0}, {1, 0}, {1, 0}}} // gap 2 + }}, + {{ + // South + {{{2, 0}, {2, 1}, {0, 0}, {0, 0}}}, // gap 0 + {{{2, 0}, {2, 1}, {0, 0}, {0, 0}}}, // gap 1 + {{{2, 0}, {2, 1}, {0, 0}, {0, 0}}} // gap 2 + }}, + {{ + // Free + {{{1, 0}, {2, 0}, {2, 1}, {2, 0}}}, // gap 0 + {{{1, 0}, {1, 0}, {2, 0}, {1, 0}}}, // gap 1 + {{{1, 0}, {1, 0}, {1, 0}, {1, 0}}} // gap 2 + }}}}, + {{// Type F1 + {{ + // East + {{{0, 0}, {0, 1}, {0, 2}, {0, 1}}}, // gap 0 + {{{0, 0}, {0, 0}, {0, 1}, {0, 0}}}, // gap 1 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 + }}, + {{ + // South + {{{3, 0}, {3, 1}, {0, 0}, {0, 0}}}, // gap 0 + {{{3, 0}, {3, 1}, {0, 0}, {0, 0}}}, // gap 1 + {{{3, 0}, {3, 1}, {0, 0}, {0, 0}}} // gap 2 + }}, + {{ + // Free + {{{0, 0}, {3, 0}, {3, 1}, {3, 0}}}, // gap 0 + {{{0, 0}, {0, 0}, {3, 0}, {0, 0}}}, // gap 1 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 + }}}}, + {{// Type F2 + {{ + // East + {{{1, 0}, {1, 0}, {1, 1}, {1, 1}}}, // gap 0 + {{{1, 0}, {1, 0}, {1, 0}, {1, 0}}}, // gap 1 + {{{1, 0}, {1, 0}, {1, 0}, {1, 0}}} // gap 2 + }}, + {{ + // South + {{{2, 0}, {2, 0}, {0, 0}, {0, 0}}}, // gap 0 + {{{2, 0}, {2, 0}, {0, 0}, {0, 0}}}, // gap 1 + {{{2, 0}, {2, 0}, {0, 0}, {0, 0}}} // gap 2 + }}, + {{ + // Free + {{{1, 0}, {1, 0}, {2, 0}, {2, 0}}}, // gap 0 + {{{1, 0}, {1, 0}, {1, 0}, {1, 0}}}, // gap 1 + {{{1, 0}, {1, 0}, {1, 0}, {1, 0}}} // gap 2 + }}}}, + {{// Type F3 + {{ + // East + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}, // gap 0 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}, // gap 1 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 + }}, + {{ + // South + {{{3, 0}, {3, 0}, {0, 0}, {0, 0}}}, // gap 0 + {{{3, 0}, {3, 0}, {0, 0}, {0, 0}}}, // gap 1 + {{{3, 0}, {3, 0}, {0, 0}, {0, 0}}} // gap 2 + }}, + {{ + // Free + {{{0, 0}, {0, 0}, {3, 0}, {3, 0}}}, // gap 0 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}}, // gap 1 + {{{0, 0}, {0, 0}, {0, 0}, {0, 0}}} // gap 2 + }}}}}}; /** * Computes the fan-out connection type of a node based on its successors' fan-in structures. @@ -254,7 +246,7 @@ inline std::array * @return Integer code (0–3) indicating the fan-out connection pattern. */ template -uint64_t calculate_fanout_connection_type(const Ntk& ntk, const mockturtle::node& n) +uint8_t calculate_fanout_connection_type(const Ntk& ntk, const mockturtle::node& n) { // order the POs if (ntk.is_po(n)) @@ -276,7 +268,7 @@ uint64_t calculate_fanout_connection_type(const Ntk& ntk, const mockturtle::node { return 2; } - // both fan-puts are connected with a neighbour + // both fan-outs are connected with a neighbor return 3; } @@ -292,10 +284,9 @@ uint64_t calculate_fanout_connection_type(const Ntk& ntk, const mockturtle::node * @param n Node for which to compute the predecessor gap. * @return Gap size (clamped to a maximum of 2). */ - template -uint64_t calculate_predecessor_gap(const Ntk& ntk, const mockturtle::node_map, Ntk>& node2pos, - const uint64_t lvl, const mockturtle::node& n) +uint8_t calculate_predecessor_gap(const Ntk& ntk, const mockturtle::node_map, Ntk>& node2pos, + const uint64_t lvl, const mockturtle::node& n) { // return if in the PI level if (lvl == 0) @@ -314,7 +305,7 @@ uint64_t calculate_predecessor_gap(const Ntk& ntk, const mockturtle::node_map>(node2pos[pre]); - const auto pre2_t = static_cast>(node2pos[pre_neighbour]); + const auto pre2_t = static_cast>(node2pos[pre_neighbor]); assert(pre1_t.y > pre2_t.y); return std::min(pre1_t.y - pre2_t.y - 1, 2); @@ -382,7 +373,7 @@ uint64_t calculate_buffer_connection_type(const Ntk& ntk, const mockturtle::node * @return Orientation code (0–2) describing the node's relative position. */ template -uint64_t calculate_allowed_orientation(const Ntk& ntk, const mockturtle::node& n) +uint8_t calculate_allowed_orientation(const Ntk& ntk, const mockturtle::node& n) { auto fc = fanins(ntk, n); assert(fc.fanin_nodes.size() == 1); @@ -476,13 +467,13 @@ int calculate_start_orientation(const Ntk& ntk, const uint32_t lvl) template std::tuple, std::vector> compute_pr_variables(const Ntk& ntk, const mockturtle::node_map, Ntk>& node2pos, - const uint32_t& lvl) + const uint32_t lvl) { std::vector orientation(ntk.rank_width(lvl)); std::vector new_lines(ntk.rank_width(lvl)); // get the lookup tables for the gate types - const auto& buffer_lu = get_buffer_lookup(); - const auto& fanout_lu = get_fanout_lookup(); + const auto& buffer_lu = BUFFER_LOOKUP; + const auto& fanout_lu = FANOUT_LOOKUP; ntk.foreach_node_in_rank( lvl, @@ -548,8 +539,7 @@ compute_pr_variables(const Ntk& ntk, const mockturtle::node_map -std::vector compute_two_input_indices(const Ntk& ntk, const uint64_t& lvl) +std::vector compute_two_input_indices(const Ntk& ntk, const uint64_t lvl) { std::vector two_input_indices{}; two_input_indices.reserve(ntk.rank_width(lvl)); @@ -589,7 +579,7 @@ std::vector compute_two_input_indices(const Ntk& ntk, const uint64_ template std::vector calculate_two_input_new_lines(const Ntk& ntk, const mockturtle::node_map, Ntk>& node2pos, - const uint32_t& lvl) + const uint32_t lvl) { std::vector cluster_new_lines{}; cluster_new_lines.reserve(ntk.rank_width(lvl)); @@ -605,8 +595,8 @@ std::vector calculate_two_input_new_lines(const Ntk& // compute the max_gap for two fan-ins of anode const auto &pre1 = fc.fanin_nodes[0], &pre2 = fc.fanin_nodes[1]; - const auto pre1_t = static_cast>(node2pos[pre1]), - pre2_t = static_cast>(node2pos[pre2]); + const auto pre1_t = static_cast>(node2pos[pre1]); + const auto pre2_t = static_cast>(node2pos[pre2]); cluster_new_lines.emplace_back(static_cast(pre2_t.y - pre1_t.y - 1)); } @@ -725,7 +715,7 @@ inline void adjust_final_values(std::vector& x, std::vector& template std::pair, std::vector> compute_wiring(const Ntk& ntk, const mockturtle::node_map, Ntk>& node2pos, - const std::vector& new_lines, const uint64_t& lvl) + const std::vector& new_lines, const uint64_t lvl) { // Initialize 2-input indices const auto two_input_indices = compute_two_input_indices(ntk, lvl); @@ -851,7 +841,6 @@ compute_wiring(const Ntk& ntk, const mockturtle::node_map class plane_impl { @@ -915,7 +904,6 @@ class plane_impl else { place_t = {place_t.x - 1, place_t.y + 1}; - // node2pos[n] = layout.move_node(pi2node[n], prec_pos); if (place_t.x == 0) { node2pos[n] = layout.move_node(pi2node[n], place_t); @@ -994,10 +982,11 @@ class plane_impl // if node has two fanins (or three fanins with one of them being constant) else { - const auto &pre1 = fc.fanin_nodes[0], pre2 = fc.fanin_nodes[1]; + const auto& pre1 = fc.fanin_nodes[0]; + const auto& pre2 = fc.fanin_nodes[1]; - auto pre1_t = static_cast>(node2pos[pre1]), - pre2_t = static_cast>(node2pos[pre2]); + auto pre1_t = static_cast>(node2pos[pre1]); + auto pre2_t = static_cast>(node2pos[pre2]); // Resolve new lines if (x[i] != 0) @@ -1039,8 +1028,8 @@ class plane_impl } // place and route POs - std::unordered_map::node, int> count_map; - int add_line = 0; + mockturtle::node_map count_map{ntk, 0}; + int add_line = 0; // the number of outputs on a node is limited to 2, due to fanout substitution ntk.foreach_po( [this, &layout, &first_pos, &place_t, &node2pos, &count_map, &add_line](const auto& po) @@ -1114,11 +1103,21 @@ class plane_impl } private: + /** + * The input network wrapped in a fanout view. + */ mockturtle::fanout_view ntk; - + /** + * The parameters controlling the planar layout from a network embedding. + */ planar_layout_from_network_embedding_params ps; + /** + * Reference to the statistics collected during planar layout generation. + */ planar_layout_from_network_embedding_stats& pst; - + /** + * Primary output counter. + */ uint32_t po_counter{0}; }; @@ -1161,7 +1160,7 @@ Lyt plane(const Ntk& ntk, planar_layout_from_network_embedding_params ps = {}, mincross_params p_min{}; p_min.optimize = false; - auto ntk_min = mincross(ntk, p_min, &st_min); // counts crossings + mincross(ntk, p_min, &st_min); // counts crossings if (st_min.num_crossings != 0) { throw std::invalid_argument("Input network has to be planar"); @@ -1181,4 +1180,5 @@ Lyt plane(const Ntk& ntk, planar_layout_from_network_embedding_params ps = {}, } } // namespace fiction + #endif // FICTION_PLANAR_LAYOUT_FROM_NETWORK_EMBEDDING_HPP From f71bb365c7607ad6add84c159dfb942809087857 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 18 Dec 2025 15:03:50 +0000 Subject: [PATCH 66/67] :memo: Update pyfiction docstrings Signed-off-by: GitHub Actions --- .../pyfiction/pybind11_mkdoc_docstrings.hpp | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index 6d612f1929..bc02b4c21b 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -8018,20 +8018,6 @@ static const char *__doc_fiction_detail_generate_edge_intersection_graph_impl_ps static const char *__doc_fiction_detail_generate_edge_intersection_graph_impl_run = R"doc()doc"; -static const char *__doc_fiction_detail_get_buffer_lookup = -R"doc(Defines a 3D lookup table using `std::array` and encapsulates it -within a function. This table encodes all possible combinations of the -previous level, connection type, orientations, and surrounding spacing -(gaps). Based on these inputs, it returns the corresponding -orientation and spacing configuration for the current buffer.)doc"; - -static const char *__doc_fiction_detail_get_fanout_lookup = -R"doc(Defines a 3D lookup table using `std::array` and encapsulates it -within a function. This table encodes all possible combinations of the -previous level, connection type, orientations, and surrounding spacing -(gaps). Based on these inputs, it returns the corresponding -orientation and spacing configuration for the current fanout.)doc"; - static const char *__doc_fiction_detail_get_offset = R"doc(Utility function to calculate the offset that has to be subtracted from any x-coordinate on the hexagonal layout. @@ -10223,15 +10209,15 @@ Parameter ``p``: Parameter ``st``: Statistics object used to collect runtime and layout information.)doc"; -static const char *__doc_fiction_detail_plane_impl_ntk = R"doc()doc"; +static const char *__doc_fiction_detail_plane_impl_ntk = R"doc(The input network wrapped in a fanout view.)doc"; static const char *__doc_fiction_detail_plane_impl_plane_impl = R"doc()doc"; -static const char *__doc_fiction_detail_plane_impl_po_counter = R"doc()doc"; +static const char *__doc_fiction_detail_plane_impl_po_counter = R"doc(Primary output counter.)doc"; -static const char *__doc_fiction_detail_plane_impl_ps = R"doc()doc"; +static const char *__doc_fiction_detail_plane_impl_ps = R"doc(The parameters controlling the planar layout from a network embedding.)doc"; -static const char *__doc_fiction_detail_plane_impl_pst = R"doc()doc"; +static const char *__doc_fiction_detail_plane_impl_pst = R"doc(Reference to the statistics collected during planar layout generation.)doc"; static const char *__doc_fiction_detail_plane_impl_run = R"doc()doc"; From 77957dd689cafd691aa7242232e55bc395867676 Mon Sep 17 00:00:00 2001 From: benjamin Date: Mon, 19 Jan 2026 10:17:15 +0100 Subject: [PATCH 67/67] :art: added support for virtual PIs --- .../node_duplication_planarization.hpp | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp index c07fcdb10f..526234e60d 100644 --- a/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -126,6 +126,7 @@ initialize_copy_network_duplicates(Ntk const& src) static_assert(mockturtle::has_foreach_pi_v, "Ntk does not implement the foreach_pi method"); static_assert(mockturtle::has_create_pi_v>, "virtual_pi_network does not implement create_pi"); + static_assert(mockturtle::has_rank_position_v && "Ntk does not implement the rank_position function"); mockturtle::node_map>>, Ntk> old2new(src); virtual_pi_network dest; @@ -135,7 +136,22 @@ initialize_copy_network_duplicates(Ntk const& src) { old2new[src.get_constant(true)].push_back(dest.get_constant(true)); } - src.foreach_pi([&](auto const& n) { old2new[n].push_back(dest.create_pi()); }); + if constexpr (has_is_real_pi_v) + { + src.foreach_pi_unranked( + [&](auto const& n) + { + if (src.is_real_pi(n)) + { + old2new[n].push_back(dest.create_pi()); + } + }); + } + else + { + src.foreach_pi_unranked([&](auto const& n) { old2new[n].push_back(dest.create_pi()); }); + } + return {dest, old2new}; } @@ -196,8 +212,10 @@ gather_fanin_signals(const Ntk& ntk, NtkDest& ntk_dest_v, const mockturtle::node // Check if the candidate matches the original fan-in or a duplicate. // Also, verify if the candidate has already reached its fan-out limit. + // THis adjusts for virtual PIS + const auto fanout_limit = ntk.is_pi(fn) ? 1 : ntk.fanout_size(fn); if ((std::find(tgt_signal_v.cbegin(), tgt_signal_v.cend(), candidate_sig) != tgt_signal_v.cend()) && - (ntk_dest_v.fanout_size(node_at_index) < ntk.fanout_size(fn))) + (ntk_dest_v.fanout_size(node_at_index) < fanout_limit)) { // Set the local node_index. if (i == 0) @@ -283,18 +301,27 @@ create_virtual_pi_ntk_from_duplicated_nodes(const Ntk& ntk, const levelized_node // If the node is a PI create virtual PIs for duplicates. if (ntk.is_pi(nd)) { - if (node_status[nd]) + mockturtle::node pi = nd; + if constexpr (has_is_virtual_pi_v) { - const auto new_sig = ntk_dest_v.create_virtual_pi(nd); + if (ntk.is_virtual_pi(pi)) + { + pi = ntk.get_real_pi(pi); + node_status[pi] = true; + } + } + if (node_status[pi]) + { + const auto new_sig = ntk_dest_v.create_virtual_pi(pi); lvl_new.push_back(ntk_dest_v.get_node(new_sig)); old2new_v[nd].push_back(new_sig); } else { - const auto& sigs = old2new_v[nd]; + const auto& sigs = old2new_v[pi]; assert(!sigs.empty()); lvl_new.push_back(ntk_dest_v.get_node(sigs.front())); - node_status[nd] = true; + node_status[pi] = true; } } else @@ -578,6 +605,10 @@ class node_duplication_planarization_impl } else { + if (ntk.fanout_size(node) == 1) + { + vec.insert(vec.begin(), node); + } fo_st = fanout_state::SATURATED; } }