diff --git a/include/mockturtle/algorithms/mig_resub.hpp b/include/mockturtle/algorithms/mig_resub.hpp index e6677abbc..6c57e0130 100644 --- a/include/mockturtle/algorithms/mig_resub.hpp +++ b/include/mockturtle/algorithms/mig_resub.hpp @@ -34,6 +34,8 @@ #include #include +#include +#include namespace kitty { @@ -643,6 +645,62 @@ struct mig_resub_functor binate_divisors bdivs; }; /* mig_resub_functor */ +template> +struct mig_resub_functor_new +{ +public: + using node = mig_network::node; + using signal = mig_network::signal; + using stats = mig_resub_stats; + +public: + explicit mig_resub_functor_new( Ntk& ntk, Simulator const& sim, std::vector const& divs, uint32_t num_divs, stats& st ) + : ntk( ntk ) + , sim( sim ) + , tts( ntk ) + , divs( divs ) + , st( st ) + { + assert( divs.size() == num_divs ); (void)num_divs; + div_signals.reserve( divs.size() ); + } + + std::optional operator()( node const& root, TTcare care, uint32_t required, uint32_t max_inserts, uint32_t potential_gain, uint32_t& real_gain ) + { + (void)care; (void)required; + kitty::partial_truth_table root_tt; + root_tt = sim.get_tt( sim.get_phase( root ) ? !ntk.make_signal( root ) : ntk.make_signal( root ) ); + Engine engine( root_tt ); + for ( auto const& d : divs ) + { + div_signals.emplace_back( sim.get_phase( d ) ? !ntk.make_signal( d ) : ntk.make_signal( d ) ); + tts[d] = sim.get_tt( div_signals.back() ); + } + engine.add_divisors( divs.begin(), divs.end(), tts ); + + auto const res = engine.compute_function( std::min( potential_gain - 1, max_inserts ) ); + if ( res ) + { + signal ret; + real_gain = potential_gain - (*res).num_gates(); + insert( ntk, div_signals.begin(), div_signals.end(), *res, [&]( signal const& s ){ ret = s; } ); + return ret; + } + else + { + return std::nullopt; + } + } + +private: + Ntk& ntk; + Simulator const& sim; + unordered_node_map tts; + std::vector const& divs; + std::vector div_signals; + stats& st; +}; + /*! \brief MIG-specific resubstitution algorithm. * * This algorithms iterates over each node, creates a @@ -707,7 +765,7 @@ void mig_resubstitution( Ntk& ntk, resubstitution_params const& ps = {}, resubst { using truthtable_t = kitty::static_truth_table<8u>; using truthtable_dc_t = kitty::dynamic_truth_table; - using resub_impl_t = detail::resubstitution_impl, truthtable_dc_t>>>; + using resub_impl_t = detail::resubstitution_impl, truthtable_dc_t>>>; resubstitution_stats st; typename resub_impl_t::engine_st_t engine_st; @@ -732,7 +790,7 @@ void mig_resubstitution( Ntk& ntk, resubstitution_params const& ps = {}, resubst { using truthtable_t = kitty::dynamic_truth_table; using truthtable_dc_t = kitty::dynamic_truth_table; - using resub_impl_t = detail::resubstitution_impl, truthtable_dc_t>>>; + using resub_impl_t = detail::resubstitution_impl, truthtable_dc_t>>>; resubstitution_stats st; typename resub_impl_t::engine_st_t engine_st; diff --git a/include/mockturtle/algorithms/mig_resyn_engines.hpp b/include/mockturtle/algorithms/mig_resyn_engines.hpp new file mode 100644 index 000000000..5812a4717 --- /dev/null +++ b/include/mockturtle/algorithms/mig_resyn_engines.hpp @@ -0,0 +1,1203 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2020 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 mig_resyn_engines.hpp + \brief Implements resynthesis methods for MIGs. + + \author Siang-Yun Lee +*/ + +#pragma once + +#include "../utils/index_list.hpp" + +#include + +#include +#include + +namespace mockturtle +{ + +template +class mig_resyn_engine_bottom_up +{ +public: + explicit mig_resyn_engine_bottom_up( TT const& target ) + : num_bits( target.num_bits() ), divisors( { ~target, target } ) + { } + + template + void add_divisor( node_type const& node, truth_table_storage_type const& tts ) + { + assert( tts[node].num_bits() == num_bits ); + divisors.emplace_back( tts[node] ^ divisors.at( 0u ) ); // XNOR target = XOR ~target + divisors.emplace_back( ~tts[node] ^ divisors.at( 0u ) ); + index_list.add_inputs(); + } + + template + void add_divisors( iterator_type begin, iterator_type end, truth_table_storage_type const& tts ) + { + while ( begin != end ) + { + add_divisor( *begin, tts ); + ++begin; + } + } + + std::optional compute_function( uint32_t num_inserts ) + { + uint64_t max_score = 0u; + max_i = 0u; + for ( auto i = 0u; i < divisors.size(); ++i ) + { + uint32_t score = kitty::count_ones( divisors.at( i ) ); + if ( score > max_score ) + { + max_score = score; + max_i = i; + if ( max_score == num_bits ) + { + break; + } + } + } + /* 0-resub (including constants) */ + if ( max_score == num_bits ) + { + index_list.add_output( max_i ); + return index_list; + } + + if ( num_inserts == 0u ) + { + return std::nullopt; + } + size_limit = divisors.size() + num_inserts * 2; + + return bottom_up_approach(); + } + +private: + std::optional bottom_up_approach() + { + //maj_nodes.emplace_back( maj_node{uint32_t( divisors.size() ), {max_i}} ); + TT const& function_i = divisors.at( max_i ); + current_lit = divisors.size(); + return bottom_up_approach_rec( function_i ); + } + + std::optional bottom_up_approach_rec( TT const& function_i ) + { + /* THINK: Should we consider reusing newly-built nodes (nodes in maj_nodes) in addition to divisors? */ + + /* the second fanin: 2 * #newly-covered-bits + 1 * #cover-again-bits */ + uint64_t max_score = 0u; + max_j = 0u; + auto const not_covered_by_i = ~function_i; + for ( auto j = 0u; j < divisors.size(); ++j ) + { + auto const covered_by_j = divisors.at( j ); + uint32_t score = kitty::count_ones( covered_by_j ) + kitty::count_ones( not_covered_by_i & covered_by_j ); + if ( score > max_score && (j >> 1) != (max_i >> 1) ) + { + max_score = score; + max_j = j; + } + } + //maj_nodes.back().fanins.emplace_back( max_j ); + + /* the third fanin: only care about the disagreed bits */ + max_score = 0u; + max_k = 0u; + auto const disagree_in_ij = function_i ^ divisors.at( max_j ); + for ( auto k = 0u; k < divisors.size(); ++k ) + { + uint32_t score = kitty::count_ones( divisors.at( k ) & disagree_in_ij ); + if ( score > max_score && (k >> 1) != (max_i >> 1) && (k >> 1) != (max_j >> 1) ) + { + max_score = score; + max_k = k; + } + } + //maj_nodes.back().fanins.emplace_back( max_k ); + index_list.add_maj( max_i, max_j, max_k ); + + auto const current_function = kitty::ternary_majority( function_i, divisors.at( max_j ), divisors.at( max_k ) ); + if ( kitty::is_const0( ~current_function ) ) + { + index_list.add_output( current_lit ); + return index_list; + } + else if ( current_lit + 2 < size_limit ) + { + //maj_nodes.emplace_back( maj_node{maj_nodes.back().id + 2u, {maj_nodes.back().id}} ); + max_i = current_lit; + current_lit += 2; + return bottom_up_approach_rec( current_function ); + } + else + { + return std::nullopt; + } + } + +private: + uint32_t size_limit; + uint32_t num_bits; + uint32_t current_lit; /* literal of the current topmost node */ + + uint32_t max_i, max_j, max_k; + + std::vector divisors; + mig_index_list index_list; +}; /* mig_resyn_engine_bottom_up */ + +template +class mig_resyn_engine +{ + /*! \brief Internal data structure */ + struct expansion_position + { + int32_t parent_position = -1; // maj_nodes.at( ... ) + int32_t fanin_num = -1; // 0, 1, 2 + + bool operator==( expansion_position const& e ) const + { + return parent_position == e.parent_position && fanin_num == e.fanin_num; + } + }; + + struct maj_node + { + uint32_t id; /* maj_nodes.at( id - divisors.size() ) */ + std::vector fanins; /* ids of its three fanins */ + + std::vector fanin_functions = std::vector(); + TT care = TT(); + expansion_position parent = expansion_position(); + }; + + struct simple_maj + { + std::vector fanins; /* ids of divisors */ + TT function = TT(); /* resulting function */ + }; + +public: + explicit mig_resyn_engine( TT const& target ) + : num_bits( target.num_bits() ), divisors( { ~target, target } ) + { } + + template + void add_divisor( node_type const& node, truth_table_storage_type const& tts ) + { + assert( tts[node].num_bits() == num_bits ); + divisors.emplace_back( tts[node] ^ divisors.at( 0u ) ); // XNOR target = XOR ~target + divisors.emplace_back( ~tts[node] ^ divisors.at( 0u ) ); + scores.resize( divisors.size() ); + } + + template + void add_divisors( iterator_type begin, iterator_type end, truth_table_storage_type const& tts ) + { + while ( begin != end ) + { + add_divisor( *begin, tts ); + ++begin; + } + } + + std::optional compute_function( uint32_t num_inserts ) + { + for ( auto i = 0u; i < divisors.size(); ++i ) + { + if ( kitty::is_const0( ~divisors.at( i ) ) ) + { + /* 0-resub (including constants) */ + mig_index_list index_list( divisors.size() / 2 - 1 ); + index_list.add_output( i ); + return index_list; + } + } + + if ( num_inserts == 0u ) + { + return std::nullopt; + } + size_limit = num_inserts; + + return top_down_approach(); + } + +private: + std::optional top_down_approach() + { + maj_nodes.reserve( size_limit ); + /* topmost node: care is const1 */ + TT const const1 = divisors.at( 0u ) | divisors.at( 1u ); + std::vector top_node_choices = construct_top(); + + if ( top_node_choices.size() == 1u && kitty::is_const0( ~top_node_choices[0].function ) ) + { + /* 1-resub */ + mig_index_list index_list( divisors.size() / 2 - 1 ); + index_list.add_maj( top_node_choices[0].fanins[0], top_node_choices[0].fanins[1], top_node_choices[0].fanins[2] ); + index_list.add_output( divisors.size() ); + return index_list; + } + for ( simple_maj& top_node : top_node_choices ) + { + if ( easy_refine( top_node, 0 ) || easy_refine( top_node, 1 ) ) + { + /* 1-resub */ + mig_index_list index_list( divisors.size() / 2 - 1 ); + index_list.add_maj( top_node.fanins[0], top_node.fanins[1], top_node.fanins[2] ); + index_list.add_output( divisors.size() ); + return index_list; + } + } + if ( size_limit == 1u ) + { + return std::nullopt; + } + + std::vector maj_nodes_best; + for ( simple_maj const& top_node : top_node_choices ) + { + for ( int32_t i = 0; i < 3; ++i ) + { + maj_nodes.clear(); + maj_nodes.emplace_back( maj_node{uint32_t( divisors.size() ), top_node.fanins, {divisors.at( top_node.fanins[0] ), divisors.at( top_node.fanins[1] ), divisors.at( top_node.fanins[2] )}, const1} ); + + leaves.clear(); + improve_in_parent.clear(); + shuffle.clear(); + first_round = true; + leaves.emplace_back( expansion_position{0, (int32_t)sibling_index( i, 1 )} ); + leaves.emplace_back( expansion_position{0, (int32_t)sibling_index( i, 2 )} ); + + TT const care = ~( divisors.at( top_node.fanins[sibling_index( i, 1 )] ) & divisors.at( top_node.fanins[sibling_index( i, 2 )] ) ); + if ( evaluate_one( care, divisors.at( top_node.fanins[i] ), expansion_position{0, i} ) ) + { + /* 2-resub */ + maj_nodes_best = maj_nodes; + return translate( maj_nodes_best ); + } + + if ( !refine() ) + { + continue; + } + + if ( maj_nodes_best.size() == 0u || maj_nodes.size() < maj_nodes_best.size() ) + { + maj_nodes_best = maj_nodes; + } + } + } + + if ( maj_nodes_best.size() == 0u ) + { + return std::nullopt; + } + return translate( maj_nodes_best ); + } + + bool refine() + { + while ( ( leaves.size() != 0u || improve_in_parent.size() != 0u || shuffle.size() != 0u ) && maj_nodes.size() < size_limit ) + { + if ( leaves.size() == 0u ) + { + if ( improve_in_parent.size() != 0u ) + { + leaves = improve_in_parent; + improve_in_parent.clear(); + } + else + { + leaves = shuffle; + shuffle.clear(); + } + first_round = false; + } + + uint32_t min_mismatch = num_bits + 1; + uint32_t pos = 0u; + for ( int32_t i = 0; (unsigned)i < leaves.size(); ++i ) + { + maj_node& parent_node = maj_nodes.at( leaves[i].parent_position ); + uint32_t const& fi = leaves[i].fanin_num; + TT const& original_function = parent_node.fanin_functions.at( fi ); + + if ( parent_node.fanins.at( fi ) >= divisors.size() ) /* already expanded */ + { + leaves.erase( leaves.begin() + i ); + --i; + continue; + } + + TT const care = parent_node.care & ~( sibling_func( parent_node, fi, 1 ) & sibling_func( parent_node, fi, 2 ) ); + if ( fulfilled( original_function, care ) /* already fulfilled */ + || care == parent_node.care /* probably cannot improve */ + ) + { + leaves.erase( leaves.begin() + i ); + --i; + continue; + } + + uint32_t const mismatch = count_ones( care & ~original_function ); + if ( mismatch < min_mismatch ) + { + pos = i; + min_mismatch = mismatch; + } + } + if ( leaves.size() == 0u ) + { + break; + } + expansion_position node_position = leaves.at( pos ); + leaves.erase( leaves.begin() + pos ); + + maj_node& parent_node = maj_nodes.at( node_position.parent_position ); + uint32_t const& fi = node_position.fanin_num; + TT const& original_function = parent_node.fanin_functions.at( fi ); + TT const care = parent_node.care & ~( sibling_func( parent_node, fi, 1 ) & sibling_func( parent_node, fi, 2 ) ); + + if ( evaluate_one( care, original_function, node_position ) ) + { + return true; + } + } + return false; + } + + bool evaluate_one( TT const& care, TT const& original_function, expansion_position const& node_position ) + { + maj_node& parent_node = maj_nodes.at( node_position.parent_position ); + uint32_t const& fi = node_position.fanin_num; + + simple_maj const new_node = expand_one( care ); + uint64_t const original_score = score( original_function, care ); + uint64_t const new_score = score( new_node.function, care ); + if ( new_score < original_score ) + { + return false; + } + + if ( new_score == original_score ) + { + if ( kitty::count_ones( new_node.function & parent_node.care ) > kitty::count_ones( original_function & parent_node.care ) ) + { + if ( first_round ) + { + /* We put it into a back-up queue for now */ + improve_in_parent.emplace_back( node_position ); + return false; + } + else + { + /* When there is no other possibilities, we try one in the back-up queues, and go back to the stricter state */ + first_round = true; + } + } + else if ( kitty::count_ones( new_node.function & parent_node.care ) == kitty::count_ones( original_function & parent_node.care ) && new_node.function != original_function ) + { + if ( first_round ) + { + /* We put it into a back-up queue for now */ + shuffle.emplace_back( node_position ); + return false; + } + else + { + /* When there is no other possibilities, we try one in the back-up queues, and go back to the stricter state */ + first_round = true; + } + } + else + { + return false; + } + } + + /* construct the new node */ + uint32_t const new_id = maj_nodes.size() + divisors.size(); + maj_nodes.emplace_back( maj_node{new_id, new_node.fanins, {divisors.at( new_node.fanins[0] ), divisors.at( new_node.fanins[1] ), divisors.at( new_node.fanins[2] )}, care, node_position} ); + update_fanin( parent_node, fi, new_id, new_node.function ); + + if ( kitty::is_const0( ~new_node.function & care ) ) + { + if ( node_fulfilled( maj_nodes.at( 0u ) ) ) + { + return true; + } + // TODO: add all the fulfilled nodes (trace upwards) to the divisor list, determining their indices in topological order + // This also has to be taken care of in the different runs. + } + else + { + leaves.emplace_back( expansion_position{int32_t( maj_nodes.size() - 1 ), 0} ); + leaves.emplace_back( expansion_position{int32_t( maj_nodes.size() - 1 ), 1} ); + leaves.emplace_back( expansion_position{int32_t( maj_nodes.size() - 1 ), 2} ); + } + return false; + } + + simple_maj expand_one( TT const& care ) + { + /* look up in computed_table */ + auto computed = computed_table.find( care ); + if ( computed != computed_table.end() ) + { + //std::cout<<"cache hit!\n"; + return computed->second; + } + + /* the first fanin: cover most care bits */ + uint64_t max_score = 0u; + uint32_t max_i = 0u; + for ( auto i = 0u; i < divisors.size(); ++i ) + { + scores.at( i ) = kitty::count_ones( divisors.at( i ) & care ); + if ( scores.at( i ) > max_score ) + { + max_score = scores.at( i ); + max_i = i; + } + } + + /* the second fanin: 2 * #newly-covered-bits + 1 * #cover-again-bits */ + max_score = 0u; + uint32_t max_j = 0u; + auto const not_covered_by_i = ~divisors.at( max_i ); + for ( auto j = 0u; j < divisors.size(); ++j ) + { + auto const covered_by_j = divisors.at( j ) & care; + scores.at( j ) = kitty::count_ones( covered_by_j ) + kitty::count_ones( not_covered_by_i & covered_by_j ); + if ( scores.at( j ) > max_score && !same_divisor( j, max_i ) ) + { + max_score = scores.at( j ); + max_j = j; + } + } + + /* the third fanin: 2 * #cover-never-covered-bits + 1 * #cover-covered-once-bits */ + max_score = 0u; + uint32_t max_k = 0u; + auto const not_covered_by_j = ~divisors.at( max_j ); + for ( auto k = 0u; k < divisors.size(); ++k ) + { + auto const covered_by_k = divisors.at( k ) & care; + scores.at( k ) = kitty::count_ones( covered_by_k & not_covered_by_i ) + kitty::count_ones( covered_by_k & not_covered_by_j ); + if ( scores.at( k ) > max_score && !same_divisor( k, max_i ) && !same_divisor( k, max_j ) ) + { + max_score = scores.at( k ); + max_k = k; + } + } + + computed_table[care] = simple_maj( {{max_i, max_j, max_k}, kitty::ternary_majority( divisors.at( max_i ), divisors.at( max_j ), divisors.at( max_k ) )} ); + return computed_table[care]; + } + + std::vector construct_top() + { + std::vector res; + + /* the first fanin: cover most bits */ + uint64_t max_score = 0u; + for ( auto i = 0u; i < divisors.size(); ++i ) + { + scores.at( i ) = kitty::count_ones( divisors.at( i ) ); + if ( scores.at( i ) > max_score ) + { + max_score = scores.at( i ); + } + } + for ( auto i = 0u; i < divisors.size(); ++i ) + { + if ( scores.at( i ) == max_score ) + { + if ( construct_top( i, res ) ) + { + break; + } + } + } + return res; + } + + bool construct_top( uint32_t max_i, std::vector& res ) + { + /* the second fanin: 2 * #newly-covered-bits + 1 * #cover-again-bits */ + uint64_t max_score = 0u; + auto const not_covered_by_i = ~divisors.at( max_i ); + for ( auto j = 0u; j < divisors.size(); ++j ) + { + auto const covered_by_j = divisors.at( j ); + scores.at( j ) = kitty::count_ones( covered_by_j ) + kitty::count_ones( not_covered_by_i & covered_by_j ); + if ( scores.at( j ) > max_score && !same_divisor( j, max_i ) ) + { + max_score = scores.at( j ); + } + } + for ( auto j = 0u; j < divisors.size(); ++j ) + { + if ( scores.at( j ) == max_score && !same_divisor( j, max_i ) ) + { + if ( construct_top( max_i, j, res ) ) + { + break; + } + } + } + return false; + } + + bool construct_top( uint32_t max_i, uint32_t max_j, std::vector& res ) + { + /* the third fanin: 2 * #cover-never-covered-bits + 1 * #cover-covered-once-bits */ + uint64_t max_score = 0u; + auto const not_covered_by_i = ~divisors.at( max_i ); + auto const not_covered_by_j = ~divisors.at( max_j ); + for ( auto k = 0u; k < divisors.size(); ++k ) + { + auto const covered_by_k = divisors.at( k ); + scores.at( k ) = kitty::count_ones( covered_by_k & not_covered_by_i ) + kitty::count_ones( covered_by_k & not_covered_by_j ); + if ( scores.at( k ) > max_score && !same_divisor( k, max_i ) && !same_divisor( k, max_j ) ) + { + max_score = scores.at( k ); + } + } + + for ( auto k = 0u; k < divisors.size(); ++k ) + { + if ( scores.at( k ) == max_score && !same_divisor( k, max_i ) && !same_divisor( k, max_j ) ) + { + TT const func = kitty::ternary_majority( divisors.at( max_i ), divisors.at( max_j ), divisors.at( k ) ); + if ( kitty::is_const0( ~func ) ) + { + res.clear(); + res.emplace_back( simple_maj( {{max_i, max_j, k}, func} ) ); + return true; + } + res.emplace_back( simple_maj( {{max_i, max_j, k}, func} ) ); + } + } + return false; + } + + /* try to replace the first (fi=0) or the second (fi=1) fanin with another divisor to improve coverage */ + bool easy_refine( simple_maj& n, uint32_t fi ) + { + uint64_t const original_coverage = kitty::count_ones( n.function ); + uint64_t current_coverage = original_coverage; + auto const& tt1 = divisors.at( n.fanins[fi ? 0 : 1] ); + auto const& tt2 = divisors.at( n.fanins[fi < 2 ? 2 : 1] ); + for ( auto i = 0u; i < divisors.size(); ++i ) + { + if ( same_divisor( i, n.fanins[0] ) || same_divisor( i, n.fanins[1] ) || same_divisor( i, n.fanins[2] ) ) + { + continue; + } + auto const& tti = divisors.at( i ); + uint64_t coverage = kitty::count_ones( kitty::ternary_majority( tti, tt1, tt2 ) ); + if ( coverage > current_coverage ) + { + current_coverage = coverage; + n.fanins[fi] = i; + } + } + if ( current_coverage > original_coverage ) + { + n.function = kitty::ternary_majority( divisors.at( n.fanins[0] ), divisors.at( n.fanins[1] ), divisors.at( n.fanins[2] ) ); + if ( current_coverage == num_bits ) + { + return true; + } + } + return false; + } + + mig_index_list translate( std::vector const& maj_nodes_best ) const + { + mig_index_list index_list( divisors.size() / 2 - 1 ); + std::unordered_map id_map; + for ( auto i = 0u; i < maj_nodes_best.size(); ++i ) + { + auto& n = maj_nodes_best.at( maj_nodes_best.size() - i - 1u ); + uint32_t lits[3]; + for ( auto j = 0u; j < 3u; ++j ) + { + if ( n.fanins[j] < divisors.size() ) + { + lits[j] = n.fanins[j]; + } + else + { + auto mapped = id_map.find( n.fanins[j] ); + assert( mapped != id_map.end() ); + lits[j] = mapped->second; + } + } + id_map[n.id] = divisors.size() + i * 2; + index_list.add_maj( lits[0], lits[1], lits[2] ); + } + index_list.add_output( ( id_map.find( maj_nodes_best.at( 0u ).id ) )->second ); + return index_list; + } + +private: + bool same_divisor( uint32_t const i, uint32_t const j ) + { + return ( i >> 1 ) == ( j >> 1 ); + } + + bool fulfilled( TT const& func, TT const& care ) + { + return kitty::is_const0( ~func & care ); + } + + bool node_fulfilled( maj_node const& node ) + { + return fulfilled( kitty::ternary_majority( node.fanin_functions.at( 0u ), node.fanin_functions.at( 1u ), node.fanin_functions.at( 2u ) ), node.care ); + } + + uint64_t score( TT const& func, TT const& care ) + { + return kitty::count_ones( func & care ); + } + + void update_fanin( maj_node& parent_node, uint32_t const fi, uint32_t const new_id, TT const& new_function ) + { + parent_node.fanins.at( fi ) = new_id; + TT const old_function = parent_node.fanin_functions.at( fi ); + parent_node.fanin_functions.at( fi ) = new_function; + + TT const& sibling_func1 = sibling_func( parent_node, fi, 1 ); + TT const& sibling_func2 = sibling_func( parent_node, fi, 2 ); + + update_sibling( parent_node, fi, 1, old_function, new_function, sibling_func1, sibling_func2 ); + update_sibling( parent_node, fi, 2, old_function, new_function, sibling_func2, sibling_func1 ); + + /* update grandparents */ + if ( parent_node.parent.parent_position != -1 ) /* not the topmost node */ + { + update_fanin( grandparent( parent_node ), parent_node.parent.fanin_num, parent_node.id, kitty::ternary_majority( new_function, sibling_func1, sibling_func2 ) ); + } + } + + /* Deal with the affects on siblings due to a change in one fanin function + * \param parent_node The node one of whose fanin functions is changed. + * \param fi The index of the changed fanin. (0 <= fi <= 2) + * \param sibling_num Which sibling we are updating. (1 or 2) + * \param old_function The original function of the changed fanin. + * \param new_function The new function of the changed fanin. + * \param sibling_func The function of the sibling being updated. + * \param other_sibling_func The function of the other sibling. + */ + void update_sibling( maj_node const& parent_node, uint32_t const fi, uint32_t const sibling_num, TT const& old_function, TT const& new_function, TT const& sibling_func, TT const& other_sibling_func ) + { + uint32_t index = sibling_index( fi, sibling_num ); + uint32_t id = parent_node.fanins.at( index ); + TT const old_care = care( parent_node.care, old_function, other_sibling_func ); + TT const new_care = care( parent_node.care, new_function, other_sibling_func ); + + if ( old_care != new_care ) + { + /* update care of the sibling (if it is not a divisor) */ + if ( id >= divisors.size() ) + { + update_node_care( id_to_node( id ), sibling_func, old_care, new_care ); + } + else /* add the position back to queue because there may be new opportunities */ + { + add_position( expansion_position( {int32_t( id_to_pos( parent_node.id ) ), int32_t( index )} ) ); + } + } + } + + void update_node_care( maj_node& node, TT const& func, TT const& old_care, TT const& new_care ) + { + assert( node.care == old_care ); + /* check if it was fulfilled but becomes unfulfilled */ + if ( fulfilled( func, old_care ) && !fulfilled( func, new_care ) ) + { + /* add the fanin positions back to queue */ + for ( auto fi = 0; fi < 3; ++fi ) + { + if ( node.fanins.at( fi ) < divisors.size() ) + { + add_position( expansion_position( {int32_t( id_to_pos( node.id ) ), int32_t( fi )} ) ); + } + } + } + node.care = new_care; + + /* the update may propagate to its children */ + for ( auto fi = 0; fi < 3; ++fi ) + { + if ( node.fanins.at( fi ) >= divisors.size() ) + { + TT const old_child_care = care( old_care, sibling_func( node, fi, 1 ), sibling_func( node, fi, 2 ) ); + TT const new_child_care = care( new_care, sibling_func( node, fi, 1 ), sibling_func( node, fi, 2 ) ); + if ( old_child_care != new_child_care ) + { + update_node_care( id_to_node( node.fanins.at( fi ) ), node.fanin_functions.at( fi ), old_child_care, new_child_care ); + } + } + } + } + + void add_position( expansion_position const& pos ) + { + for ( auto& l : leaves ) + { + if ( l == pos ) + { + return; + } + } + leaves.emplace_back( pos ); + } + + TT care( TT const& parent_care, TT const& sibling_func1, TT const& sibling_func2 ) + { + return parent_care & ~( sibling_func1 & sibling_func2 ); + } + + inline maj_node& grandparent( maj_node const& parent_node ) + { + return maj_nodes.at( parent_node.parent.parent_position ); + } + + inline uint32_t sibling_index( uint32_t const my_index, uint32_t const sibling_num ) + { + return ( my_index + sibling_num ) % 3; + } + + inline TT const& sibling_func( maj_node const& parent_node, uint32_t const my_index, uint32_t const sibling_num ) + { + return parent_node.fanin_functions.at( sibling_index( my_index, sibling_num ) ); + } + + inline uint32_t id_to_pos( uint32_t const id ) + { + assert( id >= divisors.size() ); + return ( id - divisors.size() ); + } + + inline maj_node& id_to_node( uint32_t const id ) + { + return maj_nodes.at( id_to_pos( id ) ); + } + +private: + uint32_t size_limit; + uint32_t num_bits; + + std::vector divisors; + std::vector scores; + std::vector maj_nodes; /* the really used nodes */ + std::unordered_map> computed_table; /* map from care to a simple_maj with divisors as fanins */ + + std::vector leaves, improve_in_parent, shuffle; + bool first_round = true; +}; /* mig_resyn_engine */ + +class mig_resyn_engine_akers +{ +public: + explicit mig_resyn_engine_akers( kitty::partial_truth_table const& target ) + : divisors( { ~target, target } ), id_to_lit( { 0, 1 } ) + /* const0, const1 */ + { } + + template + void add_divisor( node_type const& node, truth_table_storage_type const& tts ) + { + assert( tts[node].num_bits() == divisors[0].num_bits() ); + id_to_lit.emplace_back( divisors.size() ); + divisors.emplace_back( tts[node] ^ divisors.at( 0u ) ); // XNOR target = XOR ~target + id_to_lit.emplace_back( divisors.size() ); + divisors.emplace_back( ~tts[node] ^ divisors.at( 0u ) ); + index_list.add_inputs(); + } + + template + void add_divisors( iterator_type begin, iterator_type end, truth_table_storage_type const& tts ) + { + while ( begin != end ) + { + add_divisor( *begin, tts ); + ++begin; + } + } + + std::optional compute_function( uint32_t num_inserts ) + { + if ( !is_feasible() ) + { + return std::nullopt; + } + + /* search for 0-resub (including constants) */ + for ( auto i = 0u; i < divisors.size(); ++i ) + { + if ( kitty::is_const0( ~divisors[i] ) ) + { + index_list.add_output( id_to_lit[i] ); + return index_list; + } + } + + reduce(); + + while ( divisors.size() > 1 ) + { + if ( index_list.num_gates() >= num_inserts ) + { + return std::nullopt; + } + find_gate(); + add_gate(); + if ( kitty::is_const0( ~divisors.back() ) ) + { + break; + } + reduce(); + } + + index_list.add_output( id_to_lit.back() ); + return index_list; + } + +private: + void reduce() + { + uint32_t num_bits_before = 0u; + uint32_t num_divs_before = 0u; + while ( num_bits_before != divisors[0].num_bits() || num_divs_before != divisors.size() ) + { + num_bits_before = divisors[0].num_bits(); + num_divs_before = divisors.size(); + eliminate_divs(); /* reduce column */ + eliminate_bits(); /* reduce row */ + } + } + + void eliminate_divs() + { + for ( int32_t x = 0; x < (int32_t)divisors.size(); ++x ) /* try to remove divisors[x] */ + { + if ( is_feasible( x ) ) + { + divisors.erase( divisors.begin() + x ); + id_to_lit.erase( id_to_lit.begin() + x ); + --x; + } + } + } + + void eliminate_bits() + { + /* for each pair of bits, check if we can remove i or j */ + for ( int32_t i = 0; i < (int32_t)divisors[0].num_bits() - 1; ++i ) + { + for ( int32_t j = i + 1; j < (int32_t)divisors[0].num_bits(); ++j ) + { + bool can_remove_i = true, can_remove_j = true; + for ( auto x = 0u; x < divisors.size(); ++x ) + { + if ( !kitty::get_bit( divisors[x], i ) && kitty::get_bit( divisors[x], j ) ) + { + can_remove_i = false; + } + if ( kitty::get_bit( divisors[x], i ) && !kitty::get_bit( divisors[x], j ) ) + { + can_remove_j = false; + } + if ( !can_remove_i && !can_remove_j ) + { + break; + } + } + + if ( can_remove_i ) + { + for ( auto& d : divisors ) + { + d.erase_bit_swap( i ); + } + --i; + break; /* break loop j */ + } + else if ( can_remove_j ) + { + for ( auto& d : divisors ) + { + d.erase_bit_swap( j ); + } + --j; + } + } + } + } + + void find_gate() + { + /* 1. Try if there are some gates that can eliminate some columns */ + uint32_t best_num_eliminates = 0u; + /* for each column, find candidate gates that eliminates it */ + for ( auto i = 0u; i < divisors.size(); ++i ) + { + find_gate_to_eliminate( i, best_num_eliminates ); + if ( best_num_eliminates == 3 ) + { + /* cannot be better */ + break; + } + } + if ( best_num_eliminates > 0 ) + { + return; + } + + /* 2. No gate can eliminate any column. Choose a gate that misses the least essentials */ + uint32_t least_missed_essentials = divisors[0].num_bits() + 1; + /* for all possible gates (input combinations) */ + assert( divisors.size() >= 3 ); + for ( auto i = 0u; i < divisors.size() - 2; ++i ) + { + for ( auto j = i + 1; j < divisors.size() - 1; ++j ) + { + for ( auto k = j + 1; k < divisors.size(); ++k ) + { + kitty::partial_truth_table const gate_function = kitty::ternary_majority( divisors[i], divisors[j], divisors[k] ); + uint32_t missed_essentials = 0u; + /* for each bit */ + for ( auto b = 0u; b < gate_function.num_bits(); ++b ) + { + if ( kitty::get_bit( gate_function, b ) ) continue; + if ( is_essential( i, b ) ) + { + ++missed_essentials; + } + else if ( is_essential( j, b ) ) + { + ++missed_essentials; + } + else if ( is_essential( k, b ) ) + { + ++missed_essentials; + } + } + + if ( missed_essentials < least_missed_essentials ) + { + fanins[0] = i; fanins[1] = j; fanins[2] = k; + least_missed_essentials = missed_essentials; + } + } + } + } + } + + void find_gate_to_eliminate( uint32_t column, uint32_t& best_num_eliminates ) + { + std::vector> candidates; + /* for each of its essential bits */ + for ( auto b = 0u; b < divisors[0].num_bits(); ++b ) + { + if ( !is_essential( column, b ) ) continue; + candidates.emplace_back(); + for ( auto j = 0u; j < divisors.size(); ++j ) + { + if ( column != j && kitty::get_bit( divisors[j], b ) ) + { + candidates.back().emplace_back( j ); + } + } + if ( candidates.back().size() == 0u ) + { + /* impossible to eliminate this column */ + return; + } + } + + assert( candidates.size() >= 2 ); // why must be? but what if not? + /* try all combinations of size 2 */ + for ( auto const& j : candidates[0] ) + { + for ( auto const& k : candidates[1] ) + { + if ( j == k ) continue; + /* check if either j or k appears in all other sets */ + bool all_satisfied = true; + for ( auto s = 2u; s < candidates.size(); ++s ) + { + bool is_in_set = false; + for ( auto const& ele : candidates[s] ) + { + if ( ele == j || ele == k ) + { + is_in_set = true; + break; + } + } + if ( !is_in_set ) + { + all_satisfied = false; + break; + } + } + if ( all_satisfied ) + { + /* this gate eliminates column */ + uint32_t num_eliminates = 1u; + /* see if it also eliminates j and/or k */ + kitty::partial_truth_table const gate_function = kitty::ternary_majority( divisors[column], divisors[j], divisors[k] ); + if ( eliminates( gate_function, j ) ) + { + ++num_eliminates; + } + if ( eliminates( gate_function, k ) ) + { + ++num_eliminates; + } + if ( num_eliminates > best_num_eliminates ) + { + fanins[0] = column; fanins[1] = j; fanins[2] = k; + best_num_eliminates = num_eliminates; + if ( num_eliminates == 3 ) + { + /* cannot be better */ + return; + } + } + } + } + } + } + + void add_gate() + { + index_list.add_maj( id_to_lit[fanins[0]], id_to_lit[fanins[1]], id_to_lit[fanins[2]] ); + id_to_lit.emplace_back( ( index_list.num_pis() + index_list.num_gates() ) * 2 ); + divisors.emplace_back( kitty::ternary_majority( divisors[fanins[0]], divisors[fanins[1]], divisors[fanins[2]] ) ); + } + +private: + /* whether the table is feasible (lpsd) if divisors[x] is deleted */ + bool is_feasible( int32_t x = -1 ) const + { + if ( divisors.size() == 1 && x == 0 ) + { + /* x is the only remaining column */ + return false; + } + + /* for every pair of rows _bits[i], _bits[j] */ + for ( auto i = 0u; i < divisors[0].num_bits() - 1; ++i ) + { + for ( auto j = i + 1; j < divisors[0].num_bits(); ++j ) + { + /* check if there is another divisors[y] having both bits 1 */ + bool found = false; + for ( int32_t y = 0; y < (int32_t)divisors.size(); ++y ) + { + if ( y == x ) continue; + if ( kitty::get_bit( divisors[y], i ) && kitty::get_bit( divisors[y], j ) ) + { + found = true; + break; + } + } + if ( !found ) + { + return false; + } + } + } + return true; + } + + /* whether divisors[x]._bits[i] is essential */ + bool is_essential( uint32_t x, uint32_t i ) const + { + if ( !kitty::get_bit( divisors[x], i ) ) + { + return false; + } + + kitty::partial_truth_table tt( divisors[0].num_bits() ); + for ( auto y = 0u; y < divisors.size(); ++y ) + { + if ( x == y ) continue; + if ( !kitty::get_bit( divisors[y], i ) ) continue; + tt |= divisors[y]; + } + + return !kitty::is_const0( ~tt ); + } + + /* whether the gate eliminates a given column */ + bool eliminates( kitty::partial_truth_table const& gate_function, uint32_t column ) const + { + /* for each of its essential bits */ + for ( auto b = 0u; b < gate_function.num_bits(); ++b ) + { + if ( !kitty::get_bit( gate_function, b ) && is_essential( column, b ) ) + { + return false; + } + } + return true; + } + +private: + void print_table() const + { + for ( auto i = 0u; i < divisors.size(); ++i ) + { + std::cout << "[" << std::setw( 2 ) << id_to_lit[i] << "] "; + kitty::print_binary( divisors[i] ); + std::cout << "\n"; + } + } + +private: + std::vector divisors; + std::vector id_to_lit; + mig_index_list index_list; + uint32_t fanins[3]; +}; +} /* namespace mockturtle */ diff --git a/include/mockturtle/utils/index_list.hpp b/include/mockturtle/utils/index_list.hpp index cd6fd8b6a..913f65b17 100644 --- a/include/mockturtle/utils/index_list.hpp +++ b/include/mockturtle/utils/index_list.hpp @@ -349,7 +349,7 @@ inline std::string to_index_list_string( abc_index_list const& indices ) * * Example: The following index list creates the output function * `<, x2, x4>` with 4 inputs, 1 output, and 3 gates: - * `{4 | 1 << 8 | 2 << 16, 2, 4, 6, 4, 8, 10, 12}` + * `{4 | 1 << 8 | 3 << 16, 2, 4, 6, 4, 8, 10, 12}` */ struct mig_index_list { diff --git a/lib/kitty/kitty/dynamic_truth_table.hpp b/lib/kitty/kitty/dynamic_truth_table.hpp index 832fd53d1..8f88875fb 100644 --- a/lib/kitty/kitty/dynamic_truth_table.hpp +++ b/lib/kitty/kitty/dynamic_truth_table.hpp @@ -133,7 +133,7 @@ struct dynamic_truth_table /*! \brief Assign other truth table. This replaces the current truth table with another truth table. The truth - table type is arbitrary. The vector of bits is resized accordingly. + table type has to be complete. The vector of bits is resized accordingly. \param other Other truth table */ diff --git a/lib/kitty/kitty/kitty.hpp b/lib/kitty/kitty/kitty.hpp index b6d3210e7..1d012ca7d 100644 --- a/lib/kitty/kitty/kitty.hpp +++ b/lib/kitty/kitty/kitty.hpp @@ -34,6 +34,7 @@ #include "static_truth_table.hpp" #include "dynamic_truth_table.hpp" +#include "partial_truth_table.hpp" #include "affine.hpp" #include "algorithm.hpp" diff --git a/lib/kitty/kitty/npn.hpp b/lib/kitty/kitty/npn.hpp index 335f92a6f..c6df745fe 100755 --- a/lib/kitty/kitty/npn.hpp +++ b/lib/kitty/kitty/npn.hpp @@ -409,6 +409,7 @@ void sifting_npn_canonization_loop( TT& npn, uint32_t& phase, std::vector void sifting_p_canonization_loop( TT& p, uint32_t& phase, std::vector& perm ) { + (void)phase; auto improvement = true; auto forward = true; diff --git a/lib/kitty/kitty/partial_truth_table.hpp b/lib/kitty/kitty/partial_truth_table.hpp index 9c85663d7..848e3ab65 100644 --- a/lib/kitty/kitty/partial_truth_table.hpp +++ b/lib/kitty/kitty/partial_truth_table.hpp @@ -1,5 +1,5 @@ /* kitty: C++ truth table library - * Copyright (C) 2017-2019 EPFL + * Copyright (C) 2017-2020 EPFL * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -32,6 +32,9 @@ #pragma once +#include "detail/constants.hpp" +#include "traits.hpp" + #include #include #include @@ -39,17 +42,14 @@ #include #include -#include "detail/constants.hpp" -#include "traits.hpp" - namespace kitty { -/*! Truth table with resizable number of bits +/*! Truth table with resizable, arbitrary number of bits */ struct partial_truth_table { - /*! Standard constructor. + /*! \brief Standard constructor. \param num_bits Number of bits in use initially */ explicit partial_truth_table( uint32_t num_bits ) @@ -58,7 +58,7 @@ struct partial_truth_table { } - /*! Empty constructor. + /*! \brief Empty constructor. Creates an empty truth table. It has no bit in use. This constructor is only used for convenience, if algorithms require the existence of default @@ -66,17 +66,17 @@ struct partial_truth_table */ partial_truth_table() : _num_bits( 0 ) {} - /*! Constructs a new partial truth table instance with the same number of bits and blocks. */ + /*! \brief Constructs a new partial truth table instance with the same number of bits and blocks. */ inline partial_truth_table construct() const { return partial_truth_table( _num_bits ); } - /*! Returns number of (allocated) blocks. + /*! \brief Returns number of (allocated) blocks. */ inline auto num_blocks() const noexcept { return _bits.size(); } - /*! Returns number of (used) bits. + /*! \brief Returns number of (used) bits. */ inline auto num_bits() const noexcept { return _num_bits; } @@ -137,7 +137,7 @@ struct partial_truth_table return *this; } - /*! Masks the number of valid truth table bits. + /*! \brief Masks the number of valid truth table bits. If not all the bits in the last block are used up, we block out the remaining bits (fill with zero). @@ -145,12 +145,21 @@ struct partial_truth_table */ inline void mask_bits() noexcept { - if ( _num_bits % 64 ) + if ( _num_bits & 0x3f ) { - _bits.back() &= 0xFFFFFFFFFFFFFFFF >> ( 64 - ( _num_bits % 64 ) ); + _bits.back() &= 0xFFFFFFFFFFFFFFFF >> ( 64 - ( _num_bits & 0x3f ) ); } } + /*! \brief Resize the truth table to have the given number of bits. + + If the desired length is larger than the current length, zeros will + be filled up in the end of the truth table. If the desired length + is shorter than the current length, extra bits in the end of the + truth table will be discarded. + + \param num_bits Desired length (number of bits) + */ inline void resize( int num_bits ) noexcept { _num_bits = num_bits; @@ -161,15 +170,21 @@ struct partial_truth_table mask_bits(); } + /*! \brief Add a bit to the end of the truth table. + \param bit Bit to be added + */ inline void add_bit( bool bit ) noexcept { resize( _num_bits + 1 ); if ( bit ) { - _bits.back() |= (uint64_t)1 << ( _num_bits % 64 - 1 ); + _bits.back() |= (uint64_t)1 << ( ( _num_bits & 0x3f ) - 1 ); } } + /*! \brief Add several bits to the end of the truth table. + \param bits Bits to be added + */ inline void add_bits( std::vector& bits ) noexcept { for ( unsigned i = 0; i < bits.size(); ++i ) @@ -178,29 +193,88 @@ struct partial_truth_table } } - /* \param num_bits Number of bits in `bits` to be added (count from LSB) */ + /*! \brief Add several bits to the end of the truth table. + \param bits Bits to be added + \param num_bits Number of bits in `bits` to be added (counted from LSB) + */ inline void add_bits( uint64_t bits, int num_bits = 64 ) noexcept { assert( num_bits <= 64 ); - if ( ( _num_bits % 64 ) + num_bits <= 64 ) /* no need for a new block */ + if ( ( _num_bits & 0x3f ) + num_bits <= 64 ) /* no need for a new block */ { if ( _bits.size() == 0u ) { _bits.emplace_back( 0u ); } - _bits.back() |= bits << ( _num_bits % 64 ); + _bits.back() |= bits << ( _num_bits & 0x3f ); } else { - auto first_half_len = 64 - ( _num_bits % 64 ); - _bits.back() |= bits << ( _num_bits % 64 ); + auto first_half_len = 64 - ( _num_bits & 0x3f ); + _bits.back() |= bits << ( _num_bits & 0x3f ); _bits.emplace_back( 0u ); _bits.back() |= ( bits & ( 0xFFFFFFFFFFFFFFFF >> ( 64 - num_bits ) ) ) >> first_half_len; } _num_bits += num_bits; } + /*! \brief Overwrite the value at position `from` to position `to`. + \param from Position of the bit to be copied + \param to Position of the bit to be overwritten + */ + inline void copy_bit( uint32_t from, uint32_t to ) + { + if ( ( _bits[from >> 6] >> ( from & 0x3f ) ) & 0x1 ) + { + /* set_bit( to ) */ + _bits[to >> 6] |= uint64_t( 1 ) << ( to & 0x3f ); + } + else + { + /* clear_bit( to ) */ + _bits[to >> 6] &= ~( uint64_t( 1 ) << ( to & 0x3f ) ); + } + } + + /*! \brief Erase the bit at the given position by swapping with the + last bit and shrinking the truth table. + + This method is faster than `erase_bit` but do not keep the order + of the bits. + + \param position Position of the bit to be erased + */ + inline void erase_bit_swap( uint32_t position ) + { + assert( position < _num_bits ); + /* move the last bit (`_num_bits - 1`) to `position` */ + copy_bit( _num_bits - 1, position ); + resize( _num_bits - 1 ); + } + + /*! \brief Erase the bit at the given position and shift all the following bits. + + This method keeps the order of the bits but is slower and + do not keep the position of the bits. + + \param position Position of the bit to be erased + */ + inline void erase_bit_shift( uint32_t position ) + { + assert( position < _num_bits ); + uint64_t mask = 0xFFFFFFFFFFFFFFFF << ( position & 0x3f ); + _bits[position >> 6] = ( _bits[position >> 6] & ~mask ) | ( _bits[position >> 6] >> 1 & mask ); + uint32_t hole = position | 0x3f; + while ( hole < _num_bits - 1 ) + { + copy_bit( hole + 1, hole ); + hole += 64; + _bits[hole >> 6] >>= 1; + } + resize( _num_bits - 1 ); + } + /*! \cond PRIVATE */ public: /* fields */ std::vector _bits; diff --git a/lib/kitty/kitty/static_truth_table.hpp b/lib/kitty/kitty/static_truth_table.hpp index fcdb94b79..ce8000d6b 100644 --- a/lib/kitty/kitty/static_truth_table.hpp +++ b/lib/kitty/kitty/static_truth_table.hpp @@ -251,7 +251,7 @@ struct static_truth_table template::value>> static_truth_table& operator=( const TT& other ) { - if ( other.num_vars() == num_vars() ) + if ( other.num_bits() == num_bits() ) { std::copy( other.begin(), other.end(), begin() ); } diff --git a/test/algorithms/mig_resyn_engines.cpp b/test/algorithms/mig_resyn_engines.cpp new file mode 100644 index 000000000..a50ed31c9 --- /dev/null +++ b/test/algorithms/mig_resyn_engines.cpp @@ -0,0 +1,113 @@ +#include + +#include +#include +#include +#include + +#include +#include +#include + +using namespace mockturtle; + +template +void test_0resub() +{ + std::vector tts( 4, kitty::partial_truth_table( 8 ) ); + + kitty::create_from_binary_string( tts[0], "00110110" ); + kitty::create_from_binary_string( tts[1], "11111100" ); + kitty::create_from_binary_string( tts[2], "10000001" ); + kitty::create_from_binary_string( tts[3], "11001001" ); + + Engine engine( tts[0] ); + engine.add_divisor( 1, tts ); + engine.add_divisor( 2, tts ); + engine.add_divisor( 3, tts ); + + const auto res = engine.compute_function( 0u ); + CHECK( res ); + CHECK( (*res).num_gates() == 0u ); + CHECK( (*res).raw()[1] == 7u ); +} + +template +void test_1resub() +{ + std::vector tts( 3, kitty::partial_truth_table( 8 ) ); + kitty::partial_truth_table target( 8 ); + + kitty::create_from_binary_string( target, "01110110" ); + kitty::create_from_binary_string( tts[0], "11110100" ); + kitty::create_from_binary_string( tts[1], "11001001" ); + kitty::create_from_binary_string( tts[2], "01000111" ); + + Engine engine( target ); + for ( auto i = 0u; i < tts.size(); ++i ) + { + engine.add_divisor( i, tts ); + } + // target = <1,~2,3> + + const auto res = engine.compute_function( 1u ); + CHECK( res ); + CHECK( (*res).num_gates() == 1u ); + + mig_network mig; + decode( mig, *res ); + partial_simulator sim( tts ); + const auto ans = simulate( mig, sim )[0]; + CHECK( target == ans ); +} + +template +void test_2resub() +{ + std::vector tts( 4, kitty::partial_truth_table( 8 ) ); + kitty::partial_truth_table target( 8 ); + + kitty::create_from_binary_string( target, "00101110" ); + kitty::create_from_binary_string( tts[0], "11101111" ); + kitty::create_from_binary_string( tts[1], "00100000" ); + kitty::create_from_binary_string( tts[2], "10011110" ); + kitty::create_from_binary_string( tts[3], "01011111" ); + + Engine engine( target ); + for ( auto i = 0u; i < tts.size(); ++i ) + { + engine.add_divisor( i, tts ); + } + // target = <<1,2,3>,2,4> + + const auto res = engine.compute_function( 2u ); + CHECK( res ); + CHECK( (*res).num_gates() == 2u ); + + mig_network mig; + decode( mig, *res ); + partial_simulator sim( tts ); + const auto ans = simulate( mig, sim )[0]; + CHECK( target == ans ); +} + +TEST_CASE( "MIG resynthesis engines -- 0-resub", "[mig_resyn]" ) +{ + test_0resub>(); + test_0resub>(); + test_0resub(); +} + +TEST_CASE( "MIG resynthesis engines -- 1-resub", "[mig_resyn]" ) +{ + test_1resub>(); + test_1resub>(); + test_1resub(); +} + +TEST_CASE( "MIG resynthesis engines -- 2-resub", "[mig_resyn]" ) +{ + test_2resub>(); + test_2resub>(); + test_2resub(); +}