diff --git a/docs/algorithms/index_restructuring.rst b/docs/algorithms/index_restructuring.rst index d162cf0a8..abf6475d2 100644 --- a/docs/algorithms/index_restructuring.rst +++ b/docs/algorithms/index_restructuring.rst @@ -29,5 +29,6 @@ Logic restructuring and optimization functional_reduction refactoring aig_balancing + xag_balancing balancing cost_generic_resub \ No newline at end of file diff --git a/docs/algorithms/mapper.rst b/docs/algorithms/mapper.rst index 9d4441229..d206aa2b0 100644 --- a/docs/algorithms/mapper.rst +++ b/docs/algorithms/mapper.rst @@ -47,7 +47,7 @@ using a NPN resynthesis database of structures: /* load the npn database in the library */ mig_npn_resynthesis resyn{ true }; - exact_library exact_lib( resyn ); + exact_library exact_lib( resyn ); /* perform graph mapping */ map_params ps; @@ -90,7 +90,7 @@ database of structures: /* load the npn database in the library */ mig_npn_resynthesis resyn{ true }; - exact_library, mig_npn_resynthesis> exact_lib( resyn ); + exact_library> exact_lib( resyn ); /* perform graph mapping */ map_params ps; @@ -109,7 +109,7 @@ leverage satisfiability don't cares: mig_npn_resynthesis resyn{ true }; exact_library_params lps; lps.compute_dc_classes = true; - exact_library exact_lib( resyn, lps ); + exact_library exact_lib( resyn, lps ); /* perform area-oriented rewriting */ map_params ps; diff --git a/docs/algorithms/rewrite.rst b/docs/algorithms/rewrite.rst index d1a88bda6..831d4e504 100644 --- a/docs/algorithms/rewrite.rst +++ b/docs/algorithms/rewrite.rst @@ -16,7 +16,7 @@ networks. In this case the maximum number of variables for a node function is mig_npn_resynthesis resyn{ true }; exact_library_params eps; eps.np_classification = false; - exact_library exact_lib( resyn, eps ); + exact_library exact_lib( resyn, eps ); It is possible to change the cost function of nodes in rewrite. Here is an example, in which the cost function only accounts for AND gates in a network, @@ -36,7 +36,7 @@ which corresponds to the multiplicative complexity of a function. SomeResynthesisClass resyn; exact_library_params eps; eps.np_classification = false; - exact_library exact_lib( resyn, eps ); + exact_library exact_lib( resyn, eps ); rewrite( ntk, exact_lib ); Rewrite supports also satisfiability don't cares: @@ -51,7 +51,7 @@ Rewrite supports also satisfiability don't cares: exact_library_params eps; eps.np_classification = false; eps.compute_dc_classes = true; - exact_library exact_lib( resyn, eps ); + exact_library exact_lib( resyn, eps ); /* rewrite */ rewrite_params ps; diff --git a/docs/algorithms/xag_balancing.rst b/docs/algorithms/xag_balancing.rst new file mode 100644 index 000000000..a81229327 --- /dev/null +++ b/docs/algorithms/xag_balancing.rst @@ -0,0 +1,15 @@ +XAG balancing +------------- + +**Header:** ``mockturtle/algorithms/xag_balancing.hpp`` + +Parameters +~~~~~~~~~~ + +.. doxygenstruct:: mockturtle::xag_balancing_params + :members: + +Algorithm +~~~~~~~~~ + +.. doxygenfunction:: mockturtle::xag_balance diff --git a/docs/changelog.rst b/docs/changelog.rst index a1fbc84aa..42c41111c 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -37,6 +37,7 @@ v0.4 (not yet released) - Adding a new technology mapper supporting multi-output cells (`emap`) `#623 `_ - Adding circuit extraction of half and full adders (`extract_adders`) `#623 `_ - Adding don't care support in rewriting (`map`, `rewrite`) `#623 `_ + - XAG balancing (`xag_balance`) `#627 `_ * I/O: - Write gates to GENLIB file (`write_genlib`) `#606 `_ * Views: diff --git a/docs/requirements.txt b/docs/requirements.txt index 74a2c468a..afc4ee582 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1,3 @@ sphinx==5.1.1 -breathe==4.34.0 \ No newline at end of file +breathe==4.34.0 +sphinx-rtd-theme==1.2.2 \ No newline at end of file diff --git a/experiments/aqfp_flow_date.cpp b/experiments/aqfp_flow_date.cpp index a2f03c634..e0421d0ca 100644 --- a/experiments/aqfp_flow_date.cpp +++ b/experiments/aqfp_flow_date.cpp @@ -166,7 +166,7 @@ struct opt_stats_t uint32_t jj_level_after_exact; }; -mig_network remapping_round( mig_network const& ntk, exact_library const& exact_lib, opt_params_t const& opt_params, opt_stats_t& stats ) +mig_network remapping_round( mig_network const& ntk, exact_library const& exact_lib, opt_params_t const& opt_params, opt_stats_t& stats ) { map_params psm; psm.skip_delay_round = false; @@ -354,7 +354,8 @@ int main( int argc, char** argv ) /* library to map to MIGs */ mig_npn_resynthesis resyn{ true }; exact_library_params eps; - exact_library exact_lib( resyn, eps ); + eps.np_classification = true; + exact_library exact_lib( resyn, eps ); /* database loading for aqfp resynthesis*/ auto db3_str = get_database( "db3" ); diff --git a/experiments/mapper.cpp b/experiments/mapper.cpp index dc3913ba9..64511b670 100644 --- a/experiments/mapper.cpp +++ b/experiments/mapper.cpp @@ -80,7 +80,8 @@ int main() /* library to map to MIGs */ mig_npn_resynthesis resyn{ true }; exact_library_params eps; - exact_library exact_lib( resyn, eps ); + eps.np_classification = true; + exact_library exact_lib( resyn, eps ); /* library to map to technology */ std::vector gates; diff --git a/experiments/rewrite.cpp b/experiments/rewrite.cpp index 99b2f4e67..b6044c6cb 100644 --- a/experiments/rewrite.cpp +++ b/experiments/rewrite.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -44,10 +45,8 @@ int main() experiment exp( "rewrite", "benchmark", "size_before", "size_after", "depth_before", "depth_after", "runtime", "equivalent" ); - xag_npn_resynthesis resyn; - exact_library_params eps; - eps.np_classification = false; - exact_library exact_lib( resyn, eps ); + xag_npn_resynthesis resyn; + exact_library exact_lib( resyn ); for ( auto const& benchmark : epfl_benchmarks() ) { @@ -62,6 +61,8 @@ int main() rewrite_params ps; rewrite_stats st; + xag_balance( xag ); + uint32_t const size_before = xag.num_gates(); uint32_t const depth_before = depth_view( xag ).depth(); diff --git a/include/mockturtle/algorithms/lut_mapper.hpp b/include/mockturtle/algorithms/lut_mapper.hpp index 1594fac59..f1be98aa7 100644 --- a/include/mockturtle/algorithms/lut_mapper.hpp +++ b/include/mockturtle/algorithms/lut_mapper.hpp @@ -58,6 +58,7 @@ #include "../views/mffc_view.hpp" #include "../views/topo_view.hpp" #include "cleanup.hpp" +#include "collapse_mapped.hpp" #include "cut_enumeration.hpp" #include "exorcism.hpp" #include "simulation.hpp" @@ -2248,6 +2249,12 @@ class lut_map_impl #pragma region Dump network klut_network create_lut_network() { + /* specialized method: does not support buffer/inverter sweeping */ + if ( StoreFunction && ps.cut_enumeration_ps.minimize_truth_table ) + { + return create_lut_network_mapped(); + } + klut_network res; node_map, Ntk> node_to_signal( ntk ); @@ -2330,6 +2337,7 @@ class lut_map_impl } ); /* TODO: add sequential compatibility */ + edges = 0; for ( auto const& n : topo_order ) { if ( ntk.is_ci( n ) || ntk.is_constant( n ) ) @@ -2344,6 +2352,7 @@ class lut_map_impl kitty::dynamic_truth_table tt; std::vector> children; std::tie( tt, children ) = create_lut( n, node_to_signal, node_driver_type ); + edges += children.size(); switch ( node_driver_type[n] ) { @@ -2360,6 +2369,7 @@ class lut_map_impl case driver_type::mixed: node_to_signal[n] = res.create_node( children, tt ); opposites[n] = res.create_node( children, ~tt ); + edges += children.size(); break; } } @@ -2379,6 +2389,46 @@ class lut_map_impl return res; } + klut_network create_lut_network_mapped() + { + klut_network res; + mapping_view mapping_ntk{ ntk }; + + /* load mapping info */ + for ( auto const& n : topo_order ) + { + if ( ntk.is_ci( n ) || ntk.is_constant( n ) ) + continue; + + const auto index = ntk.node_to_index( n ); + if ( node_match[index].map_refs == 0 ) + continue; + + std::vector nodes; + auto const& best_cut = cuts[index][0]; + + for ( auto const& l : best_cut ) + { + nodes.push_back( ntk.index_to_node( l ) ); + } + mapping_ntk.add_to_mapping( n, nodes.begin(), nodes.end() ); + + if constexpr ( StoreFunction ) + { + mapping_ntk.set_cell_function( n, truth_tables[best_cut->func_id] ); + } + } + + /* generate mapped network */ + collapse_mapped_network( res, mapping_ntk ); + + st.area = area; + st.delay = delay; + st.edges = edges; + + return res; + } + inline lut_info create_lut( node const& n, node_map, Ntk>& node_to_signal, node_map const& node_driver_type ) { auto const& best_cut = cuts[ntk.node_to_index( n )][0]; @@ -2389,48 +2439,41 @@ class lut_map_impl children.push_back( node_to_signal[ntk.index_to_node( l )] ); } - kitty::dynamic_truth_table tt; + /* recursively compute the function for each choice until success */ + ntk.incr_trav_id(); + unordered_node_map node_to_value( ntk ); - if constexpr ( StoreFunction ) + /* add constants */ + node_to_value[ntk.get_node( ntk.get_constant( false ) )] = kitty::dynamic_truth_table( best_cut.size() ); + ntk.set_visited( ntk.get_node( ntk.get_constant( false ) ), ntk.trav_id() ); + if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) { - tt = truth_tables[best_cut->func_id]; + node_to_value[ntk.get_node( ntk.get_constant( true ) )] = ~kitty::dynamic_truth_table( best_cut.size() ); + ntk.set_visited( ntk.get_node( ntk.get_constant( true ) ), ntk.trav_id() ); } - else - { - /* recursively compute the function for each choice until success */ - ntk.incr_trav_id(); - unordered_node_map node_to_value( ntk ); - /* add constants */ - node_to_value[ntk.get_node( ntk.get_constant( false ) )] = kitty::dynamic_truth_table( best_cut.size() ); - ntk.set_visited( ntk.get_node( ntk.get_constant( false ) ), ntk.trav_id() ); - if ( ntk.get_node( ntk.get_constant( false ) ) != ntk.get_node( ntk.get_constant( true ) ) ) - { - node_to_value[ntk.get_node( ntk.get_constant( true ) )] = ~kitty::dynamic_truth_table( best_cut.size() ); - ntk.set_visited( ntk.get_node( ntk.get_constant( true ) ), ntk.trav_id() ); - } + /* add leaves */ + uint32_t ctr = 0; + for ( uint32_t leaf : best_cut ) + { + kitty::dynamic_truth_table tt_leaf( best_cut.size() ); + kitty::create_nth_var( tt_leaf, ctr++, node_driver_type[ntk.index_to_node( leaf )] == driver_type::neg ); + node_to_value[ntk.index_to_node( leaf )] = tt_leaf; + ntk.set_visited( ntk.index_to_node( leaf ), ntk.trav_id() ); + } - /* add leaves */ - uint32_t ctr = 0; - for ( uint32_t leaf : best_cut ) - { - kitty::dynamic_truth_table tt_leaf( best_cut.size() ); - kitty::create_nth_var( tt_leaf, ctr++, node_driver_type[ntk.index_to_node( leaf )] == driver_type::neg ); - node_to_value[ntk.index_to_node( leaf )] = tt_leaf; - ntk.set_visited( ntk.index_to_node( leaf ), ntk.trav_id() ); - } + /* recursively compute the function */ + ntk.foreach_fanin( n, [&]( auto const& f ) { + compute_function_rec( ntk.get_node( f ), node_to_value ); + } ); - /* recursively compute the function */ - ntk.foreach_fanin( n, [&]( auto const& f ) { - compute_function_rec( ntk.get_node( f ), node_to_value ); - } ); + std::vector tts; + ntk.foreach_fanin( n, [&]( auto const& f ) { + tts.push_back( node_to_value[ntk.get_node( f )] ); + } ); + TT tt = ntk.compute( n, tts.begin(), tts.end() ); - std::vector tts; - ntk.foreach_fanin( n, [&]( auto const& f ) { - tts.push_back( node_to_value[ntk.get_node( f )] ); - } ); - tt = ntk.compute( n, tts.begin(), tts.end() ); - } + minimize_support( tt, children ); return { tt, children }; } @@ -2491,6 +2534,48 @@ class lut_map_impl st.delay = delay; st.edges = edges; } + + void minimize_support( TT& tt, std::vector>& children ) + { + uint32_t support = 0u; + uint32_t support_size = 0u; + for ( uint32_t i = 0u; i < tt.num_vars(); ++i ) + { + if ( kitty::has_var( tt, i ) ) + { + support |= 1u << i; + ++support_size; + } + } + + /* variables not in the support are the most significative */ + if ( ( support & ( support + 1u ) ) == 0u ) + { + if ( support_size != children.size() ) + { + children.erase( children.begin() + support_size, children.end() ); + tt = kitty::shrink_to( tt, support_size ); + } + + return; + } + + /* vacuous variables */ + const auto support_vector = kitty::min_base_inplace( tt ); + assert( support_vector.size() != children.size() ); + + auto tt_shrink = shrink_to( tt, support_size ); + std::vector> children_support( support_size ); + + auto it_support = support_vector.begin(); + auto it_children = children_support.begin(); + while ( it_support != support_vector.end() ) + { + *it_children++ = children[*it_support++]; + } + + children = std::move( children_support ); + } #pragma endregion #pragma region balancing diff --git a/include/mockturtle/algorithms/mapper.hpp b/include/mockturtle/algorithms/mapper.hpp index 3ec273084..441f981a6 100644 --- a/include/mockturtle/algorithms/mapper.hpp +++ b/include/mockturtle/algorithms/mapper.hpp @@ -1880,7 +1880,7 @@ struct node_match_t float flows[3]; }; -template +template class exact_map_impl { public: @@ -1889,7 +1889,7 @@ class exact_map_impl using cut_t = typename network_cuts_t::cut_t; public: - explicit exact_map_impl( Ntk& ntk, exact_library const& library, map_params const& ps, map_stats& st ) + explicit exact_map_impl( Ntk& ntk, exact_library const& library, map_params const& ps, map_stats& st ) : ntk( ntk ), library( library ), ps( ps ), @@ -3380,7 +3380,7 @@ class exact_map_impl private: Ntk& ntk; - exact_library const& library; + exact_library const& library; map_params const& ps; map_stats& st; @@ -3443,8 +3443,8 @@ class exact_map_impl * \param ps Mapping params * \param pst Mapping statistics */ -template -NtkDest map( Ntk& ntk, exact_library const& library, map_params const& ps = {}, map_stats* pst = nullptr ) +template +NtkDest map( Ntk& ntk, exact_library const& library, map_params const& ps = {}, map_stats* pst = nullptr ) { static_assert( is_network_type_v, "Ntk is not a network type" ); static_assert( has_size_v, "Ntk does not implement the size method" ); @@ -3463,7 +3463,7 @@ NtkDest map( Ntk& ntk, exact_library const& libra static_assert( has_foreach_ro_v == has_create_ro_v, "Ntk and NtkDest networks are not both sequential" ); map_stats st; - detail::exact_map_impl p( ntk, library, ps, st ); + detail::exact_map_impl p( ntk, library, ps, st ); auto res = p.run(); st.time_total = st.time_mapping + st.cut_enumeration_st.time_total; diff --git a/include/mockturtle/algorithms/xag_balancing.hpp b/include/mockturtle/algorithms/xag_balancing.hpp new file mode 100644 index 000000000..6bac3cea4 --- /dev/null +++ b/include/mockturtle/algorithms/xag_balancing.hpp @@ -0,0 +1,618 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2023 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file xag_balancing.hpp + \brief Balances the XAG to reduce the depth + + \author Alessandro Tempia Calvino +*/ + +#pragma once + +#include +#include +#include + +#include "cleanup.hpp" +#include "../networks/aig.hpp" +#include "../traits.hpp" +#include "../views/depth_view.hpp" +#include "../views/fanout_view.hpp" + +namespace mockturtle +{ + +struct xag_balancing_params +{ + /*! \brief Minimizes the number of levels. */ + bool minimize_levels{ true }; +}; + +namespace detail +{ + +template +class xag_balance_impl +{ +public: + static constexpr size_t storage_init_size = 30; + using node = typename Ntk::node; + using signal = typename Ntk::signal; + using storage_t = std::vector>; + +public: + xag_balance_impl( Ntk& ntk, xag_balancing_params const& ps ) + : ntk( ntk ), ps( ps ), storage( storage_init_size ) + { + } + + void run() + { + ntk.clear_values(); + + for ( auto i = 0; i < storage_init_size; ++i ) + storage[i].reserve( 10 ); + + /* balance every CO */ + ntk.foreach_co( [&]( auto const& f ) { + balance_rec( ntk.get_node( f ), 0 ); + } ); + } + +private: + signal balance_rec( node const& n, uint32_t level ) + { + if ( ntk.is_ci( n ) ) + return ntk.make_signal( n ); + + /* node has been replaced in a previous recursion */ + if ( ntk.is_dead( n ) || ntk.value( n ) > 0 ) + { + return ntk.make_signal( find_substituted_node( n ) ); + } + + if ( level >= storage.size() ) + { + storage.emplace_back( std::vector() ); + storage.back().reserve( 10 ); + } + + /* collect leaves of the AND or XOR trees */ + bool polarity = false; + bool is_and = true; + if constexpr ( has_is_xor_v ) + { + if ( ntk.is_and( n ) ) + { + collect_leaves_and( n, storage[level] ); + } + else + { + polarity = collect_leaves_xor( n, storage[level] ); + is_and = false; + } + } + else + { + collect_leaves_and( n, storage[level] ); + } + + if ( storage[level].size() == 0 ) + { + ntk.substitute_node( n, ntk.get_constant( polarity ) ); + return ntk.get_constant( polarity ); + } + + /* recur over the leaves */ + for ( auto& f : storage[level] ) + { + signal new_signal = balance_rec( ntk.get_node( f ), level + 1 ); + f = new_signal ^ ntk.is_complemented( f ); + } + + assert( storage[level].size() > 1 ); + + /* sort by decreasing level */ + std::sort( storage[level].begin(), storage[level].end(), [this]( auto const& a, auto const& b ) { + return ntk.level( ntk.get_node( a ) ) > ntk.level( ntk.get_node( b ) ); + } ); + + /* mark TFI cone of n */ + ntk.incr_trav_id(); + mark_tfi( ntk.make_signal( n ), true ); + + /* generate the AND or XOR tree */ + if ( is_and ) + { + while ( storage[level].size() > 1 ) + { + /* explore multiple possibilities to find logic sharing */ + if ( ps.minimize_levels ) + pick_nodes_and( storage[level], find_left_most_at_level( storage[level] ) ); + else + pick_nodes_and_area( storage[level] ); + + /* pop the two selected nodes to create the new AND gate */ + signal child1 = storage[level].back(); + storage[level].pop_back(); + signal child2 = storage[level].back(); + storage[level].pop_back(); + signal new_sig = ntk.create_and( child1, child2 ); + + /* update level for AND node */ + update_level( ntk.get_node( new_sig ) ); + + /* insert the new node back */ + insert_node_sorted_and( storage[level], new_sig ); + } + } + else + { + while ( storage[level].size() > 1 ) + { + /* explore multiple possibilities to find logic sharing */ + if ( ps.minimize_levels ) + pick_nodes_xor( storage[level], find_left_most_at_level( storage[level] ) ); + else + pick_nodes_xor_area( storage[level] ); + + /* pop the two selected nodes to create the new XOR gate */ + signal child1 = storage[level].back(); + storage[level].pop_back(); + signal child2 = storage[level].back(); + storage[level].pop_back(); + signal new_sig = ntk.create_xor( child1, child2 ); + + /* update level for XOR node */ + update_level( ntk.get_node( new_sig ) ); + + /* insert the new node back */ + insert_node_sorted_xor( storage[level], new_sig, polarity ); + } + } + + signal root = storage[level][0] ^ polarity; + + /* replace if new */ + if ( n != ntk.get_node( root ) ) + { + ntk.substitute_node_no_restrash( n, root ); + } + + /* remember the substitution and the new node as already balanced */ + ntk.set_value( n, ntk.node_to_index( ntk.get_node( root ) ) ); + ntk.set_value( ntk.get_node( root ), ntk.node_to_index( ntk.get_node( root ) ) ); + + /* clean leaves storage */ + storage[level].clear(); + + return root; + } + + void collect_leaves_and( node const& n, std::vector& leaves ) + { + ntk.incr_trav_id(); + + int ret = collect_leaves_and_rec( ntk.make_signal( n ), leaves, true ); + + /* check for constant false */ + if ( ret < 0 ) + { + leaves.clear(); + } + } + + int collect_leaves_and_rec( signal const& f, std::vector& leaves, bool is_root ) + { + node n = ntk.get_node( f ); + + /* check if already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + { + for ( signal const& s : leaves ) + { + if ( ntk.get_node( s ) != n ) + continue; + + if ( s == f ) + return 1; /* same polarity: duplicate */ + else + return -1; /* opposite polarity: const0 */ + } + + return 0; + } + + /* set as leaf if signal is complemented or is a XOR or is a CI or has a multiple fanout */ + if ( !is_root && ( ntk.is_complemented( f ) || ntk.is_xor( n ) || ntk.is_ci( n ) || ntk.fanout_size( n ) > 1 ) ) + { + leaves.push_back( f ); + ntk.set_visited( n, ntk.trav_id() ); + return 0; + } + + int ret = 0; + ntk.foreach_fanin( n, [&]( auto const& child ) { + ret |= collect_leaves_and_rec( child, leaves, false ); + } ); + + return ret; + } + + bool collect_leaves_xor( node const& n, std::vector& leaves ) + { + ntk.incr_trav_id(); + + int ret = collect_leaves_xor_rec( ntk.make_signal( n ), leaves, true ); + + /* return top polarity */ + return ret ? true : false; + } + + int collect_leaves_xor_rec( signal const& f, std::vector& leaves, bool is_root ) + { + node n = ntk.get_node( f ); + + /* check if already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + { + auto it = leaves.begin(); + while ( it != leaves.end() ) + { + if ( ntk.get_node( *it ) != n ) + { + ++it; + continue; + } + + /* remove node (XOR property) */ + if ( ntk.get_node( *it ) == n ) + leaves.erase( it ); + + return 0; + } + + return 0; + } + + /* set as leaf if signal is an AND or is a CI or has a multiple fanout */ + if ( !is_root && ( ntk.is_and( n ) || ntk.is_ci( n ) || ntk.fanout_size( n ) > 1 ) ) + { + leaves.push_back( f ^ ntk.is_complemented( f ) ); + ntk.set_visited( n, ntk.trav_id() ); + return 0; + } + + int ret = 0; + ntk.foreach_fanin( n, [&]( auto const& child ) { + ret ^= ntk.is_complemented( child ) ? 1 : 0; + ret ^= collect_leaves_xor_rec( child, leaves, false ); + } ); + + return ret; + } + + size_t find_left_most_at_level( std::vector const& leaves ) + { + size_t pointer = leaves.size() - 1; + uint32_t current_level = ntk.level( ntk.get_node( leaves[leaves.size() - 2] ) ); + + while ( pointer > 0 ) + { + if ( ntk.level( ntk.get_node( leaves[pointer - 1] ) ) > current_level ) + break; + + --pointer; + } + + assert( ntk.level( ntk.get_node( leaves[pointer] ) ) == current_level ); + return pointer; + } + + void pick_nodes_and( std::vector& leaves, size_t left_most ) + { + size_t right_most = leaves.size() - 2; + + if ( ntk.level( ntk.get_node( leaves[leaves.size() - 1] ) ) == ntk.level( ntk.get_node( leaves[leaves.size() - 2] ) ) ) + right_most = left_most; + + for ( size_t right_pointer = leaves.size() - 1; right_pointer > right_most; --right_pointer ) + { + assert( left_most < right_pointer ); + + size_t left_pointer = right_pointer; + while ( left_pointer-- > left_most ) + { + /* select if node exists */ + std::optional pnode = ntk.has_and( leaves[right_pointer], leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[right_pointer] != leaves[leaves.size() - 1] ) + std::swap( leaves[right_pointer], leaves[leaves.size() - 1] ); + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + } + + void pick_nodes_and_area( std::vector& leaves ) + { + for ( size_t right_pointer = leaves.size() - 1; right_pointer > 0; --right_pointer ) + { + size_t left_pointer = right_pointer; + while ( left_pointer-- > 0 ) + { + /* select if node exists */ + std::optional pnode = ntk.has_and( leaves[right_pointer], leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[right_pointer] != leaves[leaves.size() - 1] ) + std::swap( leaves[right_pointer], leaves[leaves.size() - 1] ); + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + } + + void pick_nodes_xor( std::vector& leaves, size_t left_most ) + { + size_t right_most = leaves.size() - 2; + + if ( ntk.level( ntk.get_node( leaves[leaves.size() - 1] ) ) == ntk.level( ntk.get_node( leaves[leaves.size() - 2] ) ) ) + right_most = left_most; + + for ( size_t right_pointer = leaves.size() - 1; right_pointer > right_most; --right_pointer ) + { + assert( left_most < right_pointer ); + + size_t left_pointer = right_pointer; + while ( left_pointer-- > left_most ) + { + /* select if node exists */ + std::optional pnode = ntk.has_xor( leaves[right_pointer], leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[right_pointer] != leaves[leaves.size() - 1] ) + std::swap( leaves[right_pointer], leaves[leaves.size() - 1] ); + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + } + + void pick_nodes_xor_area( std::vector& leaves ) + { + for ( size_t right_pointer = leaves.size() - 1; right_pointer > 0; --right_pointer ) + { + size_t left_pointer = right_pointer; + while ( left_pointer-- > 0 ) + { + /* select if node exists */ + std::optional pnode = ntk.has_xor( leaves[right_pointer], leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[right_pointer] != leaves[leaves.size() - 1] ) + std::swap( leaves[right_pointer], leaves[leaves.size() - 1] ); + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + } + + void insert_node_sorted_and( std::vector& leaves, signal const& f ) + { + node n = ntk.get_node( f ); + + /* check uniqueness */ + for ( auto const& s : leaves ) + { + if ( s == f ) + return; + } + + leaves.push_back( f ); + for ( size_t i = leaves.size() - 1; i > 0; --i ) + { + auto& s2 = leaves[i - 1]; + + if ( ntk.level( ntk.get_node( s2 ) ) < ntk.level( n ) ) + { + std::swap( s2, leaves[i] ); + } + else + { + break; + } + } + } + + void insert_node_sorted_xor( std::vector& leaves, signal const& f, bool& polarity ) + { + node n = ntk.get_node( f ); + + /* check uniqueness */ + auto it = leaves.begin(); + while ( it != leaves.end() ) + { + if ( ntk.get_node( *it ) == ntk.get_node( f ) ) + { + polarity ^= ntk.is_complemented( f ) ^ ntk.is_complemented( *it ); + leaves.erase( it ); + return; + } + + ++it; + } + + leaves.push_back( f ); + for ( size_t i = leaves.size() - 1; i > 0; --i ) + { + auto& s2 = leaves[i - 1]; + + if ( ntk.level( ntk.get_node( s2 ) ) < ntk.level( n ) ) + { + std::swap( s2, leaves[i] ); + } + else + { + break; + } + } + } + + void update_level( node const& n ) + { + uint32_t l = 0; + ntk.foreach_fanin( n, [&]( auto const& f ) { + l = std::max( l, ntk.level( ntk.get_node( f ) ) ); + } ); + + ntk.set_level( n, l + 1 ); + } + + node find_substituted_node( node n ) + { + while ( ntk.is_dead( n ) ) + n = ntk.index_to_node( ntk.value( n ) ); + + return n; + } + + void mark_tfi( signal const& f, bool is_root ) + { + node n = ntk.get_node( f ); + + /* check if already visited */ + if ( ntk.visited( n ) == ntk.trav_id() ) + return; + + ntk.set_visited( n, ntk.trav_id() ); + + /* set as leaf if signal is complemented or is a CI or has a multiple fanout */ + if ( !is_root && ( ntk.is_complemented( f ) || ntk.is_ci( n ) || ntk.fanout_size( n ) > 1 ) ) + { + return; + } + + ntk.foreach_fanin( n, [&]( auto const& child ) { + mark_tfi( child, false ); + } ); + } + +private: + Ntk& ntk; + xag_balancing_params const& ps; + + storage_t storage; +}; + +} /* namespace detail */ + +/*! \brief XAG balancing. + * + * This method balance the XAG to reduce the + * depth. Level minimization can be turned off. + * In this case, balancing tries to reconstruct + * AND and XOR trees such that logic sharing is maximized. + * + * **Required network functions:** + * - `get_node` + * - `node_to_index` + * - `get_constant` + * - `create_pi` + * - `create_po` + * - `create_not` + * - `is_complemented` + * - `foreach_node` + * - `foreach_pi` + * - `foreach_po` + * - `clone_node` + * - `is_pi` + * - `is_constant` + * - `has_and` + */ +template +void xag_balance( Ntk& ntk, xag_balancing_params const& ps = {} ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_node_to_index_v, "Ntk does not implement the node_to_index method" ); + static_assert( has_get_constant_v, "Ntk does not implement the get_constant method" ); + static_assert( has_foreach_node_v, "Ntk does not implement the foreach_node method" ); + static_assert( has_foreach_pi_v, "Ntk does not implement the foreach_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_clone_node_v, "Ntk does not implement the clone_node method" ); + static_assert( has_create_pi_v, "Ntk does not implement the create_pi method" ); + static_assert( has_create_po_v, "Ntk does not implement the create_po method" ); + static_assert( has_create_not_v, "Ntk does not implement the create_not method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_has_and_v, "Ntk does not implement the has_and method" ); + static_assert( has_has_xor_v, "Ntk does not implement the has_xor method" ); + + fanout_view f_ntk{ ntk }; + depth_view> d_ntk{ f_ntk }; + + detail::xag_balance_impl p( d_ntk, ps ); + p.run(); + + ntk = cleanup_dangling( ntk ); +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/networks/block.hpp b/include/mockturtle/networks/block.hpp index a31ec7c09..d9d607f6c 100644 --- a/include/mockturtle/networks/block.hpp +++ b/include/mockturtle/networks/block.hpp @@ -532,50 +532,145 @@ class block_network #pragma endregion #pragma region Restructuring - // void substitute_node( node const& old_node, signal const& new_signal ) - // { - // /* find all parents from old_node */ - // for ( auto i = 0u; i < _storage->nodes.size(); ++i ) - // { - // auto& n = _storage->nodes[i]; - // for ( auto& child : n.children ) - // { - // if ( child == old_node ) - // { - // std::vector old_children( n.children.size() ); - // std::transform( n.children.begin(), n.children.end(), old_children.begin(), []( auto c ) { return c.index; } ); - // child = new_signal; - - // // increment fan-out of new node - // _storage->nodes[new_signal].data[0].h1++; - - // for ( auto const& fn : _events->on_modified ) - // { - // ( *fn )( i, old_children ); - // } - // } - // } - // } - - // /* check outputs */ - // for ( auto& output : _storage->outputs ) - // { - // if ( output == old_node ) - // { - // output = new_signal; - - // // increment fan-out of new node - // _storage->nodes[new_signal].data[0].h1++; - // } - // } - - // // reset fan-out of old node - // _storage->nodes[old_node].data[0].h1 = 0; - // } + void replace_in_node( node const& n, node const& old_node, signal new_signal ) + { + bool in_fanin = false; + auto& nobj = _storage->nodes[n]; + for ( auto& child : nobj.children ) + { + if ( child.index == old_node ) + { + in_fanin = true; + break; + } + } + + if ( !in_fanin ) + return; + + // remember before + std::vector old_children( nobj.children.size() ); + std::transform( nobj.children.begin(), nobj.children.end(), old_children.begin(), []( auto c ) { return signal{ c }; } ); + + /* replace in node */ + for ( auto& child : nobj.children ) + { + if ( child.index == old_node ) + { + child = signal{ new_signal.data ^ ( child.data & 1 ) }; + // increment fan-out of new node + _storage->nodes[new_signal.index].data[1].h2++; + _storage->nodes[new_signal.index].data[2 + new_signal.output].h2++; + } + } + + for ( auto const& fn : _events->on_modified ) + { + ( *fn )( n, old_children ); + } + } + + void replace_in_node_no_restrash( node const& n, node const& old_node, signal new_signal ) + { + replace_in_node( n, old_node, new_signal ); + } + + void replace_in_outputs( node const& old_node, signal const& new_signal ) + { + if ( is_dead( old_node ) ) + return; + + for ( auto& output : _storage->outputs ) + { + if ( output.index == old_node ) + { + output = signal{ new_signal.data ^ ( output.data & 1 ) }; + + if ( old_node != new_signal.index ) + { + /* increment fan-in of new node */ + _storage->nodes[new_signal.index].data[1].h2++; + _storage->nodes[new_signal.index].data[2 + new_signal.output].h2++; + } + } + } + } + + void take_out_node( node const& n ) + { + /* we cannot delete CIs, constants, or already dead nodes */ + if ( n < 2 || is_ci( n ) ) + return; + + /* delete the node */ + auto& nobj = _storage->nodes[n]; + nobj.data[1].h2 = UINT32_C( 0x80000000 ); /* fanout size 0, but dead */ + + /* remove fanout count over output pins */ + for ( uint32_t i = 2; i < _storage->nodes[n].data.size(); ++i ) + { + nobj.data[i].h2 = 0; + } + + for ( auto const& fn : _events->on_delete ) + { + ( *fn )( n ); + } + + /* if the node has been deleted, then deref fanout_size of + fanins and try to take them out if their fanout_size become 0 */ + for ( auto i = 0; i < nobj.children.size(); ++i ) + { + auto& child = nobj.children[i]; + if ( fanout_size( nobj.children[i].index ) == 0 ) + { + continue; + } + + decr_fanout_size_pin( nobj.children[i].index, signal{ child }.output ); + if ( decr_fanout_size( nobj.children[i].index ) == 0 ) + { + take_out_node( nobj.children[i].index ); + } + } + } + + void revive_node( node const& n ) + { + assert( !is_dead( n ) ); + return; + } + + void substitute_node( node const& old_node, signal const& new_signal ) + { + /* find all parents from old_node */ + for ( auto idx = 2u; idx < _storage->nodes.size(); ++idx ) + { + if ( is_ci( idx ) || is_dead( idx ) ) + continue; /* ignore CIs and dead nodes */ + + replace_in_node( idx, old_node, new_signal ); + } + + /* check outputs */ + replace_in_outputs( old_node, new_signal ); + + /* recursively reset old node */ + if ( old_node != new_signal.index ) + { + take_out_node( old_node ); + } + } + + void substitute_node_no_restrash( node const& old_node, signal const& new_signal ) + { + substitute_node( old_node, new_signal ); + } inline bool is_dead( node const& n ) const { - return false; + /* A dead node is simply a dangling node */ + return ( _storage->nodes[n].data[1].h2 >> 31 ) & 1; } #pragma endregion @@ -622,17 +717,17 @@ class block_network uint32_t fanout_size( node const& n ) const { - return _storage->nodes[n].data[1].h2; + return _storage->nodes[n].data[1].h2 & UINT32_C( 0x7FFFFFFF ); } uint32_t incr_fanout_size( node const& n ) const { - return _storage->nodes[n].data[1].h2++; + return _storage->nodes[n].data[1].h2++ & UINT32_C( 0x7FFFFFFF ); } uint32_t decr_fanout_size( node const& n ) const { - return --_storage->nodes[n].data[1].h2; + return --_storage->nodes[n].data[1].h2 & UINT32_C( 0x7FFFFFFF ); } uint32_t incr_fanout_size_pin( node const& n, uint32_t pin_index ) const @@ -647,7 +742,7 @@ class block_network uint32_t fanout_size_pin( node const& n, uint32_t pin_index ) const { - return _storage->nodes[n].data[2 + pin_index].h1; + return _storage->nodes[n].data[2 + pin_index].h2; } bool is_function( node const& n ) const @@ -799,7 +894,10 @@ class block_network void foreach_node( Fn&& fn ) const { auto r = range( _storage->nodes.size() ); - detail::foreach_element( r.begin(), r.end(), fn ); + detail::foreach_element_if( + r.begin(), r.end(), + [this]( auto n ) { return !is_dead( n ); }, + fn ); } template @@ -836,7 +934,7 @@ class block_network auto r = range( 2u, _storage->nodes.size() ); /* start from 2 to avoid constants */ detail::foreach_element_if( r.begin(), r.end(), - [this]( auto n ) { return !is_ci( n ); }, + [this]( auto n ) { return !is_ci( n ) && !is_dead( n ); }, fn ); } diff --git a/include/mockturtle/utils/tech_library.hpp b/include/mockturtle/utils/tech_library.hpp index cd647b53c..dea40cbe7 100644 --- a/include/mockturtle/utils/tech_library.hpp +++ b/include/mockturtle/utils/tech_library.hpp @@ -1094,7 +1094,7 @@ struct exact_library_params float delay_inverter{ 0.0f }; /* classify in NP instead of NPN */ - bool np_classification{ true }; + bool np_classification{ false }; /* Compute DC classes for matching with don't cares */ bool compute_dc_classes{ false }; /* verbose */ @@ -1115,11 +1115,11 @@ struct exact_library_params .. code-block:: c++ - mockturtle::mig_npn_resynthesis mig_resyn{true}; - mockturtle::exact_library lib( mig_resyn ); + mockturtle::mig_npn_resynthesis mig_resyn{ true }; + mockturtle::exact_library lib( mig_resyn ); \endverbatim */ -template +template class exact_library { using supergates_list_t = std::vector>; @@ -1131,15 +1131,30 @@ class exact_library using dc_lib_t = std::unordered_map, tt_hash>; public: + explicit exact_library( exact_library_params const& ps = {} ) + : _database(), + _ps( ps ), + _super_lib(), + _dc_lib() + { + _super_lib.reserve( 222 ); + } + + template explicit exact_library( RewritingFn const& rewriting_fn, exact_library_params const& ps = {} ) : _database(), - _rewriting_fn( rewriting_fn ), _ps( ps ), _super_lib(), _dc_lib() { _super_lib.reserve( 222 ); - generate_library(); + generate_library( rewriting_fn ); + } + + template + void add_library( RewritingFn const& rewriting_fn ) + { + generate_library( rewriting_fn ); } /*! \brief Get the structures matching the function. @@ -1225,7 +1240,8 @@ class exact_library } private: - void generate_library() + template + void generate_library( RewritingFn const& rewriting_fn ) { std::vector> pis; for ( auto i = 0u; i < NInputs; ++i ) @@ -1272,7 +1288,7 @@ class exact_library }; kitty::dynamic_truth_table function = kitty::extend_to( entry, NInputs ); - _rewriting_fn( _database, function, pis.begin(), pis.end(), add_supergate ); + rewriting_fn( _database, function, pis.begin(), pis.end(), add_supergate ); if ( supergates_pos.size() > 0 ) { std::sort( supergates_pos.begin(), supergates_pos.end(), [&]( auto const& a, auto const& b ) { @@ -1381,6 +1397,8 @@ class exact_library void compute_dont_cares_classes() { + _dc_lib.clear(); + /* save the size for each NPN class */ std::unordered_map class_sizes; for ( auto const& entry : _super_lib ) @@ -1508,7 +1526,6 @@ class exact_library private: Ntk _database; - RewritingFn const& _rewriting_fn; exact_library_params const _ps; lib_t _super_lib; dc_lib_t _dc_lib; diff --git a/test/algorithms/mapper.cpp b/test/algorithms/mapper.cpp index 9ee2401f2..3e28af096 100644 --- a/test/algorithms/mapper.cpp +++ b/test/algorithms/mapper.cpp @@ -379,7 +379,7 @@ TEST_CASE( "Exact map of bad MAJ3 and constant output", "[mapper]" ) { mig_npn_resynthesis resyn{ true }; - exact_library lib( resyn ); + exact_library lib( resyn ); aig_network aig; const auto a = aig.create_pi(); @@ -407,7 +407,7 @@ TEST_CASE( "Exact map of full adder", "[mapper]" ) { xmg_npn_resynthesis resyn; - exact_library lib( resyn ); + exact_library lib( resyn ); aig_network aig; const auto a = aig.create_pi(); @@ -436,7 +436,7 @@ TEST_CASE( "Exact map should avoid cycles", "[mapper]" ) resyn_fn resyn; - exact_library lib( resyn ); + exact_library lib( resyn ); aig_network aig; const auto x0 = aig.create_pi(); @@ -472,7 +472,8 @@ TEST_CASE( "Exact map with logic sharing", "[mapper]" ) resyn_fn resyn; - exact_library lib( resyn ); + exact_library lib; + lib.add_library( resyn ); aig_network aig; const auto x0 = aig.create_pi(); @@ -504,7 +505,7 @@ TEST_CASE( "exact map of sequential AIG", "[mapper]" ) using resyn_fn = xag_npn_resynthesis; resyn_fn resyn; - exact_library, resyn_fn> lib( resyn ); + exact_library> lib( resyn ); sequential aig; const auto a = aig.create_pi(); diff --git a/test/algorithms/quality.cpp b/test/algorithms/quality.cpp index a5ea245bc..fe203a1be 100644 --- a/test/algorithms/quality.cpp +++ b/test/algorithms/quality.cpp @@ -175,7 +175,9 @@ TEST_CASE( "Test quality improvement of MIG refactoring with Akers resynthesis", TEST_CASE( "Test quality improvement of MIG mapping", "[quality]" ) { mig_npn_resynthesis resyn{ true }; - exact_library lib( resyn ); + exact_library_params eps; + eps.np_classification = true; + exact_library lib( resyn, eps ); auto const v = foreach_benchmark( [&lib]( auto& ntk, auto ) { uint32_t const before = ntk.num_gates(); map_params ps; diff --git a/test/algorithms/rewrite.cpp b/test/algorithms/rewrite.cpp index 53523a02a..95351b245 100644 --- a/test/algorithms/rewrite.cpp +++ b/test/algorithms/rewrite.cpp @@ -28,7 +28,7 @@ TEST_CASE( "Rewrite bad MAJ", "[rewrite]" ) mig_npn_resynthesis resyn; exact_library_params eps; eps.np_classification = false; - exact_library exact_lib( resyn, eps ); + exact_library exact_lib( resyn, eps ); rewrite( mig, exact_lib ); @@ -52,7 +52,7 @@ TEST_CASE( "Rewrite XMG3 using a 4-input npn database", "[rewrite]" ) xmg3_npn_resynthesis resyn; exact_library_params eps; eps.np_classification = false; - exact_library exact_lib( resyn, eps ); + exact_library exact_lib( resyn, eps ); rewrite( xmg, exact_lib ); @@ -70,7 +70,7 @@ TEST_CASE( "Rewrite constant", "[rewrite]" ) mig_npn_resynthesis resyn; exact_library_params eps; eps.np_classification = false; - exact_library exact_lib( resyn, eps ); + exact_library exact_lib( resyn, eps ); rewrite( mig, exact_lib ); @@ -92,7 +92,7 @@ TEST_CASE( "Rewrite inverted constant", "[rewrite]" ) mig_npn_resynthesis resyn; exact_library_params eps; eps.np_classification = false; - exact_library exact_lib( resyn, eps ); + exact_library exact_lib( resyn, eps ); rewrite( mig, exact_lib ); @@ -114,7 +114,8 @@ TEST_CASE( "Rewrite projection", "[rewrite]" ) mig_npn_resynthesis resyn; exact_library_params eps; eps.np_classification = false; - exact_library exact_lib( resyn, eps ); + exact_library exact_lib( eps ); + exact_lib.add_library( resyn ); rewrite( mig, exact_lib ); @@ -137,7 +138,7 @@ TEST_CASE( "Rewrite inverted projection", "[rewrite]" ) mig_npn_resynthesis resyn; exact_library_params eps; eps.np_classification = false; - exact_library exact_lib( resyn, eps ); + exact_library exact_lib( resyn, eps ); rewrite( mig, exact_lib ); @@ -173,7 +174,7 @@ TEST_CASE( "Rewrite should avoid cycles", "[rewrite]" ) xag_npn_resynthesis resyn; exact_library_params eps; eps.np_classification = false; - exact_library exact_lib( resyn, eps ); + exact_library exact_lib( resyn, eps ); rewrite( aig, exact_lib ); @@ -204,7 +205,7 @@ TEST_CASE( "Rewrite depth-preserving", "[rewrite]" ) xag_npn_resynthesis resyn; exact_library_params eps; eps.np_classification = false; - exact_library exact_lib( resyn, eps ); + exact_library exact_lib( resyn, eps ); rewrite_params ps; ps.preserve_depth = true; diff --git a/test/algorithms/xag_balancing.cpp b/test/algorithms/xag_balancing.cpp new file mode 100644 index 000000000..e20dcfa85 --- /dev/null +++ b/test/algorithms/xag_balancing.cpp @@ -0,0 +1,210 @@ +#include + +#include +#include + +#include +#include +#include + +using namespace mockturtle; + +TEST_CASE( "Balancing AND chain in AIG (XAG)", "[xag_balancing]" ) +{ + xag_network xag; + const auto a = xag.create_pi(); + const auto b = xag.create_pi(); + const auto c = xag.create_pi(); + const auto d = xag.create_pi(); + + xag.create_po( xag.create_and( a, xag.create_and( b, xag.create_and( c, d ) ) ) ); + + CHECK( depth_view{ xag }.depth() == 3u ); + + xag_balance( xag ); + + CHECK( depth_view{ xag }.depth() == 2u ); +} + +TEST_CASE( "Balance AND finding structural hashing (XAG)", "[xag_balancing]" ) +{ + xag_network xag; + const auto a = xag.create_pi(); + const auto b = xag.create_pi(); + const auto c = xag.create_pi(); + const auto d = xag.create_pi(); + + const auto f1 = xag.create_and( a, b ); + const auto f2 = xag.create_and( f1, c ); + const auto f3 = xag.create_and( b, c ); + const auto f4 = xag.create_and( f3, d ); + + xag.create_po( f2 ); + xag.create_po( f4 ); + + CHECK( depth_view{ xag }.depth() == 2u ); + CHECK( xag.num_gates() == 4u ); + + xag_balancing_params ps; + ps.minimize_levels = false; + xag_balance( xag, ps ); + + CHECK( depth_view{ xag }.depth() == 2u ); + CHECK( xag.num_gates() == 3u ); +} + +TEST_CASE( "Balance AND tree that is constant 0 (XAG)", "[xag_balancing]" ) +{ + xag_network xag; + const auto a = xag.create_pi(); + const auto b = xag.create_pi(); + const auto c = xag.create_pi(); + + const auto f1 = xag.create_and( a, b ); + const auto f2 = xag.create_and( !a, c ); + const auto f3 = xag.create_and( f1, f2 ); + + xag.create_po( f3 ); + + CHECK( depth_view{ xag }.depth() == 2u ); + CHECK( xag.num_gates() == 3u ); + + xag_balance( xag ); + + CHECK( depth_view{ xag }.depth() == 0u ); + CHECK( xag.num_gates() == 0u ); +} + +TEST_CASE( "Balance AND tree that has redundant leaves (XAG)", "[xag_balancing]" ) +{ + xag_network xag; + const auto a = xag.create_pi(); + const auto b = xag.create_pi(); + const auto c = xag.create_pi(); + + const auto f1 = xag.create_and( a, b ); + const auto f2 = xag.create_and( a, c ); + const auto f3 = xag.create_and( f1, f2 ); + + xag.create_po( f3 ); + + CHECK( depth_view{ xag }.depth() == 2u ); + CHECK( xag.num_gates() == 3u ); + + xag_balance( xag ); + + CHECK( depth_view{ xag }.depth() == 2u ); + CHECK( xag.num_gates() == 2u ); +} + +TEST_CASE( "Balancing XOR chain", "[xag_balancing]" ) +{ + xag_network xag; + const auto a = xag.create_pi(); + const auto b = xag.create_pi(); + const auto c = xag.create_pi(); + const auto d = xag.create_pi(); + + xag.create_po( xag.create_xor( a, xag.create_xor( b, xag.create_xor( c, d ) ) ) ); + + CHECK( depth_view{ xag }.depth() == 3u ); + + xag_balance( xag ); + + CHECK( depth_view{ xag }.depth() == 2u ); +} + +TEST_CASE( "Balance XOR finding structural hashing", "[xag_balancing]" ) +{ + xag_network xag; + const auto a = xag.create_pi(); + const auto b = xag.create_pi(); + const auto c = xag.create_pi(); + const auto d = xag.create_pi(); + + const auto f1 = xag.create_xor( a, b ); + const auto f2 = xag.create_xor( f1, c ); + const auto f3 = xag.create_xor( b, c ); + const auto f4 = xag.create_xor( f3, d ); + + xag.create_po( f2 ); + xag.create_po( f4 ); + + CHECK( depth_view{ xag }.depth() == 2u ); + CHECK( xag.num_gates() == 4u ); + + xag_balancing_params ps; + ps.minimize_levels = false; + xag_balance( xag, ps ); + + CHECK( depth_view{ xag }.depth() == 2u ); + CHECK( xag.num_gates() == 3u ); +} + +TEST_CASE( "Balance XOR tree that has redundant leaves", "[xag_balancing]" ) +{ + xag_network xag; + const auto a = xag.create_pi(); + const auto b = xag.create_pi(); + const auto c = xag.create_pi(); + + const auto f1 = xag.create_xor( a, b ); + const auto f2 = xag.create_xor( a, c ); + const auto f3 = xag.create_xor( f1, f2 ); + + xag.create_po( f3 ); + + CHECK( depth_view{ xag }.depth() == 2u ); + CHECK( xag.num_gates() == 3u ); + + xag_balance( xag ); + + CHECK( depth_view{ xag }.depth() == 1u ); + CHECK( xag.num_gates() == 1u ); +} + +TEST_CASE( "Balance XOR tree that has redundant leaves negated", "[xag_balancing]" ) +{ + xag_network xag; + const auto a = xag.create_pi(); + const auto b = xag.create_pi(); + const auto c = xag.create_pi(); + + const auto f1 = xag.create_xor( a, b ); + const auto f2 = xag.create_xnor( a, c ); + const auto f3 = xag.create_xor( f1, f2 ); + + xag.create_po( f3 ); + + CHECK( depth_view{ xag }.depth() == 2u ); + CHECK( xag.num_gates() == 3u ); + + xag_balance( xag ); + + CHECK( depth_view{ xag }.depth() == 1u ); + CHECK( xag.num_gates() == 1u ); +} + +TEST_CASE( "Balance XOR tree that is constant 1", "[xag_balancing]" ) +{ + xag_network xag; + const auto a = xag.create_pi(); + const auto b = xag.create_pi(); + const auto c = xag.create_pi(); + + const auto f1 = xag.create_xor( a, b ); + const auto f2 = xag.create_xor( a, c ); + const auto f3 = xag.create_xor( b, c ); + const auto f4 = xag.create_xnor( f1, f2 ); + const auto f5 = xag.create_xor( f3, f4 ); + + xag.create_po( f5 ); + + CHECK( depth_view{ xag }.depth() == 3u ); + CHECK( xag.num_gates() == 5u ); + + xag_balance( xag ); + + CHECK( depth_view{ xag }.depth() == 0u ); + CHECK( xag.num_gates() == 0u ); +} diff --git a/test/networks/block.cpp b/test/networks/block.cpp index 05cbe7d2f..9deed11fa 100644 --- a/test/networks/block.cpp +++ b/test/networks/block.cpp @@ -435,3 +435,74 @@ TEST_CASE( "Multi-output functions in block networks", "[block_net]" ) CHECK( block_net.is_multioutput( 7 ) == false ); CHECK( block_net.node_function_pin( 7, 0 )._bits[0] == 0x8 ); } + +TEST_CASE( "substitute node by another in a block network", "[block_net]" ) +{ + block_network block_net; + + const auto c0 = block_net.get_node( block_net.get_constant( false ) ); + const auto c1 = block_net.get_node( block_net.get_constant( true ) ); + const auto a = block_net.create_pi(); + const auto b = block_net.create_pi(); + + // XOR with NAND + const auto n1 = block_net.create_nand( a, b ); + const auto n2 = block_net.create_le( a, n1 ); + const auto n3 = block_net.create_lt( b, n1 ); + const auto n4 = block_net.create_ha( n2, n3 ); + const auto n5 = block_net.create_or( n3, n4 ); + const auto n6 = block_net.create_xor( a, b ); + const auto po = block_net.po_at( block_net.create_po( block_net.next_output_pin( n4 ) ) ); + + CHECK( po.index == 7 ); + CHECK( po.complement == 0 ); + CHECK( po.output == 1 ); + + std::vector> nodes; + block_net.foreach_node( [&]( auto node ) { nodes.push_back( node ); CHECK( !block_net.is_dead( node ) ); } ); + + std::vector node_ref = { block_net.get_node( c0 ), + block_net.get_node( c1 ), + block_net.get_node( a ), + block_net.get_node( b ), + block_net.get_node( n1 ), + block_net.get_node( n2 ), + block_net.get_node( n3 ), + block_net.get_node( n4 ), + block_net.get_node( n5 ), + block_net.get_node( n6 ) + }; + + CHECK( nodes == node_ref ); + CHECK( block_net.fanout_size( block_net.get_node( n4 ) ) == 2 ); + CHECK( block_net.fanout_size_pin( block_net.get_node( n4 ), 0 ) == 1 ); + CHECK( block_net.fanout_size_pin( block_net.get_node( n4 ), 1 ) == 1 ); + CHECK( block_net.fanout_size( block_net.get_node( n2 ) ) == 1 ); + CHECK( block_net.fanout_size_pin( block_net.get_node( n2 ), 0 ) == 1 ); + + block_net.foreach_po( [&]( auto f ) { + CHECK( block_net.get_node( f ) == block_net.get_node( n4 ) ); + CHECK( block_net.get_output_pin( f ) == 1 ); + } ); + + // substitute nodes + block_net.substitute_node( block_net.get_node( n4 ), n6 ); + + block_net.is_dead( block_net.get_node( n4 ) == false ); + block_net.is_dead( block_net.get_node( n2 ) == false ); + CHECK( block_net.size() == 10 ); + CHECK( block_net.fanout_size( block_net.get_node( n4 ) ) == 0 ); + CHECK( block_net.fanout_size_pin( block_net.get_node( n4 ), 0 ) == 0 ); + CHECK( block_net.fanout_size_pin( block_net.get_node( n4 ), 1 ) == 0 ); + CHECK( block_net.fanout_size( block_net.get_node( n6 ) ) == 2 ); + CHECK( block_net.fanout_size_pin( block_net.get_node( n6 ), 0 ) == 2 ); + + CHECK( block_net.is_dead( block_net.get_node( n4 ) ) ); + CHECK( block_net.is_dead( block_net.get_node( n2 ) ) ); + CHECK( block_net.fanout_size( block_net.get_node( n2 ) ) == 0 ); + CHECK( block_net.fanout_size_pin( block_net.get_node( n2 ), 0 ) == 0 ); + block_net.foreach_po( [&]( auto f ) { + CHECK( block_net.get_node( f ) == block_net.get_node( n6 ) ); + CHECK( block_net.get_output_pin( f ) == 0 ); + } ); +}