From d7baae40614888ff938edd44803d654811960211 Mon Sep 17 00:00:00 2001 From: Sonia Date: Sat, 26 Sep 2020 22:19:05 +0200 Subject: [PATCH 01/22] mig resub: bottom up approach --- .../mockturtle/algorithms/resub_engines.hpp | 260 ++++++++++++++++++ test/algorithms/resub_engines.cpp | 105 +++++++ 2 files changed, 365 insertions(+) create mode 100644 include/mockturtle/algorithms/resub_engines.hpp create mode 100644 test/algorithms/resub_engines.cpp diff --git a/include/mockturtle/algorithms/resub_engines.hpp b/include/mockturtle/algorithms/resub_engines.hpp new file mode 100644 index 000000000..e5f1fb77f --- /dev/null +++ b/include/mockturtle/algorithms/resub_engines.hpp @@ -0,0 +1,260 @@ +/* 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 resub_engines.hpp + \brief Implements generalized resubstitution engine(s). + + \author Siang-Yun Lee +*/ + +#pragma once + +#include + +namespace mockturtle +{ +template +class mig_resub_engine +{ + /*! \brief Internal data structure */ + struct maj_node + { + uint32_t id; /* ( id - divisors.size() ) / 2 = its position in maj_nodes */ + TT care; /* the care bits; not used in bottom-up approach */ + std::vector fanins; /* ids of its three fanins */ + }; + +public: + explicit mig_resub_engine( uint64_t num_divisors, uint64_t max_num_divisors = 50ul ) + : max_num_divisors( max_num_divisors ), counter( 2u ), divisors( ( num_divisors + 1 ) * 2 ), scores( ( num_divisors + 1 ) * 2 ) + { } + + template + void add_root( node_type const& node, truth_table_storage_type const& tts ) + { + target = tts[node]; + divisors.at( 1u ) = get_const1(); + divisors.at( 0u ) = ~divisors.at( 1u ); /* const 0 */ + } + + template + void add_divisor( node_type const& node, truth_table_storage_type const& tts ) + { + divisors.at( counter++ ) = tts[node]; + divisors.at( counter++ ) = ~tts[node]; + } + + template + void add_divisors( iterator_type begin, iterator_type end, truth_table_storage_type const& tts ) + { + // counter = 2u; + while ( begin != end ) + { + add_divisor( *begin, tts ); + ++begin; + } + } + + std::optional> compute_function( uint32_t num_inserts, bool use_XOR = false ) + { + /* THINK: When size limit is reached, do we go back and try other choices if there are tied scores? */ + /* OPTIMIZE: Only save one of the polarities (the better one) of the divisors. Indexing also needs to be adjusted. */ + assert( use_XOR == false ); (void)use_XOR; // not supported yet + + uint64_t max_score = 0u; + uint32_t max_i = 0u; + for ( auto i = 0u; i < divisors.size(); ++i ) + { + scores.at( i ) = kitty::count_zeros( target ^ divisors.at( i ) ); + if ( scores.at( i ) > max_score ) + { + max_score = scores.at( i ); + max_i = i; + if ( max_score == target.num_bits() ) + { + break; + } + } + } + /* 0-resub (including constants) */ + if ( max_score == target.num_bits() ) + { + return std::vector( {max_i} ); + } + + if ( num_inserts == 0u ) + { + return std::nullopt; + } + size_limit = num_inserts; + + /* the first node with the first fanin decided */ + maj_nodes.emplace_back( maj_node{uint32_t(divisors.size()), get_const1(), {max_i}} ); + + if constexpr ( use_top_down ) + { + return top_down_approach( 0u ); + } + else + { + return bottom_up_approach( divisors.at( max_i ) ); + } + } + +private: + TT get_const1() const { return target | ~target; } + + std::optional> bottom_up_approach( TT const& function_i ) + { + /* THINK: Should we consider reusing newly-built nodes (nodes in maj_nodes) in addition to divisors? */ + assert( maj_nodes.back().fanins.size() == 1u ); + + /* the second fanin */ + uint64_t max_score = 0u; + uint32_t max_j = 0u; + auto const not_covered_by_i = target ^ function_i; + for ( auto j = 0u; j < divisors.size(); ++j ) + { + auto const covered_by_j = ~( target ^ 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 ) + { + max_score = scores.at( j ); + max_j = j; + } + } + maj_nodes.back().fanins.emplace_back( max_j ); + + /* the third fanin */ + max_score = 0u; + uint32_t max_k = 0u; + auto const disagree_in_ij = function_i ^ divisors.at( max_j ); + for ( auto k = 0u; k < divisors.size(); ++k ) + { + scores.at( k ) = kitty::count_ones( ~( target ^ divisors.at( k ) ) & disagree_in_ij ); + if ( scores.at( k ) > max_score ) + { + max_score = scores.at( k ); + max_k = k; + } + } + maj_nodes.back().fanins.emplace_back( max_k ); + + auto const current_function = kitty::ternary_majority( function_i, divisors.at( max_j ), divisors.at( max_k ) ); + if ( current_function == target ) + { + std::vector index_list; + for ( auto& n : maj_nodes ) + { + index_list.emplace_back( n.fanins.at( 0u ) ); + index_list.emplace_back( n.fanins.at( 1u ) ); + index_list.emplace_back( n.fanins.at( 2u ) ); + } + index_list.emplace_back( maj_nodes.back().id ); + return index_list; + } + else if ( maj_nodes.size() < size_limit ) + { + maj_nodes.emplace_back( maj_node{maj_nodes.back().id + 2u, current_function, {maj_nodes.back().id}} ); + return bottom_up_approach( current_function ); + } + else + { + return std::nullopt; + } + } + + std::optional> top_down_approach( uint32_t node_position ) + { + maj_node const& current_node = maj_nodes.at( node_position ); + TT const& care = current_node.care; + assert( current_node.fanins.size() == 1u ); + + /* the second fanin */ + uint64_t max_score = 0u; + uint32_t max_j = 0u; + auto const not_covered_by_i = target ^ function_i; + for ( auto j = 0u; j < divisors.size(); ++j ) + { + auto const covered_by_j = ~( target ^ 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 ) + { + max_score = scores.at( j ); + max_j = j; + } + } + current_node.fanins.emplace_back( max_j ); + + /* the third fanin */ + max_score = 0u; + uint32_t max_k = 0u; + auto const not_covered_by_j = target ^ divisors.at( max_j ); + for ( auto k = 0u; k < divisors.size(); ++k ) + { + auto const covered_by_k = ~( target ^ 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 ) + { + max_score = scores.at( k ); + max_k = k; + } + } + current_node.fanins.emplace_back( max_k ); + + auto const current_function = kitty::ternary_majority( function_i, divisors.at( max_j ), divisors.at( max_k ) ); + if ( current_function == target ) + { + std::vector index_list; + // TODO + return index_list; + } + else if ( maj_nodes.size() < size_limit ) + { + maj_nodes.emplace_back( maj_node{maj_nodes.back().id + 2u, care & ( ( target ^ divisors.at( max_j ) ) + ( target ^ divisors.at( max_k ) ) ), {current_node.fanins[0]}} ); + current_node.fanins[0] = maj_nodes.back().id + 2u; + // refine this new node. if not successful, try refining the second fanin (breadth-first) + } + else + { + return std::nullopt; + } + } + + bool refine() + +private: + uint64_t max_num_divisors; + uint64_t counter; + uint32_t size_limit; + + TT target; + std::vector divisors; + std::vector scores; + std::vector maj_nodes; +}; /* mig_resub_engine */ + +} /* namespace mockturtle */ diff --git a/test/algorithms/resub_engines.cpp b/test/algorithms/resub_engines.cpp new file mode 100644 index 000000000..c502ab5cf --- /dev/null +++ b/test/algorithms/resub_engines.cpp @@ -0,0 +1,105 @@ +#include + +#include +#include +#include + +#include +#include + +using namespace mockturtle; + +TEST_CASE( "MIG resub engine -- 0-resub", "[resub_engines]" ) +{ + std::vector tts( 4, kitty::dynamic_truth_table( 3 ) ); + + 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" ); + + mig_resub_engine engine( 3 ); + engine.add_root( 0, tts ); + 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).size() == 1u ); + CHECK( (*res)[0] == 7u ); +} + +TEST_CASE( "MIG resub engine -- 1-resub", "[resub_engines]" ) +{ + std::vector tts( 4, kitty::dynamic_truth_table( 3 ) ); + + kitty::create_from_binary_string( tts[0], "01110110" ); + kitty::create_from_binary_string( tts[1], "11110100" ); + kitty::create_from_binary_string( tts[2], "11001001" ); + kitty::create_from_binary_string( tts[3], "01000111" ); + + mig_resub_engine engine( tts.size() - 1u ); + engine.add_root( 0, tts ); + for ( auto i = 1u; i < tts.size(); ++i ) + { + engine.add_divisor( i, tts ); + } + // target = <1,~2,3> + + const auto res = engine.compute_function( 1u ); + CHECK( res ); + CHECK( (*res).size() == 4u ); + + /* when translating index list, 0 and 1 are for constants */ + auto target = tts[0]; + kitty::create_from_binary_string( tts[0], "00000000" ); + + auto f1 = (*res)[0] % 2 ? ~tts[(*res)[0] / 2] : tts[(*res)[0] / 2]; + auto f2 = (*res)[1] % 2 ? ~tts[(*res)[1] / 2] : tts[(*res)[1] / 2]; + auto f3 = (*res)[2] % 2 ? ~tts[(*res)[2] / 2] : tts[(*res)[2] / 2]; + tts.emplace_back( kitty::ternary_majority( f1, f2, f3 ) ); + + auto ans = (*res)[3] % 2 ? ~tts[(*res)[3] / 2] : tts[(*res)[3] / 2]; + CHECK( target == ans ); +} + +TEST_CASE( "MIG resub engine -- bottom-up, 2-resub", "[resub_engines]" ) +{ + std::vector tts( 5, kitty::dynamic_truth_table( 3 ) ); + + kitty::create_from_binary_string( tts[0], "00101110" ); + kitty::create_from_binary_string( tts[1], "11101111" ); + kitty::create_from_binary_string( tts[2], "00100000" ); + kitty::create_from_binary_string( tts[3], "10011110" ); + kitty::create_from_binary_string( tts[4], "01011111" ); + + mig_resub_engine engine( tts.size() - 1u ); + engine.add_root( 0, tts ); + for ( auto i = 1u; 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).size() == 7u ); + + /* when translating index list, 0 and 1 are for constants */ + auto target = tts[0]; + kitty::create_from_binary_string( tts[0], "00000000" ); + + auto f1 = (*res)[0] % 2 ? ~tts[(*res)[0] / 2] : tts[(*res)[0] / 2]; + auto f2 = (*res)[1] % 2 ? ~tts[(*res)[1] / 2] : tts[(*res)[1] / 2]; + auto f3 = (*res)[2] % 2 ? ~tts[(*res)[2] / 2] : tts[(*res)[2] / 2]; + tts.emplace_back( kitty::ternary_majority( f1, f2, f3 ) ); + + f1 = (*res)[3] % 2 ? ~tts[(*res)[3] / 2] : tts[(*res)[3] / 2]; + f2 = (*res)[4] % 2 ? ~tts[(*res)[4] / 2] : tts[(*res)[4] / 2]; + f3 = (*res)[5] % 2 ? ~tts[(*res)[5] / 2] : tts[(*res)[5] / 2]; + tts.emplace_back( kitty::ternary_majority( f1, f2, f3 ) ); + + auto ans = (*res)[6] % 2 ? ~tts[(*res)[6] / 2] : tts[(*res)[6] / 2]; + CHECK( target == ans ); +} From 66b8ec6fedefc2e50ca243889ab436c5f26c0e39 Mon Sep 17 00:00:00 2001 From: Sonia Date: Fri, 9 Oct 2020 02:19:21 +0200 Subject: [PATCH 02/22] top-down: first try --- experiments/mig_resub_engine.cpp | 67 +++ .../mockturtle/algorithms/resub_engines.hpp | 412 +++++++++++++++--- 2 files changed, 414 insertions(+), 65 deletions(-) create mode 100644 experiments/mig_resub_engine.cpp diff --git a/experiments/mig_resub_engine.cpp b/experiments/mig_resub_engine.cpp new file mode 100644 index 000000000..27c7c7646 --- /dev/null +++ b/experiments/mig_resub_engine.cpp @@ -0,0 +1,67 @@ +/* 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. + */ + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +//#include + +int main() +{ + //using namespace experiments; + using namespace mockturtle; + + std::vector tts( 4, kitty::dynamic_truth_table( 3 ) ); + + // DOT: 00011010 + // ONEHOT: 00010110 + // GAMBLE: 10000001 + kitty::create_from_binary_string( tts[0], "10000001" ); // target + kitty::create_nth_var( tts[1], 0 ); + kitty::create_nth_var( tts[2], 1 ); + kitty::create_nth_var( tts[3], 2 ); + + mig_resub_engine engine( 3 ); + engine.add_root( 0, tts ); + engine.add_divisor( 1, tts ); + engine.add_divisor( 2, tts ); + engine.add_divisor( 3, tts ); + + const auto res = engine.compute_function( 5u ); + + return 0; +} diff --git a/include/mockturtle/algorithms/resub_engines.hpp b/include/mockturtle/algorithms/resub_engines.hpp index e5f1fb77f..6926dc2c9 100644 --- a/include/mockturtle/algorithms/resub_engines.hpp +++ b/include/mockturtle/algorithms/resub_engines.hpp @@ -36,15 +36,32 @@ namespace mockturtle { +#define two_cares 0 + template class mig_resub_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; /* ( id - divisors.size() ) / 2 = its position in maj_nodes */ - TT care; /* the care bits; not used in bottom-up approach */ std::vector fanins; /* ids of its three fanins */ + + /* only used in top-down */ + std::vector fanin_functions = std::vector(); + TT care = TT(); + expansion_position parent = expansion_position(); }; public: @@ -55,22 +72,23 @@ class mig_resub_engine template void add_root( node_type const& node, truth_table_storage_type const& tts ) { - target = tts[node]; - divisors.at( 1u ) = get_const1(); - divisors.at( 0u ) = ~divisors.at( 1u ); /* const 0 */ + divisors.at( 0u ) = ~tts[node]; // const 0 XNOR target = ~target + divisors.at( 1u ) = tts[node]; // const 1 XNOR target = target + num_bits = tts[node].num_bits(); } template void add_divisor( node_type const& node, truth_table_storage_type const& tts ) { - divisors.at( counter++ ) = tts[node]; - divisors.at( counter++ ) = ~tts[node]; + assert( tts[node].num_bits() == num_bits ); + divisors.at( counter++ ) = tts[node] ^ divisors.at( 0u ); // XNOR target = XOR ~target + divisors.at( counter++ ) = ~tts[node] ^ divisors.at( 0u ); } template void add_divisors( iterator_type begin, iterator_type end, truth_table_storage_type const& tts ) { - // counter = 2u; + assert( counter == 2u ); while ( begin != end ) { add_divisor( *begin, tts ); @@ -78,29 +96,25 @@ class mig_resub_engine } } - std::optional> compute_function( uint32_t num_inserts, bool use_XOR = false ) + std::optional> compute_function( uint32_t num_inserts ) { - /* THINK: When size limit is reached, do we go back and try other choices if there are tied scores? */ - /* OPTIMIZE: Only save one of the polarities (the better one) of the divisors. Indexing also needs to be adjusted. */ - assert( use_XOR == false ); (void)use_XOR; // not supported yet - uint64_t max_score = 0u; - uint32_t max_i = 0u; + max_i = 0u; for ( auto i = 0u; i < divisors.size(); ++i ) { - scores.at( i ) = kitty::count_zeros( target ^ divisors.at( i ) ); + scores.at( i ) = kitty::count_ones( divisors.at( i ) ); if ( scores.at( i ) > max_score ) { max_score = scores.at( i ); max_i = i; - if ( max_score == target.num_bits() ) + if ( max_score == num_bits ) { break; } } } /* 0-resub (including constants) */ - if ( max_score == target.num_bits() ) + if ( max_score == num_bits ) { return std::vector( {max_i} ); } @@ -111,36 +125,38 @@ class mig_resub_engine } size_limit = num_inserts; - /* the first node with the first fanin decided */ - maj_nodes.emplace_back( maj_node{uint32_t(divisors.size()), get_const1(), {max_i}} ); - if constexpr ( use_top_down ) { - return top_down_approach( 0u ); + return top_down_approach(); } else { - return bottom_up_approach( divisors.at( max_i ) ); + return bottom_up_approach(); } } private: - TT get_const1() const { return target | ~target; } + 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 ); + max_i = divisors.size(); + return bottom_up_approach_rec( function_i ); + } - std::optional> bottom_up_approach( TT const& 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? */ - assert( maj_nodes.back().fanins.size() == 1u ); - /* the second fanin */ + /* the second fanin: 2 * #newly-covered-bits + 1 * #cover-again-bits */ uint64_t max_score = 0u; - uint32_t max_j = 0u; - auto const not_covered_by_i = target ^ function_i; + max_j = 0u; + auto const not_covered_by_i = ~function_i; for ( auto j = 0u; j < divisors.size(); ++j ) { - auto const covered_by_j = ~( target ^ divisors.at( 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 ) + if ( scores.at( j ) > max_score && (j >> 1) != (max_i >> 1) ) { max_score = scores.at( j ); max_j = j; @@ -148,14 +164,14 @@ class mig_resub_engine } maj_nodes.back().fanins.emplace_back( max_j ); - /* the third fanin */ + /* the third fanin: only care about the disagreed bits */ max_score = 0u; - uint32_t max_k = 0u; + max_k = 0u; auto const disagree_in_ij = function_i ^ divisors.at( max_j ); for ( auto k = 0u; k < divisors.size(); ++k ) { - scores.at( k ) = kitty::count_ones( ~( target ^ divisors.at( k ) ) & disagree_in_ij ); - if ( scores.at( k ) > max_score ) + scores.at( k ) = kitty::count_ones( divisors.at( k ) & disagree_in_ij ); + if ( scores.at( k ) > max_score && (k >> 1) != (max_i >> 1) && (k >> 1) != (max_j >> 1) ) { max_score = scores.at( k ); max_k = k; @@ -164,7 +180,7 @@ class mig_resub_engine maj_nodes.back().fanins.emplace_back( max_k ); auto const current_function = kitty::ternary_majority( function_i, divisors.at( max_j ), divisors.at( max_k ) ); - if ( current_function == target ) + if ( kitty::is_const0( ~current_function ) ) { std::vector index_list; for ( auto& n : maj_nodes ) @@ -178,8 +194,8 @@ class mig_resub_engine } else if ( maj_nodes.size() < size_limit ) { - maj_nodes.emplace_back( maj_node{maj_nodes.back().id + 2u, current_function, {maj_nodes.back().id}} ); - return bottom_up_approach( current_function ); + maj_nodes.emplace_back( maj_node{maj_nodes.back().id + 2u, {maj_nodes.back().id}} ); + return bottom_up_approach_rec( current_function ); } else { @@ -187,74 +203,340 @@ class mig_resub_engine } } - std::optional> top_down_approach( uint32_t node_position ) + std::optional> top_down_approach() + { + maj_nodes.reserve( size_limit ); + /* topmost node: care is const1 */ + TT const const1 = divisors.at( 0u ) | divisors.at( 1u ); + #if two_cares + top_down_approach_try_one( const1, const1 ); + #else + top_down_approach_try_one( const1 ); + #endif + std::cout<<"first node built.\n"; + maj_nodes.emplace_back( maj_node{uint32_t( divisors.size() ), {max_i, max_j, max_k}, {divisors.at( max_i ), divisors.at( max_j ), divisors.at( max_k )}, const1} ); + if ( kitty::is_const0( ~kitty::ternary_majority( divisors.at( max_i ), divisors.at( max_j ), divisors.at( max_k ) ) ) ) + { + std::cout<<"care bits all fulfilled.\n"; + /* 1-resub */ + std::vector index_list; + index_list.emplace_back( maj_nodes.at( 0u ).fanins.at( 0u ) ); + index_list.emplace_back( maj_nodes.at( 0u ).fanins.at( 1u ) ); + index_list.emplace_back( maj_nodes.at( 0u ).fanins.at( 2u ) ); + index_list.emplace_back( maj_nodes.at( 0u ).id ); + return index_list; + } + else + { + leaves.emplace_back( expansion_position{0, 0} ); + leaves.emplace_back( expansion_position{0, 1} ); + leaves.emplace_back( expansion_position{0, 2} ); + } + + while ( leaves.size() != 0u && maj_nodes.size() < size_limit ) + { + // TODO: Choose a best leaf to expand + expansion_position node_position = leaves.back(); + leaves.pop_back(); + + 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 ); + + if ( parent_node.fanins.at( fi ) >= divisors.size() ) /* already expanded */ + { + continue; + } + + #if two_cares + TT const care1 = parent_node.care & ~sibling_func( parent_node, fi, 1 ); + TT const care2 = parent_node.care & ~sibling_func( parent_node, fi, 2 ); + TT const care = care1 | care2; + uint64_t const original_score = score( original_function, care1, care2 ); + #else + TT const care = parent_node.care & ~( sibling_func( parent_node, fi, 1 ) & sibling_func( parent_node, fi, 2 ) ); + uint64_t const original_score = score( original_function, care ); + #endif + + if ( fulfilled( original_function, care ) ) /* already fulfilled */ + { + continue; + } + + #if two_cares + top_down_approach_try_one( care1, care2, node_position ); + #else + top_down_approach_try_one( care, node_position ); + #endif + + auto const current_function = kitty::ternary_majority( divisors.at( max_i ), divisors.at( max_j ), divisors.at( max_k ) ); + std::cout<<"resulting function: "; kitty::print_binary(current_function); std::cout<<"\n"; + + #if two_cares + auto new_score = score( current_function, care1, care2 ); + #else + auto new_score = score( current_function, care ); + #endif + + if ( new_score < original_score ) + { + std::cout << "get worse. (" << new_score << ")\n"; + continue; + } + + if ( new_score == original_score ) + { + if ( kitty::count_ones( current_function & parent_node.care ) >= kitty::count_ones( original_function & parent_node.care ) && + current_function != original_function ) + { + std::cout<<"score stays the same but covers more care bits of parent node, or also the same but different function.\n"; + } + else + { + continue; + } + } + + /* construct the new node */ + auto new_id = maj_nodes.back().id + 2u; + maj_nodes.emplace_back( maj_node{new_id, {max_i, max_j, max_k}, {divisors.at( max_i ), divisors.at( max_j ), divisors.at( max_k )}, care, node_position} ); + update_fanin( parent_node, fi, new_id, current_function ); + + if ( kitty::is_const0( ~current_function & care ) ) + { + std::cout<<"care bits all fulfilled.\n"; + if ( node_fulfilled( maj_nodes.at( 0u ) ) ) + { + std::cout<<"\n======== solution found =========\n"; + for ( auto i = 0u; i < maj_nodes.size(); ++i ) + { + std::cout<<"[node "< index_list; + // TODO: translate + return index_list; + } + } + else + { + std::cout<<"improved but still not fulfilling all care bits, add children to queue.\n"; + 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 std::nullopt; + } + + /* default values are just redundant values because they are not used for the first node */ +#if two_cares + void top_down_approach_try_one( TT const& care1, TT const& care2, expansion_position const& node_position = {} ) +#else + void top_down_approach_try_one( TT const& care, expansion_position const& node_position = {} ) +#endif { - maj_node const& current_node = maj_nodes.at( node_position ); - TT const& care = current_node.care; - assert( current_node.fanins.size() == 1u ); + std::cout << "\n\nexpanding node " << node_position.parent_position << " at fanin " << node_position.fanin_num << ".\ncare = "; + #if two_cares + kitty::print_binary( care1 ); std::cout<<" "; kitty::print_binary( care2 ); + #else + kitty::print_binary( care ); + #endif - /* the second fanin */ + std::cout << "\nchoosing first fanin:\n"; + /* the first fanin: cover most care bits */ uint64_t max_score = 0u; - uint32_t max_j = 0u; - auto const not_covered_by_i = target ^ function_i; + max_i = 0u; + for ( auto i = 0u; i < divisors.size(); ++i ) + { + #if two_cares + scores.at( i ) = kitty::count_ones( divisors.at( i ) & care1 ) + kitty::count_ones( divisors.at( i ) & care2 ); + #else + scores.at( i ) = kitty::count_ones( divisors.at( i ) & care ); + #endif + std::cout<<"["< max_score ) + { + max_score = scores.at( i ); + max_i = i; + } + } + std::cout<<"===== chosen " << max_i <<"\n\nchoosing second fanin:\n"; + + /* the second fanin: 2 * #newly-covered-bits + 1 * #cover-again-bits */ + max_score = 0u; + 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 = ~( target ^ divisors.at( j ) ); + #if two_cares + auto const covered_by_j1 = divisors.at( j ) & care1; + auto const covered_by_j2 = divisors.at( j ) & care2; + scores.at( j ) = kitty::count_ones( covered_by_j1 ) + kitty::count_ones( not_covered_by_i & covered_by_j1 ) + kitty::count_ones( covered_by_j2 ) + kitty::count_ones( not_covered_by_i & covered_by_j2 ); + #else + 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 ) + #endif + std::cout<<"["< max_score && !same_divisor( j, max_i ) ) { max_score = scores.at( j ); max_j = j; } } - current_node.fanins.emplace_back( max_j ); + std::cout<<"===== chosen " << max_j <<"\n\nchoosing third fanin:\n"; - /* the third fanin */ + /* 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 = target ^ divisors.at( max_j ); + 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 = ~( target ^ divisors.at( k ) ); + #if two_cares + auto const covered_by_k1 = divisors.at( k ) & care1; + auto const covered_by_k2 = divisors.at( k ) & care2; + scores.at( k ) = kitty::count_ones( covered_by_k1 & not_covered_by_i ) + kitty::count_ones( covered_by_k1 & not_covered_by_j ) + kitty::count_ones( covered_by_k2 & not_covered_by_i ) + kitty::count_ones( covered_by_k2 & not_covered_by_j ); + #else + 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 ) + #endif + std::cout<<"["< max_score && !same_divisor( k, max_i ) && !same_divisor( k, max_j ) ) { max_score = scores.at( k ); max_k = k; } } - current_node.fanins.emplace_back( max_k ); + std::cout<<"===== chosen " << max_k <<"\n"; + } - auto const current_function = kitty::ternary_majority( function_i, divisors.at( max_j ), divisors.at( max_k ) ); - if ( current_function == target ) +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 ); + } + +#if two_cares + uint64_t score( TT const& func, TT const& care1, TT const& care2 ) + { + return kitty::count_ones( func & care1 ) + kitty::count_ones( func & care2 ); + } +#else + uint64_t score( TT const& func, TT const& care ) + { + return kitty::count_ones( func & care ); + } +#endif + + 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; + 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, new_function, sibling_func2 ); + update_sibling( parent_node, fi, 2, new_function, sibling_func1 ); + + /* update grandparents */ + if ( parent_node.parent.parent_position != -1 ) /* not the topmost node */ { - std::vector index_list; - // TODO - return index_list; + update_fanin( grandparent( parent_node ), parent_node.parent.fanin_num, parent_node.id, kitty::ternary_majority( new_function, sibling_func1, sibling_func2 ) ); } - else if ( maj_nodes.size() < size_limit ) + } + + void update_sibling( maj_node const& parent_node, uint32_t const fi, uint32_t const sibling_num, TT const& new_function, TT const& sibling_func ) + { + uint32_t index = sibling_index( fi, sibling_num ); + uint32_t id = parent_node.fanins.at( index ); + /* update the `care`s of the siblings (if they are not divisors) */ + if ( id >= divisors.size() ) { - maj_nodes.emplace_back( maj_node{maj_nodes.back().id + 2u, care & ( ( target ^ divisors.at( max_j ) ) + ( target ^ divisors.at( max_k ) ) ), {current_node.fanins[0]}} ); - current_node.fanins[0] = maj_nodes.back().id + 2u; - // refine this new node. if not successful, try refining the second fanin (breadth-first) + id_to_node( id ).care = parent_node.care & ~( new_function & sibling_func ); } - else + else /* or add these positions back to the expansion queue */ { - return std::nullopt; + expansion_position new_pos{int32_t( id_to_pos( parent_node.id ) ), int32_t( index )}; + bool added = false; + for ( auto& l : leaves ) + { + if ( l == new_pos ) + { + added = true; + break; + } + } + if ( !added ) + { + leaves.emplace_back( new_pos ); + } } } - bool refine() + 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 uint32_t sibling_id( maj_node const& parent_node, uint32_t const my_index, uint32_t const sibling_num ) + //{ + // return parent_node.fanins.at( sibling_index( my_index, sibling_num ) ); + //} + + 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() ) / 2; + } + + inline maj_node& id_to_node( uint32_t const id ) + { + return maj_nodes.at( id_to_pos( id ) ); + } private: uint64_t max_num_divisors; uint64_t counter; uint32_t size_limit; + uint32_t num_bits; + + uint32_t max_i, max_j, max_k; - TT target; std::vector divisors; std::vector scores; std::vector maj_nodes; + + /* pairs of (maj_nodes index, fanin number) */ + std::vector leaves; // can convert to a queue }; /* mig_resub_engine */ } /* namespace mockturtle */ From f852c740162bac10eaf5a83e96804e3eb4425c7d Mon Sep 17 00:00:00 2001 From: Sonia Date: Mon, 26 Oct 2020 14:22:18 +0100 Subject: [PATCH 03/22] caching; avoid being stuck; choosing expansion position; separate two approaches --- experiments/mig_resub_engine.cpp | 55 ++- .../mockturtle/algorithms/resub_engines.hpp | 386 +++++++++++------- test/algorithms/resub_engines.cpp | 12 +- 3 files changed, 293 insertions(+), 160 deletions(-) diff --git a/experiments/mig_resub_engine.cpp b/experiments/mig_resub_engine.cpp index 27c7c7646..673f9a5d0 100644 --- a/experiments/mig_resub_engine.cpp +++ b/experiments/mig_resub_engine.cpp @@ -44,24 +44,53 @@ int main() { //using namespace experiments; using namespace mockturtle; + auto n = 3u; - std::vector tts( 4, kitty::dynamic_truth_table( 3 ) ); + std::vector tts( n + 1, kitty::dynamic_truth_table( n ) ); - // DOT: 00011010 - // ONEHOT: 00010110 - // GAMBLE: 10000001 - kitty::create_from_binary_string( tts[0], "10000001" ); // target - kitty::create_nth_var( tts[1], 0 ); - kitty::create_nth_var( tts[2], 1 ); - kitty::create_nth_var( tts[3], 2 ); + for ( auto i = 0u; i < n; ++i ) + { + kitty::create_nth_var( tts[i+1], i ); + } - mig_resub_engine engine( 3 ); +#if 0 + kitty::create_from_hex_string( tts[0], "1e" ); // target + + mig_resub_engine engine( n ); engine.add_root( 0, tts ); - engine.add_divisor( 1, tts ); - engine.add_divisor( 2, tts ); - engine.add_divisor( 3, tts ); + for ( auto i = 0u; i < n; ++i ) + { + engine.add_divisor( i+1, tts ); + } + const auto res = engine.compute_function( 10u ); - const auto res = engine.compute_function( 5u ); +#else + uint64_t total_size = 0u; + for ( uint64_t func = 0u; func < (1 << (1< engine( n ); + engine.add_root( 0, tts ); + for ( auto i = 0u; i < n; ++i ) + { + engine.add_divisor( i+1, tts ); + } + const auto res = engine.compute_function( 10u ); + if ( !res ) + { + std::cout << " did not find solution within 10 nodes.\n"; + } + else + { + assert( ( (*res).size() - 1 ) % 3 == 0 ); + std::cout << " found solution of size " << ( (*res).size() - 1 ) / 3 << "\n"; + total_size += ( (*res).size() - 1 ) / 3; + } + } + std::cout << "total size: " << total_size << "\n"; +#endif return 0; } diff --git a/include/mockturtle/algorithms/resub_engines.hpp b/include/mockturtle/algorithms/resub_engines.hpp index 6926dc2c9..c86b88b8a 100644 --- a/include/mockturtle/algorithms/resub_engines.hpp +++ b/include/mockturtle/algorithms/resub_engines.hpp @@ -34,38 +34,23 @@ #include +#include +#include + namespace mockturtle { -#define two_cares 0 -template -class mig_resub_engine +template +class mig_resub_engine_bottom_up { - /*! \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; /* ( id - divisors.size() ) / 2 = its position in maj_nodes */ std::vector fanins; /* ids of its three fanins */ - - /* only used in top-down */ - std::vector fanin_functions = std::vector(); - TT care = TT(); - expansion_position parent = expansion_position(); }; public: - explicit mig_resub_engine( uint64_t num_divisors, uint64_t max_num_divisors = 50ul ) + explicit mig_resub_engine_bottom_up( uint64_t num_divisors, uint64_t max_num_divisors = 50ul ) : max_num_divisors( max_num_divisors ), counter( 2u ), divisors( ( num_divisors + 1 ) * 2 ), scores( ( num_divisors + 1 ) * 2 ) { } @@ -125,14 +110,7 @@ class mig_resub_engine } size_limit = num_inserts; - if constexpr ( use_top_down ) - { - return top_down_approach(); - } - else - { - return bottom_up_approach(); - } + return bottom_up_approach(); } private: @@ -203,27 +181,141 @@ class mig_resub_engine } } +private: + uint64_t max_num_divisors; + uint64_t counter; + uint32_t size_limit; + uint32_t num_bits; + + uint32_t max_i, max_j, max_k; + + std::vector divisors; + std::vector scores; + std::vector maj_nodes; + +}; /* mig_resub_engine_bottom_up */ + +template +class mig_resub_engine +{ + bool p = false; + /*! \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; /* resulting function */ + }; + + struct TTHasher + { + uint64_t operator()( TT const& tt ) const + { + return tt._bits[0]; + } + }; + +public: + explicit mig_resub_engine( uint64_t num_divisors, uint64_t max_num_divisors = 50ul ) + : max_num_divisors( max_num_divisors ), counter( 2u ), divisors( ( num_divisors + 1 ) * 2 ), scores( ( num_divisors + 1 ) * 2 ) + { } + + template + void add_root( node_type const& node, truth_table_storage_type const& tts ) + { + divisors.at( 0u ) = ~tts[node]; // const 0 XNOR target = ~target + divisors.at( 1u ) = tts[node]; // const 1 XNOR target = target + num_bits = tts[node].num_bits(); + } + + template + void add_divisor( node_type const& node, truth_table_storage_type const& tts ) + { + assert( tts[node].num_bits() == num_bits ); + divisors.at( counter++ ) = tts[node] ^ divisors.at( 0u ); // XNOR target = XOR ~target + divisors.at( counter++ ) = ~tts[node] ^ divisors.at( 0u ); + } + + template + void add_divisors( iterator_type begin, iterator_type end, truth_table_storage_type const& tts ) + { + assert( counter == 2u ); + while ( begin != end ) + { + add_divisor( *begin, tts ); + ++begin; + } + } + + std::optional> compute_function( uint32_t num_inserts ) + { + 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 ) ); + if (p) { std::cout<<"["< max_score ) + { + max_score = scores.at( i ); + max_i = i; + if ( max_score == num_bits ) + { + break; + } + } + } + /* 0-resub (including constants) */ + if ( max_score == num_bits ) + { + return std::vector( {max_i} ); + } + + 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 ); - #if two_cares - top_down_approach_try_one( const1, const1 ); - #else - top_down_approach_try_one( const1 ); - #endif - std::cout<<"first node built.\n"; - maj_nodes.emplace_back( maj_node{uint32_t( divisors.size() ), {max_i, max_j, max_k}, {divisors.at( max_i ), divisors.at( max_j ), divisors.at( max_k )}, const1} ); - if ( kitty::is_const0( ~kitty::ternary_majority( divisors.at( max_i ), divisors.at( max_j ), divisors.at( max_k ) ) ) ) + simple_maj const top_node = top_down_approach_try_one( const1 ); + 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} ); + if ( kitty::is_const0( ~top_node.function ) ) { - std::cout<<"care bits all fulfilled.\n"; /* 1-resub */ std::vector index_list; index_list.emplace_back( maj_nodes.at( 0u ).fanins.at( 0u ) ); index_list.emplace_back( maj_nodes.at( 0u ).fanins.at( 1u ) ); index_list.emplace_back( maj_nodes.at( 0u ).fanins.at( 2u ) ); - index_list.emplace_back( maj_nodes.at( 0u ).id ); + index_list.emplace_back( divisors.size() ); return index_list; } else @@ -233,63 +325,82 @@ class mig_resub_engine leaves.emplace_back( expansion_position{0, 2} ); } - while ( leaves.size() != 0u && maj_nodes.size() < size_limit ) + bool first_round = true; + while ( ( leaves.size() != 0u || back_up.size() != 0u ) && maj_nodes.size() < size_limit ) { - // TODO: Choose a best leaf to expand - expansion_position node_position = leaves.back(); - leaves.pop_back(); - - 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 ); - - if ( parent_node.fanins.at( fi ) >= divisors.size() ) /* already expanded */ + if ( leaves.size() == 0u ) { - continue; + leaves = back_up; + back_up.clear(); + first_round = false; } + // TODO: Choose a best leaf to expand + //expansion_position node_position = leaves.back(); + //leaves.pop_back(); + uint32_t max_mismatch = 0u; + 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 two_cares - TT const care1 = parent_node.care & ~sibling_func( parent_node, fi, 1 ); - TT const care2 = parent_node.care & ~sibling_func( parent_node, fi, 2 ); - TT const care = care1 | care2; - uint64_t const original_score = score( original_function, care1, care2 ); - #else - TT const care = parent_node.care & ~( sibling_func( parent_node, fi, 1 ) & sibling_func( parent_node, fi, 2 ) ); - uint64_t const original_score = score( original_function, care ); - #endif + if ( parent_node.fanins.at( fi ) >= divisors.size() ) /* already expanded */ + { + leaves.erase( leaves.begin() + i ); + --i; + continue; + } - if ( fulfilled( original_function, care ) ) /* already fulfilled */ - { - 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 > max_mismatch ) + { + pos = i; + max_mismatch = mismatch; + } } + expansion_position node_position = leaves.at( pos ); + leaves.erase( leaves.begin() + pos ); - #if two_cares - top_down_approach_try_one( care1, care2, node_position ); - #else - top_down_approach_try_one( care, node_position ); - #endif - - auto const current_function = kitty::ternary_majority( divisors.at( max_i ), divisors.at( max_j ), divisors.at( max_k ) ); - std::cout<<"resulting function: "; kitty::print_binary(current_function); std::cout<<"\n"; - - #if two_cares - auto new_score = score( current_function, care1, care2 ); - #else - auto new_score = score( current_function, care ); - #endif + 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 ) ); + simple_maj const new_node = top_down_approach_try_one( care, node_position ); + uint64_t const original_score = score( original_function, care ); + uint64_t const new_score = score( new_node.function, care ); if ( new_score < original_score ) { - std::cout << "get worse. (" << new_score << ")\n"; + if (p) { std::cout << "get worse. (" << new_score << ")\n"; } continue; } if ( new_score == original_score ) { - if ( kitty::count_ones( current_function & parent_node.care ) >= kitty::count_ones( original_function & parent_node.care ) && - current_function != original_function ) + if (p) { std::cout<<"score stays the same.\n"; } + if ( kitty::count_ones( new_node.function & parent_node.care ) > kitty::count_ones( original_function & parent_node.care ) ) { - std::cout<<"score stays the same but covers more care bits of parent node, or also the same but different function.\n"; + if (p) { std::cout<<"...but covers more care bits of parent node.\n"; } + } + 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 (p) { std::cout<<"...and also covers the same # of care bits of parent node, but the function is different.\n"; } + if ( first_round ) + { + back_up.emplace_back( node_position ); + continue; + } } else { @@ -298,15 +409,16 @@ class mig_resub_engine } /* construct the new node */ - auto new_id = maj_nodes.back().id + 2u; - maj_nodes.emplace_back( maj_node{new_id, {max_i, max_j, max_k}, {divisors.at( max_i ), divisors.at( max_j ), divisors.at( max_k )}, care, node_position} ); - update_fanin( parent_node, fi, new_id, current_function ); + 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( ~current_function & care ) ) + if ( kitty::is_const0( ~new_node.function & care ) ) { - std::cout<<"care bits all fulfilled.\n"; + if (p) { std::cout<<"care bits all fulfilled.\n"; } if ( node_fulfilled( maj_nodes.at( 0u ) ) ) { + if (p) { std::cout<<"\n======== solution found =========\n"; for ( auto i = 0u; i < maj_nodes.size(); ++i ) { @@ -321,101 +433,101 @@ class mig_resub_engine kitty::print_binary(kitty::ternary_majority(maj_nodes[i].fanin_functions[0], maj_nodes[i].fanin_functions[1], maj_nodes[i].fanin_functions[2])); std::cout<<"\n\n"; } + } std::vector index_list; - // TODO: translate + std::unordered_map id_map; + for ( auto i = 0u; i < maj_nodes.size(); ++i ) + { + auto& n = maj_nodes.at( maj_nodes.size() - i - 1u ); + for ( auto j = 0u; j < 3u; ++j ) + { + if ( n.fanins.at( j ) < divisors.size() ) + { + index_list.emplace_back( n.fanins.at( j ) ); + } + else + { + auto mapped = id_map.find( n.fanins.at( j ) ); + assert( mapped != id_map.end() ); + index_list.emplace_back( mapped->second ); + } + id_map[n.id] = divisors.size() + i * 2; + } + } + index_list.emplace_back( ( id_map.find( maj_nodes.at( 0u ).id ) )->second ); return index_list; } } else { - std::cout<<"improved but still not fulfilling all care bits, add children to queue.\n"; - 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} ); + if (p) { std::cout<<"improved but still not fulfilling all care bits, add children to queue.\n"; } + 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 std::nullopt; } /* default values are just redundant values because they are not used for the first node */ -#if two_cares - void top_down_approach_try_one( TT const& care1, TT const& care2, expansion_position const& node_position = {} ) -#else - void top_down_approach_try_one( TT const& care, expansion_position const& node_position = {} ) -#endif + simple_maj top_down_approach_try_one( TT const& care, expansion_position const& node_position = {} ) { - std::cout << "\n\nexpanding node " << node_position.parent_position << " at fanin " << node_position.fanin_num << ".\ncare = "; - #if two_cares - kitty::print_binary( care1 ); std::cout<<" "; kitty::print_binary( care2 ); - #else - kitty::print_binary( care ); - #endif - - std::cout << "\nchoosing first fanin:\n"; + if (p) { std::cout << "\n\nexpanding node " << node_position.parent_position << " at fanin " << node_position.fanin_num << ".\ncare = "; kitty::print_binary( care ); std::cout<<"\n"; } + + /* 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; - max_i = 0u; + uint32_t max_i = 0u; for ( auto i = 0u; i < divisors.size(); ++i ) { - #if two_cares - scores.at( i ) = kitty::count_ones( divisors.at( i ) & care1 ) + kitty::count_ones( divisors.at( i ) & care2 ); - #else scores.at( i ) = kitty::count_ones( divisors.at( i ) & care ); - #endif - std::cout<<"["< max_score ) { max_score = scores.at( i ); max_i = i; } } - std::cout<<"===== chosen " << max_i <<"\n\nchoosing second fanin:\n"; /* the second fanin: 2 * #newly-covered-bits + 1 * #cover-again-bits */ max_score = 0u; - max_j = 0u; + uint32_t max_j = 0u; auto const not_covered_by_i = ~divisors.at( max_i ); for ( auto j = 0u; j < divisors.size(); ++j ) { - #if two_cares - auto const covered_by_j1 = divisors.at( j ) & care1; - auto const covered_by_j2 = divisors.at( j ) & care2; - scores.at( j ) = kitty::count_ones( covered_by_j1 ) + kitty::count_ones( not_covered_by_i & covered_by_j1 ) + kitty::count_ones( covered_by_j2 ) + kitty::count_ones( not_covered_by_i & covered_by_j2 ); - #else 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 ); - #endif - std::cout<<"["< max_score && !same_divisor( j, max_i ) ) { max_score = scores.at( j ); max_j = j; } } - std::cout<<"===== chosen " << max_j <<"\n\nchoosing third fanin:\n"; /* the third fanin: 2 * #cover-never-covered-bits + 1 * #cover-covered-once-bits */ max_score = 0u; - max_k = 0u; + uint32_t max_k = 0u; auto const not_covered_by_j = ~divisors.at( max_j ); for ( auto k = 0u; k < divisors.size(); ++k ) { - #if two_cares - auto const covered_by_k1 = divisors.at( k ) & care1; - auto const covered_by_k2 = divisors.at( k ) & care2; - scores.at( k ) = kitty::count_ones( covered_by_k1 & not_covered_by_i ) + kitty::count_ones( covered_by_k1 & not_covered_by_j ) + kitty::count_ones( covered_by_k2 & not_covered_by_i ) + kitty::count_ones( covered_by_k2 & not_covered_by_j ); - #else 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 ); - #endif - std::cout<<"["< max_score && !same_divisor( k, max_i ) && !same_divisor( k, max_j ) ) { max_score = scores.at( k ); max_k = k; } } - std::cout<<"===== chosen " << max_k <<"\n"; + + if (p) { std::cout<<"resulting function: <"< = "; kitty::print_binary(kitty::ternary_majority( divisors.at( max_i ), divisors.at( max_j ), divisors.at( max_k ) )); std::cout<<"\n"; } + 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]; } private: @@ -434,17 +546,10 @@ class mig_resub_engine return fulfilled( kitty::ternary_majority( node.fanin_functions.at( 0u ), node.fanin_functions.at( 1u ), node.fanin_functions.at( 2u ) ), node.care ); } -#if two_cares - uint64_t score( TT const& func, TT const& care1, TT const& care2 ) - { - return kitty::count_ones( func & care1 ) + kitty::count_ones( func & care2 ); - } -#else uint64_t score( TT const& func, TT const& care ) { return kitty::count_ones( func & care ); } -#endif void update_fanin( maj_node& parent_node, uint32_t const fi, uint32_t const new_id, TT const& new_function ) { @@ -515,7 +620,7 @@ class mig_resub_engine inline uint32_t id_to_pos( uint32_t const id ) { assert( id >= divisors.size() ); - return ( id - divisors.size() ) / 2; + return ( id - divisors.size() ); } inline maj_node& id_to_node( uint32_t const id ) @@ -529,14 +634,13 @@ class mig_resub_engine uint32_t size_limit; uint32_t num_bits; - uint32_t max_i, max_j, max_k; - std::vector divisors; std::vector scores; - std::vector maj_nodes; + std::vector maj_nodes; /* the really used nodes */ + std::unordered_map computed_table; /* map from care to a simple_maj with divisors as fanins */ - /* pairs of (maj_nodes index, fanin number) */ - std::vector leaves; // can convert to a queue + std::vector leaves; + std::vector back_up; }; /* mig_resub_engine */ } /* namespace mockturtle */ diff --git a/test/algorithms/resub_engines.cpp b/test/algorithms/resub_engines.cpp index c502ab5cf..6bce5e453 100644 --- a/test/algorithms/resub_engines.cpp +++ b/test/algorithms/resub_engines.cpp @@ -9,7 +9,7 @@ using namespace mockturtle; -TEST_CASE( "MIG resub engine -- 0-resub", "[resub_engines]" ) +TEST_CASE( "MIG resub engine (bottom-up) -- 0-resub", "[resub_engines]" ) { std::vector tts( 4, kitty::dynamic_truth_table( 3 ) ); @@ -18,7 +18,7 @@ TEST_CASE( "MIG resub engine -- 0-resub", "[resub_engines]" ) kitty::create_from_binary_string( tts[2], "10000001" ); kitty::create_from_binary_string( tts[3], "11001001" ); - mig_resub_engine engine( 3 ); + mig_resub_engine_bottom_up engine( 3 ); engine.add_root( 0, tts ); engine.add_divisor( 1, tts ); engine.add_divisor( 2, tts ); @@ -30,7 +30,7 @@ TEST_CASE( "MIG resub engine -- 0-resub", "[resub_engines]" ) CHECK( (*res)[0] == 7u ); } -TEST_CASE( "MIG resub engine -- 1-resub", "[resub_engines]" ) +TEST_CASE( "MIG resub engine (bottom-up) -- 1-resub", "[resub_engines]" ) { std::vector tts( 4, kitty::dynamic_truth_table( 3 ) ); @@ -39,7 +39,7 @@ TEST_CASE( "MIG resub engine -- 1-resub", "[resub_engines]" ) kitty::create_from_binary_string( tts[2], "11001001" ); kitty::create_from_binary_string( tts[3], "01000111" ); - mig_resub_engine engine( tts.size() - 1u ); + mig_resub_engine_bottom_up engine( tts.size() - 1u ); engine.add_root( 0, tts ); for ( auto i = 1u; i < tts.size(); ++i ) { @@ -64,7 +64,7 @@ TEST_CASE( "MIG resub engine -- 1-resub", "[resub_engines]" ) CHECK( target == ans ); } -TEST_CASE( "MIG resub engine -- bottom-up, 2-resub", "[resub_engines]" ) +TEST_CASE( "MIG resub engine (bottom-up) -- 2-resub", "[resub_engines]" ) { std::vector tts( 5, kitty::dynamic_truth_table( 3 ) ); @@ -74,7 +74,7 @@ TEST_CASE( "MIG resub engine -- bottom-up, 2-resub", "[resub_engines]" ) kitty::create_from_binary_string( tts[3], "10011110" ); kitty::create_from_binary_string( tts[4], "01011111" ); - mig_resub_engine engine( tts.size() - 1u ); + mig_resub_engine_bottom_up engine( tts.size() - 1u ); engine.add_root( 0, tts ); for ( auto i = 1u; i < tts.size(); ++i ) { From f4604fed0f90a0b8285b7b18b1e734ba3753e910 Mon Sep 17 00:00:00 2001 From: Sonia Date: Mon, 26 Oct 2020 20:57:11 +0100 Subject: [PATCH 04/22] use kitty::hash; NPN enumeration --- experiments/mig_resub_engine.cpp | 28 ++++++++++++++----- .../mockturtle/algorithms/resub_engines.hpp | 16 ++++------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/experiments/mig_resub_engine.cpp b/experiments/mig_resub_engine.cpp index 673f9a5d0..ee9d98fb5 100644 --- a/experiments/mig_resub_engine.cpp +++ b/experiments/mig_resub_engine.cpp @@ -54,7 +54,7 @@ int main() } #if 0 - kitty::create_from_hex_string( tts[0], "1e" ); // target + kitty::create_from_hex_string( tts[0], "01d8" ); // target mig_resub_engine engine( n ); engine.add_root( 0, tts ); @@ -65,11 +65,23 @@ int main() const auto res = engine.compute_function( 10u ); #else - uint64_t total_size = 0u; - for ( uint64_t func = 0u; func < (1 << (1<> classes; + kitty::dynamic_truth_table tt( n ); + + do + { + /* apply NPN canonization and add resulting representative to set */ + const auto res = kitty::exact_npn_canonization( tt ); + classes.insert( std::get<0>( res ) ); + + /* increment truth table */ + kitty::next_inplace( tt ); + } while ( !kitty::is_const0( tt ) ); + + uint64_t total_size = 0u, failed = 0u; + for ( auto const& func : classes ) { - tts[0]._bits[0] = func; - tts[0].mask_bits(); + tts[0] = func; std::cout << "function: "; kitty::print_hex( tts[0] ); mig_resub_engine engine( n ); engine.add_root( 0, tts ); @@ -77,10 +89,11 @@ int main() { engine.add_divisor( i+1, tts ); } - const auto res = engine.compute_function( 10u ); + const auto res = engine.compute_function( 15u ); if ( !res ) { - std::cout << " did not find solution within 10 nodes.\n"; + std::cout << " did not find solution within 15 nodes.\n"; + ++failed; } else { @@ -91,6 +104,7 @@ int main() } std::cout << "total size: " << total_size << "\n"; + std::cout << "failed: " << failed << "\n"; #endif return 0; } diff --git a/include/mockturtle/algorithms/resub_engines.hpp b/include/mockturtle/algorithms/resub_engines.hpp index c86b88b8a..db866d60e 100644 --- a/include/mockturtle/algorithms/resub_engines.hpp +++ b/include/mockturtle/algorithms/resub_engines.hpp @@ -198,7 +198,7 @@ class mig_resub_engine_bottom_up template class mig_resub_engine { - bool p = false; + bool p = 0; /*! \brief Internal data structure */ struct expansion_position { @@ -227,14 +227,6 @@ class mig_resub_engine TT function; /* resulting function */ }; - struct TTHasher - { - uint64_t operator()( TT const& tt ) const - { - return tt._bits[0]; - } - }; - public: explicit mig_resub_engine( uint64_t num_divisors, uint64_t max_num_divisors = 50ul ) : max_num_divisors( max_num_divisors ), counter( 2u ), divisors( ( num_divisors + 1 ) * 2 ), scores( ( num_divisors + 1 ) * 2 ) @@ -369,6 +361,10 @@ class mig_resub_engine max_mismatch = mismatch; } } + if ( leaves.size() == 0u ) + { + break; + } expansion_position node_position = leaves.at( pos ); leaves.erase( leaves.begin() + pos ); @@ -637,7 +633,7 @@ class mig_resub_engine 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::unordered_map> computed_table; /* map from care to a simple_maj with divisors as fanins */ std::vector leaves; std::vector back_up; From 83d57f1e789778dbcffb55bac9f68190ea88bb8c Mon Sep 17 00:00:00 2001 From: Sonia Date: Mon, 26 Oct 2020 22:24:32 +0100 Subject: [PATCH 05/22] try all starting points at the first node --- experiments/mig_resub_engine.cpp | 10 +- .../mockturtle/algorithms/resub_engines.hpp | 216 ++++++++++-------- 2 files changed, 132 insertions(+), 94 deletions(-) diff --git a/experiments/mig_resub_engine.cpp b/experiments/mig_resub_engine.cpp index ee9d98fb5..802ccf24c 100644 --- a/experiments/mig_resub_engine.cpp +++ b/experiments/mig_resub_engine.cpp @@ -54,7 +54,7 @@ int main() } #if 0 - kitty::create_from_hex_string( tts[0], "01d8" ); // target + kitty::create_from_hex_string( tts[0], "17ac" ); // target mig_resub_engine engine( n ); engine.add_root( 0, tts ); @@ -63,6 +63,14 @@ int main() engine.add_divisor( i+1, tts ); } const auto res = engine.compute_function( 10u ); + if ( !res ) + { + std::cout << "did not find solution within 15 nodes.\n"; + } + else + { + std::cout << "found solution of size " << ( (*res).size() - 1 ) / 3 << "\n"; + } #else std::unordered_set> classes; diff --git a/include/mockturtle/algorithms/resub_engines.hpp b/include/mockturtle/algorithms/resub_engines.hpp index db866d60e..e94608180 100644 --- a/include/mockturtle/algorithms/resub_engines.hpp +++ b/include/mockturtle/algorithms/resub_engines.hpp @@ -198,7 +198,7 @@ class mig_resub_engine_bottom_up template class mig_resub_engine { - bool p = 0; + bool p = 0; // verbose printing /*! \brief Internal data structure */ struct expansion_position { @@ -261,27 +261,15 @@ class mig_resub_engine std::optional> compute_function( uint32_t num_inserts ) { - 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 ) ); if (p) { std::cout<<"["< max_score ) + if ( kitty::is_const0( ~divisors.at( i ) ) ) { - max_score = scores.at( i ); - max_i = i; - if ( max_score == num_bits ) - { - break; - } + /* 0-resub (including constants) */ + return std::vector( {i} ); } } - /* 0-resub (including constants) */ - if ( max_score == num_bits ) - { - return std::vector( {max_i} ); - } if ( num_inserts == 0u ) { @@ -298,26 +286,79 @@ class mig_resub_engine maj_nodes.reserve( size_limit ); /* topmost node: care is const1 */ TT const const1 = divisors.at( 0u ) | divisors.at( 1u ); - simple_maj const top_node = top_down_approach_try_one( const1 ); - 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} ); + simple_maj const top_node = expand_one( const1 ); + if ( kitty::is_const0( ~top_node.function ) ) { /* 1-resub */ std::vector index_list; - index_list.emplace_back( maj_nodes.at( 0u ).fanins.at( 0u ) ); - index_list.emplace_back( maj_nodes.at( 0u ).fanins.at( 1u ) ); - index_list.emplace_back( maj_nodes.at( 0u ).fanins.at( 2u ) ); + index_list.emplace_back( top_node.fanins.at( 0u ) ); + index_list.emplace_back( top_node.fanins.at( 1u ) ); + index_list.emplace_back( top_node.fanins.at( 2u ) ); index_list.emplace_back( divisors.size() ); return index_list; } - else + + std::vector copy; + for ( int32_t i = 0; i < 3; ++i ) { - leaves.emplace_back( expansion_position{0, 0} ); - leaves.emplace_back( expansion_position{0, 1} ); - leaves.emplace_back( expansion_position{0, 2} ); + if (p) { std::cout<<"=== try expand "< index_list; + std::unordered_map id_map; + for ( auto i = 0u; i < copy.size(); ++i ) + { + auto& n = copy.at( copy.size() - i - 1u ); + for ( auto j = 0u; j < 3u; ++j ) + { + if ( n.fanins.at( j ) < divisors.size() ) + { + index_list.emplace_back( n.fanins.at( j ) ); + } + else + { + auto mapped = id_map.find( n.fanins.at( j ) ); + assert( mapped != id_map.end() ); + index_list.emplace_back( mapped->second ); + } + id_map[n.id] = divisors.size() + i * 2; + } } + index_list.emplace_back( ( id_map.find( copy.at( 0u ).id ) )->second ); + return index_list; + } - bool first_round = true; + bool refine() + { while ( ( leaves.size() != 0u || back_up.size() != 0u ) && maj_nodes.size() < size_limit ) { if ( leaves.size() == 0u ) @@ -326,9 +367,7 @@ class mig_resub_engine back_up.clear(); first_round = false; } - // TODO: Choose a best leaf to expand - //expansion_position node_position = leaves.back(); - //leaves.pop_back(); + uint32_t max_mismatch = 0u; uint32_t pos = 0u; for ( int32_t i = 0; (unsigned)i < leaves.size(); ++i ) @@ -373,47 +412,60 @@ class mig_resub_engine 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 ) ); - simple_maj const new_node = top_down_approach_try_one( care, node_position ); - uint64_t const original_score = score( original_function, care ); - uint64_t const new_score = score( new_node.function, care ); - if ( new_score < original_score ) + if ( evaluate_one( care, original_function, node_position ) ) { - if (p) { std::cout << "get worse. (" << new_score << ")\n"; } - continue; + 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, node_position ); + uint64_t const original_score = score( original_function, care ); + uint64_t const new_score = score( new_node.function, care ); + if ( new_score < original_score ) + { + if (p) { std::cout << "get worse. (" << new_score << ")\n"; } + return false; + } - if ( new_score == original_score ) + if ( new_score == original_score ) + { + if (p) { std::cout<<"score stays the same.\n"; } + if ( kitty::count_ones( new_node.function & parent_node.care ) > kitty::count_ones( original_function & parent_node.care ) ) { - if (p) { std::cout<<"score stays the same.\n"; } - if ( kitty::count_ones( new_node.function & parent_node.care ) > kitty::count_ones( original_function & parent_node.care ) ) - { - if (p) { std::cout<<"...but covers more care bits of parent node.\n"; } - } - 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 (p) { std::cout<<"...but covers more care bits of parent node.\n"; } + } + 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 (p) { std::cout<<"...and also covers the same # of care bits of parent node, but the function is different.\n"; } + if ( first_round ) { - if (p) { std::cout<<"...and also covers the same # of care bits of parent node, but the function is different.\n"; } - if ( first_round ) - { - back_up.emplace_back( node_position ); - continue; - } - } - else - { - continue; + back_up.emplace_back( node_position ); + return false; } } + 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 ); + /* 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 ( kitty::is_const0( ~new_node.function & care ) ) + { + if (p) { std::cout<<"care bits all fulfilled.\n"; } + if ( node_fulfilled( maj_nodes.at( 0u ) ) ) { - if (p) { std::cout<<"care bits all fulfilled.\n"; } - if ( node_fulfilled( maj_nodes.at( 0u ) ) ) - { if (p) { std::cout<<"\n======== solution found =========\n"; for ( auto i = 0u; i < maj_nodes.size(); ++i ) @@ -430,43 +482,20 @@ class mig_resub_engine std::cout<<"\n\n"; } } - std::vector index_list; - std::unordered_map id_map; - for ( auto i = 0u; i < maj_nodes.size(); ++i ) - { - auto& n = maj_nodes.at( maj_nodes.size() - i - 1u ); - for ( auto j = 0u; j < 3u; ++j ) - { - if ( n.fanins.at( j ) < divisors.size() ) - { - index_list.emplace_back( n.fanins.at( j ) ); - } - else - { - auto mapped = id_map.find( n.fanins.at( j ) ); - assert( mapped != id_map.end() ); - index_list.emplace_back( mapped->second ); - } - id_map[n.id] = divisors.size() + i * 2; - } - } - index_list.emplace_back( ( id_map.find( maj_nodes.at( 0u ).id ) )->second ); - return index_list; - } - } - else - { - if (p) { std::cout<<"improved but still not fulfilling all care bits, add children to queue.\n"; } - 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 true; } } - return std::nullopt; + else + { + if (p) { std::cout<<"improved but still not fulfilling all care bits, add children to queue.\n"; } + 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; } - /* default values are just redundant values because they are not used for the first node */ - simple_maj top_down_approach_try_one( TT const& care, expansion_position const& node_position = {} ) + simple_maj expand_one( TT const& care, expansion_position const& node_position = {} ) { if (p) { std::cout << "\n\nexpanding node " << node_position.parent_position << " at fanin " << node_position.fanin_num << ".\ncare = "; kitty::print_binary( care ); std::cout<<"\n"; } @@ -637,6 +666,7 @@ class mig_resub_engine std::vector leaves; std::vector back_up; + bool first_round = true; }; /* mig_resub_engine */ } /* namespace mockturtle */ From 2afc8910a69bad23c8b24e01db9794ceaf3f3d81 Mon Sep 17 00:00:00 2001 From: Sonia Date: Sun, 15 Nov 2020 22:04:43 +0100 Subject: [PATCH 06/22] several improvements Now producing optimal solutions for all 3-input functions! Update details: try all tie-breakings of topmost node; two back-up queues of expansion positions; expand positions with less mismatches first --- .../mockturtle/algorithms/resub_engines.hpp | 198 ++++++++++++++---- 1 file changed, 161 insertions(+), 37 deletions(-) diff --git a/include/mockturtle/algorithms/resub_engines.hpp b/include/mockturtle/algorithms/resub_engines.hpp index e94608180..ae01a0f21 100644 --- a/include/mockturtle/algorithms/resub_engines.hpp +++ b/include/mockturtle/algorithms/resub_engines.hpp @@ -224,7 +224,7 @@ class mig_resub_engine struct simple_maj { std::vector fanins; /* ids of divisors */ - TT function; /* resulting function */ + TT function = TT(); /* resulting function */ }; public: @@ -286,46 +286,57 @@ class mig_resub_engine maj_nodes.reserve( size_limit ); /* topmost node: care is const1 */ TT const const1 = divisors.at( 0u ) | divisors.at( 1u ); - simple_maj const top_node = expand_one( const1 ); + //simple_maj const top_node = expand_one( const1 ); + std::vector top_node_choices = construct_top(); - if ( kitty::is_const0( ~top_node.function ) ) + if ( top_node_choices.size() == 1u && kitty::is_const0( ~top_node_choices[0].function ) ) { /* 1-resub */ std::vector index_list; - index_list.emplace_back( top_node.fanins.at( 0u ) ); - index_list.emplace_back( top_node.fanins.at( 1u ) ); - index_list.emplace_back( top_node.fanins.at( 2u ) ); + index_list.emplace_back( top_node_choices[0].fanins.at( 0u ) ); + index_list.emplace_back( top_node_choices[0].fanins.at( 1u ) ); + index_list.emplace_back( top_node_choices[0].fanins.at( 2u ) ); index_list.emplace_back( divisors.size() ); return index_list; } + if ( size_limit == 1u ) + { + return std::nullopt; + } std::vector copy; - for ( int32_t i = 0; i < 3; ++i ) + for ( simple_maj const& top_node : top_node_choices ) { - if (p) { std::cout<<"=== try expand "< =====\n"; } + for ( int32_t i = 0; i < 3; ++i ) { - /* 2-resub */ - copy = maj_nodes; - break; - } + if (p) { std::cout<<"=== try expand "< max_mismatch ) + if ( mismatch < min_mismatch ) { pos = i; - max_mismatch = mismatch; + min_mismatch = mismatch; } } if ( leaves.size() == 0u ) @@ -440,15 +459,32 @@ class mig_resub_engine if ( kitty::count_ones( new_node.function & parent_node.care ) > kitty::count_ones( original_function & parent_node.care ) ) { if (p) { std::cout<<"...but covers more care bits of parent node.\n"; } + 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 (p) { std::cout<<"...and also covers the same # of care bits of parent node, but the function is different.\n"; } if ( first_round ) { - back_up.emplace_back( node_position ); + /* 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 { @@ -555,6 +591,95 @@ class mig_resub_engine return computed_table[care]; } + std::vector construct_top() + { + if (p) { std::cout << "constructing topmost node\n"; } + 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 ) + { + 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 ) + { + TT const func = kitty::ternary_majority( divisors.at( max_i ), divisors.at( max_j ), divisors.at( k ) ); + if (p) { std::cout<<"<"< = "; kitty::print_binary(func); std::cout<<"\n"; } + 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; + } + private: bool same_divisor( uint32_t const i, uint32_t const j ) { @@ -664,8 +789,7 @@ class mig_resub_engine 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; - std::vector back_up; + std::vector leaves, improve_in_parent, shuffle; bool first_round = true; }; /* mig_resub_engine */ From efdc5d796e89e789762f9c6c8903f2622d499040 Mon Sep 17 00:00:00 2001 From: Sonia Date: Mon, 16 Nov 2020 10:54:32 +0100 Subject: [PATCH 07/22] fix performance bug --- .../mockturtle/algorithms/resub_engines.hpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/include/mockturtle/algorithms/resub_engines.hpp b/include/mockturtle/algorithms/resub_engines.hpp index ae01a0f21..c8d0b6a4f 100644 --- a/include/mockturtle/algorithms/resub_engines.hpp +++ b/include/mockturtle/algorithms/resub_engines.hpp @@ -314,6 +314,13 @@ class mig_resub_engine 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} ) ) { @@ -322,12 +329,6 @@ class mig_resub_engine break; } - 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 )} ); if ( !refine() ) { continue; @@ -372,6 +373,11 @@ class mig_resub_engine { while ( ( leaves.size() != 0u || improve_in_parent.size() != 0u || shuffle.size() != 0u ) && maj_nodes.size() < size_limit ) { + if (p) { + std::cout<<"leaves: "; for(auto l : leaves) std::cout<<"("< Date: Thu, 17 Dec 2020 21:20:04 +0100 Subject: [PATCH 08/22] use new mig_resub_engine in the resub flow --- include/mockturtle/algorithms/mig_resub.hpp | 75 ++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/include/mockturtle/algorithms/mig_resub.hpp b/include/mockturtle/algorithms/mig_resub.hpp index e6677abbc..795e0e617 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 { @@ -231,6 +233,20 @@ struct mig_resub_functor } ); if ( g ) { + /*mig_resub_engine engine( num_divs ); + unordered_node_map tts( ntk ); + tts[root] = sim.get_tt( ntk.make_signal( root ) ); + for ( auto const& d : divs ) + { + tts[d] = sim.get_tt( ntk.make_signal( d ) ); //phase may be wrong? + } + engine.add_root( root, tts ); + engine.add_divisors( divs.begin(), divs.end(), tts ); + if ( !engine.compute_function( 1 ) ) + { + std::cout << "engine didn't find 1-resub for root "< +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 ) + , engine( divs.size() ) + , divs( divs ) + , st( st ) + { + assert( divs.size() == num_divs ); (void)num_divs; + div_signals.reserve( divs.size() ); + } + + std::optional operator()( node const& root, TT care, uint32_t required, uint32_t max_inserts, uint32_t potential_gain, uint32_t& real_gain ) + { + (void)care; (void)required; + tts[root] = sim.get_tt( sim.get_phase( root ) ? !ntk.make_signal( root ) : ntk.make_signal( root ) ); + 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_root( root, tts ); + 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; + mig_resub_engine engine; + 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 +780,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; From 527378964e684db6921a57ea193bf7bf96f640f9 Mon Sep 17 00:00:00 2001 From: Sonia Date: Thu, 17 Dec 2020 21:20:56 +0100 Subject: [PATCH 09/22] updating cares; easy refine; use index_list --- .../mockturtle/algorithms/resub_engines.hpp | 214 +++++++++++++----- 1 file changed, 160 insertions(+), 54 deletions(-) diff --git a/include/mockturtle/algorithms/resub_engines.hpp b/include/mockturtle/algorithms/resub_engines.hpp index c8d0b6a4f..d8e6bed37 100644 --- a/include/mockturtle/algorithms/resub_engines.hpp +++ b/include/mockturtle/algorithms/resub_engines.hpp @@ -32,6 +32,8 @@ #pragma once +#include "../utils/index_list.hpp" + #include #include @@ -259,7 +261,7 @@ class mig_resub_engine } } - std::optional> compute_function( uint32_t num_inserts ) + std::optional compute_function( uint32_t num_inserts ) { for ( auto i = 0u; i < divisors.size(); ++i ) { @@ -267,7 +269,9 @@ class mig_resub_engine if ( kitty::is_const0( ~divisors.at( i ) ) ) { /* 0-resub (including constants) */ - return std::vector( {i} ); + mig_index_list index_list( divisors.size() ); + index_list.add_output( i ); + return index_list; } } @@ -281,7 +285,7 @@ class mig_resub_engine } private: - std::optional> top_down_approach() + std::optional top_down_approach() { maj_nodes.reserve( size_limit ); /* topmost node: care is const1 */ @@ -292,19 +296,29 @@ class mig_resub_engine if ( top_node_choices.size() == 1u && kitty::is_const0( ~top_node_choices[0].function ) ) { /* 1-resub */ - std::vector index_list; - index_list.emplace_back( top_node_choices[0].fanins.at( 0u ) ); - index_list.emplace_back( top_node_choices[0].fanins.at( 1u ) ); - index_list.emplace_back( top_node_choices[0].fanins.at( 2u ) ); - index_list.emplace_back( divisors.size() ); + mig_index_list index_list( divisors.size() ); + 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() ); + 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 (p) { std::cout<<"<"< = "; kitty::print_binary(top_node.function); std::cout<<"\n"; } + } if ( size_limit == 1u ) { return std::nullopt; } - std::vector copy; + std::vector maj_nodes_best; for ( simple_maj const& top_node : top_node_choices ) { if (p) { std::cout<<"===== try with top node <"< =====\n"; } @@ -325,8 +339,8 @@ class mig_resub_engine if ( evaluate_one( care, divisors.at( top_node.fanins[i] ), expansion_position{0, i} ) ) { /* 2-resub */ - copy = maj_nodes; - break; + maj_nodes_best = maj_nodes; + return translate( maj_nodes_best ); } if ( !refine() ) @@ -334,39 +348,18 @@ class mig_resub_engine continue; } - if ( copy.size() == 0u || maj_nodes.size() < copy.size() ) + if ( maj_nodes_best.size() == 0u || maj_nodes.size() < maj_nodes_best.size() ) { - copy = maj_nodes; + maj_nodes_best = maj_nodes; } } } - if ( copy.size() == 0u ) + if ( maj_nodes_best.size() == 0u ) { return std::nullopt; } - std::vector index_list; - std::unordered_map id_map; - for ( auto i = 0u; i < copy.size(); ++i ) - { - auto& n = copy.at( copy.size() - i - 1u ); - for ( auto j = 0u; j < 3u; ++j ) - { - if ( n.fanins.at( j ) < divisors.size() ) - { - index_list.emplace_back( n.fanins.at( j ) ); - } - else - { - auto mapped = id_map.find( n.fanins.at( j ) ); - assert( mapped != id_map.end() ); - index_list.emplace_back( mapped->second ); - } - id_map[n.id] = divisors.size() + i * 2; - } - } - index_list.emplace_back( ( id_map.find( copy.at( 0u ).id ) )->second ); - return index_list; + return translate( maj_nodes_best ); } bool refine() @@ -526,6 +519,8 @@ class mig_resub_engine } 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 { @@ -641,7 +636,7 @@ class mig_resub_engine } for ( auto j = 0u; j < divisors.size(); ++j ) { - if ( scores.at( j ) == max_score ) + if ( scores.at( j ) == max_score && !same_divisor( j, max_i ) ) { if ( construct_top( max_i, j, res ) ) { @@ -670,10 +665,9 @@ class mig_resub_engine for ( auto k = 0u; k < divisors.size(); ++k ) { - if ( scores.at( k ) == max_score ) + 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 (p) { std::cout<<"<"< = "; kitty::print_binary(func); std::cout<<"\n"; } if ( kitty::is_const0( ~func ) ) { res.clear(); @@ -686,6 +680,66 @@ class mig_resub_engine 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; + 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 ) { @@ -710,13 +764,14 @@ class mig_resub_engine 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, new_function, sibling_func2 ); - update_sibling( parent_node, fi, 2, new_function, sibling_func1 ); + 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 */ @@ -725,32 +780,83 @@ class mig_resub_engine } } - void update_sibling( maj_node const& parent_node, uint32_t const fi, uint32_t const sibling_num, TT const& new_function, TT const& sibling_func ) + /* 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 ); - /* update the `care`s of the siblings (if they are not divisors) */ - if ( id >= divisors.size() ) + 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 ) ) { - id_to_node( id ).care = parent_node.care & ~( new_function & sibling_func ); + /* 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 )} ) ); + } + } } - else /* or add these positions back to the expansion queue */ + node.care = new_care; + + /* the update may propagate to its children */ + for ( auto fi = 0; fi < 3; ++fi ) { - expansion_position new_pos{int32_t( id_to_pos( parent_node.id ) ), int32_t( index )}; - bool added = false; - for ( auto& l : leaves ) + if ( node.fanins.at( fi ) >= divisors.size() ) { - if ( l == new_pos ) + 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 ) { - added = true; - break; + update_node_care( id_to_node( node.fanins.at( fi ) ), node.fanin_functions.at( fi ), old_child_care, new_child_care ); } } - if ( !added ) + } + } + + void add_position( expansion_position const& pos ) + { + for ( auto& l : leaves ) + { + if ( l == pos ) { - leaves.emplace_back( new_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 ) From ad2af3451f4a422bddd609c39fd1ae0b07ba8a99 Mon Sep 17 00:00:00 2001 From: Sonia Date: Tue, 22 Dec 2020 17:21:01 +0100 Subject: [PATCH 10/22] port kitty changes --- lib/kitty/kitty/dynamic_truth_table.hpp | 2 +- lib/kitty/kitty/kitty.hpp | 1 + lib/kitty/kitty/npn.hpp | 1 + lib/kitty/kitty/partial_truth_table.hpp | 113 ++++++++++++++++++++---- lib/kitty/kitty/static_truth_table.hpp | 2 +- 5 files changed, 98 insertions(+), 21 deletions(-) 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 b6acf55eb..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,23 +32,24 @@ #pragma once +#include "detail/constants.hpp" +#include "traits.hpp" + #include #include #include #include #include - -#include "detail/constants.hpp" -#include "traits.hpp" +#include 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 ) @@ -57,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 @@ -65,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; } @@ -136,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). @@ -144,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; @@ -160,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 ) @@ -177,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() ); } From a5d7af42d1b9ea41e067d3739cf3bb8ecef9ca88 Mon Sep 17 00:00:00 2001 From: Sonia Date: Tue, 22 Dec 2020 22:37:42 +0100 Subject: [PATCH 11/22] re-implementation of akers synthesis --- .../mockturtle/algorithms/resub_engines.hpp | 368 +++++++++++++++++- 1 file changed, 364 insertions(+), 4 deletions(-) diff --git a/include/mockturtle/algorithms/resub_engines.hpp b/include/mockturtle/algorithms/resub_engines.hpp index d8e6bed37..0da249e79 100644 --- a/include/mockturtle/algorithms/resub_engines.hpp +++ b/include/mockturtle/algorithms/resub_engines.hpp @@ -269,7 +269,7 @@ class mig_resub_engine if ( kitty::is_const0( ~divisors.at( i ) ) ) { /* 0-resub (including constants) */ - mig_index_list index_list( divisors.size() ); + mig_index_list index_list( divisors.size() / 2 - 1 ); index_list.add_output( i ); return index_list; } @@ -296,7 +296,7 @@ class mig_resub_engine if ( top_node_choices.size() == 1u && kitty::is_const0( ~top_node_choices[0].function ) ) { /* 1-resub */ - mig_index_list index_list( divisors.size() ); + 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; @@ -306,7 +306,7 @@ class mig_resub_engine if ( easy_refine( top_node, 0 ) || easy_refine( top_node, 1 ) ) { /* 1-resub */ - mig_index_list index_list( divisors.size() ); + 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; @@ -714,7 +714,7 @@ class mig_resub_engine mig_index_list translate( std::vector const& maj_nodes_best ) const { - mig_index_list index_list; + mig_index_list index_list( divisors.size() / 2 - 1 ); std::unordered_map id_map; for ( auto i = 0u; i < maj_nodes_best.size(); ++i ) { @@ -905,4 +905,364 @@ class mig_resub_engine bool first_round = true; }; /* mig_resub_engine */ +class mig_resub_engine_akers +{ + bool p = 0; // verbose printing + +public: + explicit mig_resub_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(); + if (p) print_table(); + + while ( divisors.size() > 1 ) + { + if ( index_list.num_gates() >= num_inserts ) + { + return std::nullopt; + } + find_gate(); + add_gate(); + reduce(); + } + + index_list.add_output( id_to_lit[0] ); + 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 + { + /* 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 */ From de0d1d537fcdbb7923fed14dff2745e5fe975f3a Mon Sep 17 00:00:00 2001 From: Sonia Date: Tue, 22 Dec 2020 23:02:51 +0100 Subject: [PATCH 12/22] adjust interface --- experiments/mig_resub_engine.cpp | 29 ++++++++++++------- include/mockturtle/algorithms/mig_resub.hpp | 16 +++++----- .../mockturtle/algorithms/resub_engines.hpp | 20 ++++--------- 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/experiments/mig_resub_engine.cpp b/experiments/mig_resub_engine.cpp index 802ccf24c..142077c61 100644 --- a/experiments/mig_resub_engine.cpp +++ b/experiments/mig_resub_engine.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -46,7 +47,7 @@ int main() using namespace mockturtle; auto n = 3u; - std::vector tts( n + 1, kitty::dynamic_truth_table( n ) ); + std::vector tts( n + 1, kitty::partial_truth_table( 1 << n ) ); for ( auto i = 0u; i < n; ++i ) { @@ -54,10 +55,9 @@ int main() } #if 0 - kitty::create_from_hex_string( tts[0], "17ac" ); // target + kitty::create_from_hex_string( tts[0], "0001" ); // target - mig_resub_engine engine( n ); - engine.add_root( 0, tts ); + mig_resub_engine engine( tts[0] ); for ( auto i = 0u; i < n; ++i ) { engine.add_divisor( i+1, tts ); @@ -69,7 +69,7 @@ int main() } else { - std::cout << "found solution of size " << ( (*res).size() - 1 ) / 3 << "\n"; + std::cout << "found solution of size " << (*res).num_gates() << "\n"; } #else @@ -89,10 +89,9 @@ int main() uint64_t total_size = 0u, failed = 0u; for ( auto const& func : classes ) { - tts[0] = func; + tts[0] = func; // assign dynamic_truth_table to partial_truth_table std::cout << "function: "; kitty::print_hex( tts[0] ); - mig_resub_engine engine( n ); - engine.add_root( 0, tts ); + mig_resub_engine engine( tts[0] ); for ( auto i = 0u; i < n; ++i ) { engine.add_divisor( i+1, tts ); @@ -105,9 +104,17 @@ int main() } else { - assert( ( (*res).size() - 1 ) % 3 == 0 ); - std::cout << " found solution of size " << ( (*res).size() - 1 ) / 3 << "\n"; - total_size += ( (*res).size() - 1 ) / 3; + std::cout << " found solution of size " << (*res).num_gates() << "\n"; + total_size += (*res).num_gates(); + mig_network sol; + decode( sol, *res ); + default_simulator sim( n ); + const auto sol_tt = simulate( sol, sim )[0]; + if ( sol_tt != func ) + { + std::cout << "[error] generated network is not equivalent!\n"; + break; + } } } diff --git a/include/mockturtle/algorithms/mig_resub.hpp b/include/mockturtle/algorithms/mig_resub.hpp index 795e0e617..04ac91b25 100644 --- a/include/mockturtle/algorithms/mig_resub.hpp +++ b/include/mockturtle/algorithms/mig_resub.hpp @@ -233,14 +233,12 @@ struct mig_resub_functor } ); if ( g ) { - /*mig_resub_engine engine( num_divs ); + /*mig_resub_engine engine( sim.get_tt( sim.get_phase( root ) ? !ntk.make_signal( root ) : ntk.make_signal( root ) ) ); unordered_node_map tts( ntk ); - tts[root] = sim.get_tt( ntk.make_signal( root ) ); for ( auto const& d : divs ) { - tts[d] = sim.get_tt( ntk.make_signal( d ) ); //phase may be wrong? + tts[d] = sim.get_tt( sim.get_phase( d ) ? !ntk.make_signal( d ) : ntk.make_signal( d ) ); } - engine.add_root( root, tts ); engine.add_divisors( divs.begin(), divs.end(), tts ); if ( !engine.compute_function( 1 ) ) { @@ -672,7 +670,6 @@ struct mig_resub_functor_new : ntk( ntk ) , sim( sim ) , tts( ntk ) - , engine( divs.size() ) , divs( divs ) , st( st ) { @@ -683,13 +680,15 @@ struct mig_resub_functor_new std::optional operator()( node const& root, TT care, uint32_t required, uint32_t max_inserts, uint32_t potential_gain, uint32_t& real_gain ) { (void)care; (void)required; - tts[root] = sim.get_tt( sim.get_phase( root ) ? !ntk.make_signal( root ) : ntk.make_signal( root ) ); + kitty::partial_truth_table root_tt; + root_tt = sim.get_tt( sim.get_phase( root ) ? !ntk.make_signal( root ) : ntk.make_signal( root ) ); + //mig_resub_engine engine( root_tt ); + mig_resub_engine_akers 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_root( root, tts ); engine.add_divisors( divs.begin(), divs.end(), tts ); auto const res = engine.compute_function( std::min( potential_gain - 1, max_inserts ) ); @@ -709,8 +708,7 @@ struct mig_resub_functor_new private: Ntk& ntk; Simulator const& sim; - unordered_node_map tts; - mig_resub_engine engine; + unordered_node_map tts; std::vector const& divs; std::vector div_signals; stats& st; diff --git a/include/mockturtle/algorithms/resub_engines.hpp b/include/mockturtle/algorithms/resub_engines.hpp index 0da249e79..b166be911 100644 --- a/include/mockturtle/algorithms/resub_engines.hpp +++ b/include/mockturtle/algorithms/resub_engines.hpp @@ -230,30 +230,22 @@ class mig_resub_engine }; public: - explicit mig_resub_engine( uint64_t num_divisors, uint64_t max_num_divisors = 50ul ) - : max_num_divisors( max_num_divisors ), counter( 2u ), divisors( ( num_divisors + 1 ) * 2 ), scores( ( num_divisors + 1 ) * 2 ) + explicit mig_resub_engine( TT const& target ) + : num_bits( target.num_bits() ), divisors( { ~target, target } ) { } - template - void add_root( node_type const& node, truth_table_storage_type const& tts ) - { - divisors.at( 0u ) = ~tts[node]; // const 0 XNOR target = ~target - divisors.at( 1u ) = tts[node]; // const 1 XNOR target = target - num_bits = tts[node].num_bits(); - } - template void add_divisor( node_type const& node, truth_table_storage_type const& tts ) { assert( tts[node].num_bits() == num_bits ); - divisors.at( counter++ ) = tts[node] ^ divisors.at( 0u ); // XNOR target = XOR ~target - divisors.at( counter++ ) = ~tts[node] ^ divisors.at( 0u ); + 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 ) { - assert( counter == 2u ); while ( begin != end ) { add_divisor( *begin, tts ); @@ -891,8 +883,6 @@ class mig_resub_engine } private: - uint64_t max_num_divisors; - uint64_t counter; uint32_t size_limit; uint32_t num_bits; From e36845bca80545d89e3194cd9591dd64c335b834 Mon Sep 17 00:00:00 2001 From: Sonia Date: Thu, 31 Dec 2020 00:14:46 +0100 Subject: [PATCH 13/22] unify interfaces --- include/mockturtle/algorithms/mig_resub.hpp | 9 +- .../mockturtle/algorithms/resub_engines.hpp | 84 +++++++------------ test/algorithms/resub_engines.cpp | 83 ++++++++---------- 3 files changed, 69 insertions(+), 107 deletions(-) diff --git a/include/mockturtle/algorithms/mig_resub.hpp b/include/mockturtle/algorithms/mig_resub.hpp index 04ac91b25..274c70be3 100644 --- a/include/mockturtle/algorithms/mig_resub.hpp +++ b/include/mockturtle/algorithms/mig_resub.hpp @@ -657,7 +657,7 @@ struct mig_resub_functor binate_divisors bdivs; }; /* mig_resub_functor */ -template +template> struct mig_resub_functor_new { public: @@ -677,13 +677,12 @@ struct mig_resub_functor_new div_signals.reserve( divs.size() ); } - std::optional operator()( node const& root, TT care, uint32_t required, uint32_t max_inserts, uint32_t potential_gain, uint32_t& real_gain ) + 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 ) ); - //mig_resub_engine engine( root_tt ); - mig_resub_engine_akers engine( root_tt ); + 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 ) ); @@ -803,7 +802,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/resub_engines.hpp b/include/mockturtle/algorithms/resub_engines.hpp index b166be911..8563fa22f 100644 --- a/include/mockturtle/algorithms/resub_engines.hpp +++ b/include/mockturtle/algorithms/resub_engines.hpp @@ -45,37 +45,23 @@ namespace mockturtle template class mig_resub_engine_bottom_up { - struct maj_node - { - uint32_t id; /* ( id - divisors.size() ) / 2 = its position in maj_nodes */ - std::vector fanins; /* ids of its three fanins */ - }; - public: - explicit mig_resub_engine_bottom_up( uint64_t num_divisors, uint64_t max_num_divisors = 50ul ) - : max_num_divisors( max_num_divisors ), counter( 2u ), divisors( ( num_divisors + 1 ) * 2 ), scores( ( num_divisors + 1 ) * 2 ) + explicit mig_resub_engine_bottom_up( TT const& target ) + : num_bits( target.num_bits() ), divisors( { ~target, target } ) { } - template - void add_root( node_type const& node, truth_table_storage_type const& tts ) - { - divisors.at( 0u ) = ~tts[node]; // const 0 XNOR target = ~target - divisors.at( 1u ) = tts[node]; // const 1 XNOR target = target - num_bits = tts[node].num_bits(); - } - template void add_divisor( node_type const& node, truth_table_storage_type const& tts ) { assert( tts[node].num_bits() == num_bits ); - divisors.at( counter++ ) = tts[node] ^ divisors.at( 0u ); // XNOR target = XOR ~target - divisors.at( counter++ ) = ~tts[node] ^ divisors.at( 0u ); + 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 ) { - assert( counter == 2u ); while ( begin != end ) { add_divisor( *begin, tts ); @@ -83,16 +69,16 @@ class mig_resub_engine_bottom_up } } - std::optional> compute_function( uint32_t num_inserts ) + std::optional compute_function( uint32_t num_inserts ) { uint64_t max_score = 0u; max_i = 0u; for ( auto i = 0u; i < divisors.size(); ++i ) { - scores.at( i ) = kitty::count_ones( divisors.at( i ) ); - if ( scores.at( i ) > max_score ) + uint32_t score = kitty::count_ones( divisors.at( i ) ); + if ( score > max_score ) { - max_score = scores.at( i ); + max_score = score; max_i = i; if ( max_score == num_bits ) { @@ -103,28 +89,29 @@ class mig_resub_engine_bottom_up /* 0-resub (including constants) */ if ( max_score == num_bits ) { - return std::vector( {max_i} ); + index_list.add_output( max_i ); + return index_list; } if ( num_inserts == 0u ) { return std::nullopt; } - size_limit = num_inserts; + size_limit = divisors.size() + num_inserts * 2; return bottom_up_approach(); } private: - std::optional> bottom_up_approach() + std::optional bottom_up_approach() { - maj_nodes.emplace_back( maj_node{uint32_t( divisors.size() ), {max_i}} ); + //maj_nodes.emplace_back( maj_node{uint32_t( divisors.size() ), {max_i}} ); TT const& function_i = divisors.at( max_i ); - max_i = divisors.size(); + current_lit = divisors.size(); return bottom_up_approach_rec( function_i ); } - std::optional> bottom_up_approach_rec( TT const& 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? */ @@ -135,14 +122,14 @@ class mig_resub_engine_bottom_up 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 && (j >> 1) != (max_i >> 1) ) + 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 = scores.at( j ); + max_score = score; max_j = j; } } - maj_nodes.back().fanins.emplace_back( max_j ); + //maj_nodes.back().fanins.emplace_back( max_j ); /* the third fanin: only care about the disagreed bits */ max_score = 0u; @@ -150,31 +137,27 @@ class mig_resub_engine_bottom_up auto const disagree_in_ij = function_i ^ divisors.at( max_j ); for ( auto k = 0u; k < divisors.size(); ++k ) { - scores.at( k ) = kitty::count_ones( divisors.at( k ) & disagree_in_ij ); - if ( scores.at( k ) > max_score && (k >> 1) != (max_i >> 1) && (k >> 1) != (max_j >> 1) ) + 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 = scores.at( k ); + max_score = score; max_k = k; } } - maj_nodes.back().fanins.emplace_back( max_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 ) ) { - std::vector index_list; - for ( auto& n : maj_nodes ) - { - index_list.emplace_back( n.fanins.at( 0u ) ); - index_list.emplace_back( n.fanins.at( 1u ) ); - index_list.emplace_back( n.fanins.at( 2u ) ); - } - index_list.emplace_back( maj_nodes.back().id ); + index_list.add_output( current_lit ); return index_list; } - else if ( maj_nodes.size() < size_limit ) + else if ( current_lit + 2 < size_limit ) { - maj_nodes.emplace_back( maj_node{maj_nodes.back().id + 2u, {maj_nodes.back().id}} ); + //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 @@ -184,17 +167,14 @@ class mig_resub_engine_bottom_up } private: - uint64_t max_num_divisors; - uint64_t counter; 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; - std::vector scores; - std::vector maj_nodes; - + mig_index_list index_list; }; /* mig_resub_engine_bottom_up */ template diff --git a/test/algorithms/resub_engines.cpp b/test/algorithms/resub_engines.cpp index 6bce5e453..8837eb28c 100644 --- a/test/algorithms/resub_engines.cpp +++ b/test/algorithms/resub_engines.cpp @@ -6,6 +6,7 @@ #include #include +#include using namespace mockturtle; @@ -18,30 +19,29 @@ TEST_CASE( "MIG resub engine (bottom-up) -- 0-resub", "[resub_engines]" ) kitty::create_from_binary_string( tts[2], "10000001" ); kitty::create_from_binary_string( tts[3], "11001001" ); - mig_resub_engine_bottom_up engine( 3 ); - engine.add_root( 0, tts ); + mig_resub_engine_bottom_up 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).size() == 1u ); - CHECK( (*res)[0] == 7u ); + CHECK( (*res).num_gates() == 0u ); + CHECK( (*res).raw()[1] == 7u ); } TEST_CASE( "MIG resub engine (bottom-up) -- 1-resub", "[resub_engines]" ) { - std::vector tts( 4, kitty::dynamic_truth_table( 3 ) ); + std::vector tts( 3, kitty::partial_truth_table( 8 ) ); + kitty::partial_truth_table target( 8 ); - kitty::create_from_binary_string( tts[0], "01110110" ); - kitty::create_from_binary_string( tts[1], "11110100" ); - kitty::create_from_binary_string( tts[2], "11001001" ); - kitty::create_from_binary_string( tts[3], "01000111" ); + 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" ); - mig_resub_engine_bottom_up engine( tts.size() - 1u ); - engine.add_root( 0, tts ); - for ( auto i = 1u; i < tts.size(); ++i ) + mig_resub_engine_bottom_up engine( target ); + for ( auto i = 0u; i < tts.size(); ++i ) { engine.add_divisor( i, tts ); } @@ -49,34 +49,28 @@ TEST_CASE( "MIG resub engine (bottom-up) -- 1-resub", "[resub_engines]" ) const auto res = engine.compute_function( 1u ); CHECK( res ); - CHECK( (*res).size() == 4u ); - - /* when translating index list, 0 and 1 are for constants */ - auto target = tts[0]; - kitty::create_from_binary_string( tts[0], "00000000" ); - - auto f1 = (*res)[0] % 2 ? ~tts[(*res)[0] / 2] : tts[(*res)[0] / 2]; - auto f2 = (*res)[1] % 2 ? ~tts[(*res)[1] / 2] : tts[(*res)[1] / 2]; - auto f3 = (*res)[2] % 2 ? ~tts[(*res)[2] / 2] : tts[(*res)[2] / 2]; - tts.emplace_back( kitty::ternary_majority( f1, f2, f3 ) ); - - auto ans = (*res)[3] % 2 ? ~tts[(*res)[3] / 2] : tts[(*res)[3] / 2]; + 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 ); } TEST_CASE( "MIG resub engine (bottom-up) -- 2-resub", "[resub_engines]" ) { - std::vector tts( 5, kitty::dynamic_truth_table( 3 ) ); + std::vector tts( 4, kitty::partial_truth_table( 8 ) ); + kitty::partial_truth_table target( 8 ); - kitty::create_from_binary_string( tts[0], "00101110" ); - kitty::create_from_binary_string( tts[1], "11101111" ); - kitty::create_from_binary_string( tts[2], "00100000" ); - kitty::create_from_binary_string( tts[3], "10011110" ); - kitty::create_from_binary_string( tts[4], "01011111" ); + 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" ); - mig_resub_engine_bottom_up engine( tts.size() - 1u ); - engine.add_root( 0, tts ); - for ( auto i = 1u; i < tts.size(); ++i ) + mig_resub_engine_bottom_up engine( target ); + for ( auto i = 0u; i < tts.size(); ++i ) { engine.add_divisor( i, tts ); } @@ -84,22 +78,11 @@ TEST_CASE( "MIG resub engine (bottom-up) -- 2-resub", "[resub_engines]" ) const auto res = engine.compute_function( 2u ); CHECK( res ); - CHECK( (*res).size() == 7u ); - - /* when translating index list, 0 and 1 are for constants */ - auto target = tts[0]; - kitty::create_from_binary_string( tts[0], "00000000" ); - - auto f1 = (*res)[0] % 2 ? ~tts[(*res)[0] / 2] : tts[(*res)[0] / 2]; - auto f2 = (*res)[1] % 2 ? ~tts[(*res)[1] / 2] : tts[(*res)[1] / 2]; - auto f3 = (*res)[2] % 2 ? ~tts[(*res)[2] / 2] : tts[(*res)[2] / 2]; - tts.emplace_back( kitty::ternary_majority( f1, f2, f3 ) ); - - f1 = (*res)[3] % 2 ? ~tts[(*res)[3] / 2] : tts[(*res)[3] / 2]; - f2 = (*res)[4] % 2 ? ~tts[(*res)[4] / 2] : tts[(*res)[4] / 2]; - f3 = (*res)[5] % 2 ? ~tts[(*res)[5] / 2] : tts[(*res)[5] / 2]; - tts.emplace_back( kitty::ternary_majority( f1, f2, f3 ) ); - - auto ans = (*res)[6] % 2 ? ~tts[(*res)[6] / 2] : tts[(*res)[6] / 2]; + 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 ); } From 19dba9ba5b4c0c480ab0597ac772800aaa17acee Mon Sep 17 00:00:00 2001 From: Sonia Date: Thu, 31 Dec 2020 00:18:13 +0100 Subject: [PATCH 14/22] rename files --- include/mockturtle/algorithms/mig_resub.hpp | 2 +- .../algorithms/{resub_engines.hpp => mig_resyn_engines.hpp} | 4 ++-- test/algorithms/{resub_engines.cpp => mig_resyn_engines.cpp} | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) rename include/mockturtle/algorithms/{resub_engines.hpp => mig_resyn_engines.hpp} (99%) rename test/algorithms/{resub_engines.cpp => mig_resyn_engines.cpp} (96%) diff --git a/include/mockturtle/algorithms/mig_resub.hpp b/include/mockturtle/algorithms/mig_resub.hpp index 274c70be3..e72e5273c 100644 --- a/include/mockturtle/algorithms/mig_resub.hpp +++ b/include/mockturtle/algorithms/mig_resub.hpp @@ -34,7 +34,7 @@ #include #include -#include +#include #include namespace kitty diff --git a/include/mockturtle/algorithms/resub_engines.hpp b/include/mockturtle/algorithms/mig_resyn_engines.hpp similarity index 99% rename from include/mockturtle/algorithms/resub_engines.hpp rename to include/mockturtle/algorithms/mig_resyn_engines.hpp index 8563fa22f..7c61b1c46 100644 --- a/include/mockturtle/algorithms/resub_engines.hpp +++ b/include/mockturtle/algorithms/mig_resyn_engines.hpp @@ -24,8 +24,8 @@ */ /*! - \file resub_engines.hpp - \brief Implements generalized resubstitution engine(s). + \file mig_resyn_engines.hpp + \brief Implements resynthesis methods for MIGs. \author Siang-Yun Lee */ diff --git a/test/algorithms/resub_engines.cpp b/test/algorithms/mig_resyn_engines.cpp similarity index 96% rename from test/algorithms/resub_engines.cpp rename to test/algorithms/mig_resyn_engines.cpp index 8837eb28c..e49b4ae38 100644 --- a/test/algorithms/resub_engines.cpp +++ b/test/algorithms/mig_resyn_engines.cpp @@ -2,10 +2,11 @@ #include #include +#include #include #include -#include +#include #include using namespace mockturtle; From 6b6f0aac1a1a7155928fd2fce098de0db50d5b4b Mon Sep 17 00:00:00 2001 From: Sonia Date: Thu, 31 Dec 2020 00:21:27 +0100 Subject: [PATCH 15/22] Delete mig_resub_engine.cpp --- experiments/mig_resub_engine.cpp | 125 ------------------------------- 1 file changed, 125 deletions(-) delete mode 100644 experiments/mig_resub_engine.cpp diff --git a/experiments/mig_resub_engine.cpp b/experiments/mig_resub_engine.cpp deleted file mode 100644 index 142077c61..000000000 --- a/experiments/mig_resub_engine.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* 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. - */ - -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -//#include - -int main() -{ - //using namespace experiments; - using namespace mockturtle; - auto n = 3u; - - std::vector tts( n + 1, kitty::partial_truth_table( 1 << n ) ); - - for ( auto i = 0u; i < n; ++i ) - { - kitty::create_nth_var( tts[i+1], i ); - } - -#if 0 - kitty::create_from_hex_string( tts[0], "0001" ); // target - - mig_resub_engine engine( tts[0] ); - for ( auto i = 0u; i < n; ++i ) - { - engine.add_divisor( i+1, tts ); - } - const auto res = engine.compute_function( 10u ); - if ( !res ) - { - std::cout << "did not find solution within 15 nodes.\n"; - } - else - { - std::cout << "found solution of size " << (*res).num_gates() << "\n"; - } - -#else - std::unordered_set> classes; - kitty::dynamic_truth_table tt( n ); - - do - { - /* apply NPN canonization and add resulting representative to set */ - const auto res = kitty::exact_npn_canonization( tt ); - classes.insert( std::get<0>( res ) ); - - /* increment truth table */ - kitty::next_inplace( tt ); - } while ( !kitty::is_const0( tt ) ); - - uint64_t total_size = 0u, failed = 0u; - for ( auto const& func : classes ) - { - tts[0] = func; // assign dynamic_truth_table to partial_truth_table - std::cout << "function: "; kitty::print_hex( tts[0] ); - mig_resub_engine engine( tts[0] ); - for ( auto i = 0u; i < n; ++i ) - { - engine.add_divisor( i+1, tts ); - } - const auto res = engine.compute_function( 15u ); - if ( !res ) - { - std::cout << " did not find solution within 15 nodes.\n"; - ++failed; - } - else - { - std::cout << " found solution of size " << (*res).num_gates() << "\n"; - total_size += (*res).num_gates(); - mig_network sol; - decode( sol, *res ); - default_simulator sim( n ); - const auto sol_tt = simulate( sol, sim )[0]; - if ( sol_tt != func ) - { - std::cout << "[error] generated network is not equivalent!\n"; - break; - } - } - } - - std::cout << "total size: " << total_size << "\n"; - std::cout << "failed: " << failed << "\n"; -#endif - return 0; -} From ee8a1912bc60e687000743eed951f3bbcb384cc7 Mon Sep 17 00:00:00 2001 From: Sonia Date: Thu, 31 Dec 2020 00:33:53 +0100 Subject: [PATCH 16/22] rename & add tests --- include/mockturtle/algorithms/mig_resub.hpp | 14 +------ .../algorithms/mig_resyn_engines.hpp | 16 ++++---- test/algorithms/mig_resyn_engines.cpp | 38 +++++++++++++++---- 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/include/mockturtle/algorithms/mig_resub.hpp b/include/mockturtle/algorithms/mig_resub.hpp index e72e5273c..6c57e0130 100644 --- a/include/mockturtle/algorithms/mig_resub.hpp +++ b/include/mockturtle/algorithms/mig_resub.hpp @@ -233,18 +233,6 @@ struct mig_resub_functor } ); if ( g ) { - /*mig_resub_engine engine( sim.get_tt( sim.get_phase( root ) ? !ntk.make_signal( root ) : ntk.make_signal( root ) ) ); - unordered_node_map tts( ntk ); - for ( auto const& d : divs ) - { - tts[d] = sim.get_tt( sim.get_phase( d ) ? !ntk.make_signal( d ) : ntk.make_signal( d ) ); - } - engine.add_divisors( divs.begin(), divs.end(), tts ); - if ( !engine.compute_function( 1 ) ) - { - std::cout << "engine didn't find 1-resub for root "<> +template> struct mig_resub_functor_new { public: diff --git a/include/mockturtle/algorithms/mig_resyn_engines.hpp b/include/mockturtle/algorithms/mig_resyn_engines.hpp index 7c61b1c46..c71470904 100644 --- a/include/mockturtle/algorithms/mig_resyn_engines.hpp +++ b/include/mockturtle/algorithms/mig_resyn_engines.hpp @@ -43,10 +43,10 @@ namespace mockturtle { template -class mig_resub_engine_bottom_up +class mig_resyn_engine_bottom_up { public: - explicit mig_resub_engine_bottom_up( TT const& target ) + explicit mig_resyn_engine_bottom_up( TT const& target ) : num_bits( target.num_bits() ), divisors( { ~target, target } ) { } @@ -175,10 +175,10 @@ class mig_resub_engine_bottom_up std::vector divisors; mig_index_list index_list; -}; /* mig_resub_engine_bottom_up */ +}; /* mig_resyn_engine_bottom_up */ template -class mig_resub_engine +class mig_resyn_engine { bool p = 0; // verbose printing /*! \brief Internal data structure */ @@ -210,7 +210,7 @@ class mig_resub_engine }; public: - explicit mig_resub_engine( TT const& target ) + explicit mig_resyn_engine( TT const& target ) : num_bits( target.num_bits() ), divisors( { ~target, target } ) { } @@ -873,14 +873,14 @@ class mig_resub_engine std::vector leaves, improve_in_parent, shuffle; bool first_round = true; -}; /* mig_resub_engine */ +}; /* mig_resyn_engine */ -class mig_resub_engine_akers +class mig_resyn_engine_akers { bool p = 0; // verbose printing public: - explicit mig_resub_engine_akers( kitty::partial_truth_table const& target ) + explicit mig_resyn_engine_akers( kitty::partial_truth_table const& target ) : divisors( { ~target, target } ), id_to_lit( { 0, 1 } ) /* const0, const1 */ { } diff --git a/test/algorithms/mig_resyn_engines.cpp b/test/algorithms/mig_resyn_engines.cpp index e49b4ae38..a50ed31c9 100644 --- a/test/algorithms/mig_resyn_engines.cpp +++ b/test/algorithms/mig_resyn_engines.cpp @@ -11,16 +11,17 @@ using namespace mockturtle; -TEST_CASE( "MIG resub engine (bottom-up) -- 0-resub", "[resub_engines]" ) +template +void test_0resub() { - std::vector tts( 4, kitty::dynamic_truth_table( 3 ) ); + 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" ); - mig_resub_engine_bottom_up engine( tts[0] ); + Engine engine( tts[0] ); engine.add_divisor( 1, tts ); engine.add_divisor( 2, tts ); engine.add_divisor( 3, tts ); @@ -31,7 +32,8 @@ TEST_CASE( "MIG resub engine (bottom-up) -- 0-resub", "[resub_engines]" ) CHECK( (*res).raw()[1] == 7u ); } -TEST_CASE( "MIG resub engine (bottom-up) -- 1-resub", "[resub_engines]" ) +template +void test_1resub() { std::vector tts( 3, kitty::partial_truth_table( 8 ) ); kitty::partial_truth_table target( 8 ); @@ -41,7 +43,7 @@ TEST_CASE( "MIG resub engine (bottom-up) -- 1-resub", "[resub_engines]" ) kitty::create_from_binary_string( tts[1], "11001001" ); kitty::create_from_binary_string( tts[2], "01000111" ); - mig_resub_engine_bottom_up engine( target ); + Engine engine( target ); for ( auto i = 0u; i < tts.size(); ++i ) { engine.add_divisor( i, tts ); @@ -59,7 +61,8 @@ TEST_CASE( "MIG resub engine (bottom-up) -- 1-resub", "[resub_engines]" ) CHECK( target == ans ); } -TEST_CASE( "MIG resub engine (bottom-up) -- 2-resub", "[resub_engines]" ) +template +void test_2resub() { std::vector tts( 4, kitty::partial_truth_table( 8 ) ); kitty::partial_truth_table target( 8 ); @@ -70,7 +73,7 @@ TEST_CASE( "MIG resub engine (bottom-up) -- 2-resub", "[resub_engines]" ) kitty::create_from_binary_string( tts[2], "10011110" ); kitty::create_from_binary_string( tts[3], "01011111" ); - mig_resub_engine_bottom_up engine( target ); + Engine engine( target ); for ( auto i = 0u; i < tts.size(); ++i ) { engine.add_divisor( i, tts ); @@ -87,3 +90,24 @@ TEST_CASE( "MIG resub engine (bottom-up) -- 2-resub", "[resub_engines]" ) 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(); +} From ce9ded299f7c870d3b05bc20d1473d2d2b835a68 Mon Sep 17 00:00:00 2001 From: Sonia Date: Thu, 31 Dec 2020 00:39:48 +0100 Subject: [PATCH 17/22] cleanup --- .../algorithms/mig_resyn_engines.hpp | 49 +------------------ 1 file changed, 2 insertions(+), 47 deletions(-) diff --git a/include/mockturtle/algorithms/mig_resyn_engines.hpp b/include/mockturtle/algorithms/mig_resyn_engines.hpp index c71470904..a6baf445e 100644 --- a/include/mockturtle/algorithms/mig_resyn_engines.hpp +++ b/include/mockturtle/algorithms/mig_resyn_engines.hpp @@ -180,7 +180,6 @@ class mig_resyn_engine_bottom_up template class mig_resyn_engine { - bool p = 0; // verbose printing /*! \brief Internal data structure */ struct expansion_position { @@ -237,7 +236,6 @@ class mig_resyn_engine { for ( auto i = 0u; i < divisors.size(); ++i ) { - if (p) { std::cout<<"["< top_node_choices = construct_top(); if ( top_node_choices.size() == 1u && kitty::is_const0( ~top_node_choices[0].function ) ) @@ -283,7 +280,6 @@ class mig_resyn_engine index_list.add_output( divisors.size() ); return index_list; } - if (p) { std::cout<<"<"< = "; kitty::print_binary(top_node.function); std::cout<<"\n"; } } if ( size_limit == 1u ) { @@ -293,10 +289,8 @@ class mig_resyn_engine std::vector maj_nodes_best; for ( simple_maj const& top_node : top_node_choices ) { - if (p) { std::cout<<"===== try with top node <"< =====\n"; } for ( int32_t i = 0; i < 3; ++i ) { - if (p) { std::cout<<"=== try expand "< kitty::count_ones( original_function & parent_node.care ) ) { - if (p) { std::cout<<"...but covers more care bits of parent node.\n"; } if ( first_round ) { /* We put it into a back-up queue for now */ @@ -444,7 +430,6 @@ class mig_resyn_engine } 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 (p) { std::cout<<"...and also covers the same # of care bits of parent node, but the function is different.\n"; } if ( first_round ) { /* We put it into a back-up queue for now */ @@ -470,25 +455,8 @@ class mig_resyn_engine if ( kitty::is_const0( ~new_node.function & care ) ) { - if (p) { std::cout<<"care bits all fulfilled.\n"; } if ( node_fulfilled( maj_nodes.at( 0u ) ) ) { - if (p) { - std::cout<<"\n======== solution found =========\n"; - for ( auto i = 0u; i < maj_nodes.size(); ++i ) - { - std::cout<<"[node "< = "; kitty::print_binary(kitty::ternary_majority( divisors.at( max_i ), divisors.at( max_j ), divisors.at( max_k ) )); std::cout<<"\n"; } 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() { - if (p) { std::cout << "constructing topmost node\n"; } std::vector res; /* the first fanin: cover most bits */ @@ -841,11 +804,6 @@ class mig_resyn_engine return ( my_index + sibling_num ) % 3; } - //inline uint32_t sibling_id( maj_node const& parent_node, uint32_t const my_index, uint32_t const sibling_num ) - //{ - // return parent_node.fanins.at( sibling_index( my_index, sibling_num ) ); - //} - 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 ) ); @@ -877,8 +835,6 @@ class mig_resyn_engine class mig_resyn_engine_akers { - bool p = 0; // verbose printing - public: explicit mig_resyn_engine_akers( kitty::partial_truth_table const& target ) : divisors( { ~target, target } ), id_to_lit( { 0, 1 } ) @@ -924,7 +880,6 @@ class mig_resyn_engine_akers } reduce(); - if (p) print_table(); while ( divisors.size() > 1 ) { From 549bf31607228fe86426b0359ec4606dcac0e572 Mon Sep 17 00:00:00 2001 From: Sonia Date: Thu, 31 Dec 2020 00:39:58 +0100 Subject: [PATCH 18/22] typo in index_list --- include/mockturtle/utils/index_list.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 { From 6bdafa64e3525e204dd62cc79ee630c086e7c54b Mon Sep 17 00:00:00 2001 From: Sonia Date: Thu, 31 Dec 2020 16:42:31 +0100 Subject: [PATCH 19/22] try to see if the problem comes from the new tests --- test/algorithms/mig_resyn_engines.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/algorithms/mig_resyn_engines.cpp b/test/algorithms/mig_resyn_engines.cpp index a50ed31c9..dc8e93f68 100644 --- a/test/algorithms/mig_resyn_engines.cpp +++ b/test/algorithms/mig_resyn_engines.cpp @@ -30,6 +30,7 @@ void test_0resub() CHECK( res ); CHECK( (*res).num_gates() == 0u ); CHECK( (*res).raw()[1] == 7u ); + std::cout<<"test_0resub finished\n"; } template @@ -59,6 +60,7 @@ void test_1resub() partial_simulator sim( tts ); const auto ans = simulate( mig, sim )[0]; CHECK( target == ans ); + std::cout<<"test_1resub finished\n"; } template @@ -89,6 +91,7 @@ void test_2resub() partial_simulator sim( tts ); const auto ans = simulate( mig, sim )[0]; CHECK( target == ans ); + std::cout<<"test_2resub finished\n"; } TEST_CASE( "MIG resynthesis engines -- 0-resub", "[mig_resyn]" ) From a9a41fb3399ee70e2d5c334dbfbb5ac1626efd9f Mon Sep 17 00:00:00 2001 From: Sonia Date: Thu, 31 Dec 2020 17:08:23 +0100 Subject: [PATCH 20/22] debug --- include/mockturtle/algorithms/mig_resyn_engines.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/mockturtle/algorithms/mig_resyn_engines.hpp b/include/mockturtle/algorithms/mig_resyn_engines.hpp index a6baf445e..ab9587a12 100644 --- a/include/mockturtle/algorithms/mig_resyn_engines.hpp +++ b/include/mockturtle/algorithms/mig_resyn_engines.hpp @@ -899,6 +899,7 @@ class mig_resyn_engine_akers private: void reduce() { + std::cout << "calling reduce with " << divisors.size() << " columns and " << divisors[0].num_bits() << " bits\n"; 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() ) From d2fae0fb64db558bcbfe7795690d84279d3c2d33 Mon Sep 17 00:00:00 2001 From: Sonia Date: Thu, 31 Dec 2020 17:27:01 +0100 Subject: [PATCH 21/22] debug --- include/mockturtle/algorithms/mig_resyn_engines.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/mockturtle/algorithms/mig_resyn_engines.hpp b/include/mockturtle/algorithms/mig_resyn_engines.hpp index ab9587a12..ce6d2d46f 100644 --- a/include/mockturtle/algorithms/mig_resyn_engines.hpp +++ b/include/mockturtle/algorithms/mig_resyn_engines.hpp @@ -907,7 +907,9 @@ class mig_resyn_engine_akers num_bits_before = divisors[0].num_bits(); num_divs_before = divisors.size(); eliminate_divs(); /* reduce column */ + std::cout << "...eliminates to " << divisors.size() << " columns\n"; eliminate_bits(); /* reduce row */ + std::cout << "...eliminates to " << divisors[0].num_bits() << " bits\n"; } } From 1b47a63bb0d22015a568f2639b0d2820b07d9037 Mon Sep 17 00:00:00 2001 From: Sonia Date: Thu, 31 Dec 2020 18:05:44 +0100 Subject: [PATCH 22/22] debug & cleanup --- .../mockturtle/algorithms/mig_resyn_engines.hpp | 15 +++++++++++---- test/algorithms/mig_resyn_engines.cpp | 3 --- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/include/mockturtle/algorithms/mig_resyn_engines.hpp b/include/mockturtle/algorithms/mig_resyn_engines.hpp index ce6d2d46f..5812a4717 100644 --- a/include/mockturtle/algorithms/mig_resyn_engines.hpp +++ b/include/mockturtle/algorithms/mig_resyn_engines.hpp @@ -889,17 +889,20 @@ class mig_resyn_engine_akers } find_gate(); add_gate(); + if ( kitty::is_const0( ~divisors.back() ) ) + { + break; + } reduce(); } - index_list.add_output( id_to_lit[0] ); + index_list.add_output( id_to_lit.back() ); return index_list; } private: void reduce() { - std::cout << "calling reduce with " << divisors.size() << " columns and " << divisors[0].num_bits() << " bits\n"; 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() ) @@ -907,9 +910,7 @@ class mig_resyn_engine_akers num_bits_before = divisors[0].num_bits(); num_divs_before = divisors.size(); eliminate_divs(); /* reduce column */ - std::cout << "...eliminates to " << divisors.size() << " columns\n"; eliminate_bits(); /* reduce row */ - std::cout << "...eliminates to " << divisors[0].num_bits() << " bits\n"; } } @@ -1118,6 +1119,12 @@ class mig_resyn_engine_akers /* 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 ) { diff --git a/test/algorithms/mig_resyn_engines.cpp b/test/algorithms/mig_resyn_engines.cpp index dc8e93f68..a50ed31c9 100644 --- a/test/algorithms/mig_resyn_engines.cpp +++ b/test/algorithms/mig_resyn_engines.cpp @@ -30,7 +30,6 @@ void test_0resub() CHECK( res ); CHECK( (*res).num_gates() == 0u ); CHECK( (*res).raw()[1] == 7u ); - std::cout<<"test_0resub finished\n"; } template @@ -60,7 +59,6 @@ void test_1resub() partial_simulator sim( tts ); const auto ans = simulate( mig, sim )[0]; CHECK( target == ans ); - std::cout<<"test_1resub finished\n"; } template @@ -91,7 +89,6 @@ void test_2resub() partial_simulator sim( tts ); const auto ans = simulate( mig, sim )[0]; CHECK( target == ans ); - std::cout<<"test_2resub finished\n"; } TEST_CASE( "MIG resynthesis engines -- 0-resub", "[mig_resyn]" )