From 2130e72e6e57e67c89c74cafb2c711a7a28abebe Mon Sep 17 00:00:00 2001 From: Bugra Eryilmaz <70061581+BugraEryilmaz@users.noreply.github.com> Date: Tue, 6 Jun 2023 14:14:15 +0200 Subject: [PATCH] Inverter optimization and propagation (#610) * optimization initial * inv propagation * bug fix --- .../algorithms/mig_inv_optimization.hpp | 355 ++++++++++++++++++ .../algorithms/mig_inv_propagation.hpp | 293 +++++++++++++++ include/mockturtle/networks/mig.hpp | 6 +- test/algorithms/mig_inv_optimization.cpp | 207 ++++++++++ test/algorithms/mig_inv_propagation.cpp | 187 +++++++++ 5 files changed, 1047 insertions(+), 1 deletion(-) create mode 100644 include/mockturtle/algorithms/mig_inv_optimization.hpp create mode 100644 include/mockturtle/algorithms/mig_inv_propagation.hpp create mode 100644 test/algorithms/mig_inv_optimization.cpp create mode 100644 test/algorithms/mig_inv_propagation.cpp diff --git a/include/mockturtle/algorithms/mig_inv_optimization.hpp b/include/mockturtle/algorithms/mig_inv_optimization.hpp new file mode 100644 index 000000000..f5b1bda2b --- /dev/null +++ b/include/mockturtle/algorithms/mig_inv_optimization.hpp @@ -0,0 +1,355 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mig_inv_optimization.hpp + \brief MIG inverter optimization + + \author Bugra Eryilmaz +*/ + +#pragma once + +#include "../networks/storage.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/fanout_view.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Statistics for mig_inv_optimization. */ +struct mig_inv_optimization_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Number of one level inverted nodes. */ + int num_inverted{ 0 }; + + /*! \brief Number of two level inverted nodes. */ + int num_two_level_inverted{ 0 }; + + /*! \brief Total gain in terms of number of inverters. */ + int total_gain{ 0 }; +}; + +namespace detail +{ + +template +class mig_inv_optimization_impl +{ +public: + mig_inv_optimization_impl( Ntk& ntk, mig_inv_optimization_stats& st ) + : ntk( ntk ), st( st ) + { + } + + void run() + { + stopwatch t( st.time_total ); + + minimize(); + } + +private: + /*! \brief implements the inverter minimization algorithm */ + void minimize() + { + bool changed = true; + while ( changed ) + { + changed = false; + ntk.foreach_gate( [&]( auto const& f ) { + int _gain = gain( f ); + if ( _gain > 0 ) + { + st.num_inverted++; + st.total_gain += _gain; + changed = true; + invert_node( f ); + } + else if ( two_level_gain( f ) > 0 ) + { + st.num_two_level_inverted++; + st.total_gain += _gain; + changed = true; + std::vector> _nodes_to_invert; + ntk.foreach_fanout( f, [&]( auto const& parent ) { + // convert each fanout if inverting it makes sense + int _subgain = 0; + _subgain += gain( parent ); + if ( is_complemented_parent( parent, f ) ) + // if the connection between f and parent is complemented, we counted the same gain twice which will not be inverted at all + _subgain -= 2; + else + // if the connection between f and parent is not complemented, we counted the same negative gain twice which will not be inverted at all + _subgain += 2; + if ( _subgain > 0 ) + { + st.total_gain += _subgain; + _nodes_to_invert.push_back( parent ); + } + } ); + invert_node( f ); + for ( auto const& n : _nodes_to_invert ) + { + invert_node( n ); + } + } + } ); + } + } + + /*! \brief calculates the decrease in the number of inverters if this node is inverted and all fanouts that is beneficial also inverted */ + int two_level_gain( node n ) + { + int _gain = 0; + _gain += gain( n ); + + ntk.foreach_fanout( n, [&]( auto const& f ) { + int _subgain = 0; + _subgain += gain( f ); + if ( is_complemented_parent( f, n ) ) + // if the connection between f and parent is complemented, we counted the same gain twice which will not be inverted at all + _subgain -= 2; + else + // if the connection between f and parent is not complemented, we counted the same negative gain twice which will not be inverted at all + _subgain += 2; + + // convert each fanout if inverting it makes sense + if ( _subgain > 0 ) + { + _gain += _subgain; + } + } ); + + return _gain; + } + + /*! \brief calculates the decrease in the number of inverters if this node is inverted */ + int gain( node n ) + { + if ( ntk.is_dead( n ) ) + { + std::cerr << "node" << n << " is dead\n"; + return 0; + } + int _gain = 0; + + // count the inverted and non-inverted fanins + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.is_constant( ntk.get_node( f ) ) ) + return; + update_gain_is_complemented( f, _gain ); + } ); + + // count the inverted and non-inverted fanouts + ntk.foreach_fanout( n, [&]( auto const& parent ) { + if ( is_complemented_parent( parent, n ) ) + _gain++; + else + _gain--; + } ); + + // count the inverted and non-inverted POs + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.get_node( f ) == n ) + update_gain_is_complemented( f, _gain ); + } ); + return _gain; + } + + /*! \brief increases the gain if signal f is complemented, decreases otherwise. */ + void update_gain_is_complemented( signal f, int& _gain ) + { + if ( ntk.is_complemented( f ) ) + _gain++; + else + _gain--; + } + + /*! \brief checks if parent is parent of child and returns if the connection is complemented. */ + bool is_complemented_parent( node parent, node child ) + { + bool ret = false; + bool changed = false; + ntk.foreach_fanin( parent, [&]( auto const& f ) { + if ( ntk.get_node( f ) == child ) + { + changed = true; + ret = ntk.is_complemented( f ); + } + } ); + if ( !changed ) + { + std::cerr << "parent " << parent << " is not parent of child " << child << "\n"; + } + return ret; + } + + /*! \brief inverts the inputs and changes all occurances of the node with the !inverted_node. */ + void invert_node( node n ) + { + signal a, b, c; + ntk.foreach_fanin( n, [&]( auto const& f, auto idx ) { + if ( idx == 0 ) + a = f; + else if ( idx == 1 ) + b = f; + else if ( idx == 2 ) + c = f; + } ); + signal new_node = !create_maj_directly( !a, !b, !c ); + ntk.substitute_node( n, new_node ); + ntk.replace_in_outputs( n, new_node ); + } + + /*! \brief original create_maj function was inverting the node + if more than 2 of the inputs were inverted which is + not suitable for the algorithm, so I removed that part. */ + signal create_maj_directly( signal a, signal b, signal c ) + { + /* order inputs */ + if ( a.index > b.index ) + { + std::swap( a, b ); + if ( b.index > c.index ) + std::swap( b, c ); + if ( a.index > b.index ) + std::swap( a, b ); + } + else + { + if ( b.index > c.index ) + std::swap( b, c ); + if ( a.index > b.index ) + std::swap( a, b ); + } + + /* trivial cases */ + if ( a.index == b.index ) + { + return ( a.complement == b.complement ) ? a : c; + } + else if ( b.index == c.index ) + { + return ( b.complement == c.complement ) ? b : a; + } + + std::shared_ptr>>::element_type::node_type nd; + nd.children[0] = a; + nd.children[1] = b; + nd.children[2] = c; + + /* structural hashing */ + const auto it = ntk._storage->hash.find( nd ); + if ( it != ntk._storage->hash.end() ) + { + return { it->second, 0 }; + } + + const auto index = ntk._storage->nodes.size(); + + if ( index >= .9 * ntk._storage->nodes.capacity() ) + { + ntk._storage->nodes.reserve( static_cast( 3.1415f * index ) ); + ntk._storage->hash.reserve( static_cast( 3.1415f * index ) ); + } + + ntk._storage->nodes.push_back( nd ); + + ntk._storage->hash[nd] = index; + + /* increase ref-count to children */ + ntk._storage->nodes[a.index].data[0].h1++; + ntk._storage->nodes[b.index].data[0].h1++; + ntk._storage->nodes[c.index].data[0].h1++; + + for ( auto const& fn : ntk._events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + +private: + Ntk& ntk; + mig_inv_optimization_stats& st; +}; + +} // namespace detail + +/*! \brief MIG inverter optimization. + * + * This algorithm tries to reduce the number + * of inverters in a MIG network without + * increasing the node number. It checks each + * node for 1 level and 2 level optimization + * opportunuties and inverts the node if it + * decreases the number of inverted connections. + * It does not count constant values as inverted + * even if they are complemented in the graph. + * + * **Required network functions:** + * 'foreach_fanin' + * 'foreach_fanout' + * 'substitute_node' + * 'replace_in_outputs' + * 'is_complemented' + * 'get_node' + * 'is_dead' + * 'is_constant' + * 'foreach_gate' + */ +template +void mig_inv_optimization( Ntk& ntk, mig_inv_optimization_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_foreach_fanout_v, "Ntk does not implement the foreach_fanout method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_replace_in_outputs_v, "Ntk does not implement the replace_in_outputs method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_is_dead_v, "Ntk does not implement the is_dead method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_foreach_gate_v, "Ntk does not implement the foreach_gate method" ); + + mig_inv_optimization_stats st; + detail::mig_inv_optimization_impl p( ntk, st ); + p.run(); + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/algorithms/mig_inv_propagation.hpp b/include/mockturtle/algorithms/mig_inv_propagation.hpp new file mode 100644 index 000000000..10c97bef9 --- /dev/null +++ b/include/mockturtle/algorithms/mig_inv_propagation.hpp @@ -0,0 +1,293 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2022 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file mig_inv_propagation.hpp + \brief MIG inverter optimization + + \author Bugra Eryilmaz +*/ + +#pragma once + +#include "../networks/storage.hpp" +#include "../utils/stopwatch.hpp" +#include "../views/fanout_view.hpp" + +#include +#include + +namespace mockturtle +{ + +/*! \brief Statistics for mig_inv_propagation. */ +struct mig_inv_propagation_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{ 0 }; + + /*! \brief Increase in the node count. */ + int node_incraese{ 0 }; + + /*! \brief Total gain in terms of number of inverters. */ + int total_gain{ 0 }; +}; + +namespace detail +{ + +template +class mig_inv_propagation_impl +{ +public: + mig_inv_propagation_impl( Ntk& ntk, mig_inv_propagation_stats& st ) + : ntk( ntk ), st( st ) + { + } + + void run() + { + stopwatch t( st.time_total ); + + propagate(); + } + +private: + /*! \brief implements the inverter propagation algorithm */ + void propagate() + { + // starting from primary outputs, propagate the inversions + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + // if it is complemented, invert the node + auto old_node = ntk.get_node( f ); + auto new_node = invert_node( old_node ); + + // replace the po with the inverted node + ntk.replace_in_outputs( old_node, new_node ); + + // check if the old node should stay alive + if ( ntk.fanout_size( old_node ) == 0 ) + { + ntk.take_out_node( old_node ); + } + + // propagate the inversions to the inputs + propagate_helper( ntk.get_node( new_node ) ); + } + else + { + // propagate the inversions to the inputs + propagate_helper( ntk.get_node( f ) ); + } + } ); + } + + void propagate_helper( node n ) + { + std::vector> complement_list; + // for each fanin, check if it is complemented + ntk.foreach_fanin( n, [&]( auto const& f, auto idx ) { + // skip if it is a constant, PI + if ( ntk.is_constant( ntk.get_node( f ) ) || ntk.is_pi( ntk.get_node( f ) ) ) + return; + // there should not be a dead child + if ( ntk.is_dead( ntk.get_node( f ) ) ) + std::cerr << "node " << ntk.get_node( f ) << " is dead\n"; + + // lazy substitute node since child order is fixed and + // changing one child will mix the order and create a bug + // in the foreach_fanin loop + if ( ntk.is_complemented( f ) ) + { + complement_list.push_back( ntk.get_node( f ) ); + } + else + { + // propagate the inversions to the inputs + propagate_helper( ntk.get_node( f ) ); + } + } ); + + // lazy invert the complemented fanins + for ( auto const& f : complement_list ) + { + // for each complemented fanin, invert the node + auto new_node = invert_node( f ); + // replace the fanin with the inverted node + if ( auto simplification = ntk.replace_in_node( n, f, new_node ) ) + ntk.substitute_node( simplification->first, simplification->second ); + + // check if the old node should stay alive + if ( ntk.fanout_size( f ) == 0 ) + { + ntk.take_out_node( f ); + } + + // propagate the inversions to the inputs + propagate_helper( ntk.get_node( new_node ) ); + } + } + + /*! \brief gets maj(a,b,c) returns !maj(!a,!b,!c). */ + signal invert_node( node n ) + { + signal a, b, c; + ntk.foreach_fanin( n, [&]( auto const& f, auto idx ) { + if ( idx == 0 ) + a = f; + else if ( idx == 1 ) + b = f; + else if ( idx == 2 ) + c = f; + } ); + signal new_node = !create_maj_directly( !a, !b, !c ); + return new_node; + } + + /*! \brief original create_maj function was inverting the node + if more than 2 of the inputs were inverted which is + not suitable for the algorithm, so I removed that part. */ + signal create_maj_directly( signal a, signal b, signal c ) + { + /* order inputs */ + if ( a.index > b.index ) + { + std::swap( a, b ); + if ( b.index > c.index ) + std::swap( b, c ); + if ( a.index > b.index ) + std::swap( a, b ); + } + else + { + if ( b.index > c.index ) + std::swap( b, c ); + if ( a.index > b.index ) + std::swap( a, b ); + } + + /* trivial cases */ + if ( a.index == b.index ) + { + return ( a.complement == b.complement ) ? a : c; + } + else if ( b.index == c.index ) + { + return ( b.complement == c.complement ) ? b : a; + } + + std::shared_ptr>>::element_type::node_type nd; + nd.children[0] = a; + nd.children[1] = b; + nd.children[2] = c; + + /* structural hashing */ + const auto it = ntk._storage->hash.find( nd ); + if ( it != ntk._storage->hash.end() ) + { + return { it->second, 0 }; + } + + const auto index = ntk._storage->nodes.size(); + + if ( index >= .9 * ntk._storage->nodes.capacity() ) + { + ntk._storage->nodes.reserve( static_cast( 3.1415f * index ) ); + ntk._storage->hash.reserve( static_cast( 3.1415f * index ) ); + } + + ntk._storage->nodes.push_back( nd ); + + ntk._storage->hash[nd] = index; + + /* increase ref-count to children */ + ntk._storage->nodes[a.index].data[0].h1++; + ntk._storage->nodes[b.index].data[0].h1++; + ntk._storage->nodes[c.index].data[0].h1++; + + for ( auto const& fn : ntk._events->on_add ) + { + ( *fn )( index ); + } + + return { index, 0 }; + } + +private: + Ntk& ntk; + mig_inv_propagation_stats& st; +}; + +} // namespace detail + +/*! \brief MIG inverter propagation. + * + * This algorithm tries to push all + * the inverters to the inputs. + * However, it can increase the number + * of nodes while doing so. + * + * **Required network functions:** + * get_node + * substitute_node + * take_out_node + * foreach_fanin + * replace_in_node + * fanout_size + * is_complemented + * is_dead + * is_constant + * is_pi + * foreach_po + */ +template +void mig_inv_propagation( Ntk& ntk, mig_inv_propagation_stats* pst = nullptr ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node method" ); + static_assert( has_substitute_node_v, "Ntk does not implement the substitute_node method" ); + static_assert( has_take_out_node_v, "Ntk does not implement the take_out_node method" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin method" ); + static_assert( has_replace_in_node_v, "Ntk does not implement the replace_in_node method" ); + static_assert( has_fanout_size_v, "Ntk does not implement the fanout_size method" ); + static_assert( has_is_complemented_v, "Ntk does not implement the is_complemented method" ); + static_assert( has_is_dead_v, "Ntk does not implement the is_dead method" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant method" ); + static_assert( has_is_pi_v, "Ntk does not implement the is_pi method" ); + static_assert( has_foreach_po_v, "Ntk does not implement the foreach_po method" ); + + mig_inv_propagation_stats st; + detail::mig_inv_propagation_impl p( ntk, st ); + p.run(); + + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */ \ No newline at end of file diff --git a/include/mockturtle/networks/mig.hpp b/include/mockturtle/networks/mig.hpp index 0b7a92347..216f9ebb4 100644 --- a/include/mockturtle/networks/mig.hpp +++ b/include/mockturtle/networks/mig.hpp @@ -548,6 +548,8 @@ class mig_network // update the reference counter of the new signal _storage->nodes[new_signal.index].data[0].h1++; + // update the reference counter of the old signal + _storage->nodes[old_node].data[0].h1--; for ( auto const& fn : _events->on_modified ) { @@ -571,8 +573,10 @@ class mig_network if ( old_node != new_signal.index ) { - // increment fan-in of new node + // increment fan-out of new node _storage->nodes[new_signal.index].data[0].h1++; + // decrement fan-out of old node + _storage->nodes[old_node].data[0].h1--; } } } diff --git a/test/algorithms/mig_inv_optimization.cpp b/test/algorithms/mig_inv_optimization.cpp new file mode 100644 index 000000000..0c8793b42 --- /dev/null +++ b/test/algorithms/mig_inv_optimization.cpp @@ -0,0 +1,207 @@ +#include + +#include +#include +#include +#include + +using namespace mockturtle; + +namespace mockturtle +{ +namespace inv_opt_test +{ +template +int number_of_inverted( Ntk const& ntk ) +{ + int num_inverted{ 0 }; + ntk.foreach_gate( [&]( auto const& n ) { + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.is_constant( ntk.get_node( f ) ) ) + { + return; + } + if ( ntk.is_complemented( f ) ) + { + num_inverted++; + } + } ); + } ); + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + num_inverted++; + } + } ); + return num_inverted; +} +} // namespace inv_opt_test +} // namespace mockturtle + +TEST_CASE( "MIG inverter optimization basic", "[mig_inv_optimization]" ) +{ + mig_network mig; + mig_inv_optimization_stats st; + + const auto a = mig.create_pi(); + const auto b = mig.create_pi(); + const auto c = mig.create_pi(); + const auto d = mig.create_pi(); + + const auto f1 = mig.create_maj( !a, b, c ); + const auto f2 = mig.create_maj( !a, b, d ); + const auto f3 = mig.create_maj( a, !f1, f2 ); + const auto f4 = mig.create_maj( a, !f1, b ); + + mig.create_po( f3 ); + mig.create_po( f4 ); + + fanout_view fanout_mig{ mig }; + + auto old = inv_opt_test::number_of_inverted( fanout_mig ); + mig_inv_optimization( fanout_mig, &st ); + auto newer = inv_opt_test::number_of_inverted( fanout_mig ); + CHECK( old - newer == 1 ); + CHECK( old - newer == st.total_gain ); +} + +TEST_CASE( "MIG inverter optimization constant input 0", "[mig_inv_optimization]" ) +{ + mig_network mig; + mig_inv_optimization_stats st; + + const auto a = mig.create_pi(); + const auto b = mig.create_pi(); + const auto c = mig.create_pi(); + + const auto f1 = mig.create_maj( !a, b, mig.get_constant( 0 ) ); + const auto f2 = mig.create_maj( !a, b, c ); + const auto f3 = mig.create_maj( a, !f1, f2 ); + + mig.create_po( f3 ); + + fanout_view fanout_mig{ mig }; + + auto old = inv_opt_test::number_of_inverted( fanout_mig ); + mig_inv_optimization( fanout_mig, &st ); + auto newer = inv_opt_test::number_of_inverted( fanout_mig ); + CHECK( old - newer == 1 ); + CHECK( old - newer == st.total_gain ); +} + +TEST_CASE( "MIG inverter optimization constant input 1", "[mig_inv_optimization]" ) +{ + mig_network mig; + mig_inv_optimization_stats st; + + const auto a = mig.create_pi(); + const auto b = mig.create_pi(); + const auto c = mig.create_pi(); + + const auto f1 = mig.create_maj( a, b, mig.get_constant( 1 ) ); + const auto f2 = mig.create_maj( !a, b, c ); + const auto f3 = mig.create_maj( a, !f1, f2 ); + const auto f4 = mig.create_maj( a, !f1, c ); + + mig.create_po( f3 ); + mig.create_po( f4 ); + + fanout_view fanout_mig{ mig }; + + auto old = inv_opt_test::number_of_inverted( fanout_mig ); + mig_inv_optimization( fanout_mig, &st ); + auto newer = inv_opt_test::number_of_inverted( fanout_mig ); + CHECK( old - newer == 0 ); + CHECK( old - newer == st.total_gain ); +} + +TEST_CASE( "MIG inverter optimization output", "[mig_inv_optimization]" ) +{ + mig_network mig; + mig_inv_optimization_stats st; + + const auto a = mig.create_pi(); + const auto b = mig.create_pi(); + const auto c = mig.create_pi(); + const auto d = mig.create_pi(); + + const auto f1 = mig.create_maj( !a, b, c ); + const auto f2 = mig.create_maj( !a, b, d ); + const auto f3 = mig.create_maj( a, !f1, f2 ); + + mig.create_po( f3 ); + mig.create_po( !f1 ); + + fanout_view fanout_mig{ mig }; + + auto old = inv_opt_test::number_of_inverted( fanout_mig ); + mig_inv_optimization( fanout_mig, &st ); + auto newer = inv_opt_test::number_of_inverted( fanout_mig ); + CHECK( old - newer == 1 ); + CHECK( old - newer == st.total_gain ); +} + +TEST_CASE( "MIG inverter optimization complex", "[mig_inv_optimization]" ) +{ + mig_network mig; + mig_inv_optimization_stats st; + + const auto zero = mig.get_constant( false ); + + const auto x1 = mig.create_pi(); + const auto x2 = mig.create_pi(); + const auto x3 = mig.create_pi(); + + const auto y1 = mig.create_maj( x1, !x2, x3 ); + const auto y2 = mig.create_maj( zero, !x2, x3 ); + + const auto z1 = mig.create_maj( y1, y2, !x3 ); + const auto z2 = mig.create_maj( x2, x3, !y2 ); + const auto z3 = mig.create_maj( zero, x1, !y2 ); + const auto z4 = mig.create_maj( x2, !y1, zero ); + const auto z5 = mig.create_maj( x1, !y1, zero ); + + const auto t1 = mig.create_maj( z1, z2, !z3 ); + const auto t2 = mig.create_maj( z1, !x1, zero ); + + mig.create_po( !t1 ); + mig.create_po( !t2 ); + mig.create_po( z4 ); + mig.create_po( z5 ); + + fanout_view fanout_mig{ mig }; + + auto old = inv_opt_test::number_of_inverted( fanout_mig ); + mig_inv_optimization( fanout_mig, &st ); + auto newer = inv_opt_test::number_of_inverted( fanout_mig ); + CHECK( old - newer == 11 - 4 ); + CHECK( old - newer == st.total_gain ); +} + +TEST_CASE( "MIG inverter two level", "[mig_inv_optimization]" ) +{ + mig_network mig; + mig_inv_optimization_stats st; + + const auto a = mig.create_pi(); + const auto b = mig.create_pi(); + const auto c = mig.create_pi(); + const auto d = mig.create_pi(); + + const auto f1 = mig.create_maj( !a, b, c ); + const auto f2 = mig.create_maj( !a, b, d ); + const auto f3 = mig.create_maj( !a, f1, f2 ); + const auto f4 = mig.create_maj( a, !f1, f2 ); + const auto f5 = mig.create_maj( a, !f1, f4 ); + + mig.create_po( !f3 ); + mig.create_po( f5 ); + + fanout_view fanout_mig{ mig }; + + auto old = inv_opt_test::number_of_inverted( fanout_mig ); + mig_inv_optimization( fanout_mig, &st ); + auto newer = inv_opt_test::number_of_inverted( fanout_mig ); + CHECK( old - newer == 2 ); + CHECK( old - newer == st.total_gain ); +} \ No newline at end of file diff --git a/test/algorithms/mig_inv_propagation.cpp b/test/algorithms/mig_inv_propagation.cpp new file mode 100644 index 000000000..991b20bea --- /dev/null +++ b/test/algorithms/mig_inv_propagation.cpp @@ -0,0 +1,187 @@ +#include + +#include +#include +#include + +using namespace mockturtle; + +namespace mockturtle +{ +namespace inv_prop_test +{ +template +int number_of_inverted( Ntk const& ntk ) +{ + int num_inverted{ 0 }; + ntk.foreach_gate( [&]( auto const& n ) { + ntk.foreach_fanin( n, [&]( auto const& f ) { + if ( ntk.is_dead( ntk.get_node( f ) ) ) + { + std::cerr << "dead node " << ntk.get_node( f ) << std::endl; + return; + } + if ( ntk.is_constant( ntk.get_node( f ) ) || ntk.is_pi( ntk.get_node( f ) ) ) + { + return; + } + if ( ntk.is_complemented( f ) ) + { + num_inverted++; + } + } ); + } ); + ntk.foreach_po( [&]( auto const& f ) { + if ( ntk.is_complemented( f ) ) + { + num_inverted++; + } + } ); + return num_inverted; +} +} // namespace inv_prop_test +} // namespace mockturtle + +TEST_CASE( "MIG inverter propagation basic", "[mig_inv_propagation]" ) +{ + mig_network mig; + mig_inv_propagation_stats st; + + const auto a = mig.create_pi(); + const auto b = mig.create_pi(); + const auto c = mig.create_pi(); + const auto d = mig.create_pi(); + + const auto f1 = mig.create_maj( !a, b, c ); + const auto f2 = mig.create_maj( !a, b, d ); + const auto f3 = mig.create_maj( a, !f1, f2 ); + const auto f4 = mig.create_maj( a, !f1, b ); + + mig.create_po( f3 ); + mig.create_po( f4 ); + + mig_inv_propagation( mig, &st ); + auto inv_count = inv_prop_test::number_of_inverted( mig ); + CHECK( inv_count == 0 ); +} + +TEST_CASE( "MIG inverter propagation constant input 0", "[mig_inv_propagation]" ) +{ + mig_network mig; + mig_inv_propagation_stats st; + + const auto a = mig.create_pi(); + const auto b = mig.create_pi(); + const auto c = mig.create_pi(); + + const auto f1 = mig.create_maj( !a, b, mig.get_constant( 0 ) ); + const auto f2 = mig.create_maj( !a, b, c ); + const auto f3 = mig.create_maj( a, !f1, f2 ); + + mig.create_po( f3 ); + + mig_inv_propagation( mig, &st ); + auto inv_count = inv_prop_test::number_of_inverted( mig ); + CHECK( inv_count == 0 ); +} + +TEST_CASE( "MIG inverter propagation constant input 1", "[mig_inv_propagation]" ) +{ + mig_network mig; + mig_inv_propagation_stats st; + + const auto a = mig.create_pi(); + const auto b = mig.create_pi(); + const auto c = mig.create_pi(); + + const auto f1 = mig.create_maj( a, b, mig.get_constant( 1 ) ); + const auto f2 = mig.create_maj( !a, b, c ); + const auto f3 = mig.create_maj( a, !f1, f2 ); + const auto f4 = mig.create_maj( a, !f1, c ); + + mig.create_po( f3 ); + mig.create_po( f4 ); + + mig_inv_propagation( mig, &st ); + auto inv_count = inv_prop_test::number_of_inverted( mig ); + CHECK( inv_count == 0 ); +} + +TEST_CASE( "MIG inverter propagation output", "[mig_inv_propagation]" ) +{ + mig_network mig; + mig_inv_propagation_stats st; + + const auto a = mig.create_pi(); + const auto b = mig.create_pi(); + const auto c = mig.create_pi(); + const auto d = mig.create_pi(); + + const auto f1 = mig.create_maj( !a, b, c ); + const auto f2 = mig.create_maj( !a, b, d ); + const auto f3 = mig.create_maj( a, !f1, f2 ); + + mig.create_po( f3 ); + mig.create_po( !f1 ); + + mig_inv_propagation( mig, &st ); + auto inv_count = inv_prop_test::number_of_inverted( mig ); + CHECK( inv_count == 0 ); +} + +TEST_CASE( "MIG inverter propagation complex", "[mig_inv_propagation]" ) +{ + mig_network mig; + mig_inv_propagation_stats st; + + const auto zero = mig.get_constant( false ); + + const auto x1 = mig.create_pi(); + const auto x2 = mig.create_pi(); + const auto x3 = mig.create_pi(); + + const auto y1 = mig.create_maj( x1, !x2, x3 ); + const auto y2 = mig.create_maj( zero, !x2, x3 ); + + const auto z1 = mig.create_maj( y1, y2, !x3 ); + const auto z2 = mig.create_maj( x2, x3, !y2 ); + const auto z3 = mig.create_maj( zero, x1, !y2 ); + const auto z4 = mig.create_maj( x2, !y1, zero ); + const auto z5 = mig.create_maj( x1, !y1, zero ); + + const auto t1 = mig.create_maj( z1, z2, !z3 ); + const auto t2 = mig.create_maj( z1, !x1, zero ); + + mig.create_po( !t1 ); + mig.create_po( !t2 ); + mig.create_po( z4 ); + mig.create_po( z5 ); + + mig_inv_propagation( mig, &st ); + auto inv_count = inv_prop_test::number_of_inverted( mig ); + CHECK( inv_count == 0 ); +} + +TEST_CASE( "MIG inverter propagation two level", "[mig_inv_propagation]" ) +{ + mig_network mig; + mig_inv_propagation_stats st; + + const auto a = mig.create_pi(); + const auto b = mig.create_pi(); + const auto c = mig.create_pi(); + const auto d = mig.create_pi(); + + const auto f1 = mig.create_maj( !a, b, c ); + const auto f2 = mig.create_maj( !a, b, d ); + const auto f3 = mig.create_maj( !a, f1, f2 ); + const auto f4 = mig.create_maj( a, !f1, f2 ); + const auto f5 = mig.create_maj( a, !f1, f4 ); + + mig.create_po( !f3 ); + mig.create_po( f5 ); + + mig_inv_propagation( mig, &st ); + auto inv_count = inv_prop_test::number_of_inverted( mig ); + CHECK( inv_count == 0 ); +} \ No newline at end of file