diff --git a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp index 2f8cdd768..21f42df17 100644 --- a/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp +++ b/bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp @@ -2347,6 +2347,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 @@ -3761,6 +3809,20 @@ Parameter ``neutral_defect_spacing_overwrite``: A pair of uint16_t values representing the number of horizontal and vertical SiDBs affected by the given defect type.)doc"; +static const char *__doc_fiction_delete_virtual_pis = +R"doc(Deletes virtual primary inputs from a network. This can mainly be used +for equivalence checking. If the network does not have any virtual PIs +stored, the network is returned. + +Template parameter ``Ntk``: + The type of network. + +Parameter ``ntk``: + The input network. + +Returns: + The resulting network after virtual primary inputs are deleted.)doc"; + static const char *__doc_fiction_dependent_cell_mode = R"doc(An enumeration of modes for the dependent cell.)doc"; static const char *__doc_fiction_dependent_cell_mode_FIXED = @@ -4583,6 +4645,16 @@ Parameter ``bdl_iterator``: static const char *__doc_fiction_detail_critical_temperature_impl_stats = R"doc(Statistics.)doc"; +static const char *__doc_fiction_detail_delete_virtual_pis_impl = R"doc()doc"; + +static const char *__doc_fiction_detail_delete_virtual_pis_impl_delete_virtual_pis_impl = R"doc()doc"; + +static const char *__doc_fiction_detail_delete_virtual_pis_impl_ntk = R"doc()doc"; + +static const char *__doc_fiction_detail_delete_virtual_pis_impl_ntk_topo = R"doc()doc"; + +static const char *__doc_fiction_detail_delete_virtual_pis_impl_run = R"doc()doc"; + static const char *__doc_fiction_detail_delete_wires = R"doc(This function deletes wires from the provided `wiring_reduction_layout` based on the specified coordinates and @@ -7214,6 +7286,39 @@ 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_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 ``delayValue``: + 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"; @@ -10128,6 +10233,32 @@ Parameter ``ps``: Returns: sidb_simulation_result is returned with all results.)doc"; +static const char *__doc_fiction_extended_rank_view = +R"doc(@class extended_rank_view + +If already a rank_interface exists only the depth_view constructor +gets called. + +Template parameter ``Ntk``: + The network type.)doc"; + +static const char *__doc_fiction_extended_rank_view_2 = +R"doc(Deduction guide for `extended_rank_view' + +Template parameter ``T``: + Network type deduced from the construction context of + `extended_rank_view`.)doc"; + +static const char *__doc_fiction_extended_rank_view_3 = +R"doc(Deduction guide for `extended_rank_view` with two constructor +arguments + +Template parameter ``T``: + Network type deduced from the construction context of + `extended_rank_view`.)doc"; + +static const char *__doc_fiction_extended_rank_view_extended_rank_view = R"doc()doc"; + static const char *__doc_fiction_extract_routing_objectives = R"doc(Extracts all routing objectives from the given layout. To this end, all routing paths in the layout are traversed, starting at each PI. @@ -12373,6 +12504,8 @@ Parameter ``m``: static const char *__doc_fiction_gray_code_iterator_start_number = R"doc(Start number of the iteration.)doc"; +static const char *__doc_fiction_handle_virtual_pis = R"doc()doc"; + static const char *__doc_fiction_has_above = R"doc()doc"; static const char *__doc_fiction_has_assign_charge_state = R"doc()doc"; @@ -12405,6 +12538,8 @@ static const char *__doc_fiction_has_foreach_incoming_clocked_zone = R"doc()doc" static const char *__doc_fiction_has_foreach_outgoing_clocked_zone = R"doc()doc"; +static const char *__doc_fiction_has_foreach_real_pi = R"doc()doc"; + static const char *__doc_fiction_has_foreach_sidb_defect = R"doc()doc"; static const char *__doc_fiction_has_foreach_tile = R"doc()doc"; @@ -12417,6 +12552,8 @@ static const char *__doc_fiction_has_get_gate_ports = R"doc()doc"; static const char *__doc_fiction_has_get_layout_name = R"doc()doc"; +static const char *__doc_fiction_has_get_real_pi = R"doc()doc"; + static const char *__doc_fiction_has_get_sidb_defect = R"doc()doc"; static const char *__doc_fiction_has_high_degree_fanin_nodes = @@ -12513,10 +12650,16 @@ static const char *__doc_fiction_has_north_east = R"doc()doc"; static const char *__doc_fiction_has_north_west = R"doc()doc"; +static const char *__doc_fiction_has_num_real_pis = R"doc()doc"; + +static const char *__doc_fiction_has_num_virtual_pis = R"doc()doc"; + static const char *__doc_fiction_has_ordinal_operations = R"doc()doc"; static const char *__doc_fiction_has_post_layout_optimization = R"doc()doc"; +static const char *__doc_fiction_has_remove_virtual_input_nodes = R"doc()doc"; + static const char *__doc_fiction_has_set_layout_name = R"doc()doc"; static const char *__doc_fiction_has_south = R"doc()doc"; @@ -14181,6 +14324,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). + +Returns: + A view of the planarized virtual_pi_network created in the format + of extended_rank_view. + +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.)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, @@ -18405,6 +18594,197 @@ static const char *__doc_fiction_vertical_shift_cartesian = R"doc(\verbatim +-------+ | | | +-------+ | | | +-------+ | | | +-------+ \endverbatim)doc"; +static const char *__doc_fiction_virtual_miter = +R"doc(This method combines two networks to a combinational miter like a +mockturtle::miter. Additionally, either of the input networks can be a +network with virtual inputs, which are duplicated PIs. In this case +the duplicated PIs are deleted and all edges connecting to them are +remapped on their originating PI. + +Therefore, the miter has the same number of inputs and one primary +output. This output is the OR of XORs of all primary output pairs. In +other words, the miter outputs 1 for all input assignments in which +the two input networks differ. + +All networks may have different types. The method returns an optional, +which is `nullopt`, whenever the two input networks don't match in +their number of primary inputs and primary outputs.)doc"; + +static const char *__doc_fiction_virtual_pi_network = R"doc()doc"; + +static const char *__doc_fiction_virtual_pi_network_clone = R"doc(Clones the virtual_pi_network object.)doc"; + +static const char *__doc_fiction_virtual_pi_network_create_virtual_pi = +R"doc(Create a virtual PI, which is a mapping to a real PI. + +This function creates a virtual PI mapping to a real PI in the +network. It adds a PI to the underlying network, but marks it as +virtual and stores a mapping to a real PI. + +Parameter ``real_pi``: + The node representing the real PI in the network. + +Returns: + The signal of the newly created virtual PI.)doc"; + +static const char *__doc_fiction_virtual_pi_network_foreach_real_ci = +R"doc(Iterates over the virtual CIs of the circuit and applies a given +function. + +Template parameter ``Fn``: + The type of the function to be applied. + +Parameter ``fn``: + The function to be applied.)doc"; + +static const char *__doc_fiction_virtual_pi_network_foreach_real_pi = +R"doc(Iterates over the real PIs of the circuit and applies a given +function. + +Template parameter ``Fn``: + The type of the function. + +Parameter ``fn``: + The function to be applied to each primary input.)doc"; + +static const char *__doc_fiction_virtual_pi_network_foreach_virtual_ci = +R"doc(Iterates over the virtual CIs of the circuit and applies a given +function. + +Template parameter ``Fn``: + The type of the function. + +Parameter ``fn``: + The function to be applied to each primary input.)doc"; + +static const char *__doc_fiction_virtual_pi_network_foreach_virtual_pi = +R"doc(Iterates over the virtual PIs of the circuit and applies a given +function. + +Template parameter ``Fn``: + The type of the function. + +Parameter ``fn``: + The function to be applied to each primary input.)doc"; + +static const char *__doc_fiction_virtual_pi_network_get_real_pi = +R"doc(Get the real PI associated with a virtual PI node. + +Parameter ``v_pi``: + The virtual pi node to retrieve the real pi for. + +Returns: + The real pi associated with the virtual pi node.)doc"; + +static const char *__doc_fiction_virtual_pi_network_is_real_ci = +R"doc(Check if a given node is a real CI in the virtual_pi_network. + +Parameter ``n``: + The node to check. + +Returns: + True if the node is a real CI, false otherwise.)doc"; + +static const char *__doc_fiction_virtual_pi_network_is_real_pi = +R"doc(Check if a given node is a real PI. Real PIs are created with +create_pi(). + +Parameter ``n``: + The node to check. + +Returns: + True if the node is a real PI, false otherwise.)doc"; + +static const char *__doc_fiction_virtual_pi_network_is_virtual_ci = +R"doc(Check if a given node is a virtual CI in the virtual_pi_network. + +Parameter ``n``: + The node to check. + +Returns: + True if the node is a virtual CI, false otherwise.)doc"; + +static const char *__doc_fiction_virtual_pi_network_is_virtual_pi = +R"doc(Check if a given node is a virtual PI. Virtual PIs are created with +create_virtual_pi(). + +Parameter ``n``: + The node to check. + +Returns: + True if the node is a virtual PI, false otherwise.)doc"; + +static const char *__doc_fiction_virtual_pi_network_num_real_cis = +R"doc(Get the number of real CIs in the virtual_pi_network. + +Returns: + The number of real CIs as a uint32_t.)doc"; + +static const char *__doc_fiction_virtual_pi_network_num_real_pis = +R"doc(Get the number of real PIs in the virtual_pi_network. + +Returns: + The number of real PIs as a uint32_t.)doc"; + +static const char *__doc_fiction_virtual_pi_network_num_virtual_cis = +R"doc(Get the number of virtual CIs in the virtual_pi_network. + +Returns: + The number of virtual CIs as a uint32_t.)doc"; + +static const char *__doc_fiction_virtual_pi_network_num_virtual_pis = +R"doc(Get the number of virtual PIs in the virtual_pi_network. + +Returns: + The number of virtual PIs as a uint32_t.)doc"; + +static const char *__doc_fiction_virtual_pi_network_real_size = +R"doc(Calculate the real size of the virtual_pi_network. + +The real size of the network is considered the size without virtual +PIs. + +Returns: + The real size of the virtual_pi_network as a uint32_t.)doc"; + +static const char *__doc_fiction_virtual_pi_network_v_storage = R"doc(Shared pointer of the virtual PI storage.)doc"; + +static const char *__doc_fiction_virtual_pi_network_virtual_pi_network = +R"doc(Default constructor for the `virtual_pi_network` class. Initializes +`v_storage` as a shared pointer.)doc"; + +static const char *__doc_fiction_virtual_pi_network_virtual_pi_network_2 = +R"doc(Constructor for the `virtual_pi_network` class that takes a network as +input. It adds the functionalities of the `virtual_pi_network` class +on top of the network. + +Template parameter ``Ntk``: + Network type. + +Parameter ``ntk``: + Input network.)doc"; + +static const char *__doc_fiction_virtual_pi_network_virtual_pi_network_3 = +R"doc(Constructor for the `virtual_pi_network` class that takes a network +and a shared pointer to a `virtual_storage` object. THis is used for +cloning. + +Template parameter ``Ntk``: + Network type. + +Parameter ``ntk``: + Input network. + +Parameter ``s``: + Shared pointer to the `virtual_storage` object to be used by this + `virtual_pi_network`.)doc"; + +static const char *__doc_fiction_virtual_pi_network_virtual_storage = R"doc()doc"; + +static const char *__doc_fiction_virtual_pi_network_virtual_storage_map_virt_to_real_pi = R"doc(Map from virtual_pis to real_pis.)doc"; + +static const char *__doc_fiction_virtual_pi_network_virtual_storage_virtual_inputs = R"doc(Vector storing virtual_inputs.)doc"; + static const char *__doc_fiction_volume = R"doc(Computes the volume of a given coordinate assuming its origin is (0, 0, 0). Calculates :math:`(|x| + 1) \cdot (|y| + 1) \cdot (|z| + 1)` by diff --git a/experiments/legalization/legalization.cpp b/experiments/legalization/legalization.cpp new file mode 100644 index 000000000..394ce1243 --- /dev/null +++ b/experiments/legalization/legalization.cpp @@ -0,0 +1,214 @@ +// +// Created by benjamin on 8/26/24. +// + +#include "experiments.hpp" +#include "fiction/algorithms/network_transformation/fanout_substitution.hpp" +#include "fiction/algorithms/properties/check_planarity_balanced.hpp" +#include "fiction/layouts/cartesian_layout.hpp" +#include "fiction/layouts/clocked_layout.hpp" +#include "fiction/layouts/coordinates.hpp" +#include "fiction/layouts/gate_level_layout.hpp" +#include "fiction/layouts/tile_based_layout.hpp" +#include "fiction_experiments.hpp" + +#include +#include +#include // scalable heuristic for physical design +#include +#include +#include // read networks from files +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +template +Ntk read_ntk(const std::string& name) +{ + fmt::print("[i] processing {}\n", name); + + std::ostringstream os{}; + + fiction::network_reader reader{fiction_experiments::benchmark_path(name), os}; + + const auto nets = reader.get_networks(); + + return *nets.front(); +} + +int main() // NOLINT +{ + experiments::experiment + wiring_reduction_exp{"planarization", "benchmark", "inputs", "virt_inputs", + "outputs", "initial nodes", "nodes buffered", "nodes planarized", + "is_planar", "equivalent(ntk)", "equivalent(lyt)"}; + + for (const auto& entry : + std::filesystem::directory_iterator("/home/benjamin/Documents/Repositories/working/fiction/benchmarks/IWLS93")) + { + continue; + fmt::print("[i] processing {}\n", entry.path().filename().string()); + + /*if ( "sqrt8ml.v" != entry.path().filename().string()) + { + continue; + }*/ + /*if ( "C1355.v" != entry.path().filename().string()) + { + continue; + }*/ + if ("C432.v" == entry.path().filename().string()) + { + continue; + } + if ("ex4p.v" == entry.path().filename().string()) + { + continue; + } + if ("apex1.v" == entry.path().filename().string()) + { + continue; + } + + std::ostringstream os{}; + + fiction::network_reader reader{entry.path().string(), os}; + + const auto nets = reader.get_networks(); + + auto benchmark_network = *nets.front(); + + fiction::network_balancing_params ps; + ps.unify_outputs = true; + + const auto b = fiction::network_balancing( + fiction::fanout_substitution(benchmark_network), ps); + + // happens with exp4.v + /*const auto fc = fanins(_b, 9213); + + std::cout << "Node 9213: " << fc.fanin_nodes.size() << std::endl; + std::cout << "Node 9213: " << _b.is_buf(9213) << std::endl;*/ + + // happens with apex1.v + /*const auto fc = fanins(_b, 8771); + + std::cout << "Node 9213: " << fc.fanin_nodes.size() << std::endl; + std::cout << "Node 9213: " << _b.is_buf(8771) << std::endl;*/ + + if (b.size() > 10000) + { + continue; + } + + const auto planarized_b = fiction::node_duplication_planarization(b); + const auto is_planar = fiction::check_planarity(planarized_b); + if (planarized_b.size() > 20000) + { + continue; + } + mockturtle::equivalence_checking_stats st; + const auto cec_m = mockturtle::equivalence_checking( + *fiction::virtual_miter(benchmark_network, planarized_b), {}, &st); + assert(cec_m.has_value()); + + fiction::orthogonal_physical_design_stats stats{}; + using gate_layout = fiction::gate_level_layout< + fiction::clocked_layout>>>; + auto layout = fiction::orthogonal(planarized_b, {}, &stats); + + const fiction::gate_level_drv_params ps_d{}; + fiction::gate_level_drv_stats st_d{}; + + fiction::gate_level_drvs(layout, ps_d, &st_d); + + const auto miter = mockturtle::miter(planarized_b, layout); + bool eq = false; + if (miter) + { + mockturtle::equivalence_checking_stats st_o; + + const auto ce = mockturtle::equivalence_checking(*miter, {}, &st_o); + eq = ce.value(); + } + wiring_reduction_exp(entry.path().filename().string(), benchmark_network.num_pis(), + planarized_b.num_virtual_pis(), benchmark_network.num_pos(), benchmark_network.num_gates(), + b.num_gates(), planarized_b.num_gates(), is_planar, cec_m.value(), eq); + wiring_reduction_exp.save(); + wiring_reduction_exp.table(); + } + + static constexpr const std::uint64_t bench_select = + (fiction_experiments::trindade16 | fiction_experiments::fontes18); + for (const auto& benchmark : fiction_experiments::all_benchmarks(bench_select)) + { + const auto benchmark_network = read_ntk(benchmark); + + fiction::network_balancing_params ps; + ps.unify_outputs = true; + + const auto b = fiction::network_balancing( + fiction::fanout_substitution(benchmark_network), ps); + + const auto planarized_b = fiction::node_duplication_planarization(b); + + const auto is_planar = fiction::check_planarity(planarized_b); + + if (planarized_b.size() > 20000) + { + wiring_reduction_exp(benchmark, benchmark_network.num_pis(), planarized_b.num_virtual_pis(), + benchmark_network.num_pos(), benchmark_network.num_gates(), b.num_gates(), + planarized_b.num_gates(), is_planar, false, false); + wiring_reduction_exp.save(); + wiring_reduction_exp.table(); + continue; + } + + // check equivalence + mockturtle::equivalence_checking_stats st; + const auto cec_m = mockturtle::equivalence_checking( + *fiction::virtual_miter(benchmark_network, planarized_b), {}, &st); + assert(cec_m.has_value()); + + fiction::orthogonal_physical_design_stats stats{}; + using gate_layout = fiction::gate_level_layout< + fiction::clocked_layout>>>; + auto layout = fiction::orthogonal(planarized_b, {}, &stats); + + fiction::gate_level_drv_params ps_d{}; + fiction::gate_level_drv_stats st_d{}; + + fiction::gate_level_drvs(layout, ps_d, &st_d); + + const auto miter = mockturtle::miter(planarized_b, layout); + bool eq = false; + if (miter) + { + mockturtle::equivalence_checking_stats st_o; + + const auto ce = mockturtle::equivalence_checking(*miter, {}, &st_o); + eq = ce.value(); + } + + // log results + wiring_reduction_exp(benchmark, benchmark_network.num_pis(), planarized_b.num_virtual_pis(), + benchmark_network.num_pos(), benchmark_network.num_gates(), b.num_gates(), + planarized_b.num_gates(), is_planar, cec_m.value(), eq); + + wiring_reduction_exp.save(); + wiring_reduction_exp.table(); + } + + return EXIT_SUCCESS; +} diff --git a/include/fiction/algorithms/network_transformation/buffer_removal.hpp b/include/fiction/algorithms/network_transformation/buffer_removal.hpp new file mode 100644 index 000000000..76b172a99 --- /dev/null +++ b/include/fiction/algorithms/network_transformation/buffer_removal.hpp @@ -0,0 +1,318 @@ +// +// Created by benjamin on 9/24/24. +// + +#ifndef FICTION_BUFFER_REMOVAL_HPP +#define FICTION_BUFFER_REMOVAL_HPP + +#include "fiction/traits.hpp" +#include "fiction/utils/name_utils.hpp" + +#include +#include +#include + +#include +#include +#include + +#if (PROGRESS_BARS) +#include +#endif + +namespace fiction +{ + +/** + * Initializes a copy of the source network like in mockturtle::initialize_copy_network. Additionally, this modification + * supports virtual PIs created via `virtual_pi_network. + * + * @tparam NtkDest The type of the destination network. + * @tparam NtkSrc The type of the source network. + * @param src The source network to be copied. + * @return A pair of the destination network and the node map. + */ +template +std::pair, NtkSrc>> +initialize_copy_network_v(NtkSrc const& src) +{ + static_assert(mockturtle::is_network_type_v, "NtkDest is not a network type"); + static_assert(mockturtle::is_network_type_v, "NtkSrc is not a network type"); + + static_assert(mockturtle::has_get_constant_v, "NtkDest does not implement the get_constant method"); + static_assert(mockturtle::has_create_pi_v, "NtkDest does not implement the create_pi method"); + static_assert(mockturtle::has_get_constant_v, "NtkSrc does not implement the get_constant method"); + static_assert(mockturtle::has_get_node_v, "NtkSrc does not implement the get_node method"); + static_assert(mockturtle::has_foreach_pi_v, "NtkSrc does not implement the foreach_pi method"); + + mockturtle::node_map, NtkSrc> old2new(src); + NtkDest dest; + old2new[src.get_constant(false)] = dest.get_constant(false); + if (src.get_node(src.get_constant(true)) != src.get_node(src.get_constant(false))) + { + old2new[src.get_constant(true)] = dest.get_constant(true); + } + if constexpr (fiction::has_foreach_real_pi_v) + { + src.foreach_real_pi([&](auto const& n) { old2new[n] = dest.create_pi(); }); + src.foreach_virtual_pi([&](auto const& n) { old2new[n] = dest.create_virtual_pi(src.get_real_pi(n)); }); + } + else + { + src.foreach_pi([&](auto const& n) { old2new[n] = dest.create_pi(); }); + } + + return {dest, old2new}; +} + +namespace detail +{ + +template +class remove_buffer_impl +{ + public: + using signal = typename Ntk::signal; + using node = typename Ntk::node; + using TopoNtkSrc = mockturtle::topo_view; + + explicit remove_buffer_impl(const Ntk& ntk_src) : ntk{ntk_src} {} + + signal skip_buffer_chain_rec(node n, signal s, mockturtle::node_map& old2new) + { + // Buffer only have one fan-in && make sure it is not a fan-out node + if (ntk.is_buf(n) && ntk.fanout_size(n) == 1) + { + const auto fc = fanins(ntk, n); + assert(fc.fanin_nodes.size() == 1 && "Error: Node is not a buffer -> check the conditions for this path"); + const auto fn = fc.fanin_nodes[0]; + const auto tgt_signal = old2new[fn]; + + // Recursive call: Traverse if the fanin node is also a buffer + return skip_buffer_chain_rec(fn, tgt_signal, old2new); + } + + // If the node 'n' is not a buffer, return the current signal 's' + return s; + } + + Ntk run() + { + auto init = fiction::initialize_copy_network_v(ntk); + auto& ntk_dest = init.first; + auto& old2new = init.second; + + const auto gather_fanin_signals = [this, &ntk_dest, &old2new](const auto& n) + { + std::vector children{}; + + ntk.foreach_fanin(n, + [this, &ntk_dest, &old2new, &children](const auto& f) + { + const auto fn = ntk.get_node(f); + const auto tgt_signal = old2new[fn]; + + // skip buffer chains + const auto tgt_signal_wo_buf = skip_buffer_chain_rec(fn, tgt_signal, old2new); + + children.emplace_back(ntk.is_complemented(f) ? + ntk_dest.create_not(tgt_signal_wo_buf) : + tgt_signal_wo_buf); + }); + + return children; + }; + +#if (PROGRESS_BARS) + // initialize a progress bar + mockturtle::progress_bar bar{static_cast(ntk.num_gates()), "[i] network conversion: |{0}|"}; +#endif + + ntk.foreach_gate( + [&, this](const auto& g, [[maybe_unused]] auto i) + { + // skip buffer nodes + if (ntk.is_buf(g) && ntk.fanout_size(g) == 1) + { + return true; + } + // the rest of the code is taken from `fiction::convert_network`. + auto children = gather_fanin_signals(g); + +#if (PROGRESS_BARS) + // update progress + bar(i); +#endif + + if constexpr (mockturtle::has_is_and_v && mockturtle::has_create_and_v) + { + if (ntk.is_and(g)) + { + old2new[g] = ntk_dest.create_and(children[0], children[1]); + return true; + } + } + if constexpr (mockturtle::has_is_or_v && mockturtle::has_create_or_v) + { + if (ntk.is_or(g)) + { + old2new[g] = ntk_dest.create_or(children[0], children[1]); + return true; + } + } + if constexpr (mockturtle::has_is_xor_v && mockturtle::has_create_xor_v) + { + if (ntk.is_xor(g)) + { + old2new[g] = ntk_dest.create_xor(children[0], children[1]); + return true; + } + } + if constexpr (mockturtle::has_is_maj_v && mockturtle::has_create_maj_v) + { + if (ntk.is_maj(g)) + { + old2new[g] = ntk_dest.create_maj(children[0], children[1], children[2]); + return true; + } + } + if constexpr (mockturtle::has_is_nary_and_v && mockturtle::has_create_nary_and_v) + { + if (ntk.is_nary_and(g)) + { + old2new[g] = ntk_dest.create_nary_and(children); + return true; + } + } + if constexpr (mockturtle::has_is_nary_or_v && mockturtle::has_create_nary_or_v) + { + if (ntk.is_nary_or(g)) + { + old2new[g] = ntk_dest.create_nary_or(children); + return true; + } + } + if constexpr (mockturtle::has_is_nary_xor_v && mockturtle::has_create_nary_xor_v) + { + if (ntk.is_nary_xor(g)) + { + old2new[g] = ntk_dest.create_nary_xor(children); + return true; + } + } + if constexpr (fiction::has_is_buf_v && mockturtle::has_create_buf_v) + { + if (ntk.is_buf(g)) + { + old2new[g] = ntk_dest.create_buf(children[0]); + return true; + } + } + if constexpr (mockturtle::has_node_function_v && mockturtle::has_create_node_v) + { + old2new[g] = ntk_dest.create_node(children, ntk.node_function(g)); + return true; + } + assert(false); + return true; + }); + + ntk.foreach_po( + [this, &ntk_dest, &old2new](const auto& po) + { + const auto tgt_signal = old2new[ntk.get_node(po)]; + + // skip buffer chains + const auto tgt_signal_wo_buf = skip_buffer_chain_rec(ntk.get_node(po), tgt_signal, old2new); + + const auto tgt_po = + ntk.is_complemented(po) ? ntk_dest.create_not(tgt_signal_wo_buf) : tgt_signal_wo_buf; + + ntk_dest.create_po(tgt_po); + }); + + // restore signal names if applicable + fiction::restore_names(ntk, ntk_dest, old2new); + + return ntk_dest; + } + + private: + Ntk ntk; +}; +} // namespace detail + +/** + * Checks if a given logic network contains a buffer gate. + * + * @tparam Ntk The type of the logic network. + * @param ntk The logic network. + * @return true if the network contains a buffer gate, false otherwise. + */ +template +bool constains_buffer(Ntk ntk) +{ + bool has_buf = false; + ntk.foreach_gate( + [&ntk, &has_buf](const auto& g) + { + if (ntk.is_buf(g)) + { + has_buf = true; + } + }); + return has_buf; +} + +/** + * Removes buffer nodes in a network. The idea is the same as in mockturtle::remove_buffer_chains()`, but node deletion + * is not supported for `buffered_klut_networks`, and therefore, an approach similar to `fiction::convert_network` is + * chosen. + * + * @note Fanout nodes are also flagged with `is_buf` in fiction, but not removed by this class. + * + * @tparam Ntk The network type. + * @param ntk The original network. + * @return The network with buffers removed. + */ +template +Ntk remove_buffer(const Ntk& ntk) +{ + static_assert(mockturtle::is_network_type_v, "Ntk is not a network type"); + + static_assert(mockturtle::has_is_buf_v, "Ntk does not implement the is_buf function"); + static_assert(mockturtle::has_get_node_v, "Ntk does not implement the get_node function"); + static_assert(mockturtle::has_is_complemented_v, "Ntk does not implement the is_complemented function"); + static_assert(mockturtle::has_foreach_pi_v, "Ntk does not implement the foreach_pi function"); + static_assert(mockturtle::has_foreach_gate_v, "Ntk does not implement the foreach_gate function"); + static_assert(mockturtle::has_foreach_po_v, "Ntk does not implement the foreach_po function"); + static_assert(mockturtle::has_foreach_fanin_v, "Ntk does not implement the foreach_fanin function"); + + static_assert(mockturtle::has_get_constant_v, "Ntk does not implement the get_constant function"); + + static_assert(mockturtle::has_create_pi_v, "Ntk does not implement the create_pi function"); + static_assert(mockturtle::has_create_po_v, "Ntk does not implement the create_po function"); + static_assert(mockturtle::has_create_not_v, "Ntk does not implement the create_not function"); + static_assert(mockturtle::has_create_and_v, "Ntk does not implement the create_and function"); + static_assert(mockturtle::has_create_or_v, "Ntk does not implement the create_or function"); + static_assert(mockturtle::has_create_xor_v, "Ntk does not implement the create_xor function"); + static_assert(mockturtle::has_create_maj_v, "Ntk does not implement the create_maj function"); + // TODO handle ci/ro/etc... + + assert(ntk.is_combinational() && "Network has to be combinational"); + + if (!constains_buffer(ntk)) + { + return ntk; + } + + detail::remove_buffer_impl p{ntk}; + + auto result = p.run(); + + return result; +} + +} // namespace fiction + +#endif // FICTION_BUFFER_REMOVAL_HPP 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 000000000..1dc50ee07 --- /dev/null +++ b/include/fiction/algorithms/network_transformation/node_duplication_planarization.hpp @@ -0,0 +1,716 @@ +// +// Created by benjamin on 11.06.24. +// + +#ifndef FICTION_NODE_DUPLICATION_PLANARIZATION_HPP +#define FICTION_NODE_DUPLICATION_PLANARIZATION_HPP + +#include "fiction/networks/views/extended_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 delayValue 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 NtkSrc Type of the source 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>>, NtkSrc>> +initialize_copy_network_virtual(NtkSrc const& src) +{ + static_assert(mockturtle::is_network_type_v>, "NtkDest is not a network type"); + static_assert(mockturtle::is_network_type_v, "NtkSrc is not a network type"); + + static_assert(mockturtle::has_get_constant_v>, + "NtkDest does not implement the get_constant method"); + static_assert(mockturtle::has_create_pi_v>, + "NtkDest does not implement the create_pi method"); + static_assert(mockturtle::has_is_pi_v>, "NtkDest does not implement the is_pi method"); + static_assert(mockturtle::has_create_not_v>, + "NtkDest does not implement the create_not method"); + static_assert(mockturtle::has_get_constant_v, "NtkSrc does not implement the get_constant method"); + static_assert(mockturtle::has_get_node_v, "NtkSrc does not implement the get_node method"); + static_assert(mockturtle::has_foreach_pi_v, "NtkSrc does not implement the foreach_pi method"); + static_assert(mockturtle::has_foreach_po_v, "NtkSrc does not implement the foreach_po method"); + static_assert(mockturtle::has_is_complemented_v, "NtkSrc does not implement the is_complemented method"); + static_assert(mockturtle::has_foreach_fanin_v, "NtkSrc does not implement the foreach_fanin method"); + + mockturtle::node_map>>, NtkSrc> 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}; +} + +/** + * 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_virtual(ntk); + auto& ntk_dest_v = init_v.first; + auto& old2new_v = init_v.second; + + /** + * 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[edge_it] 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[edge_it] against all entries in old2new[n], + * then try lvl[edge_it+1] then try lvl[edge_it+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. + */ + const auto gather_fanin_signals = [&](const auto& n, const auto& lvl, std::size_t& edge_it) + { + std::vector children{}; + const auto edge_it_int = edge_it; + +#ifndef NDEBUG + // Useful Debug parameter to check that fanins need to be adjacent. + int64_t first_fi_edge_it = -1; +#endif + + ntk.foreach_fanin(n, + [&](const auto& f) + { + const auto fn = ntk.get_node(f); + auto tgt_signal_v = old2new_v[fn]; + + assert(edge_it_int < lvl.size() && "The fanin iterator is out of scope"); + + bool break_loop = false; + for (const auto& possible_node : tgt_signal_v) + { + const auto it = ntk.fanin_size(n) + 1; + if (ntk.fanin_size(n) == children.size()) + { + break; + } + for (std::size_t i = 0; i < it; i++) + { + if (edge_it_int + i < lvl.size() && lvl[edge_it_int + i] == possible_node && + ntk_dest_v.fanout_size(possible_node) < 2) + { +#ifndef NDEBUG + if (first_fi_edge_it != -1) + { + if (!ntk.is_maj(n)) + { + assert(edge_it_int + i == first_fi_edge_it + 1 || + edge_it_int + i == first_fi_edge_it - 1); + } + } + first_fi_edge_it = static_cast(edge_it_int + i); +#endif + children.emplace_back(possible_node); + if (edge_it_int + i > edge_it) + { + edge_it = edge_it_int + i; + } + break_loop = true; + break; + } + } + if (break_loop) + { + break; + } + } + }); + return children; + }; + + std::size_t edge_it = 0; + for (std::size_t i = ntk_lvls.size(); i-- > 0;) + { + edge_it = 0; + const auto& lvl = ntk_lvls[i]; + auto& lvl_new = ntk_lvls_new[i]; + for (const auto& nd : lvl) + { + 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(nd, ntk_lvls_new[i + 1], edge_it); + 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 do not get duplicated since the algorithm starts at the POs and duplicates other nodes according + // to 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} + {} + + /** + * Computes the delay in a given slice (each possible order of node_pairs) of an H-graph. + * + * This function iterates over the fanins of the given node and computes the delay for all possible orders + * of these nodes that form a node_pair. The delay computation depends on the node's connections and position + * within the graph. If there is a connection between two node_pairs, the delay is incremented by 1. If not, + * the delay is incremented by 2. Default delay for the first node is 1. If a node_pair doesn't have a connection + * and its delay (when increased by two) is less than the existing delay, then this node_pair's delay is updated. + * + * The processed node_pairs are pushed back to the 'lvl_pairs' data 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 + * level. + */ + 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.size() == 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 a minimal crossing view (implement mincross.c from + // graphviz) + + // this solves equal paths, 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 shortest path (delay) 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. + */ + 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 + 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]] extended_rank_view> + run(std::vector>>& ntk_lvls_new) + { + std::vector> pos{}; + pos.reserve(ntk.num_pos()); + ntk.foreach_po( + [this, &pos](const auto 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> v_level{}; + v_level.reserve(pos.size()); + // Process the first level + for (const auto& po : pos) + { + // Recalculate the levels to start from the pos + fis.clear(); + compute_slice_delays(po); + v_level.push_back(po); + } + + ntk_lvls.push_back(v_level); + v_level.clear(); + + v_level = compute_node_order(); + + bool f_final_level = check_final_level(v_level); + + // Process all other levels + while (!v_level.empty() && !f_final_level) + { + // Push the level to the network + ntk_lvls.push_back(v_level); + lvl_pairs.clear(); + // Store the nodes of the next level + for (const auto& cur_node : v_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 + v_level.clear(); + // Compute the next level + v_level = compute_node_order(); + // Check if we are at the final level + f_final_level = check_final_level(v_level); + } + // Push the final level (PIs) + if (f_final_level) + { + ntk_lvls.push_back(v_level); + } + + // create virtual pi network + const 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 + return extended_rank_view(virtual_ntk, ntk_lvls_new); + } + + 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). + * + * @return A view of the planarized virtual_pi_network created in the format of extended_rank_view. + * + * @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. + */ +template +[[nodiscard]] extended_rank_view> +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"); + // ToDo: This might also be implemented, so that it works for all Ntk types + static_assert(mockturtle::has_create_node_v, "NtkSrc does not implement the create_node 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/properties/check_planarity_balanced.hpp b/include/fiction/algorithms/properties/check_planarity_balanced.hpp new file mode 100644 index 000000000..6a4da0a84 --- /dev/null +++ b/include/fiction/algorithms/properties/check_planarity_balanced.hpp @@ -0,0 +1,122 @@ +// +// Created by benjamin on 17.07.24. +// + +#ifndef FICTION_CHECK_PLANARITY_BALANCED_HPP +#define FICTION_CHECK_PLANARITY_BALANCED_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_BALANCED_HPP diff --git a/include/fiction/algorithms/verification/virtual_miter.hpp b/include/fiction/algorithms/verification/virtual_miter.hpp new file mode 100644 index 000000000..2061a8aaf --- /dev/null +++ b/include/fiction/algorithms/verification/virtual_miter.hpp @@ -0,0 +1,105 @@ +// +// Created by benjamin on 7/31/24. +// + +#ifndef FICTION_VIRTUAL_MITER_HPP +#define FICTION_VIRTUAL_MITER_HPP + +#include "fiction/networks/virtual_pi_network.hpp" + +#include + +#include +#include +#include + +namespace fiction +{ + +template +auto handle_virtual_pis(const Ntk& network) +{ + if constexpr (has_num_virtual_pis_v) + { + return delete_virtual_pis(network); + } + else + { + return network; + } +} + +/** + * This method combines two networks to a combinational miter like a mockturtle::miter. Additionally, either of the + * input networks can be a network with virtual inputs, which are duplicated PIs. In this case + * the duplicated PIs are deleted and all edges connecting to them are remapped on their originating PI. + * + * Therefore, the miter has the same number of inputs and one primary output. This output is the OR of XORs of all + * primary output pairs. In other words, the miter outputs 1 for all input assignments in which the two input networks + * differ. + * + * All networks may have different types. The method returns an optional, which is `nullopt`, whenever the two input + * networks don't match in their number of primary inputs and primary outputs. + */ +template +std::optional virtual_miter(const NtkSource1& ntk1_in, const NtkSource2& ntk2_in) +{ + static_assert(mockturtle::is_network_type_v, "NtkSource1 is not a network type"); + static_assert(mockturtle::is_network_type_v, "NtkSource2 is not a network type"); + static_assert(mockturtle::is_network_type_v, "NtkDest is not a network type"); + + static_assert(mockturtle::has_num_pis_v, "NtkSource1 does not implement the num_pis method"); + static_assert(mockturtle::has_num_pos_v, "NtkSource1 does not implement the num_pos method"); + static_assert(mockturtle::has_num_pis_v, "NtkSource2 does not implement the num_pis method"); + static_assert(mockturtle::has_num_pos_v, "NtkSource2 does not implement the num_pos method"); + static_assert(mockturtle::has_create_pi_v, "NtkDest does not implement the create_pi method"); + static_assert(mockturtle::has_create_po_v, "NtkDest does not implement the create_po method"); + static_assert(mockturtle::has_create_xor_v, "NtkDest does not implement the create_xor method"); + static_assert(mockturtle::has_create_nary_or_v, "NtkDest does not implement the create_nary_or method"); + + auto ntk1 = handle_virtual_pis(ntk1_in); + auto ntk2 = handle_virtual_pis(ntk2_in); + + /* both networks must have same number of inputs and outputs */ + if ((ntk1.num_pis() != ntk2.num_pis()) || (ntk1.num_pos() != ntk2.num_pos())) + { + return std::nullopt; + } + + /* create primary inputs */ + NtkDest dest; + std::vector> pis; + for (auto i = 0u; i < ntk1.num_pis(); ++i) + { + pis.push_back(dest.create_pi()); + } + + /* copy networks */ + const auto pos1 = cleanup_dangling(ntk1, dest, pis.begin(), pis.end()); + const auto pos2 = cleanup_dangling(ntk2, dest, pis.begin(), pis.end()); + + if constexpr (mockturtle::has_EXODC_interface_v) + { + ntk1.build_oe_miter(dest, pos1, pos2); + return dest; + } + if constexpr (mockturtle::has_EXODC_interface_v) + { + ntk2.build_oe_miter(dest, pos1, pos2); + return dest; + } + + /* create XOR of output pairs */ + std::vector> xor_outputs; + std::transform(pos1.begin(), pos1.end(), pos2.begin(), std::back_inserter(xor_outputs), + [&](auto const& o1, auto const& o2) { return dest.create_xor(o1, o2); }); + + /* create big OR of XOR gates */ + dest.create_po(dest.create_nary_or(xor_outputs)); + + return dest; +} + +} // namespace fiction + +#endif // FICTION_VIRTUAL_MITER_HPP diff --git a/include/fiction/networks/views/extended_rank_view.hpp b/include/fiction/networks/views/extended_rank_view.hpp new file mode 100644 index 000000000..9a85ad48e --- /dev/null +++ b/include/fiction/networks/views/extended_rank_view.hpp @@ -0,0 +1,547 @@ +// +// Created by benjamin on 18.06.24. +// + +#ifndef FICTION_EXTENDED_RANK_VIEW_HPP +#define FICTION_EXTENDED_RANK_VIEW_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace fiction +{ +/** + * @class extended_rank_view. Extends the mockturtle::rank_view + * + * Provides a view with node ranks for given networks. It adds functionalities to modify ranks. Most importantly, the + * new `init_ranks()` function allows an array of nodes to be provided, which sets the levels and ranks of the nodes in + * the network. Additionally, the `modify_rank()` function allows to pass a vector to assign the ranks of nodes in one + * specific level. + * + * This class template is specialized depending on whether the provided network has a rank interface or not. The rank + * interface is detected using the type traits defined in mockturtle. Specifically, + * - has_rank_position_v + * - has_at_rank_position_v + * - has_swap_v + * - has_width_v + * - has_foreach_node_in_rank_v + * - has_foreach_gate_in_rank_v + * + * @tparam Ntk The network type. + * @tparam has_rank_interface Boolean flag checked compile-time, determines if the provided Ntk supports the rank + * interface + */ +template && mockturtle::has_at_rank_position_v && + mockturtle::has_swap_v && mockturtle::has_width_v && + mockturtle::has_foreach_node_in_rank_v && mockturtle::has_foreach_gate_in_rank_v> +class extended_rank_view +{}; + +/** + * @class extended_rank_view + * + * If already a rank_interface exists only the depth_view constructor gets called. + * + * @tparam Ntk The network type. + */ +template +class extended_rank_view : public mockturtle::depth_view +{ + public: + extended_rank_view(const Ntk& ntk) : mockturtle::depth_view(ntk) {} +}; + +/** + * @class extended_rank_view + * + * If no rank_interface exists, inherits from mockturtle::depth_view and initializes ranks for the network. + * + * @tparam Ntk The network type. + */ +template +class extended_rank_view : public mockturtle::depth_view +{ + public: + static constexpr bool is_topologically_sorted = true; + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + /** + * Default constructor. + * Constructs an empty extended_rank_view object, initializes base class and class variables, and verifies that the + * Network Type (Ntk) needs to support certain methods. + */ + explicit extended_rank_view() : mockturtle::depth_view(), rank_pos{*this}, ranks{}, max_rank_width{0} + { + static_assert(mockturtle::is_network_type_v, "Ntk is not a network type"); + static_assert(mockturtle::has_foreach_node_v, "Ntk does not implement the foreach_node method"); + static_assert(mockturtle::has_get_node_v, "Ntk does not implement the get_node method"); + static_assert(mockturtle::has_num_pis_v, "Ntk does not implement the num_pis method"); + static_assert(mockturtle::has_is_ci_v, "Ntk does not implement the is_ci method"); + static_assert(mockturtle::has_is_constant_v, "Ntk does not implement the is_constant method"); + + add_event = Ntk::events().register_add_event([this](auto const& n) { on_add(n); }); + } + + /** + * Constructs an extended_rank_view from an existing network. + * Calls the base class constructor with the provided network, initializes class members, and registers network + * events. Also, it ensures that the Network Type (Ntk) needs to support certain methods. + * + * @param ntk Reference to the network. + */ + explicit extended_rank_view(const Ntk& ntk) : + mockturtle::depth_view{ntk}, + rank_pos{ntk}, + ranks{this->depth() + 1}, + max_rank_width{0} + { + static_assert(mockturtle::is_network_type_v, "Ntk is not a network type"); + static_assert(mockturtle::has_foreach_node_v, "Ntk does not implement the foreach_node method"); + static_assert(mockturtle::has_get_node_v, "Ntk does not implement the get_node method"); + static_assert(mockturtle::has_num_pis_v, "Ntk does not implement the num_pis method"); + static_assert(mockturtle::has_is_ci_v, "Ntk does not implement the is_ci method"); + static_assert(mockturtle::has_is_constant_v, "Ntk does not implement the is_constant method"); + + init_ranks(); + + add_event = Ntk::events().register_add_event([this](auto const& n) { on_add(n); }); + } + + /** + * Constructs an extended_rank_view from an existing network and a specific initial rank configuration. + * + * @param ntk Reference to the network. + * @param ranks A vector of vectors specifying initial ranks for the nodes within the network. + */ + explicit extended_rank_view(const Ntk& ntk, const std::vector>& ranks) : + mockturtle::depth_view{ntk}, + rank_pos{ntk}, + ranks{this->depth() + 1}, + max_rank_width{0} + { + static_assert(mockturtle::is_network_type_v, "Ntk is not a network type"); + static_assert(mockturtle::has_foreach_node_v, "Ntk does not implement the foreach_node method"); + static_assert(mockturtle::has_get_node_v, "Ntk does not implement the get_node method"); + static_assert(mockturtle::has_num_pis_v, "Ntk does not implement the num_pis method"); + static_assert(mockturtle::has_is_ci_v, "Ntk does not implement the is_ci method"); + static_assert(mockturtle::has_is_constant_v, "Ntk does not implement the is_constant method"); + + init_ranks(ranks); + + add_event = Ntk::events().register_add_event([this](auto const& n) { on_add(n); }); + } + + /** + * Copy constructor creates a new extended_rank_view by copying the content of another extended_rank_view. + * + * @param other The other extended_rank_view object to be copied. + */ + extended_rank_view(const extended_rank_view& other) : + mockturtle::depth_view(other), + rank_pos{other.rank_pos}, + ranks{other.ranks}, + max_rank_width{other.max_rank_width} + { + add_event = Ntk::events().register_add_event([this](auto const& n) { on_add(n); }); + } + + /** + * Overloaded assignment operator for copying `extended_rank_view` content of another `extended_rank_view` object. + * + * @param other The source `extended_rank_view` object whose contents are being copied. + * @return A reference to the current object, enabling chain assignments. + */ + extended_rank_view& operator=(const extended_rank_view& other) + { + /* delete the event of this network */ + Ntk::events().release_add_event(add_event); + + /* update the base class */ + this->_storage = other._storage; + this->_events = other._events; + + /* copy */ + rank_pos = other.rank_pos; + ranks = other.ranks; + max_rank_width = other.max_rank_width; + + /* register new event in the other network */ + add_event = Ntk::events().register_add_event([this](auto const& n) { on_add(n); }); + + return *this; + } + + /** + * Destructor for extended_rank_view. + */ + ~extended_rank_view() + { + Ntk::events().release_add_event(add_event); + } + /** + * Returns the rank position of a node. + * + * @param n Node to get the rank position of. + * @return Rank position of node `n`. + */ + uint32_t rank_position(const node& n) const noexcept + { + assert(!this->is_constant(n) && "node must not be constant"); + + return rank_pos[n]; + } + + /** + * Verifies the validity of ranks and rank positions within the extended_rank_view context. + * + * @return A boolean indicating whether the ranks and rank positions are valid (true) or not (false). + */ + bool check_validity() const noexcept + { + for (std::size_t i = 0; i < ranks.size(); ++i) + { + const auto& rank = ranks[i]; + uint32_t expected_rank_pos = 0; + for (const auto& n : rank) + { + // Check if the level is different from the rank level + if (this->level(n) != i) + { + return false; + } + // Check if the rank_pos is not in ascending order + if (rank_pos[n] != expected_rank_pos) + { + return false; + } + ++expected_rank_pos; // Increment the expected rank_pos + } + } + return true; + } + + /** + * Updates the nodes and associated rank positions for a specific level within the rank. + * + * @param level Level at which to replace nodes. + * @param nodes The new nodes to be set at the given level. + */ + void modify_rank(const uint32_t level, const std::vector& nodes) + { + auto& rank = ranks[level]; + assert(rank.size() == nodes.size()); + rank = nodes; + std::for_each(rank.cbegin(), rank.cend(), [this, i = 0u](auto const& n) mutable { rank_pos[n] = i++; }); + } + + /** + * Fetches a node at a specific rank position + * + * @param level The level in the network, from which the node is to be fetched. + * @param pos The position within the rank from which to obtain the node. + * @return The node that resides at the `pos` in the `level`. + */ + [[nodiscard]] node at_rank_position(const uint32_t level, const uint32_t pos) const noexcept + { + assert(level < ranks.size() && "level must be less than the number of ranks"); + assert(pos < ranks[level].size() && "pos must be less than the number of nodes in rank"); + + return ranks[level][pos]; + } + /** + * Returns the width of the widest rank in the network. + * + * @return Width of the widest rank in the network. + */ + uint32_t width() const noexcept + { + return max_rank_width; + } + /** + * Returns the width of the rank. + * + * @return Width of the rank. + */ + uint32_t rank_width(const uint32_t level) noexcept + { + auto& rank = ranks[level]; + return rank.size(); + } + /** + * Swaps the positions of two nodes in the same rank. + * + * @param n1 First node to swap. + * @param n2 Second node to swap. + */ + void swap(const node& n1, const node& n2) noexcept + { + assert(this->level(n1) == this->level(n2) && "nodes must be in the same rank"); + + auto& pos1 = rank_pos[n1]; + auto& pos2 = rank_pos[n2]; + + std::swap(ranks[this->level(n1)][pos1], ranks[this->level(n2)][pos2]); + std::swap(pos1, pos2); + } + /** + * Sorts the given rank according to a comparator. + * + * @tparam Cmp Functor type that compares two nodes. It needs to fulfill the requirements of `Compare` (named C++ + * requirement). + * @param level The level of the rank to sort. + * @param cmp The comparator to use. + */ + template + void sort_rank(const uint32_t level, const Cmp& cmp) + { + // level must be less than the number of ranks + if (level < ranks.size()) + { + auto& rank = ranks[level]; + + std::sort(rank.begin(), rank.end(), cmp); + std::for_each(rank.cbegin(), rank.cend(), [this, i = 0u](auto const& n) mutable { rank_pos[n] = i++; }); + } + } + /** + * Applies a given function to each node in the rank level in order. + * + * @tparam Fn Functor type. + * @param level The rank to apply fn to. + * @param fn The function to apply. + */ + template + void foreach_node_in_rank(const uint32_t level, Fn&& fn) const + { + // level must be less than the number of ranks + if (level < ranks.size()) + { + auto const& rank = ranks[level]; + + mockturtle::detail::foreach_element(rank.cbegin(), rank.cend(), std::forward(fn)); + } + } + /** + * Applies a given function to each node in rank order. + * + * This function overrides the `foreach_node` method of the base class. + * + * @tparam Fn Functor type. + * @param fn The function to apply. + */ + template + void foreach_node(Fn&& fn) const + { + for (auto l = 0; l < ranks.size(); ++l) + { + foreach_node_in_rank(l, std::forward(fn)); + } + } + /** + * Applies a given function to each gate in the rank level in order. + * + * @tparam Fn Functor type. + * @param level The rank to apply fn to. + * @param fn The function to apply. + */ + template + void foreach_gate_in_rank(const uint32_t level, Fn&& fn) const + { + // level must be less than the number of ranks + if (level < ranks.size()) + { + auto const& rank = ranks[level]; + + mockturtle::detail::foreach_element_if( + rank.cbegin(), rank.cend(), [this](auto const& n) { return !this->is_ci(n); }, std::forward(fn)); + } + } + /** + * Applies a given function to each gate in rank order. + * + * This function overrides the `foreach_gate` method of the base class. + * + * @tparam Fn Functor type. + * @param fn The function to apply. + */ + template + void foreach_gate(Fn&& fn) const + { + for (auto l = 0; l < ranks.size(); ++l) + { + foreach_gate_in_rank(l, std::forward(fn)); + } + } + /** + * Applies a given function to each PI in rank order. + * + * This function overrides the `foreach_pi` method of the base class. + * + * @tparam Fn Functor type. + * @param fn The function to apply. + */ + template + void foreach_pi(Fn&& fn) const + { + std::vector pis{}; + pis.reserve(this->num_pis()); + + mockturtle::depth_view::foreach_pi([&pis](auto const& pi) { pis.push_back(pi); }); + std::sort(pis.begin(), pis.end(), + [this](auto const& n1, auto const& n2) { return rank_pos[n1] < rank_pos[n2]; }); + mockturtle::detail::foreach_element(pis.cbegin(), pis.cend(), std::forward(fn)); + } + /** + * Applies a given function to each CI in rank order. + * + * This function overrides the `foreach_ci` method of the base class. + * + * @tparam Fn Functor type. + * @param fn The function to apply. + */ + template + void foreach_ci(Fn&& fn) const + { + std::vector pis{}; + pis.reserve(this->num_pis()); + + mockturtle::depth_view::foreach_ci([&pis](auto const& pi) { pis.push_back(pi); }); + std::sort(pis.begin(), pis.end(), + [this](auto const& n1, auto const& n2) { return rank_pos[n1] < rank_pos[n2]; }); + mockturtle::detail::foreach_element(pis.cbegin(), pis.cend(), std::forward(fn)); + } + /** + * Overrides the base class method to also call the add_event on create_pi(). + * + * @return Newly created PI signal. + */ + signal create_pi() + { + auto const n = mockturtle::depth_view::create_pi(); + this->resize_levels(); + on_add(this->get_node(n)); + return n; + } + + private: + mockturtle::node_map rank_pos; + std::vector> ranks; + uint32_t max_rank_width; + + std::shared_ptr::add_event_type> add_event; + + /** + * Inserts a node into the rank and updates the rank position, ranks, and max rank width accordingly. + * + * @param n The node to insert into the rank. + */ + void insert_in_rank(const node& n) + { + auto& rank = ranks[this->level(n)]; + rank_pos[n] = rank.size(); + rank.push_back(n); + max_rank_width = std::max(max_rank_width, static_cast(rank.size())); + } + + /** + * Adds a new node to the skip list. + * + * @param n The node to be added. + */ + void on_add(const node& n) noexcept + { + if (this->level(n) >= ranks.size()) + { + // add sufficient ranks to store the new node + ranks.insert(ranks.end(), this->level(n) - ranks.size() + 1, {}); + } + rank_pos.resize(); + + insert_in_rank(n); + } + + /** + * Initializes the ranks for the given network. It traverses the nodes in the network using a depth-first search and + * inserts each non-constant node into the rank. + * + * This function is noexcept. + */ + void init_ranks() noexcept + { + mockturtle::depth_view::foreach_node( + [this](auto const& n) + { + if (!this->is_constant(n)) + { + insert_in_rank(n); + } + }); + } + + /** + * Implements a node into the rank list at the given rank level. + * + * @param n The node to be inserted. + * @param rank_level The level at which the node should be inserted in the rank. + */ + void insert_in_rank(const node& n, std::size_t rank_level) + { + assert(rank_level < ranks.size()); + auto& rank = ranks[rank_level]; + rank_pos[n] = rank.size(); + rank.push_back(n); + max_rank_width = std::max(max_rank_width, static_cast(rank.size())); + } + + /** + * Initializes the ranks with the given array of nodes. + * + * @param input_ranks The input vector of ranks. + */ + void init_ranks(const std::vector>& input_ranks) + { + for (std::size_t i = 0; i < input_ranks.size(); ++i) + { + auto const& rank_nodes = input_ranks[i]; + for (auto const& n : rank_nodes) + { + if (!this->is_constant(n)) + { + insert_in_rank(n, i); + } + } + } + } +}; + +/** + * Deduction guide for `extended_rank_view' + * + * @tparam T Network type deduced from the construction context of `extended_rank_view`. + */ +template +extended_rank_view(const T&) -> extended_rank_view; + +/** + * Deduction guide for `extended_rank_view` with two constructor arguments + * + * @tparam T Network type deduced from the construction context of `extended_rank_view`. + */ +template +extended_rank_view(const T&, std::vector>) -> extended_rank_view; + +} // namespace fiction + +#endif // FICTION_EXTENDED_RANK_VIEW_HPP diff --git a/include/fiction/networks/virtual_pi_network.hpp b/include/fiction/networks/virtual_pi_network.hpp new file mode 100644 index 000000000..73feca15c --- /dev/null +++ b/include/fiction/networks/virtual_pi_network.hpp @@ -0,0 +1,568 @@ +// +// Created by benjamin on 14.06.24. +// + +#ifndef FICTION_VIRTUAL_PI_NETWORK_HPP +#define FICTION_VIRTUAL_PI_NETWORK_HPP + +#include "fiction/utils/name_utils.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace fiction +{ +/* Network with additional "virtual" PIs. + * + * "Virtual" PIs (Primary Inputs) are used to manage the duplication of PIs in the network. Each "real" PI can have + * an arbitrary number of "virtual" PIs, which are copies of the original "real" PI. + * A "virtual" PI can be created by duplicating a "real" PI. + * To keep track of this relationship, there is a mapping of each "virtual" PI to its corresponding "real" PI in the + * network. + * + * @tparam Ntk Network type. + */ +template +class virtual_pi_network : public Ntk +{ + public: + using storage = typename Ntk::storage; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + struct virtual_storage + { + /** + * Vector storing virtual_inputs. + */ + std::vector virtual_inputs{}; + /** + * Map from virtual_pis to real_pis. + */ + phmap::parallel_flat_hash_map map_virt_to_real_pi{}; + }; + + using v_strg = std::shared_ptr; + + /** + * Default constructor for the `virtual_pi_network` class. + * Initializes `v_storage` as a shared pointer. + */ + virtual_pi_network() : Ntk(), v_storage(std::make_shared()) + { + static_assert(mockturtle::has_create_pi_v, "NtkSrc does not implement the create_pi function"); + static_assert(mockturtle::has_clone_v, "NtkSrc does not implement the clone function"); + static_assert(mockturtle::has_size_v, "NtkSrc does not implement the size function"); + static_assert(mockturtle::has_get_node_v, "NtkSrc does not implement the get_node function"); + static_assert(mockturtle::has_is_pi_v, "NtkSrc does not implement the is_pi function"); + static_assert(mockturtle::has_is_ci_v, "NtkSrc does not implement the is_ci function"); + static_assert(mockturtle::has_num_pis_v, "NtkSrc does not implement the num_pis function"); + static_assert(mockturtle::has_num_cis_v, "NtkSrc does not implement the num_cis function"); + static_assert(mockturtle::has_get_node_v, "NtkSrc does not implement the get_node function"); + } + + /** + * Constructor for the `virtual_pi_network` class that takes a network as input. It adds the functionalities of + * the `virtual_pi_network` class on top of the network. + * + * @tparam Ntk Network type. + * @param ntk Input network. + */ + explicit virtual_pi_network(const Ntk& ntk) : Ntk(ntk.clone()), v_storage(std::make_shared()) + { + static_assert(mockturtle::has_create_pi_v, "NtkSrc does not implement the create_pi function"); + static_assert(mockturtle::has_clone_v, "NtkSrc does not implement the clone function"); + static_assert(mockturtle::has_size_v, "NtkSrc does not implement the size function"); + static_assert(mockturtle::has_get_node_v, "NtkSrc does not implement the get_node function"); + static_assert(mockturtle::has_is_pi_v, "NtkSrc does not implement the is_pi function"); + static_assert(mockturtle::has_is_ci_v, "NtkSrc does not implement the is_ci function"); + static_assert(mockturtle::has_num_pis_v, "NtkSrc does not implement the num_pis function"); + static_assert(mockturtle::has_num_cis_v, "NtkSrc does not implement the num_cis function"); + static_assert(mockturtle::has_get_node_v, "NtkSrc does not implement the get_node function"); + } + + /** + * Constructor for the `virtual_pi_network` class that takes a network and a shared pointer to a `virtual_storage` + * object. THis is used for cloning. + * + * @tparam Ntk Network type. + * @param ntk Input network. + * @param s Shared pointer to the `virtual_storage` object to be used by this `virtual_pi_network`. + */ + explicit virtual_pi_network(const Ntk& ntk, std::shared_ptr s) : Ntk(ntk), v_storage(std::move(s)) + { + static_assert(mockturtle::has_create_pi_v, "NtkSrc does not implement the create_pi function"); + static_assert(mockturtle::has_clone_v, "NtkSrc does not implement the clone function"); + static_assert(mockturtle::has_size_v, "NtkSrc does not implement the size function"); + static_assert(mockturtle::has_get_node_v, "NtkSrc does not implement the get_node function"); + static_assert(mockturtle::has_is_pi_v, "NtkSrc does not implement the is_pi function"); + static_assert(mockturtle::has_is_ci_v, "NtkSrc does not implement the is_ci function"); + static_assert(mockturtle::has_num_pis_v, "NtkSrc does not implement the num_pis function"); + static_assert(mockturtle::has_num_cis_v, "NtkSrc does not implement the num_cis function"); + static_assert(mockturtle::has_get_node_v, "NtkSrc does not implement the get_node function"); + } + + /** + * Clones the virtual_pi_network object. + */ + [[nodiscard]] virtual_pi_network clone() const + { + return virtual_pi_network( + // Clone the network + Ntk::clone(), + // Create a new shared_ptr to virtual_storage using a copy of the current _storage + std::make_shared(*v_storage)); + } + + /** + * Calculate the real size of the virtual_pi_network. + * + * The real size of the network is considered the size without virtual PIs. + * + * @return The real size of the virtual_pi_network as a uint32_t. + */ + [[nodiscard]] auto real_size() const + { + return static_cast(Ntk::size() - v_storage->virtual_inputs.size()); + } + + /** + * Create a virtual PI, which is a mapping to a real PI. + * + * This function creates a virtual PI mapping to a real PI in the network. It adds a PI to the underlying network, + * but marks it as virtual and stores a mapping to a real PI. + * + * @param real_pi The node representing the real PI in the network. + * @return The signal of the newly created virtual PI. + */ + signal create_virtual_pi(const signal& real_pi) + { + const signal s = Ntk::create_pi(); + v_storage->virtual_inputs.emplace_back(Ntk::get_node(s)); + v_storage->map_virt_to_real_pi.insert({Ntk::get_node(s), Ntk::get_node(real_pi)}); + return s; + } + + /** + * Check if a given node is a virtual PI. Virtual PIs are created with create_virtual_pi(). + * + * @param n The node to check. + * @return True if the node is a virtual PI, false otherwise. + */ + [[nodiscard]] bool is_virtual_pi(node const& n) const + { + return std::find(v_storage->virtual_inputs.cbegin(), v_storage->virtual_inputs.cend(), n) != + v_storage->virtual_inputs.cend(); + } + + /** + * Check if a given node is a real PI. Real PIs are created with create_pi(). + * + * @param n The node to check. + * @return True if the node is a real PI, false otherwise. + */ + [[nodiscard]] bool is_real_pi(node const& n) const + { + return (Ntk::is_pi(n) && !is_virtual_pi(n)); + } + + /** + * Check if a given node is a virtual CI in the virtual_pi_network. + * + * @param n The node to check. + * @return True if the node is a virtual CI, false otherwise. + */ + [[nodiscard]] bool is_virtual_ci(node const& n) const + { + return std::find(v_storage->virtual_inputs.cbegin(), v_storage->virtual_inputs.cend(), n) != + v_storage->virtual_inputs.cend(); + } + + /** + * Check if a given node is a real CI in the virtual_pi_network. + * + * @param n The node to check. + * @return True if the node is a real CI, false otherwise. + */ + [[nodiscard]] bool is_real_ci(node const& n) const + { + return (Ntk::is_ci(n) && !is_virtual_ci(n)); + } + + /** + * Get the number of virtual CIs in the virtual_pi_network. + * + * @return The number of virtual CIs as a uint32_t. + */ + [[nodiscard]] auto num_virtual_cis() const + { + return static_cast(v_storage->virtual_inputs.size()); + } + + /** + * Get the number of real CIs in the virtual_pi_network. + * + * @return The number of real CIs as a uint32_t. + */ + [[nodiscard]] auto num_real_cis() const + { + return static_cast(Ntk::num_cis() - num_virtual_cis()); + } + + /** + * Get the number of virtual PIs in the virtual_pi_network. + * + * @return The number of virtual PIs as a uint32_t. + */ + [[nodiscard]] auto num_virtual_pis() const + { + return static_cast(v_storage->virtual_inputs.size()); + } + + /** + * Get the number of real PIs in the virtual_pi_network. + * + * @return The number of real PIs as a uint32_t. + */ + [[nodiscard]] auto num_real_pis() const + { + return static_cast(Ntk::num_pis() - num_virtual_pis()); + } + + /** + * Get the real PI associated with a virtual PI node. + * @param v_pi The virtual pi node to retrieve the real pi for. + * @return The real pi associated with the virtual pi node. + */ + [[nodiscard]] auto get_real_pi(const node& v_pi) const + { + const auto it = v_storage->map_virt_to_real_pi.find(v_pi); + + assert(it != v_storage->map_virt_to_real_pi.end() && "Error: node is not a virtual pi"); + + return it->second; + } + + /** + * Iterates over the real PIs of the circuit and applies a given function. + * + * @tparam Fn The type of the function. + * @param fn The function to be applied to each primary input. + */ + template + void foreach_real_pi(Fn&& fn) const + { + static_cast(this)->foreach_pi( + [&](const auto& i) + { + if (!is_virtual_pi(i)) + { + std::forward(fn)(i); + } + }); + } + + /** + * Iterates over the virtual PIs of the circuit and applies a given function. + * + * @tparam Fn The type of the function. + * @param fn The function to be applied to each primary input. + */ + template + void foreach_virtual_pi(Fn&& fn) const + { + mockturtle::detail::foreach_element(v_storage->virtual_inputs.cbegin(), v_storage->virtual_inputs.cend(), fn); + } + + /** + * Iterates over the virtual CIs of the circuit and applies a given function. + * + * @tparam Fn The type of the function to be applied. + * @param fn The function to be applied. + */ + template + void foreach_real_ci(Fn&& fn) + { + static_cast(this)->foreach_ci( + [&](const auto& i) + { + if (!is_virtual_ci(i)) + { + std::forward(fn)(i); + } + }); + } + + /** + * Iterates over the virtual CIs of the circuit and applies a given function. + * + * @tparam Fn The type of the function. + * @param fn The function to be applied to each primary input. + */ + template + void foreach_virtual_ci(Fn&& fn) const + { + mockturtle::detail::foreach_element(v_storage->virtual_inputs.cbegin(), v_storage->virtual_inputs.cend(), fn); + } + + /** + * Removes virtual input nodes from the virtual_pi_network. + * + * This function removes the virtual input nodes from the network by substituting them with their corresponding + * real input nodes. It then performs a cleanup to remove any dangling PIs.. + * Finally, it clears the virtual_inputs and map_virt_to_real_pi data structures in the _storage object. + */ + /*void remove_virtual_input_nodes() + { + for (const auto& map_item : v_storage->map_virt_to_real_pi) + { + Ntk::substitute_node(map_item.first, map_item.second); + } + + *this = mockturtle::cleanup_dangling(*this, 1); + + // Clear virtual_inputs after using it + v_storage->virtual_inputs.clear(); + v_storage->map_virt_to_real_pi.clear(); + }*/ + + private: + /** + * Shared pointer of the virtual PI storage. + */ + v_strg v_storage; +}; + +namespace detail +{ + +template +class delete_virtual_pis_impl +{ + public: + explicit delete_virtual_pis_impl(const Ntk& ntk_src) : ntk{ntk_src}, ntk_topo{ntk_src} {} + + // auto run() -> decltype(this->ntk.clone()) + auto run() -> decltype(std::declval().clone()) + { + auto init = initialize_copy_virtual_pi_network(ntk); + auto& ntk_dest_ref = init.first; + // cloning resolves runtime issues with rank_views, but might return a different network type. + auto ntk_dest = ntk_dest_ref.clone(); + auto& old2new = init.second; + + const auto gather_fanin_signals = [this, &ntk_dest, &old2new](const auto& n) + { + std::vector children{}; + + ntk.foreach_fanin(n, + [this, &ntk_dest, &old2new, &children](const auto& f) + { + auto fn = ntk.get_node(f); + + if (ntk.is_virtual_pi(fn)) + { + fn = ntk.get_real_pi(fn); + } + auto tgt_signal = old2new[fn]; + + children.emplace_back(ntk.is_complemented(f) ? ntk_dest.create_not(tgt_signal) : + tgt_signal); + }); + + return children; + }; + +#if (PROGRESS_BARS) + // initialize a progress bar + mockturtle::progress_bar bar{static_cast(ntk.num_gates()), "[i] network conversion: |{0}|"}; +#endif + + ntk_topo.foreach_gate( + [&, this](const auto& g, [[maybe_unused]] auto i) + { + auto children = gather_fanin_signals(g); + +#if (PROGRESS_BARS) + // update progress + bar(i); +#endif + + if constexpr (mockturtle::has_is_and_v && mockturtle::has_create_and_v) + { + if (ntk.is_and(g)) + { + old2new[g] = ntk_dest.create_and(children[0], children[1]); + return true; + } + } + if constexpr (mockturtle::has_is_or_v && mockturtle::has_create_or_v) + { + if (ntk.is_or(g)) + { + old2new[g] = ntk_dest.create_or(children[0], children[1]); + return true; + } + } + if constexpr (mockturtle::has_is_xor_v && mockturtle::has_create_xor_v) + { + if (ntk.is_xor(g)) + { + old2new[g] = ntk_dest.create_xor(children[0], children[1]); + return true; + } + } + if constexpr (mockturtle::has_is_maj_v && mockturtle::has_create_maj_v) + { + if (ntk.is_maj(g)) + { + old2new[g] = ntk_dest.create_maj(children[0], children[1], children[2]); + return true; + } + } + if constexpr (mockturtle::has_is_nary_and_v && mockturtle::has_create_nary_and_v) + { + if (ntk.is_nary_and(g)) + { + old2new[g] = ntk_dest.create_nary_and(children); + return true; + } + } + if constexpr (mockturtle::has_is_nary_or_v && mockturtle::has_create_nary_or_v) + { + if (ntk.is_nary_or(g)) + { + old2new[g] = ntk_dest.create_nary_or(children); + return true; + } + } + if constexpr (mockturtle::has_is_nary_xor_v && mockturtle::has_create_nary_xor_v) + { + if (ntk.is_nary_xor(g)) + { + old2new[g] = ntk_dest.create_nary_xor(children); + return true; + } + } + if constexpr (fiction::has_is_buf_v && mockturtle::has_create_buf_v) + { + if (ntk.is_buf(g)) + { + old2new[g] = ntk_dest.create_buf(children[0]); + return true; + } + } + if constexpr (mockturtle::has_node_function_v && mockturtle::has_create_node_v) + { + old2new[g] = ntk_dest.create_node(children, ntk.node_function(g)); + return true; + } + + return true; + }); + + ntk.foreach_po( + [this, &ntk_dest, &old2new](const auto& po) + { + const auto tgt_signal = old2new[ntk.get_node(po)]; + const auto tgt_po = ntk.is_complemented(po) ? ntk_dest.create_not(tgt_signal) : tgt_signal; + + ntk_dest.create_po(tgt_po); + }); + + // restore signal names if applicable + fiction::restore_names(ntk, ntk_dest, old2new); + + return ntk_dest; // ntk_dest + } + + private: + using TopoNtkSrc = mockturtle::topo_view; + Ntk ntk; + TopoNtkSrc ntk_topo; + + [[nodiscard]] std::pair, Ntk>> + initialize_copy_virtual_pi_network(Ntk& src) + { + static_assert(mockturtle::is_network_type_v, "NtkDest is not a network type"); + static_assert(mockturtle::is_network_type_v, "NtkSrc is not a network type"); + + static_assert(mockturtle::has_get_constant_v, "NtkDest does not implement the get_constant method"); + static_assert(mockturtle::has_create_pi_v, "NtkDest does not implement the create_pi method"); + static_assert(mockturtle::has_get_constant_v, "NtkSrc does not implement the get_constant method"); + static_assert(mockturtle::has_get_node_v, "NtkSrc does not implement the get_node method"); + static_assert(has_foreach_real_pi_v, "NtkSrc does not implement the foreach_pi method"); + + mockturtle::node_map, Ntk> old2new(src); + Ntk dest; + old2new[src.get_constant(false)] = dest.get_constant(false); + if (src.get_node(src.get_constant(true)) != src.get_node(src.get_constant(false))) + { + old2new[src.get_constant(true)] = dest.get_constant(true); + } + src.foreach_real_pi([&](auto const& n) { old2new[n] = dest.create_pi(); }); + return {dest, old2new}; + } +}; +} // namespace detail + +/** + * Deletes virtual primary inputs from a network. This can mainly be used for equivalence checking. + * If the network does not have any virtual PIs stored, the network is returned. + * + * @tparam Ntk The type of network. + * @param ntk The input network. + * @return The resulting network after virtual primary inputs are deleted. + */ +template +auto delete_virtual_pis(const Ntk& ntk) -> decltype(std::declval().clone()) +{ + static_assert(mockturtle::is_network_type_v, "Ntk is not a network type"); + + static_assert(mockturtle::has_get_node_v, "Ntk does not implement the get_node function"); + static_assert(mockturtle::has_is_complemented_v, "Ntk does not implement the is_complemented function"); + static_assert(mockturtle::has_foreach_pi_v, "Ntk does not implement the foreach_pi function"); + static_assert(has_get_real_pi_v, "Ntk does not implement the has_get_real_pi function"); + static_assert(has_num_real_pis_v, "Ntk does not implement the has_num_real_pis function"); + static_assert(mockturtle::has_foreach_gate_v, "Ntk does not implement the foreach_gate function"); + static_assert(mockturtle::has_foreach_po_v, "Ntk does not implement the foreach_po function"); + static_assert(mockturtle::has_foreach_fanin_v, "Ntk does not implement the foreach_fanin function"); + + static_assert(mockturtle::has_get_constant_v, "Ntk does not implement the get_constant function"); + + static_assert(mockturtle::has_create_pi_v, "Ntk does not implement the create_pi function"); + static_assert(mockturtle::has_create_po_v, "Ntk does not implement the create_po function"); + static_assert(mockturtle::has_create_not_v, "Ntk does not implement the create_not function"); + static_assert(mockturtle::has_create_and_v, "Ntk does not implement the create_and function"); + static_assert(mockturtle::has_create_or_v, "Ntk does not implement the create_or function"); + static_assert(mockturtle::has_create_xor_v, "Ntk does not implement the create_xor function"); + static_assert(mockturtle::has_create_maj_v, "Ntk does not implement the create_maj function"); + + assert(ntk.is_combinational() && "Network has to be combinational"); + + if (ntk.num_virtual_pis() == 0) + { + return ntk; + } + + detail::delete_virtual_pis_impl p{ntk}; + + auto result = p.run(); + + return result; +} + +} // namespace fiction + +#endif // FICTION_VIRTUAL_PI_NETWORK_HPP diff --git a/include/fiction/traits.hpp b/include/fiction/traits.hpp index a67d005c3..c539ab777 100644 --- a/include/fiction/traits.hpp +++ b/include/fiction/traits.hpp @@ -1284,6 +1284,73 @@ template inline constexpr bool has_is_and_xor_v = has_is_and_xor::value; #pragma endregion +#pragma region has_num_real_pis +template +struct has_num_real_pis : std::false_type +{}; + +template +struct has_num_real_pis().num_real_pis())>> : std::true_type +{}; + +template +inline constexpr bool has_num_real_pis_v = has_num_real_pis::value; +#pragma endregion + +#pragma region has_num_virtual_pis +template +struct has_num_virtual_pis : std::false_type +{}; + +template +struct has_num_virtual_pis().num_virtual_pis())>> : std::true_type +{}; + +template +inline constexpr bool has_num_virtual_pis_v = has_num_virtual_pis::value; +#pragma endregion + +#pragma region has_foreach_pi +template +struct has_foreach_real_pi : std::false_type +{}; + +template +struct has_foreach_real_pi().foreach_real_pi( + std::declval, uint32_t)>()))>> : std::true_type +{}; + +template +inline constexpr bool has_foreach_real_pi_v = has_foreach_real_pi::value; +#pragma endregion + +#pragma region has_index_to_node +template +struct has_get_real_pi : std::false_type +{}; + +template +struct has_get_real_pi().get_real_pi(mockturtle::node()))>> + : std::true_type +{}; + +template +inline constexpr bool has_get_real_pi_v = has_get_real_pi::value; +#pragma endregion + +#pragma region has_remove_virtual_input_nodes +template +struct has_remove_virtual_input_nodes : std::false_type +{}; + +template +struct has_remove_virtual_input_nodes().remove_virtual_input_nodes())>> + : std::true_type +{}; + +template +inline constexpr bool has_remove_virtual_input_nodes_v = has_remove_virtual_input_nodes::value; +#pragma endregion } // namespace fiction #endif // FICTION_TRAITS_HPP diff --git a/test/algorithms/network_transformation/buffer_removal.cpp b/test/algorithms/network_transformation/buffer_removal.cpp new file mode 100644 index 000000000..c111f41d8 --- /dev/null +++ b/test/algorithms/network_transformation/buffer_removal.cpp @@ -0,0 +1,102 @@ +// +// Created by benjamin on 9/24/24. +// + +#include + +#include "fiction/algorithms/network_transformation/fanout_substitution.hpp" +#include "fiction/algorithms/network_transformation/network_balancing.hpp" +#include "fiction/algorithms/network_transformation/node_duplication_planarization.hpp" +#include "fiction/algorithms/verification/virtual_miter.hpp" +#include "fiction/networks/technology_network.hpp" + +#include + +#include +#include + +using namespace fiction; + +TEST_CASE("Remove one buffer", "[buffer-removal]") +{ + technology_network tec{}; + const auto p0 = tec.create_pi(); + const auto p1 = tec.create_pi(); + const auto b0 = tec.create_buf(p0); + const auto a0 = tec.create_and(b0, p1); + tec.create_po(a0); + + const auto removed = remove_buffer(tec); + + mockturtle::equivalence_checking_stats st; + const auto cec_m = mockturtle::equivalence_checking(*mockturtle::miter(tec, removed), {}, &st); + REQUIRE(cec_m.has_value()); + CHECK(cec_m.value() == 1); + + CHECK(tec.num_gates() == removed.num_gates() + 1); +} + +TEST_CASE("Remove one buffer chain", "[buffer-removal]") +{ + technology_network tec{}; + const auto p0 = tec.create_pi(); + const auto p1 = tec.create_pi(); + const auto b0 = tec.create_buf(p0); + const auto b1 = tec.create_buf(b0); + const auto b2 = tec.create_buf(b1); + const auto a0 = tec.create_and(b2, p1); + tec.create_po(a0); + + const auto removed = remove_buffer(tec); + + mockturtle::equivalence_checking_stats st; + const auto cec_m = mockturtle::equivalence_checking(*mockturtle::miter(tec, removed), {}, &st); + REQUIRE(cec_m.has_value()); + CHECK(cec_m.value() == 1); + + CHECK(tec.num_gates() == removed.num_gates() + 3); +} + +TEST_CASE("Remove multiple buffers", "[buffer-removal]") +{ + fiction::technology_network tec; + const auto pi0 = tec.create_pi(); + const auto pi1 = tec.create_pi(); + const auto pi2 = tec.create_pi(); + const auto pi3 = tec.create_pi(); + const auto pi4 = tec.create_pi(); + + const auto a0 = tec.create_and(pi0, pi1); + const auto o0 = tec.create_or(pi0, pi1); + const auto a1 = tec.create_and(pi3, pi4); + const auto o1 = tec.create_or(pi3, pi4); + + const auto a2 = tec.create_and(o0, pi2); + const auto a3 = tec.create_and(o1, pi2); + const auto a4 = tec.create_and(a2, a3); + + tec.create_po(a0); + tec.create_po(a4); + tec.create_po(a1); + + fiction::network_balancing_params ps; + ps.unify_outputs = true; + + const auto _b = fiction::network_balancing( + fiction::fanout_substitution(tec), ps); + + fiction::node_duplication_planarization_params dps; + dps.po_order = fiction::node_duplication_planarization_params::output_order::KEEP_PO_ORDER; + + auto planarized_b = fiction::node_duplication_planarization(_b, dps); + // clone to get rid of the extended_rank_view + auto planar = planarized_b.clone(); + // remove the buffer nodes + auto removed = remove_buffer(planar); + + mockturtle::equivalence_checking_stats st; + const auto cec_m = + mockturtle::equivalence_checking(*fiction::virtual_miter(tec, removed), {}, &st); + REQUIRE(cec_m.has_value()); + CHECK(cec_m.value() == 1); +} 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 000000000..a9aefbad9 --- /dev/null +++ b/test/algorithms/network_transformation/node_duplication_planarization.cpp @@ -0,0 +1,174 @@ +// +// Created by benjamin on 11.06.24. +// + +#include + +#include "fiction/algorithms/network_transformation/network_balancing.hpp" + +#include +#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 = network_balancing(tec, ps); + + const auto vpi_r = mockturtle::rank_view(tec_b); + + auto planarized_maj = node_duplication_planarization(tec_b); + + 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 = network_balancing(tec, ps); + + const auto vpi_r = mockturtle::rank_view(tec_b); + + auto planarized_maj = node_duplication_planarization(tec_b); + + 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 = network_balancing(aig, ps); + + const auto vpi_r = mockturtle::rank_view(aig_b); + + auto planarized_maj = node_duplication_planarization(aig_b); + + 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 = network_balancing(aig, ps); + + const auto vpi_r = mockturtle::rank_view(aig_b); + + auto planarized_maj = node_duplication_planarization(aig_b); + + 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 = network_balancing(aig, ps); + + const auto vpi_r = mockturtle::rank_view(aig_b); + + auto planarized_maj = node_duplication_planarization(aig_b); + + 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); +} diff --git a/test/algorithms/properties/check_planarity_balanced.cpp b/test/algorithms/properties/check_planarity_balanced.cpp new file mode 100644 index 000000000..0bc7bda89 --- /dev/null +++ b/test/algorithms/properties/check_planarity_balanced.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); + + extended_rank_view tec_r(aig); + + const std::vector nodes_rank0{1, 2, 3, 6}; + const std::vector nodes_rank1{4, 5}; + + tec_r.modify_rank(0, nodes_rank0); + tec_r.modify_rank(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); + + extended_rank_view tec_r(tec); + + const std::vector nodes_rank0{2, 3, 4}; + const std::vector nodes_rank1{5, 6}; + + tec_r.modify_rank(0, nodes_rank0); + tec_r.modify_rank(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); + + extended_rank_view tec_r(tec); + + const std::vector nodes_rank0{2, 3, 4}; + const std::vector nodes_rank1{5, 6}; + + tec_r.modify_rank(0, nodes_rank0); + tec_r.modify_rank(1, nodes_rank1); + + const bool planar = check_planarity(tec_r); + + CHECK(planar == 0); +} diff --git a/test/algorithms/verification/virtual_miter.cpp b/test/algorithms/verification/virtual_miter.cpp new file mode 100644 index 000000000..ec6d5bc7a --- /dev/null +++ b/test/algorithms/verification/virtual_miter.cpp @@ -0,0 +1,58 @@ +// +// Created by benjamin on 8/26/24. +// + +#include + +#include +#include +#include + +#include + +using namespace fiction; + +TEST_CASE("Miter network positions", "[virtual-miter]") +{ + technology_network tec{}; + const auto x1 = tec.create_pi(); + const auto x2 = tec.create_pi(); + const auto a1 = tec.create_and(x1, x2); + const auto o1 = tec.create_or(x1, x2); + tec.create_po(a1); + tec.create_po(o1); + + virtual_pi_network vpi_ntk_1{}; + const auto x1_v = vpi_ntk_1.create_pi(); + const auto x2_v = vpi_ntk_1.create_pi(); + const auto v1 = vpi_ntk_1.create_virtual_pi(x1_v); + const auto a1_v = vpi_ntk_1.create_and(x1_v, x2_v); + const auto o1_v = vpi_ntk_1.create_or(v1, x2_v); + vpi_ntk_1.create_po(a1_v); + vpi_ntk_1.create_po(o1_v); + + virtual_pi_network vpi_ntk_2{}; + const auto x1_v2 = vpi_ntk_2.create_pi(); + const auto x2_v2 = vpi_ntk_2.create_pi(); + const auto v1_2 = vpi_ntk_2.create_virtual_pi(x1_v2); + const auto a1_v2 = vpi_ntk_2.create_and(v1_2, x2_v2); + const auto o1_v2 = vpi_ntk_2.create_or(x1_v2, x2_v2); + vpi_ntk_2.create_po(a1_v2); + vpi_ntk_2.create_po(o1_v2); + + mockturtle::equivalence_checking_stats st; + auto maybe_cec_m = + mockturtle::equivalence_checking(*fiction::virtual_miter(vpi_ntk_1, tec), {}, &st); + REQUIRE(maybe_cec_m.has_value()); + CHECK(*maybe_cec_m == 1); + + maybe_cec_m = + mockturtle::equivalence_checking(*fiction::virtual_miter(tec, vpi_ntk_2), {}, &st); + REQUIRE(maybe_cec_m.has_value()); + CHECK(*maybe_cec_m == 1); + + maybe_cec_m = + mockturtle::equivalence_checking(*fiction::virtual_miter(vpi_ntk_1, vpi_ntk_2), {}, &st); + REQUIRE(maybe_cec_m.has_value()); + CHECK(*maybe_cec_m == 1); +} diff --git a/test/networks/views/extended_rank_view.cpp b/test/networks/views/extended_rank_view.cpp new file mode 100644 index 000000000..945db2a4a --- /dev/null +++ b/test/networks/views/extended_rank_view.cpp @@ -0,0 +1,180 @@ +// +// Created by benjamin on 18.06.24. +// + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace fiction; + +TEMPLATE_TEST_CASE("traits", "[extended_rank_view]", mockturtle::aig_network, mockturtle::mig_network, + mockturtle::xag_network, mockturtle::xmg_network, mockturtle::klut_network, + mockturtle::cover_network, mockturtle::buffered_aig_network, mockturtle::buffered_mig_network, + mockturtle::crossed_klut_network, mockturtle::buffered_crossed_klut_network) +{ + CHECK(mockturtle::is_network_type_v); + CHECK(!mockturtle::has_rank_position_v); + CHECK(!mockturtle::has_at_rank_position_v); + CHECK(!mockturtle::has_width_v); + CHECK(!mockturtle::has_sort_rank_v); + CHECK(!mockturtle::has_foreach_node_in_rank_v); + CHECK(!mockturtle::has_foreach_gate_in_rank_v); + CHECK(!mockturtle::is_topologically_sorted_v); + + using rank_ntk = extended_rank_view; + + CHECK(mockturtle::is_network_type_v); + CHECK(mockturtle::has_rank_position_v); + CHECK(mockturtle::has_at_rank_position_v); + CHECK(mockturtle::has_width_v); + CHECK(mockturtle::has_sort_rank_v); + CHECK(mockturtle::has_foreach_node_in_rank_v); + CHECK(mockturtle::has_foreach_gate_in_rank_v); + CHECK(mockturtle::is_topologically_sorted_v); + + using rank_rank_ntk = extended_rank_view; + + CHECK(mockturtle::is_network_type_v); + CHECK(mockturtle::has_rank_position_v); + CHECK(mockturtle::has_at_rank_position_v); + CHECK(mockturtle::has_width_v); + CHECK(mockturtle::has_sort_rank_v); + CHECK(mockturtle::has_foreach_node_in_rank_v); + CHECK(mockturtle::has_foreach_gate_in_rank_v); + CHECK(mockturtle::is_topologically_sorted_v); +} + +TEST_CASE("Check modify ranks", "[extended-rank-view]") +{ + 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 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_and({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_balanced = network_balancing(tec, ps); + + auto vpi_r = extended_rank_view(tec_balanced); + + const std::vector nodes = {13, 10, 14}; + vpi_r.modify_rank(2, nodes); + + CHECK(vpi_r.check_validity() == 1); +} + +TEMPLATE_TEST_CASE("Check equivalence checking", "[extended_rank_view]", mockturtle::aig_network, + mockturtle::mig_network, mockturtle::xag_network, mockturtle::xmg_network, mockturtle::klut_network, + mockturtle::buffered_aig_network, mockturtle::buffered_mig_network, mockturtle::crossed_klut_network, + mockturtle::buffered_crossed_klut_network) +{ + TestType ntk{}; + + const auto a = ntk.create_pi(); + const auto b = ntk.create_pi(); + + const auto a_t = ntk.create_pi(); + const auto b_t = ntk.create_pi(); + + const auto f1 = ntk.create_and(a, b); + + const auto f1_t = ntk.create_and(a_t, b); + const auto f2_t = ntk.create_and(b_t, a_t); + const auto f3_t = ntk.create_or(a_t, b_t); + + ntk.create_po(f1); + + ntk.create_po(f1_t); + ntk.create_po(f2_t); + ntk.create_po(f3_t); + + network_balancing_params ps; + ps.unify_outputs = true; + + const auto ntk_r = extended_rank_view(ntk); + + mockturtle::equivalence_checking_stats st; + const auto maybe_cec_m = + mockturtle::equivalence_checking(*fiction::virtual_miter(ntk, ntk_r), {}, &st); + REQUIRE(maybe_cec_m.has_value()); + const bool cec_m = *maybe_cec_m; + CHECK(cec_m == 1); + CHECK(ntk_r.check_validity() == 1); +} + +TEST_CASE("Check equivalence checking for virtual PIs", "[extended-rank-view]") +{ + technology_network tec{}; + virtual_pi_network vpi{}; + + const auto a = vpi.create_pi(); + const auto b = vpi.create_pi(); + + const auto a_t = tec.create_pi(); + const auto b_t = tec.create_pi(); + + const auto c = vpi.create_virtual_pi(a); + const auto d = vpi.create_virtual_pi(b); + + const auto f1 = vpi.create_and(a, b); + const auto f2 = vpi.create_and(b, c); + const auto f3 = vpi.create_or(a, d); + + const auto f1_t = tec.create_and(a_t, b); + const auto f2_t = tec.create_and(b_t, a_t); + const auto f3_t = tec.create_or(a_t, b_t); + + vpi.create_po(f1); + vpi.create_po(f2); + vpi.create_po(f3); + + tec.create_po(f1_t); + tec.create_po(f2_t); + tec.create_po(f3_t); + + network_balancing_params ps; + ps.unify_outputs = true; + + auto vpi_r = extended_rank_view(vpi); + + mockturtle::equivalence_checking_stats st; + const auto maybe_cec_m = + mockturtle::equivalence_checking(*fiction::virtual_miter(tec, vpi_r), {}, &st); + REQUIRE(maybe_cec_m.has_value()); + const bool cec_m = *maybe_cec_m; + CHECK(cec_m == 1); + CHECK(vpi_r.check_validity() == 1); +} diff --git a/test/networks/virtual_pi_network.cpp b/test/networks/virtual_pi_network.cpp new file mode 100644 index 000000000..a7c9600a2 --- /dev/null +++ b/test/networks/virtual_pi_network.cpp @@ -0,0 +1,223 @@ +// +// Created by benjamin on 14.06.24. +// + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace fiction; + +TEST_CASE("Copy technology network and size consistency", "[virtual-pi-view]") +{ + technology_network tec{}; + + const auto a = tec.create_pi(); + const auto b = tec.create_pi(); + + const auto f1 = tec.create_and(a, b); + + virtual_pi_network vpi{tec}; + + const auto c = vpi.create_virtual_pi(a); + const auto d = vpi.create_virtual_pi(b); + + const auto f2 = vpi.create_and(b, c); + const auto f3 = vpi.create_or(a, d); + + vpi.create_po(f1); + vpi.create_po(f2); + vpi.create_po(f3); + + // virtual_pi_network is a deep copy. THe original network won't be affected by any changes after the copy + CHECK(tec.size() == 5); + // This is the size disregarding virtual PIs. So this would correspond to the size in a normal technology_network + CHECK(vpi.real_size() == 7); + // This is the size with the virtual PIs + CHECK(vpi.size() == 9); + const auto non_vpi = delete_virtual_pis(vpi); + // After removing/remapping the virtual PIs to their real PIs the sizes are equal + CHECK(non_vpi.real_size() == non_vpi.size()); + CHECK(non_vpi.size() == vpi.size() - vpi.num_virtual_pis()); + CHECK(non_vpi.size() == 7); +} + +TEMPLATE_TEST_CASE("Copy networks and size consistency", "[virtual-pi-view]", mockturtle::aig_network, + mockturtle::xag_network) +{ + TestType ntk{}; + + const auto a = ntk.create_pi(); + const auto b = ntk.create_pi(); + + const auto f1 = ntk.create_and(a, b); + + virtual_pi_network vpi{ntk}; + + const auto c = vpi.create_virtual_pi(a); + const auto d = vpi.create_virtual_pi(b); + + const auto f2 = vpi.create_and(b, c); + const auto f3 = vpi.create_or(a, d); + + vpi.create_po(f1); + vpi.create_po(f2); + vpi.create_po(f3); + + // virtual_pi_network is a deep copy. THe original network won't be affected by any changes after the copy + CHECK(ntk.size() == 4); + // This is the size disregarding virtual PIs. So this would correspond to the size in a normal technology_network + CHECK(vpi.real_size() == 6); + // This is the size with the virtual PIs + CHECK(vpi.size() == 8); + const auto non_vpi = delete_virtual_pis(vpi); + // After removing/remapping the virtual PIs to their real PIs the sizes are equal + CHECK(non_vpi.real_size() == non_vpi.size()); + // Minus one, since AIG nodes get hashed when creating the new AIG + CHECK(non_vpi.size() == vpi.size() - vpi.num_virtual_pis() - 1); + CHECK(non_vpi.size() == 5); +} + +TEST_CASE("Check equivalence of an extended_rank view", "[virtual-pi-view]") +{ + technology_network tec{}; + virtual_pi_network vpi{}; + + const auto a = vpi.create_pi(); + const auto b = vpi.create_pi(); + + const auto a_t = tec.create_pi(); + const auto b_t = tec.create_pi(); + + const auto c = vpi.create_virtual_pi(a); + const auto d = vpi.create_virtual_pi(b); + + const auto f1 = vpi.create_and(a, b); + const auto f2 = vpi.create_and(b, c); + const auto f3 = vpi.create_or(a, d); + + const auto f1_t = tec.create_and(a_t, b); + const auto f2_t = tec.create_and(b_t, a_t); + const auto f3_t = tec.create_or(a_t, b_t); + + vpi.create_po(f1); + vpi.create_po(f2); + vpi.create_po(f3); + + tec.create_po(f1_t); + tec.create_po(f2_t); + tec.create_po(f3_t); + + auto ntk_r = extended_rank_view(vpi); + + mockturtle::equivalence_checking_stats st; + const auto maybe_cec_m = + mockturtle::equivalence_checking(*fiction::virtual_miter(tec, ntk_r), {}, &st); + REQUIRE(maybe_cec_m.has_value()); + CHECK(*maybe_cec_m == 1); +} + +TEMPLATE_TEST_CASE("Remove PIs and check equivalence", "[virtual-pi-view]", mockturtle::aig_network, + mockturtle::xag_network) +{ + TestType tec{}; + virtual_pi_network vpi{}; + + const auto a = vpi.create_pi(); + const auto b = vpi.create_pi(); + + const auto a_t = tec.create_pi(); + const auto b_t = tec.create_pi(); + + const auto c = vpi.create_virtual_pi(a); + const auto d = vpi.create_virtual_pi(b); + + const auto f1 = vpi.create_and(a, b); + const auto f2 = vpi.create_and(b, c); + const auto f3 = vpi.create_and(a, d); + + const auto f1_t = tec.create_and(a_t, b); + const auto f2_t = tec.create_and(b_t, a_t); + const auto f3_t = tec.create_and(a_t, b_t); + + vpi.create_po(f1); + vpi.create_po(f2); + vpi.create_po(f3); + + tec.create_po(f1_t); + tec.create_po(f2_t); + tec.create_po(f3_t); + + auto non_virt = delete_virtual_pis(vpi); + CHECK(non_virt.size() == + vpi.size() - vpi.num_virtual_pis() - 2); // When creating the AIG, the nodes will be hashed. Since all AND + // nodes are the same, only one node will be created. + + CHECK(non_virt.real_size() == non_virt.size()); + + mockturtle::equivalence_checking_stats st; + const auto maybe_cec_m = + mockturtle::equivalence_checking(*mockturtle::miter(tec, non_virt), {}, &st); + REQUIRE(maybe_cec_m.has_value()); + CHECK(*maybe_cec_m == 1); +} + +TEST_CASE("Remove PIs and check equivalence with different sizes", "[virtual-pi-view]") +{ + 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_and({x3, x4, x5}); + tec.create_po(f1); + tec.create_po(f2); + tec.create_po(f3); + + virtual_pi_network vpi{}; + + const auto a = vpi.create_pi(); + const auto b = vpi.create_pi(); + const auto c = vpi.create_pi(); + const auto d = vpi.create_pi(); + + const auto d_v = vpi.create_virtual_pi(d); + + const auto e = vpi.create_pi(); + + const auto f7 = vpi.create_not(b); + const auto f8 = vpi.create_and(a, b); + const auto f9 = vpi.create_and(c, d); + const auto f10 = vpi.create_buf(c); + const auto f11 = vpi.create_and(d_v, e); + + const auto f12 = vpi.create_buf(f7); + const auto f13 = vpi.create_and(f8, f9); + const auto f14 = vpi.create_and(f10, f11); + + vpi.create_po(f12); + vpi.create_po(f13); + vpi.create_po(f14); + + auto non_virt = delete_virtual_pis(vpi); + CHECK(non_virt.size() == vpi.size() - vpi.num_virtual_pis()); + + mockturtle::equivalence_checking_stats st; + const auto maybe_cec_m = + mockturtle::equivalence_checking(*mockturtle::miter(tec, non_virt), {}, &st); + REQUIRE(maybe_cec_m.has_value()); + CHECK(*maybe_cec_m == 1); +}