From 6efb33be063bd9fafe297daf73d951deda2365bd Mon Sep 17 00:00:00 2001 From: Alessandro Tempia Calvino <44085674+aletempiac@users.noreply.github.com> Date: Mon, 19 Feb 2024 18:33:53 +0100 Subject: [PATCH] Fixing non-simplified gates handling in networks (#632) * Improving scalability of AIG and XAG balancing * Fixing issues in substitute_node_no_restrash that may create buffer nodes, which are seen as PIs --- .../mockturtle/algorithms/aig_balancing.hpp | 67 ++++++++- .../mockturtle/algorithms/xag_balancing.hpp | 131 ++++++++++++++++-- include/mockturtle/networks/aig.hpp | 6 +- include/mockturtle/networks/mig.hpp | 6 +- include/mockturtle/networks/xag.hpp | 35 +++-- include/mockturtle/networks/xmg.hpp | 27 +++- test/algorithms/aig_balancing.cpp | 52 +++++++ test/algorithms/xag_balancing.cpp | 81 +++++++++++ 8 files changed, 374 insertions(+), 31 deletions(-) diff --git a/include/mockturtle/algorithms/aig_balancing.hpp b/include/mockturtle/algorithms/aig_balancing.hpp index 758284e15..f782514f7 100644 --- a/include/mockturtle/algorithms/aig_balancing.hpp +++ b/include/mockturtle/algorithms/aig_balancing.hpp @@ -49,6 +49,9 @@ struct aig_balancing_params { /*! \brief Minimizes the number of levels. */ bool minimize_levels{ true }; + + /*! \brief Use fast version, it may not find some area optimizations. */ + bool fast_mode{ true }; }; namespace detail @@ -131,10 +134,20 @@ class aig_balance_impl while ( storage[level].size() > 1 ) { /* explore multiple possibilities to find logic sharing */ - if ( ps.minimize_levels ) - pick_nodes( storage[level], find_left_most_at_level( storage[level] ) ); + if ( ps.fast_mode ) + { + if ( ps.minimize_levels ) + pick_nodes_fast( storage[level], find_left_most_at_level( storage[level] ) ); + else + pick_nodes_area_fast( storage[level] ); + } else - pick_nodes_area( storage[level] ); + { + if ( ps.minimize_levels ) + pick_nodes( storage[level], find_left_most_at_level( storage[level] ) ); + else + pick_nodes_area( storage[level] ); + } /* pop the two selected nodes to create the new AND gate */ signal child1 = storage[level].back(); @@ -235,7 +248,7 @@ class aig_balance_impl return pointer; } - void pick_nodes( std::vector& leaves, size_t left_most ) + inline void pick_nodes( std::vector& leaves, size_t left_most ) { size_t right_most = leaves.size() - 2; @@ -269,7 +282,29 @@ class aig_balance_impl } } - void pick_nodes_area( std::vector& leaves ) + inline void pick_nodes_fast( std::vector& leaves, size_t left_most ) + { + size_t left_pointer = leaves.size() - 1; + while ( left_pointer-- > left_most ) + { + /* select if node exists */ + std::optional pnode = ntk.has_and( leaves.back(), leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + + inline void pick_nodes_area( std::vector& leaves ) { for ( size_t right_pointer = leaves.size() - 1; right_pointer > 0; --right_pointer ) { @@ -296,6 +331,28 @@ class aig_balance_impl } } + inline void pick_nodes_area_fast( std::vector& leaves ) + { + size_t left_pointer = leaves.size() - 1; + while ( left_pointer-- > 0 ) + { + /* select if node exists */ + std::optional pnode = ntk.has_and( leaves.back(), leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + void insert_node_sorted( std::vector& leaves, signal const& f ) { node n = ntk.get_node( f ); diff --git a/include/mockturtle/algorithms/xag_balancing.hpp b/include/mockturtle/algorithms/xag_balancing.hpp index 6bac3cea4..0b109e90e 100644 --- a/include/mockturtle/algorithms/xag_balancing.hpp +++ b/include/mockturtle/algorithms/xag_balancing.hpp @@ -49,6 +49,9 @@ struct xag_balancing_params { /*! \brief Minimizes the number of levels. */ bool minimize_levels{ true }; + + /*! \brief Use fast version, it may not find some area optimizations. */ + bool fast_mode{ true }; }; namespace detail @@ -150,10 +153,20 @@ class xag_balance_impl while ( storage[level].size() > 1 ) { /* explore multiple possibilities to find logic sharing */ - if ( ps.minimize_levels ) - pick_nodes_and( storage[level], find_left_most_at_level( storage[level] ) ); + if ( ps.fast_mode ) + { + if ( ps.minimize_levels ) + pick_nodes_and_fast( storage[level], find_left_most_at_level( storage[level] ) ); + else + pick_nodes_and_area_fast( storage[level] ); + } else - pick_nodes_and_area( storage[level] ); + { + if ( ps.minimize_levels ) + pick_nodes_and( storage[level], find_left_most_at_level( storage[level] ) ); + else + pick_nodes_and_area( storage[level] ); + } /* pop the two selected nodes to create the new AND gate */ signal child1 = storage[level].back(); @@ -174,10 +187,20 @@ class xag_balance_impl while ( storage[level].size() > 1 ) { /* explore multiple possibilities to find logic sharing */ - if ( ps.minimize_levels ) - pick_nodes_xor( storage[level], find_left_most_at_level( storage[level] ) ); + if ( ps.fast_mode ) + { + if ( ps.minimize_levels ) + pick_nodes_xor_fast( storage[level], find_left_most_at_level( storage[level] ) ); + else + pick_nodes_xor_area_fast( storage[level] ); + } else - pick_nodes_xor_area( storage[level] ); + { + if ( ps.minimize_levels ) + pick_nodes_xor( storage[level], find_left_most_at_level( storage[level] ) ); + else + pick_nodes_xor_area( storage[level] ); + } /* pop the two selected nodes to create the new XOR gate */ signal child1 = storage[level].back(); @@ -332,7 +355,7 @@ class xag_balance_impl return pointer; } - void pick_nodes_and( std::vector& leaves, size_t left_most ) + inline void pick_nodes_and( std::vector& leaves, size_t left_most ) { size_t right_most = leaves.size() - 2; @@ -366,7 +389,29 @@ class xag_balance_impl } } - void pick_nodes_and_area( std::vector& leaves ) + inline void pick_nodes_and_fast( std::vector& leaves, size_t left_most ) + { + size_t left_pointer = leaves.size() - 1; + while ( left_pointer-- > left_most ) + { + /* select if node exists */ + std::optional pnode = ntk.has_and( leaves.back(), leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + + inline void pick_nodes_and_area( std::vector& leaves ) { for ( size_t right_pointer = leaves.size() - 1; right_pointer > 0; --right_pointer ) { @@ -393,7 +438,29 @@ class xag_balance_impl } } - void pick_nodes_xor( std::vector& leaves, size_t left_most ) + inline void pick_nodes_and_area_fast( std::vector& leaves ) + { + size_t left_pointer = leaves.size() - 1; + while ( left_pointer-- > 0 ) + { + /* select if node exists */ + std::optional pnode = ntk.has_and( leaves.back(), leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + + inline void pick_nodes_xor( std::vector& leaves, size_t left_most ) { size_t right_most = leaves.size() - 2; @@ -427,7 +494,29 @@ class xag_balance_impl } } - void pick_nodes_xor_area( std::vector& leaves ) + inline void pick_nodes_xor_fast( std::vector& leaves, size_t left_most ) + { + size_t left_pointer = leaves.size() - 1; + while ( left_pointer-- > left_most ) + { + /* select if node exists */ + std::optional pnode = ntk.has_xor( leaves.back(), leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + + inline void pick_nodes_xor_area( std::vector& leaves ) { for ( size_t right_pointer = leaves.size() - 1; right_pointer > 0; --right_pointer ) { @@ -454,6 +543,28 @@ class xag_balance_impl } } + inline void pick_nodes_xor_area_fast( std::vector& leaves ) + { + size_t left_pointer = leaves.size() - 1; + while ( left_pointer-- > 0 ) + { + /* select if node exists */ + std::optional pnode = ntk.has_xor( leaves.back(), leaves[left_pointer] ); + if ( pnode.has_value() ) + { + /* already present in TFI */ + if ( ntk.visited( ntk.get_node( *pnode ) ) == ntk.trav_id() ) + { + continue; + } + + if ( leaves[left_pointer] != leaves[leaves.size() - 2] ) + std::swap( leaves[left_pointer], leaves[leaves.size() - 2] ); + break; + } + } + } + void insert_node_sorted_and( std::vector& leaves, signal const& f ) { node n = ntk.get_node( f ); diff --git a/include/mockturtle/networks/aig.hpp b/include/mockturtle/networks/aig.hpp index 65372ed73..c8c4643a9 100644 --- a/include/mockturtle/networks/aig.hpp +++ b/include/mockturtle/networks/aig.hpp @@ -83,6 +83,7 @@ struct aig_hash `data[0].h1`: Fan-out size (we use MSB to indicate whether a node is dead) `data[0].h2`: Application-specific value `data[1].h1`: Visited flag + `data[1].h2`: Is terminal node (PI or CI) */ using aig_storage = storage, empty_storage_data, @@ -206,6 +207,7 @@ class aig_network const auto index = _storage->nodes.size(); auto& node = _storage->nodes.emplace_back(); node.children[0].data = node.children[1].data = _storage->inputs.size(); + node.data[1].h2 = 1; // mark as PI _storage->inputs.emplace_back( index ); return { index, 0 }; } @@ -231,12 +233,12 @@ class aig_network bool is_ci( node const& n ) const { - return _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data; + return _storage->nodes[n].data[1].h2 == 1; } bool is_pi( node const& n ) const { - return _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data && !is_constant( n ); + return _storage->nodes[n].data[1].h2 == 1 && !is_constant( n ); } bool constant_value( node const& n ) const diff --git a/include/mockturtle/networks/mig.hpp b/include/mockturtle/networks/mig.hpp index bc9babadb..c8646ac28 100644 --- a/include/mockturtle/networks/mig.hpp +++ b/include/mockturtle/networks/mig.hpp @@ -67,6 +67,7 @@ namespace mockturtle `data[0].h1`: Fan-out size (we use MSB to indicate whether a node is dead) `data[0].h2`: Application-specific value `data[1].h1`: Visited flag + `data[1].h2`: Is terminal node (PI or CI) */ using mig_storage = storage>; @@ -187,6 +188,7 @@ class mig_network const auto index = _storage->nodes.size(); auto& node = _storage->nodes.emplace_back(); node.children[0].data = node.children[1].data = node.children[2].data = _storage->inputs.size(); + node.data[1].h2 = 1; // mark as PI _storage->inputs.emplace_back( index ); return { index, 0 }; } @@ -212,12 +214,12 @@ class mig_network bool is_ci( node const& n ) const { - return _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data && _storage->nodes[n].children[0].data == _storage->nodes[n].children[2].data; + return _storage->nodes[n].data[1].h2 == 1; } bool is_pi( node const& n ) const { - return _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data && _storage->nodes[n].children[0].data == _storage->nodes[n].children[2].data && !is_constant( n ); + return _storage->nodes[n].data[1].h2 == 1 && !is_constant( n ); } bool constant_value( node const& n ) const diff --git a/include/mockturtle/networks/xag.hpp b/include/mockturtle/networks/xag.hpp index cd46ee11b..17606a28d 100644 --- a/include/mockturtle/networks/xag.hpp +++ b/include/mockturtle/networks/xag.hpp @@ -82,6 +82,7 @@ struct xag_hash `data[0].h1`: Fan-out size (we use MSB to indicate whether a node is dead) `data[0].h2`: Application-specific value `data[1].h1`: Visited flag + `data[1].h2`: Is terminal node (PI or CI) */ using xag_storage = storage, empty_storage_data, @@ -204,6 +205,7 @@ class xag_network const auto index = _storage->nodes.size(); auto& node = _storage->nodes.emplace_back(); node.children[0].data = node.children[1].data = _storage->inputs.size(); + node.data[1].h2 = 1; // mark as PI _storage->inputs.emplace_back( index ); return { index, 0 }; } @@ -229,12 +231,12 @@ class xag_network bool is_ci( node const& n ) const { - return _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data; + return _storage->nodes[n].data[1].h2 == 1; } bool is_pi( node const& n ) const { - return _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data && !is_constant( n ); + return _storage->nodes[n].data[1].h2 == 1 && !is_constant( n ); } bool constant_value( node const& n ) const @@ -524,7 +526,7 @@ class xag_network } // determine gate type of n - auto _is_and = node.children[0].index < node.children[1].index; + auto _is_and = node.children[0].index <= node.children[1].index; // determine potential new children of node n signal child1 = new_signal; @@ -610,7 +612,7 @@ class xag_network } // determine gate type of n - auto _is_and = node.children[0].index < node.children[1].index; + auto _is_and = node.children[0].index <= node.children[1].index; // determine potential new children of node n signal child1 = new_signal; @@ -621,6 +623,21 @@ class xag_network std::swap( child0, child1 ); } + // if a buffer is created adjust the polarities + if ( child0.index == child1.index && !_is_and ) + { + if ( child0.complement == child1.complement ) + { + child0.data = 0; // the buffer is a constant zero + child1.data = 0; // the buffer is a constant zero + } + else + { + child0.data = 1; // the buffer is a constant one + child1.data = 1; // the buffer is a constant zero + } + } + // don't check for trivial cases // remember before @@ -859,7 +876,7 @@ class xag_network bool is_and( node const& n ) const { - return n > 0 && !is_ci( n ) && ( _storage->nodes[n].children[0].index < _storage->nodes[n].children[1].index ); + return n > 0 && !is_ci( n ) && ( _storage->nodes[n].children[0].index <= _storage->nodes[n].children[1].index ); } bool is_or( node const& n ) const @@ -914,7 +931,7 @@ class xag_network kitty::dynamic_truth_table node_function( const node& n ) const { kitty::dynamic_truth_table _func( 2 ); - if ( _storage->nodes[n].children[0u].index < _storage->nodes[n].children[1u].index ) + if ( _storage->nodes[n].children[0u].index <= _storage->nodes[n].children[1u].index ) { _func._bits[0] = 0x8; return _func; @@ -1115,7 +1132,7 @@ class xag_network auto v1 = *begin++; auto v2 = *begin++; - if ( c1.index < c2.index ) + if ( c1.index <= c2.index ) { return ( v1 ^ c1.weight ) && ( v2 ^ c2.weight ); } @@ -1139,7 +1156,7 @@ class xag_network auto tt1 = *begin++; auto tt2 = *begin++; - if ( c1.index < c2.index ) + if ( c1.index <= c2.index ) { return ( c1.weight ? ~tt1 : tt1 ) & ( c2.weight ? ~tt2 : tt2 ); } @@ -1170,7 +1187,7 @@ class xag_network assert( result.num_blocks() == tt1.num_blocks() || ( result.num_blocks() == tt1.num_blocks() - 1 && result.num_bits() % 64 == 0 ) ); result.resize( tt1.num_bits() ); - if ( c1.index < c2.index ) + if ( c1.index <= c2.index ) { result._bits.back() = ( c1.weight ? ~( tt1._bits.back() ) : tt1._bits.back() ) & ( c2.weight ? ~( tt2._bits.back() ) : tt2._bits.back() ); } diff --git a/include/mockturtle/networks/xmg.hpp b/include/mockturtle/networks/xmg.hpp index 074012913..536b68e7d 100644 --- a/include/mockturtle/networks/xmg.hpp +++ b/include/mockturtle/networks/xmg.hpp @@ -66,6 +66,7 @@ namespace mockturtle `data[0].h1`: Fan-out size (we use MSB to indicate whether a node is dead) `data[0].h2`: Application-specific value `data[1].h1`: Visited flag + `data[1].h2`: Is terminal node (PI or CI) */ using xmg_storage = storage>; @@ -186,6 +187,7 @@ class xmg_network const auto index = _storage->nodes.size(); auto& node = _storage->nodes.emplace_back(); node.children[0].data = node.children[1].data = node.children[2].data = _storage->inputs.size(); + node.data[1].h2 = 1; // mark as PI _storage->inputs.emplace_back( index ); return { index, 0 }; } @@ -211,12 +213,12 @@ class xmg_network bool is_ci( node const& n ) const { - return _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data && _storage->nodes[n].children[0].data == _storage->nodes[n].children[2].data; + return _storage->nodes[n].data[1].h2 == 1; } bool is_pi( node const& n ) const { - return _storage->nodes[n].children[0].data == _storage->nodes[n].children[1].data && _storage->nodes[n].children[0].data == _storage->nodes[n].children[2].data && !is_constant( n ); + return _storage->nodes[n].data[1].h2 == 1 && !is_constant( n ); } bool constant_value( node const& n ) const @@ -785,6 +787,25 @@ class xmg_network assert( child0.index >= child1.index ); assert( child1.index >= child2.index ); + + // check same fanins: transform the XOR3 into a MAJ3 + if ( child0.index == child1.index ) + { + child0.index = child2.index; + child1.index = child2.index; + if ( child0.complement == child1.complement ) + { + child0.complement = child2.complement; + child1.complement = child2.complement; + } + else + { + child0.complement = !child2.complement; + child1.complement = !child2.complement; + } + + _is_maj = true; + } } // normalize complemented edges @@ -1065,7 +1086,7 @@ class xmg_network bool is_maj( node const& n ) const { - return n > 0 && !is_ci( n ) && _storage->nodes[n].children[0].index < _storage->nodes[n].children[1].index; + return n > 0 && !is_ci( n ) && _storage->nodes[n].children[0].index <= _storage->nodes[n].children[1].index; } bool is_ite( node const& n ) const diff --git a/test/algorithms/aig_balancing.cpp b/test/algorithms/aig_balancing.cpp index f8a6f6d9e..58922e1e6 100644 --- a/test/algorithms/aig_balancing.cpp +++ b/test/algorithms/aig_balancing.cpp @@ -53,6 +53,34 @@ TEST_CASE( "Balance AND finding structural hashing", "[aig_balancing]" ) CHECK( aig.num_gates() == 3u ); } +TEST_CASE( "Balance AND finding structural hashing slow", "[aig_balancing]" ) +{ + aig_network aig; + const auto a = aig.create_pi(); + const auto b = aig.create_pi(); + const auto c = aig.create_pi(); + const auto d = aig.create_pi(); + + const auto f1 = aig.create_and( a, b ); + const auto f2 = aig.create_and( f1, c ); + const auto f3 = aig.create_and( b, c ); + const auto f4 = aig.create_and( f3, d ); + + aig.create_po( f2 ); + aig.create_po( f4 ); + + CHECK( depth_view{ aig }.depth() == 2u ); + CHECK( aig.num_gates() == 4u ); + + aig_balancing_params ps; + ps.minimize_levels = false; + ps.fast_mode = false; + aig_balance( aig, ps ); + + CHECK( depth_view{ aig }.depth() == 2u ); + CHECK( aig.num_gates() == 3u ); +} + TEST_CASE( "Balance AND tree that is constant 0", "[aig_balancing]" ) { aig_network aig; @@ -96,3 +124,27 @@ TEST_CASE( "Balance AND tree that has redundant leaves", "[aig_balancing]" ) CHECK( depth_view{ aig }.depth() == 2u ); CHECK( aig.num_gates() == 2u ); } + +TEST_CASE( "Balance AND tree that has redundant leaves slow", "[aig_balancing]" ) +{ + aig_network aig; + const auto a = aig.create_pi(); + const auto b = aig.create_pi(); + const auto c = aig.create_pi(); + + const auto f1 = aig.create_and( a, b ); + const auto f2 = aig.create_and( a, c ); + const auto f3 = aig.create_and( f1, f2 ); + + aig.create_po( f3 ); + + CHECK( depth_view{ aig }.depth() == 2u ); + CHECK( aig.num_gates() == 3u ); + + aig_balancing_params ps; + ps.fast_mode = false; + aig_balance( aig, ps ); + + CHECK( depth_view{ aig }.depth() == 2u ); + CHECK( aig.num_gates() == 2u ); +} diff --git a/test/algorithms/xag_balancing.cpp b/test/algorithms/xag_balancing.cpp index e20dcfa85..6c4f103d1 100644 --- a/test/algorithms/xag_balancing.cpp +++ b/test/algorithms/xag_balancing.cpp @@ -53,6 +53,35 @@ TEST_CASE( "Balance AND finding structural hashing (XAG)", "[xag_balancing]" ) CHECK( xag.num_gates() == 3u ); } +TEST_CASE( "Balance AND finding structural hashing (XAG) slow", "[xag_balancing]" ) +{ + xag_network xag; + const auto a = xag.create_pi(); + const auto b = xag.create_pi(); + const auto c = xag.create_pi(); + const auto d = xag.create_pi(); + + const auto f1 = xag.create_and( a, b ); + const auto f2 = xag.create_and( f1, c ); + const auto f3 = xag.create_and( b, c ); + const auto f4 = xag.create_and( f3, d ); + + xag.create_po( f2 ); + xag.create_po( f4 ); + + CHECK( depth_view{ xag }.depth() == 2u ); + CHECK( xag.num_gates() == 4u ); + + xag_balancing_params ps; + ps.minimize_levels = false; + ps.fast_mode = false; + + xag_balance( xag, ps ); + + CHECK( depth_view{ xag }.depth() == 2u ); + CHECK( xag.num_gates() == 3u ); +} + TEST_CASE( "Balance AND tree that is constant 0 (XAG)", "[xag_balancing]" ) { xag_network xag; @@ -97,6 +126,30 @@ TEST_CASE( "Balance AND tree that has redundant leaves (XAG)", "[xag_balancing]" CHECK( xag.num_gates() == 2u ); } +TEST_CASE( "Balance AND tree that has redundant leaves (XAG) slow", "[xag_balancing]" ) +{ + xag_network xag; + const auto a = xag.create_pi(); + const auto b = xag.create_pi(); + const auto c = xag.create_pi(); + + const auto f1 = xag.create_and( a, b ); + const auto f2 = xag.create_and( a, c ); + const auto f3 = xag.create_and( f1, f2 ); + + xag.create_po( f3 ); + + CHECK( depth_view{ xag }.depth() == 2u ); + CHECK( xag.num_gates() == 3u ); + + xag_balancing_params ps; + ps.fast_mode = false; + xag_balance( xag, ps ); + + CHECK( depth_view{ xag }.depth() == 2u ); + CHECK( xag.num_gates() == 2u ); +} + TEST_CASE( "Balancing XOR chain", "[xag_balancing]" ) { xag_network xag; @@ -141,6 +194,34 @@ TEST_CASE( "Balance XOR finding structural hashing", "[xag_balancing]" ) CHECK( xag.num_gates() == 3u ); } +TEST_CASE( "Balance XOR finding structural hashing slow", "[xag_balancing]" ) +{ + xag_network xag; + const auto a = xag.create_pi(); + const auto b = xag.create_pi(); + const auto c = xag.create_pi(); + const auto d = xag.create_pi(); + + const auto f1 = xag.create_xor( a, b ); + const auto f2 = xag.create_xor( f1, c ); + const auto f3 = xag.create_xor( b, c ); + const auto f4 = xag.create_xor( f3, d ); + + xag.create_po( f2 ); + xag.create_po( f4 ); + + CHECK( depth_view{ xag }.depth() == 2u ); + CHECK( xag.num_gates() == 4u ); + + xag_balancing_params ps; + ps.minimize_levels = false; + ps.fast_mode = false; + xag_balance( xag, ps ); + + CHECK( depth_view{ xag }.depth() == 2u ); + CHECK( xag.num_gates() == 3u ); +} + TEST_CASE( "Balance XOR tree that has redundant leaves", "[xag_balancing]" ) { xag_network xag;